diff --git a/src/evaluator/tree_walker.rs b/src/evaluator/tree_walker.rs index 2b97f0f..437e57c 100644 --- a/src/evaluator/tree_walker.rs +++ b/src/evaluator/tree_walker.rs @@ -47,6 +47,7 @@ impl Evaluator for TreeWalker { Expression::Identifier(v) => self.eval_identifier(v, env), Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)), Expression::StringLiteral(s) => Some(Object::String(s.value)), + Expression::ArrayLiteral(v) => unimplemented!(), Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::PrefixExpression(p) => { let expr = self.eval(Node::Expression(*p.right), env)?; diff --git a/src/lexer.rs b/src/lexer.rs index 873c96e..66d675a 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -51,6 +51,8 @@ pub enum TokenType { RParen, LBrace, RBrace, + LBracket, + RBracket, // Keywords Function, @@ -81,6 +83,8 @@ impl Display for TokenType { TokenType::RParen => ")", TokenType::LBrace => "{", TokenType::RBrace => "}", + TokenType::LBracket => "[", + TokenType::RBracket => "]", TokenType::Function => "fn", TokenType::If => "if", TokenType::Else => "else", @@ -241,6 +245,8 @@ impl<'a> Iterator for Lexer<'a> { Some(';') => Some(token!(TokenType::Semicolon)), Some('(') => Some(token!(TokenType::LParen)), Some(')') => Some(token!(TokenType::RParen)), + Some('[') => Some(token!(TokenType::LBracket)), + Some(']') => Some(token!(TokenType::RBracket)), Some('{') => Some(token!(TokenType::LBrace)), Some('}') => Some(token!(TokenType::RBrace)), Some('!') => { @@ -379,6 +385,7 @@ mod tests { \"foobar\" \"foo bar\" + [1,2]; " ) @@ -433,6 +440,12 @@ mod tests { token!(TokenType::Semicolon), token!(TokenType::String, "foobar"), token!(TokenType::String, "foo bar"), + token!(TokenType::LBracket), + token!(TokenType::Int, "1"), + token!(TokenType::Comma), + token!(TokenType::Int, "2"), + token!(TokenType::RBracket), + token!(TokenType::Semicolon), token!(TokenType::EOF), ], ); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index e137e6c..31a0aa0 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -7,7 +7,7 @@ use { std::{ cmp::PartialOrd, convert::From, - fmt::{Display, Formatter, Result as FmtResult}, + fmt::{Display, Formatter, Result as FmtResult, Write}, }, }; @@ -189,6 +189,7 @@ pub enum Expression { Identifier(Identifier), IntegerLiteral(IntegerLiteral), StringLiteral(StringLiteral), + ArrayLiteral(ArrayLiteral), PrefixExpression(PrefixExpression), InfixExpression(InfixExpression), BooleanExpression(BooleanExpression), @@ -198,7 +199,11 @@ pub enum Expression { } impl Expression { - fn parse(parser: &mut Parser, ctoken: Token, precedence: ExpressionPriority) -> Option { + pub fn parse( + parser: &mut Parser, + ctoken: Token, + precedence: ExpressionPriority, + ) -> Option { match parser.prefix_parse_fns.get(&ctoken.name) { Some(prefix) => { let mut left_expr = prefix(parser, ctoken); @@ -244,6 +249,7 @@ impl Display for Expression { Expression::Identifier(v) => v.to_string(), Expression::IntegerLiteral(v) => v.value.to_string(), Expression::StringLiteral(v) => v.value.to_string(), + Expression::ArrayLiteral(v) => v.to_string(), Expression::PrefixExpression(v) => v.to_string(), Expression::InfixExpression(v) => v.to_string(), Expression::BooleanExpression(v) => v.to_string(), @@ -333,6 +339,33 @@ impl StringLiteral { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ArrayLiteral { + elements: Vec, +} + +impl ArrayLiteral { + pub fn new(expr: Vec) -> Self { + Self { elements: expr } + } + pub fn parse(parser: &mut Parser, _token: Token) -> Option { + let mut out = ArrayLiteral { elements: vec![] }; + + out.elements = parser.parse_expression_list(TokenType::RBracket)?; + + Some(Expression::ArrayLiteral(out)) + } +} + +impl Display for ArrayLiteral { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_char('[').unwrap(); + f.write_str(&self.elements.iter().map(|x| x.to_string()).join(", ")) + .unwrap(); + f.write_char(']') + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct PrefixExpression { pub operator: TokenType, @@ -591,49 +624,14 @@ impl CallExpression { } } - pub fn parse(parser: &mut Parser, ctoken: Token, expr: Expression) -> Option { - let args = Self::parse_call_arguments(parser, ctoken); + pub fn parse(parser: &mut Parser, _ctoken: Token, expr: Expression) -> Option { + let args = parser.parse_expression_list(TokenType::RParen)?; Some(Expression::CallExpression(Self { function: Box::new(expr), - arguments: args?, + arguments: args, })) } - - fn parse_call_arguments(parser: &mut Parser, _ctoken: Token) -> Option> { - let mut expressions = vec![]; - if let Some(token) = parser.lexer.peek() { - if token.name == TokenType::RParen { - parser.lexer.next(); - return Some(expressions); - } - } - let ntoken = match parser.lexer.next() { - Some(token) => token, - None => return Some(expressions), - }; - - if let Some(expr) = Expression::parse(parser, ntoken, ExpressionPriority::Lowest) { - expressions.push(expr); - } else { - return Some(expressions); - } - while parser.peek_token_is(TokenType::Comma) { - parser.lexer.next(); - let token = match parser.lexer.next() { - Some(v) => v, - None => return Some(expressions), - }; - if let Some(expr) = Expression::parse(parser, token, ExpressionPriority::Lowest) { - expressions.push(expr); - } else { - return Some(expressions); - } - } - parser.expect_peek(TokenType::RParen)?; - - Some(expressions) - } } impl Display for CallExpression { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2ffd219..77bc534 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -58,6 +58,7 @@ impl<'a> Parser<'a> { parser.register_prefix(TokenType::LParen, Expression::parse_grouped_expression); parser.register_prefix(TokenType::If, IfExpression::parse); parser.register_prefix(TokenType::Function, FunctionLiteral::parse); + parser.register_prefix(TokenType::LBracket, ArrayLiteral::parse); // Neat trick! // Call expressions looks like (). @@ -146,6 +147,37 @@ impl<'a> Parser<'a> { None => ExpressionPriority::Lowest, } } + + fn parse_expression_list(&mut self, end_token: TokenType) -> Option> { + let mut out = vec![]; + + if self.peek_token_is(end_token) { + self.lexer.next(); + return Some(out); + } + + let next_token = self.lexer.next()?; + out.push(Expression::parse( + self, + next_token, + ExpressionPriority::Lowest, + )?); + + while self.peek_token_is(TokenType::Comma) { + self.lexer.next(); + let next_token = self.lexer.next()?; + + out.push(Expression::parse( + self, + next_token, + ExpressionPriority::Lowest, + )?); + } + + self.expect_peek(end_token)?; + + Some(out) + } } #[derive(PartialEq, Debug)] @@ -892,4 +924,29 @@ mod tests { assert_eq!(program.unwrap().to_string(), test.1); } } + + #[test] + fn array_literals() { + let test_cases = [( + "[1, 2 * 2, 3 + 3]", + vec![Statement::ExpressionStatement(ExpressionStatement::new( + token!(TokenType::LBracket), + Expression::ArrayLiteral(ArrayLiteral::new(vec![ + Expression::IntegerLiteral(IntegerLiteral::new(1)), + Expression::InfixExpression(InfixExpression::new( + Expression::IntegerLiteral(IntegerLiteral::new(2)), + TokenType::Asterisk, + Expression::IntegerLiteral(IntegerLiteral::new(2)), + )), + Expression::InfixExpression(InfixExpression::new( + Expression::IntegerLiteral(IntegerLiteral::new(3)), + TokenType::Plus, + Expression::IntegerLiteral(IntegerLiteral::new(3)), + )), + ])), + ))], + )]; + + check_test_cases(&test_cases); + } }