diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ce1906..74bd57f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## TODO - Parse __more__ binary operators +- Parse `Type name = value` bindings - Parse more complex bindings - Watch mode - Improve error messages @@ -21,7 +22,7 @@ - [x] Parse function call parameters - [x] Codegen function call parameters -- [ ] Parse function declaration arguments +- [x] Parse function declaration arguments (Type id) - [ ] Begin work on semantic analysis - [ ] Symbol table - [ ] Typecheck bindings diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index 1a309c9..cbd6da1 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -29,6 +29,11 @@ pub struct Block { #[derive(Debug)] pub struct ParamsList {} +pub struct Parameter { + pub identifier: Box, + pub datatype: Box, +} + #[derive(Debug)] pub enum Expression { Number(Box), diff --git a/src/syntax/params_list.rs b/src/syntax/params_list.rs index ca57547..43c8804 100644 --- a/src/syntax/params_list.rs +++ b/src/syntax/params_list.rs @@ -4,7 +4,10 @@ use crate::{ syntax::utils::parse_token_type, }; -use super::{ast::ParamsList, ParseResult}; +use super::{ + ast::{Parameter, ParamsList}, + utils, ParseResult, +}; pub fn parse_params_list<'a>( tokens: &'a Vec, @@ -21,6 +24,36 @@ pub fn parse_params_list<'a>( }; current_pos = next_pos; + // Parse parameters definitions, separated by commas + let mut parameters = Vec::::new(); + loop { + let (next_parameter, next_pos) = match parse_param_definition(tokens, current_pos) { + ParseResult::Ok(parameter, next_pos) => (parameter, next_pos), + ParseResult::Err(error) => { + return ParseResult::Err(error); + } + _ => break, + }; + current_pos = next_pos; + parameters.push(next_parameter); + + // Parse comma. This also parses a trailing comma + match parse_token_type(tokens, current_pos, TokenType::Comma) { + ParseResult::Ok(_, next) => { + current_pos = next; + } + // This should never happen + ParseResult::Err(err) => return ParseResult::Err(err), + ParseResult::Mismatch(_) => { + // Something other than a comma was found. It must be a closing paren ) + // Still, break the loop, assume there are no more arguments + // TODO: This could be a good place to write a detailed error? + break; + } + ParseResult::Unmatched => break, + }; + } + // Parse closing paren let (_closing_paren, next_pos) = match parse_token_type(tokens, current_pos, TokenType::RightParen) { @@ -45,3 +78,60 @@ pub fn parse_params_list<'a>( ParseResult::Ok(ParamsList {}, current_pos) } + +fn parse_param_definition<'a>( + tokens: &'a Vec, + pos: usize, +) -> ParseResult { + // Parse a single parameter definition of the form: + // - Type identifier + // There will be more constructs in the future, like: + // - Type identifier = default_value + // - FunctionType identifier + // - Pattern identifier (e.g. Some[String] value)? + + let mut current_pos = pos; + let (datatype, next_pos) = + match utils::parse_token_type(tokens, current_pos, TokenType::Datatype) { + ParseResult::Ok(token, next) => (token, next), + ParseResult::Err(err) => { + return ParseResult::Err(err); + } + // If there is no datatype this construction doesn't apply. + // Return a mismatch and let the caller handle it + ParseResult::Mismatch(t) => return ParseResult::Mismatch(t), + ParseResult::Unmatched => return ParseResult::Unmatched, + }; + current_pos = next_pos; + + let (identifier, next_pos) = + match utils::parse_token_type(tokens, current_pos, TokenType::Identifier) { + ParseResult::Ok(token, next) => (token, next), + ParseResult::Err(err) => { + return ParseResult::Err(err); + } + // However, if we fail to parse an identifier, it's an error + ParseResult::Mismatch(_) => { + return ParseResult::Err(SyntaxError { + reason: String::from("Expected an identifier for the parameter."), + error_start: tokens[pos].position, + error_end: tokens[pos].get_end_position(), + }); + } + ParseResult::Unmatched => { + return ParseResult::Err(SyntaxError { + reason: String::from("Expected an identifier for the parameter."), + error_start: tokens[pos].position, + error_end: tokens[pos].get_end_position(), + }) + } + }; + + ParseResult::Ok( + Parameter { + identifier: Box::new(identifier.value.clone()), + datatype: Box::new(datatype.value.clone()), + }, + next_pos, + ) +}