added arrays, index operations

This commit is contained in:
Ishan Jain 2024-05-26 20:03:26 +05:30
parent 3eeec60d3b
commit cef5e1676e
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
5 changed files with 151 additions and 5 deletions

View File

@ -18,6 +18,7 @@ pub const BUILTINS: LazyCell<HashMap<&'static str, Object>> = 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)),
}
}),

View File

@ -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<Object>,
}
#[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);
}
}

View File

@ -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<Object> {
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()
}
}

View File

@ -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<Expression>,
pub elements: Vec<Expression>,
}
impl ArrayLiteral {
@ -366,6 +371,34 @@ impl Display for ArrayLiteral {
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct IndexExpression {
pub left: Box<Expression>,
pub index: Box<Expression>,
}
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<Expression> {
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,

View File

@ -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);
}
}