diff --git a/CHANGELOG.md b/CHANGELOG.md index a10f437..d3b0c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,28 @@ ## TODO -- Implement functions as first class citizens -- Parse __more__ binary operators -- Parse more complex bindings -- Namespace identifiers in the symbol table -- Stdlib -- Document code -- Watch mode -- Simple language server -- Decide how to handle comments in the syntax (?)(should comments mean something like in rust?) -- Abstract the parsing of datatypes, such that in the future generics can be implemented in a single place -- Begin work on the code formatter -- Remove all panic! and todo! -- Change REPL to execute code only after `;;` is found -- Forward the code generated by the REPL to the PHP repl -- Test assignment parsing +- [ ] Implement functions as first class citizens +- [ ] Parse __more__ binary operators +- [ ] Parse more complex bindings +- [ ] Namespace identifiers in the symbol table +- [ ] Stdlib +- [ ] Document code +- [ ] Watch mode +- [ ] Simple language server +- [ ] Decide how to handle comments in the syntax (?)(should comments mean something like in rust?) +- [ ] Abstract the parsing of datatypes, such that in the future generics can be implemented in a single place +- [ ] Begin work on the code formatter +- [ ] Remove all panic! and todo! +- [ ] Change REPL to execute code only after `;;` is found +- [ ] Forward the code generated by the REPL to the PHP repl +- [ ] Test assignment parsing + + +## v0.1.4 + +- [ ] Parse obj/map/dict syntax +- [ ] Parse tuple syntax +- [ ] Parse class instantiation syntax ## v0.1.3 @@ -25,9 +32,9 @@ - [ ] Generate php code from current AST - [x] Test correct operator precedence - [x] Parse assignments -- [ ] Parse dot `.` operator +- [x] Parse dot `.` operator - [ ] Parse logic operators `&& ||` -- [ ] Parse Array access `arr[pos]` +- [x] Parse Array access `arr[pos]` - [ ] Parse namespace operator `::` - [ ] Implement subtyping for numbers diff --git a/src/error_handling/error_messages.rs b/src/error_handling/error_messages.rs index ea01446..93d1fa7 100644 --- a/src/error_handling/error_messages.rs +++ b/src/error_handling/error_messages.rs @@ -24,6 +24,7 @@ pub const COMPILER_TODO: u32 = 20; pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21; pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22; pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23; +pub const SYNTAX_INVALID_ARRAY_ACCESS: u32 = 24; /// Reads the error codes from the error code list pub fn error_code_to_string() -> String { diff --git a/src/php_ast/transformers/expression.rs b/src/php_ast/transformers/expression.rs index 020e2e2..5542f9a 100644 --- a/src/php_ast/transformers/expression.rs +++ b/src/php_ast/transformers/expression.rs @@ -45,6 +45,7 @@ impl<'a> PHPTransformable<'a> for Expression<'_> { PExpresssion::BinaryOp(Box::new(left_value), Box::new(right_value), &op.value) } Expression::Array(_) => unimplemented!("transform array into php"), + Expression::ArrayAcccess(_) => unimplemented!("transform array access into php"), } } } diff --git a/src/semantic/checks/expression/mod.rs b/src/semantic/checks/expression/mod.rs index f935f8f..9e3dd39 100644 --- a/src/semantic/checks/expression/mod.rs +++ b/src/semantic/checks/expression/mod.rs @@ -262,6 +262,7 @@ impl SemanticCheck for Expression<'_> { Ok(()) } + Expression::ArrayAcccess(_array_access) => unimplemented!("typecheck array access"), } } } diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index df72d2c..2c1c0bf 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -219,6 +219,7 @@ impl Typed for Expression<'_> { let first_type = arr.exps[0].get_type(scope)?; Ok(Type::Generic("Array".into(), vec![first_type])) } + Expression::ArrayAcccess(_array_access) => unimplemented!("typecheck array access"), } } } diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index cd67556..3cc7d33 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -121,6 +121,7 @@ pub enum Expression<'a> { /// left expression, right expression, operator BinaryOperator(Box>, Box>, &'a Token), Array(Array<'a>), + ArrayAcccess(ArrayAccess<'a>), } #[derive(Debug)] @@ -132,6 +133,13 @@ pub struct Array<'a> { pub end: usize, } +#[derive(Debug)] +pub struct ArrayAccess<'a> { + pub left_expr: Box>, + pub idx_expr: Box>, + pub end_pos: usize, +} + impl Positionable for Expression<'_> { /// Returns the absolute start and end position /// of this expression @@ -158,6 +166,10 @@ impl Positionable for Expression<'_> { end, exps: _, }) => (*start, *end), + Expression::ArrayAcccess(a) => { + let (start, _) = a.left_expr.get_position(); + (start, a.end_pos) + } } } } diff --git a/src/syntax/parsers/expression/function_call_expr.rs b/src/syntax/parsers/expression/function_call_expr.rs index 2480fff..d86de3b 100644 --- a/src/syntax/parsers/expression/function_call_expr.rs +++ b/src/syntax/parsers/expression/function_call_expr.rs @@ -1,8 +1,11 @@ use crate::{ - lexic::token::Token, + error_handling::{error_messages::SYNTAX_INVALID_ARRAY_ACCESS, ErrorContainer, ErrorLabel}, + lexic::token::{Token, TokenType}, syntax::{ - ast::{functions::FunctionCall, Expression}, + ast::{functions::FunctionCall, ArrayAccess, Expression}, functions::arguments_list, + parseable::Parseable, + utils::parse_token_type, ParsingError, ParsingResult, }, }; @@ -11,6 +14,7 @@ use crate::{ /// /// ```ebnf /// function call expr = primary, "(", (arguments list)?, ")" +/// | primary, "[", expressions, "]" /// | primary; /// ``` pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult { @@ -19,21 +23,90 @@ pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult { _ => return Err(ParsingError::Unmatched), }; + // Attempt to parse a function call // Parse arguments list - let (arguments, next_pos) = match arguments_list::try_parse(tokens, next_pos) { - Ok((args, next)) => (args, next), - Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), - _ => { - return Ok((primary_expr, next_pos)); + match arguments_list::try_parse(tokens, next_pos) { + Ok((arguments, next_pos)) => { + let fun_call = FunctionCall { + function: Box::new(primary_expr), + arguments: Box::new(arguments), + }; + + return Ok((Expression::FunctionCall(fun_call), next_pos)); } + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + _ => {} }; - let fun_call = FunctionCall { - function: Box::new(primary_expr), - arguments: Box::new(arguments), + // Attempt to parse an array access + match try_parse_array_access(tokens, next_pos) { + Ok(((array_idx, end_pos), next_pos)) => { + let fun_call = ArrayAccess { + left_expr: Box::new(primary_expr), + idx_expr: Box::new(array_idx), + end_pos, + }; + + return Ok((Expression::ArrayAcccess(fun_call), next_pos)); + } + Err(ParsingError::Err(err)) => return Err(ParsingError::Err(err)), + _ => {} }; - Ok((Expression::FunctionCall(fun_call), next_pos)) + // Return the parsed primary + return Ok((primary_expr, next_pos)); +} + +pub fn try_parse_array_access( + tokens: &Vec, + pos: usize, +) -> ParsingResult<(Expression, usize)> { + let (_token, next_pos) = parse_token_type(&tokens, pos, TokenType::LeftBracket)?; + + // parse an expression + let (exp, next_pos) = match Expression::try_parse(tokens, next_pos) { + Ok(t) => t, + Err(ParsingError::Mismatch(e)) => { + let label = ErrorLabel { + message: String::from("Expected an expression for this array access"), + start: e.position, + end: e.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_ARRAY_ACCESS, + error_offset: e.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); + } + Err(ParsingError::Unmatched) => { + // + let label = ErrorLabel { + message: String::from("Expected an expression for this array access"), + start: pos, + end: pos + 1, + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_ARRAY_ACCESS, + error_offset: pos, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); + } + Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), + }; + + // parse the closing bracket + let (closed_bracket, next) = match parse_token_type(tokens, next_pos, TokenType::RightBracket) { + Ok(t) => t, + Err(e) => return Err(e), + }; + + return Ok(((exp, closed_bracket.get_end_position()), next)); } #[cfg(test)]