From cef5e1676efb57ca875154fbca3e48bb9213bd28 Mon Sep 17 00:00:00 2001 From: Ishan Jain Date: Sun, 26 May 2024 20:03:26 +0530 Subject: [PATCH] added arrays, index operations --- src/evaluator/builtins.rs | 1 + src/evaluator/mod.rs | 51 ++++++++++++++++++++++++++++++++++-- src/evaluator/tree_walker.rs | 38 +++++++++++++++++++++++++-- src/parser/ast.rs | 35 ++++++++++++++++++++++++- src/parser/mod.rs | 31 ++++++++++++++++++++++ 5 files changed, 151 insertions(+), 5 deletions(-) diff --git a/src/evaluator/builtins.rs b/src/evaluator/builtins.rs index 888176b..2ebb932 100644 --- a/src/evaluator/builtins.rs +++ b/src/evaluator/builtins.rs @@ -18,6 +18,7 @@ pub const BUILTINS: LazyCell> = LazyCell::new(|| { match &args[0] { Object::String(s) => Object::Integer(s.len() as i64), + Object::Array(s) => Object::Integer(s.elements.len() as i64), v => Object::Error(format!("argument to `len` not supported, got {}", v)), } }), diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 9d7e055..ee1e75c 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -61,6 +61,7 @@ pub enum Object { Error(String), Function(Function), Builtin(BuiltinFunction), + Array(Array), Null, } @@ -74,6 +75,9 @@ impl Object { Object::Error(s) => s.to_string(), Object::Null => "NULL".into(), Object::Builtin(_) => "builtin function".to_string(), + Object::Array(v) => { + format!("[{}]", v.elements.iter().map(|x| x.to_string()).join(", ")) + } Object::Function(s) => { let mut out = String::new(); @@ -101,6 +105,7 @@ impl Display for Object { Object::Function(_) => "FUNCTION", Object::Null => "NULL", Object::Builtin(_) => "BUILTIN", + Object::Array(_) => "ARRAY", }) } } @@ -133,6 +138,11 @@ impl PartialEq for BuiltinFunction { } } +#[derive(Debug, Clone, PartialEq)] +pub struct Array { + elements: Vec, +} + #[cfg(test)] mod tests { use std::{assert_matches::assert_matches, cell::RefCell, rc::Rc}; @@ -142,6 +152,8 @@ mod tests { lexer::Lexer, parser::{ast::Node, Parser}, }; + + use super::Array; const TRUE: Object = Object::Boolean(true); const FALSE: Object = Object::Boolean(false); const NULL: Object = Object::Null; @@ -438,12 +450,11 @@ mod tests { #[test] fn builtin_function() { - // This test case allocates memory for `foobar=9999` over and over - // even though it is never used. let test_cases = [ ("len(\"\")", Some(Object::Integer(0))), ("len(\"four\")", Some(Object::Integer(4))), ("len(\"hello world\")", Some(Object::Integer(11))), + ("len([1,2,3,4])", Some(Object::Integer(4))), ( "len(1)", Some(Object::Error( @@ -460,4 +471,40 @@ mod tests { run_test_cases(&test_cases); } + + #[test] + fn array_literals() { + let test_cases = [( + "[1, 2* 2, 3+3]", + Some(Object::Array(Array { + elements: vec![Object::Integer(1), Object::Integer(4), Object::Integer(6)], + })), + )]; + + run_test_cases(&test_cases); + } + + #[test] + fn array_index_expressions() { + let test_cases = [ + ("[1, 2, 3][0]", Some(Object::Integer(1))), + ("[1, 2, 3][1]", Some(Object::Integer(2))), + ("[1, 2, 3][2]", Some(Object::Integer(3))), + ("let i = 0; [1][i];", Some(Object::Integer(1))), + ("[1,2,3][1+1]", Some(Object::Integer(3))), + ("let array = [1,2,3]; array[2];", Some(Object::Integer(3))), + ( + "let array = [1,2,3]; array[0]+array[1]+array[2];", + Some(Object::Integer(6)), + ), + ( + "let array = [1,2,3]; let i = array[0]; array[i];", + Some(Object::Integer(2)), + ), + ("[1,2,3][3];", Some(Object::Null)), + ("[1,2,3][-1];", Some(Object::Null)), + ]; + + run_test_cases(&test_cases); + } } diff --git a/src/evaluator/tree_walker.rs b/src/evaluator/tree_walker.rs index 437e57c..2825bdd 100644 --- a/src/evaluator/tree_walker.rs +++ b/src/evaluator/tree_walker.rs @@ -4,7 +4,7 @@ use std::{cell::RefCell, rc::Rc}; // It's just constantly unwrapping enums from one place and rewrapping it to some other enum(or even the same enum) and returning it // The error handling story is pretty bad too use crate::{ - evaluator::{Evaluator, Object}, + evaluator::{Array, Evaluator, Object}, lexer::TokenType, parser::ast::{ BlockStatement, Expression, ExpressionStatement, Identifier, LetStatement, Node, Program, @@ -47,7 +47,22 @@ 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::ArrayLiteral(v) => { + println!("{:?}", v); + + let args = match self.eval_expression(v.elements, env) { + Ok(v) => v, + Err(e) => return Some(e), + }; + + Some(Object::Array(Array { elements: args })) + } + Expression::IndexExpression(v) => { + let left = self.eval(Node::Expression(*v.left), env.clone())?; + let index = self.eval(Node::Expression(*v.index), env)?; + + self.eval_index_expression(left, index) + } Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::PrefixExpression(p) => { let expr = self.eval(Node::Expression(*p.right), env)?; @@ -288,4 +303,23 @@ impl TreeWalker { resp } + + fn eval_index_expression(&self, left: Object, index: Object) -> Option { + match (&left, index) { + (Object::Array(a), Object::Integer(i)) => Some(Self::eval_array_index_expression(a, i)), + + _ => Some(Object::Error(format!( + "index operator not supported: {}", + left + ))), + } + } + + fn eval_array_index_expression(array: &Array, index: i64) -> Object { + let max = array.elements.len() as i64; + if index < 0 || index >= max { + return Object::Null; + } + array.elements[index as usize].clone() + } } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 31a0aa0..6f209a4 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -182,6 +182,7 @@ pub enum ExpressionPriority { Product = 4, Prefix = 5, Call = 6, + Index = 7, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -190,6 +191,7 @@ pub enum Expression { IntegerLiteral(IntegerLiteral), StringLiteral(StringLiteral), ArrayLiteral(ArrayLiteral), + IndexExpression(IndexExpression), PrefixExpression(PrefixExpression), InfixExpression(InfixExpression), BooleanExpression(BooleanExpression), @@ -256,6 +258,9 @@ impl Display for Expression { Expression::IfExpression(v) => v.to_string(), Expression::FunctionExpression(v) => v.to_string(), Expression::CallExpression(v) => v.to_string(), + Expression::IndexExpression(v) => { + format!("({}[{}])", v.left, v.index) + } }; f.write_str(&value) @@ -341,7 +346,7 @@ impl StringLiteral { #[derive(Debug, PartialEq, Eq, Clone)] pub struct ArrayLiteral { - elements: Vec, + pub elements: Vec, } impl ArrayLiteral { @@ -366,6 +371,34 @@ impl Display for ArrayLiteral { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct IndexExpression { + pub left: Box, + pub index: Box, +} + +impl IndexExpression { + pub fn new(left: Expression, index: Expression) -> Self { + Self { + left: Box::new(left), + index: Box::new(index), + } + } + + pub fn parse(parser: &mut Parser, _token: Token, left: Expression) -> Option { + let next_token = parser.lexer.next()?; + + let index = Expression::parse(parser, next_token, ExpressionPriority::Lowest)?; + + parser.expect_peek(TokenType::RBracket)?; + + Some(Expression::IndexExpression(IndexExpression { + left: Box::new(left), + index: Box::new(index), + })) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct PrefixExpression { pub operator: TokenType, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 77bc534..3df6b26 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -28,6 +28,7 @@ lazy_static! { m.insert(TokenType::Slash, ExpressionPriority::Product); m.insert(TokenType::Asterisk, ExpressionPriority::Product); m.insert(TokenType::LParen, ExpressionPriority::Call); + m.insert(TokenType::LBracket, ExpressionPriority::Index); m }; } @@ -72,6 +73,8 @@ impl<'a> Parser<'a> { parser.register_infix(TokenType::NotEquals, InfixExpression::parse); parser.register_infix(TokenType::LessThan, InfixExpression::parse); parser.register_infix(TokenType::GreaterThan, InfixExpression::parse); + parser.register_infix(TokenType::LBracket, IndexExpression::parse); + parser } @@ -548,6 +551,14 @@ mod tests { ("2 / (5 + 5)", "(2 / (5 + 5))"), ("-(5 + 5)", "(-(5 + 5))"), ("!(true == true)", "(!(true == true))"), + ( + "a * [1, 2, 3, 4][b * c] * d", + "((a * ([1, 2, 3, 4][(b * c)])) * d)", + ), + ( + "add(a * b[2], b[1], 2 * [1, 2][1])", + "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", + ), ]; for test in test_cases.iter() { @@ -949,4 +960,24 @@ mod tests { check_test_cases(&test_cases); } + + #[test] + fn index_expression() { + let test_cases = [( + "myArray[ 1 + 1 ]", + vec![Statement::ExpressionStatement(ExpressionStatement::new( + token!(TokenType::Ident, "myArray"), + Expression::IndexExpression(IndexExpression::new( + Expression::Identifier(Identifier::new(TokenType::Ident, "myArray")), + Expression::InfixExpression(InfixExpression::new( + Expression::IntegerLiteral(IntegerLiteral::new(1)), + TokenType::Plus, + Expression::IntegerLiteral(IntegerLiteral::new(1)), + )), + )), + ))], + )]; + + check_test_cases(&test_cases); + } }