diff --git a/src/ast/mod.rs b/src/ast/mod.rs index bd1ba36..e509a13 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -16,12 +16,16 @@ pub trait StatementTrait: NodeTrait { pub enum Expression { Identifier(Identifier), IntegerLiteral(IntegerLiteral), + StringLiteral(StringLiteral), + Boolean(Boolean), PrefixExpression(PrefixExpression), InfixExpression(InfixExpression), - Boolean(Boolean), IfExpression(IfExpression), FunctionLiteral(FunctionLiteral), CallExpression(CallExpression), + ArrayLiteral(ArrayLiteral), + HashLiteral(HashLiteral), + IndexExpression(IndexExpression), } impl NodeTrait for Expression { @@ -29,12 +33,16 @@ impl NodeTrait for Expression { match self { Expression::Identifier(i) => i.token_literal(), Expression::IntegerLiteral(i) => i.token_literal(), + Expression::StringLiteral(i) => i.token_literal(), + Expression::Boolean(i) => i.token_literal(), Expression::PrefixExpression(i) => i.token_literal(), Expression::InfixExpression(i) => i.token_literal(), - Expression::Boolean(i) => i.token_literal(), Expression::IfExpression(i) => i.token_literal(), Expression::FunctionLiteral(i) => i.token_literal(), Expression::CallExpression(i) => i.token_literal(), + Expression::ArrayLiteral(i) => i.token_literal(), + Expression::HashLiteral(i) => i.token_literal(), + Expression::IndexExpression(i) => i.token_literal(), } } @@ -42,12 +50,16 @@ impl NodeTrait for Expression { match self { Expression::Identifier(i) => i.string(), Expression::IntegerLiteral(i) => i.string(), + Expression::StringLiteral(i) => i.string(), + Expression::Boolean(i) => i.string(), Expression::PrefixExpression(i) => i.string(), Expression::InfixExpression(i) => i.string(), - Expression::Boolean(i) => i.string(), Expression::IfExpression(i) => i.string(), Expression::FunctionLiteral(i) => i.string(), Expression::CallExpression(i) => i.string(), + Expression::ArrayLiteral(i) => i.string(), + Expression::HashLiteral(i) => i.string(), + Expression::IndexExpression(i) => i.string(), } } @@ -380,3 +392,101 @@ impl NodeTrait for CallExpression { self } } + +#[derive(Debug)] +pub struct StringLiteral { + pub token: Token, + pub value: String, +} + +impl NodeTrait for StringLiteral { + fn token_literal(&self) -> String { + String::from(&self.token.literal) + } + + fn string(&self) -> String { + self.value.clone() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub struct ArrayLiteral { + pub token: Token, + pub elements: Vec, +} + +impl NodeTrait for ArrayLiteral { + fn token_literal(&self) -> String { + String::from(&self.token.literal) + } + + fn string(&self) -> String { + let mut out = String::new(); + out.push_str("["); + let elements: Vec = self.elements.iter().map(|e| e.string()).collect(); + out.push_str(&elements.join(", ")); + out.push_str("]"); + out + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub struct HashLiteral { + pub token: Token, + pub pairs: Vec<(Expression, Expression)>, +} + +impl NodeTrait for HashLiteral { + fn token_literal(&self) -> String { + String::from(&self.token.literal) + } + + fn string(&self) -> String { + let mut out = String::new(); + out.push_str("{"); + let pairs: Vec = self + .pairs + .iter() + .map(|(k, v)| format!("{}: {}", k.string(), v.string())) + .collect(); + out.push_str(&pairs.join(", ")); + out.push_str("}"); + out + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub struct IndexExpression { + pub token: Token, + pub left: Box, + pub index: Box, +} + +impl NodeTrait for IndexExpression { + fn token_literal(&self) -> String { + String::from(&self.token.literal) + } + + fn string(&self) -> String { + let mut out = String::new(); + out.push_str("("); + out.push_str(&self.left.string()); + out.push_str("["); + out.push_str(&self.index.string()); + out.push_str("])"); + out + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 80d5e96..8f4a760 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -1,8 +1,9 @@ use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; use crate::ast::{self, NodeTrait}; -use crate::object::{self, Environment, Null, Object, ObjectType}; +use crate::object::{self, Environment, Hashable, Null, Object, ObjectType}; const NULL: Null = Null; const BOOL_TRUE: object::Boolean = object::Boolean { value: true }; @@ -85,6 +86,9 @@ fn eval_expression( ast::Expression::IntegerLiteral(int) => { Some(Box::new(object::Integer { value: int.value })) } + ast::Expression::StringLiteral(s) => Some(Box::new(object::StringObj { + value: s.value.clone(), + })), ast::Expression::Boolean(b) => Some(Box::new(native_bool_to_bool_object(b.value))), ast::Expression::PrefixExpression(expr) => { let right = eval_expression(expr.right.as_ref(), Rc::clone(&env))?; @@ -121,6 +125,25 @@ fn eval_expression( } apply_function(function, args) } + ast::Expression::ArrayLiteral(arr) => { + let elements = eval_expressions(&arr.elements, Rc::clone(&env))?; + if elements.len() == 1 && is_error(elements[0].as_ref()) { + return Some(elements.into_iter().next().unwrap()); + } + Some(Box::new(object::Array { elements })) + } + ast::Expression::HashLiteral(hash) => eval_hash_literal(hash, env), + ast::Expression::IndexExpression(idx) => { + let left = eval_expression(idx.left.as_ref(), Rc::clone(&env))?; + if is_error(left.as_ref()) { + return Some(left); + } + let index = eval_expression(idx.index.as_ref(), Rc::clone(&env))?; + if is_error(index.as_ref()) { + return Some(index); + } + Some(eval_index_expression(left, index)) + } } } @@ -168,6 +191,10 @@ fn apply_function( return Some(Box::new(NULL)); } + if let Some(builtin) = function.as_any().downcast_ref::() { + return Some((builtin.func)(args)); + } + Some(new_error(format!( "not a function: {:?}", function.object_type() @@ -237,6 +264,10 @@ fn eval_infix_expression( return eval_boolean_infix_expression(operator, left, right); } + if left.object_type() == ObjectType::STRING && right.object_type() == ObjectType::STRING { + return eval_string_infix_expression(operator, left, right); + } + if left.object_type() != right.object_type() { return Some(new_error(format!( "type mismatch: {:?} {} {:?}", @@ -292,6 +323,37 @@ fn eval_integer_infix_expression( } } +fn eval_string_infix_expression( + operator: &str, + left: Box, + right: Box, +) -> Option> { + let left_val = left + .as_any() + .downcast_ref::() + .unwrap() + .value + .clone(); + let right_val = right + .as_any() + .downcast_ref::() + .unwrap() + .value + .clone(); + + match operator { + "+" => Some(Box::new(object::StringObj { + value: format!("{}{}", left_val, right_val), + })), + _ => Some(new_error(format!( + "unknown operator: {:?} {} {:?}", + left.object_type(), + operator, + right.object_type() + ))), + } +} + fn eval_boolean_infix_expression( operator: &str, left: Box, @@ -316,6 +378,128 @@ fn eval_boolean_infix_expression( } } +fn eval_hash_literal( + hash: &ast::HashLiteral, + env: Rc>, +) -> Option> { + let mut pairs = HashMap::new(); + + for (k, v) in &hash.pairs { + let key = eval_expression(k, Rc::clone(&env))?; + if is_error(key.as_ref()) { + return Some(key); + } + let value = eval_expression(v, Rc::clone(&env))?; + if is_error(value.as_ref()) { + return Some(value); + } + + let hash_key = match key.object_type() { + ObjectType::INTEGER => { + key.as_any() + .downcast_ref::() + .unwrap() + .hash_key() + } + ObjectType::BOOLEAN => { + key.as_any() + .downcast_ref::() + .unwrap() + .hash_key() + } + ObjectType::STRING => { + key.as_any() + .downcast_ref::() + .unwrap() + .hash_key() + } + _ => return Some(new_error(format!("unusable as hash key: {:?}", key.object_type()))), + }; + + pairs.insert( + hash_key, + object::HashPair { + key: key.clone_box(), + value: value.clone_box(), + }, + ); + } + + Some(Box::new(object::Hash { pairs })) +} + +fn eval_index_expression(left: Box, index: Box) -> Box { + match (left.object_type(), index.object_type()) { + (ObjectType::ARRAY, ObjectType::INTEGER) => eval_array_index_expression(left, index), + (ObjectType::HASH, _) => eval_hash_index_expression(left, index), + (ObjectType::STRING, ObjectType::INTEGER) => eval_string_index_expression(left, index), + _ => new_error(format!( + "index operator not supported: {:?}[{:?}]", + left.object_type(), + index.object_type() + )), + } +} + +fn eval_array_index_expression( + array: Box, + index: Box, +) -> Box { + let arr = array.as_any().downcast_ref::().unwrap(); + let idx = index.as_any().downcast_ref::().unwrap().value; + let max = arr.elements.len() as i64 - 1; + if idx < 0 || idx > max { + return Box::new(NULL); + } + arr.elements[idx as usize].clone_box() +} + +fn eval_hash_index_expression( + hash: Box, + index: Box, +) -> Box { + let hash_obj = hash.as_any().downcast_ref::().unwrap(); + + let key = match index.object_type() { + ObjectType::INTEGER => index + .as_any() + .downcast_ref::() + .unwrap() + .hash_key(), + ObjectType::BOOLEAN => index + .as_any() + .downcast_ref::() + .unwrap() + .hash_key(), + ObjectType::STRING => index + .as_any() + .downcast_ref::() + .unwrap() + .hash_key(), + _ => return new_error(format!("unusable as hash key: {:?}", index.object_type())), + }; + + match hash_obj.pairs.get(&key) { + Some(pair) => pair.value.clone_box(), + None => Box::new(NULL), + } +} + +fn eval_string_index_expression( + string: Box, + index: Box, +) -> Box { + let s = string.as_any().downcast_ref::().unwrap(); + let idx = index.as_any().downcast_ref::().unwrap().value; + let max = s.value.len() as i64 - 1; + if idx < 0 || idx > max { + return Box::new(NULL); + } + Box::new(object::StringObj { + value: s.value.chars().nth(idx as usize).unwrap().to_string(), + }) +} + fn eval_if_expression( expr: &ast::IfExpression, env: Rc>, diff --git a/src/evaluator/tests.rs b/src/evaluator/tests.rs index 123da9d..d5c0e35 100644 --- a/src/evaluator/tests.rs +++ b/src/evaluator/tests.rs @@ -30,6 +30,12 @@ fn test_null_object(obj: &dyn Object) { assert_eq!(obj.object_type(), ObjectType::NULL); } +fn test_string_object(obj: &dyn Object, expected: &str) { + assert_eq!(obj.object_type(), ObjectType::STRING); + let s = obj.as_any().downcast_ref::().unwrap(); + assert_eq!(s.value, expected); +} + #[test] fn test_eval_integer_expression() { let tests = vec![("5", 5), ("10", 10), ("-5", -5), ("-10", -10)]; @@ -219,6 +225,8 @@ fn test_error_handling() { ("5; true + false; 5", "unknown operator: BOOLEAN + BOOLEAN"), ("if (10 > 1) { true + false; }", "unknown operator: BOOLEAN + BOOLEAN"), ("foobar", "identifier not found: foobar"), + ("len(1)", "argument to `len` not supported"), + ("first(1)", "argument to `first` must be ARRAY"), ]; for (input, expected) in tests { @@ -228,3 +236,127 @@ fn test_error_handling() { assert!(err.message.contains(expected), "expected '{}' to contain '{}'", err.message, expected); } } + +#[test] +fn test_eval_string_expression() { + let input = "\"Hello World\""; + let evaluated = test_eval(input).unwrap(); + test_string_object(evaluated.as_ref(), "Hello World"); +} + +#[test] +fn test_string_concatenation() { + let input = "\"Hello\" + \" \" + \"World!\""; + let evaluated = test_eval(input).unwrap(); + test_string_object(evaluated.as_ref(), "Hello World!"); +} + +#[test] +fn test_string_indexing() { + let tests = vec![ + ("\"abc\"[0]", "a"), + ("\"abc\"[1]", "b"), + ("\"abc\"[2]", "c"), + ]; + + for (input, expected) in tests { + let evaluated = test_eval(input).unwrap(); + test_string_object(evaluated.as_ref(), expected); + } +} + +#[test] +fn test_eval_array_expression() { + let input = "[1, 2 * 2, 3 + 3]"; + let evaluated = test_eval(input).unwrap(); + assert_eq!(evaluated.object_type(), ObjectType::ARRAY); + let arr = evaluated.as_any().downcast_ref::().unwrap(); + assert_eq!(arr.elements.len(), 3); + test_integer_object(arr.elements[0].as_ref(), 1); + test_integer_object(arr.elements[1].as_ref(), 4); + test_integer_object(arr.elements[2].as_ref(), 6); +} + +#[test] +fn test_eval_array_index() { + let tests = vec![ + ("[1, 2, 3][0]", 1), + ("[1, 2, 3][1]", 2), + ("[1, 2, 3][2]", 3), + ("let i = 0; [1][i];", 1), + ("[1, 2, 3][1 + 1];", 3), + ("let myArray = [1, 2, 3]; myArray[2];", 3), + ("let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", 6), + ("let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i];", 2), + ]; + + for (input, expected) in tests { + let evaluated = test_eval(input).unwrap(); + test_integer_object(evaluated.as_ref(), expected); + } +} + +#[test] +fn test_eval_hash_expression() { + let input = r#"{"one": 1, "two": 2, "three": 3}"#; + let evaluated = test_eval(input).unwrap(); + assert_eq!(evaluated.object_type(), ObjectType::HASH); + let hash = evaluated.as_any().downcast_ref::().unwrap(); + assert_eq!(hash.pairs.len(), 3); +} + +#[test] +fn test_eval_hash_index() { + let tests = vec![ + (r#"{"foo": 5}["foo"]"#, 5), + (r#"{"foo": 5}["bar"]"#, 0), // expect null, we'll check separately + (r#"let key = "foo"; {"foo": 5}[key]"#, 5), + ("{true: 1}[true]", 1), + ("{1: 1, 2: 2}[2]", 2), + ]; + + for (input, expected) in tests { + let evaluated = test_eval(input).unwrap(); + if input.contains("\"bar\"") { + test_null_object(evaluated.as_ref()); + } else { + test_integer_object(evaluated.as_ref(), expected); + } + } +} + +#[test] +fn test_builtin_functions() { + let tests = vec![ + ("len(\"\")", 0), + ("len(\"four\")", 4), + ("len(\"hello world\")", 11), + ("len([1, 2, 3])", 3), + ("len([])", 0), + ("first([1, 2, 3])", 1), + ("last([1, 2, 3])", 3), + ]; + + for (input, expected) in tests { + let evaluated = test_eval(input).unwrap(); + test_integer_object(evaluated.as_ref(), expected); + } +} + +#[test] +fn test_builtin_array_functions() { + let input = "let a = [1, 2, 3]; push(a, 4);"; + let evaluated = test_eval(input).unwrap(); + assert_eq!(evaluated.object_type(), ObjectType::ARRAY); + let arr = evaluated.as_any().downcast_ref::().unwrap(); + assert_eq!(arr.elements.len(), 4); + test_integer_object(arr.elements[3].as_ref(), 4); + + let input = "rest([1, 2, 3]);"; + let evaluated = test_eval(input).unwrap(); + assert_eq!(evaluated.object_type(), ObjectType::ARRAY); + let arr = evaluated.as_any().downcast_ref::().unwrap(); + assert_eq!(arr.elements.len(), 2); + test_integer_object(arr.elements[0].as_ref(), 2); + test_integer_object(arr.elements[1].as_ref(), 3); +} diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index ed6b8f8..5870d9a 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -12,6 +12,7 @@ pub trait LexerTraits { fn read_char(&mut self); fn next_token(&mut self) -> Token; fn read_identifier(&mut self) -> String; + fn read_string(&mut self) -> String; fn skip_whitespace(&mut self); fn read_number(&mut self) -> String; fn peek_char(&self) -> u8; @@ -81,6 +82,14 @@ impl LexerTraits for Lexer { '>' => new_token(TokenType::Gt, lit), '{' => new_token(TokenType::Lbrace, lit), '}' => new_token(TokenType::Rbrace, lit), + '[' => new_token(TokenType::Lbracket, lit), + ']' => new_token(TokenType::Rbracket, lit), + ':' => new_token(TokenType::Colon, lit), + '"' => { + let s = self.read_string(); + self.read_char(); + return new_token_from_str(TokenType::String, s); + } _ => { if c.is_alphabetic() { let lit = self.read_identifier(); @@ -110,6 +119,17 @@ impl LexerTraits for Lexer { String::from(res) } + fn read_string(&mut self) -> String { + let position = self.position.unwrap() + 1; + self.read_char(); + while self.ch.unwrap() != b'"' && self.ch.unwrap() != 0 { + self.read_char(); + } + let read_pos = self.position.unwrap() as usize; + let position = position as usize; + String::from(&self.input[position..read_pos]) + } + fn skip_whitespace(&mut self) { while char::from(self.ch.unwrap()) == ' ' || char::from(self.ch.unwrap()) == '\n' diff --git a/src/object/mod.rs b/src/object/mod.rs index d25e7e4..aee76f8 100644 --- a/src/object/mod.rs +++ b/src/object/mod.rs @@ -13,6 +13,10 @@ pub enum ObjectType { RETURN_VALUE, ERROR, FUNCTION, + STRING, + ARRAY, + HASH, + BUILTIN, } pub trait Object { @@ -167,6 +171,151 @@ impl Object for Function { } } +pub struct StringObj { + pub value: String, +} + +impl Object for StringObj { + fn object_type(&self) -> ObjectType { + ObjectType::STRING + } + + fn inspect(&self) -> String { + self.value.clone() + } + + fn clone_box(&self) -> Box { + Box::new(StringObj { + value: self.value.clone(), + }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub struct Array { + pub elements: Vec>, +} + +impl Object for Array { + fn object_type(&self) -> ObjectType { + ObjectType::ARRAY + } + + fn inspect(&self) -> String { + let elements: Vec = self.elements.iter().map(|e| e.inspect()).collect(); + format!("[{}]", elements.join(", ")) + } + + fn clone_box(&self) -> Box { + Box::new(Array { + elements: self.elements.iter().map(|e| e.clone_box()).collect(), + }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum HashKey { + Integer(i64), + Boolean(bool), + String(String), +} + +pub trait Hashable { + fn hash_key(&self) -> HashKey; +} + +impl Hashable for Integer { + fn hash_key(&self) -> HashKey { + HashKey::Integer(self.value) + } +} + +impl Hashable for Boolean { + fn hash_key(&self) -> HashKey { + HashKey::Boolean(self.value) + } +} + +impl Hashable for StringObj { + fn hash_key(&self) -> HashKey { + HashKey::String(self.value.clone()) + } +} + +pub struct HashPair { + pub key: Box, + pub value: Box, +} + +pub struct Hash { + pub pairs: HashMap, +} + +impl Object for Hash { + fn object_type(&self) -> ObjectType { + ObjectType::HASH + } + + fn inspect(&self) -> String { + let mut pairs: Vec = self + .pairs + .values() + .map(|pair| format!("{}: {}", pair.key.inspect(), pair.value.inspect())) + .collect(); + pairs.sort(); + format!("{{{}}}", pairs.join(", ")) + } + + fn clone_box(&self) -> Box { + let mut new_pairs = HashMap::new(); + for (k, v) in &self.pairs { + new_pairs.insert( + k.clone(), + HashPair { + key: v.key.clone_box(), + value: v.value.clone_box(), + }, + ); + } + Box::new(Hash { pairs: new_pairs }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub type BuiltinFunction = fn(Vec>) -> Box; + +pub struct Builtin { + pub func: BuiltinFunction, +} + +impl Object for Builtin { + fn object_type(&self) -> ObjectType { + ObjectType::BUILTIN + } + + fn inspect(&self) -> String { + "builtin function".to_string() + } + + fn clone_box(&self) -> Box { + Box::new(Builtin { func: self.func }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + #[derive(Default)] pub struct Environment { store: HashMap>, @@ -175,10 +324,21 @@ pub struct Environment { impl Environment { pub fn new() -> Self { - Self { + let mut env = Self { store: HashMap::new(), outer: None, - } + }; + env.register_builtins(); + env + } + + fn register_builtins(&mut self) { + self.set("len", Box::new(Builtin { func: builtin_len })); + self.set("first", Box::new(Builtin { func: builtin_first })); + self.set("last", Box::new(Builtin { func: builtin_last })); + self.set("rest", Box::new(Builtin { func: builtin_rest })); + self.set("push", Box::new(Builtin { func: builtin_push })); + self.set("puts", Box::new(Builtin { func: builtin_puts })); } pub fn from_outer(outer: Rc>) -> Self { @@ -202,3 +362,121 @@ impl Environment { self.store.insert(name.to_string(), value); } } + +fn builtin_len(args: Vec>) -> Box { + if args.len() != 1 { + return Box::new(Error { + message: format!("wrong number of arguments. got={}, want=1", args.len()), + }); + } + if let Some(s) = args[0].as_any().downcast_ref::() { + return Box::new(Integer { + value: s.value.len() as i64, + }); + } + if let Some(arr) = args[0].as_any().downcast_ref::() { + return Box::new(Integer { + value: arr.elements.len() as i64, + }); + } + Box::new(Error { + message: format!("argument to `len` not supported, got {:?}", args[0].object_type()), + }) +} + +fn builtin_first(args: Vec>) -> Box { + if args.len() != 1 { + return Box::new(Error { + message: format!("wrong number of arguments. got={}, want=1", args.len()), + }); + } + if let Some(arr) = args[0].as_any().downcast_ref::() { + if !arr.elements.is_empty() { + return arr.elements[0].clone_box(); + } + return Box::new(Null); + } + Box::new(Error { + message: format!( + "argument to `first` must be ARRAY, got {:?}", + args[0].object_type() + ), + }) +} + +fn builtin_last(args: Vec>) -> Box { + if args.len() != 1 { + return Box::new(Error { + message: format!("wrong number of arguments. got={}, want=1", args.len()), + }); + } + if let Some(arr) = args[0].as_any().downcast_ref::() { + if let Some(last) = arr.elements.last() { + return last.clone_box(); + } + return Box::new(Null); + } + Box::new(Error { + message: format!( + "argument to `last` must be ARRAY, got {:?}", + args[0].object_type() + ), + }) +} + +fn builtin_rest(args: Vec>) -> Box { + if args.len() != 1 { + return Box::new(Error { + message: format!("wrong number of arguments. got={}, want=1", args.len()), + }); + } + if let Some(arr) = args[0].as_any().downcast_ref::() { + if arr.elements.is_empty() { + return Box::new(Null); + } + let mut new_elements = Vec::new(); + for (i, el) in arr.elements.iter().enumerate() { + if i != 0 { + new_elements.push(el.clone_box()); + } + } + return Box::new(Array { + elements: new_elements, + }); + } + Box::new(Error { + message: format!( + "argument to `rest` must be ARRAY, got {:?}", + args[0].object_type() + ), + }) +} + +fn builtin_push(args: Vec>) -> Box { + if args.len() != 2 { + return Box::new(Error { + message: format!("wrong number of arguments. got={}, want=2", args.len()), + }); + } + if let Some(arr) = args[0].as_any().downcast_ref::() { + let mut new_elements: Vec> = + arr.elements.iter().map(|e| e.clone_box()).collect(); + new_elements.push(args[1].clone_box()); + return Box::new(Array { + elements: new_elements, + }); + } + Box::new(Error { + message: format!( + "argument to `push` must be ARRAY, got {:?}", + args[0].object_type() + ), + }) +} + +fn builtin_puts(args: Vec>) -> Box { + for arg in args { + println!("{}", arg.inspect()); + } + Box::new(Null) +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 52704b2..90f8264 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -51,7 +51,11 @@ pub fn new_parser(l: lexer::Lexer) -> Parser { p.register_prefix(TokenType::Lparen, Parser::parse_grouped_expression); p.register_prefix(TokenType::If, Parser::parse_if_expression); p.register_prefix(TokenType::Function, Parser::parse_function_literal); + p.register_prefix(TokenType::String, Parser::parse_string_literal); + p.register_prefix(TokenType::Lbracket, Parser::parse_array_literal); + p.register_prefix(TokenType::Lbrace, Parser::parse_hash_literal); p.register_infix(TokenType::Lparen, Parser::parse_call_expression); + p.register_infix(TokenType::Lbracket, Parser::parse_index_expression); p.next_token(); p.next_token(); p @@ -96,6 +100,7 @@ impl Parser { TokenType::Plus | TokenType::Minus => Precedence::Sum, TokenType::Slash | TokenType::Asterisk => Precedence::Product, TokenType::Lparen => Precedence::Call, + TokenType::Lbracket => Precedence::Index, _ => Precedence::Lowest, } } @@ -107,6 +112,7 @@ impl Parser { TokenType::Plus | TokenType::Minus => Precedence::Sum, TokenType::Slash | TokenType::Asterisk => Precedence::Product, TokenType::Lparen => Precedence::Call, + TokenType::Lbracket => Precedence::Index, _ => Precedence::Lowest, } } @@ -304,6 +310,83 @@ impl Parser { })) } + fn parse_string_literal(&mut self) -> Option { + Some(ast::Expression::StringLiteral(ast::StringLiteral { + token: self.cur_token.clone(), + value: self.cur_token.literal.clone(), + })) + } + + fn parse_array_literal(&mut self) -> Option { + let token = self.cur_token.clone(); + let elements = self.parse_expression_list(TokenType::Rbracket)?; + Some(ast::Expression::ArrayLiteral(ast::ArrayLiteral { + token, + elements, + })) + } + + fn parse_hash_literal(&mut self) -> Option { + let token = self.cur_token.clone(); + let mut pairs = Vec::new(); + + while !self.peek_token_is(TokenType::Rbrace) { + self.next_token(); + let key = self.parse_expression(Precedence::Lowest)?; + if !self.expect_peek(TokenType::Colon) { + return None; + } + self.next_token(); + let value = self.parse_expression(Precedence::Lowest)?; + pairs.push((key, value)); + if !self.peek_token_is(TokenType::Rbrace) && !self.expect_peek(TokenType::Comma) { + return None; + } + } + + if !self.expect_peek(TokenType::Rbrace) { + return None; + } + + Some(ast::Expression::HashLiteral(ast::HashLiteral { token, pairs })) + } + + fn parse_expression_list(&mut self, end: TokenType) -> Option> { + let mut list = Vec::new(); + if self.peek_token_is(end) { + self.next_token(); + return Some(list); + } + self.next_token(); + list.push(self.parse_expression(Precedence::Lowest)?); + while self.peek_token_is(TokenType::Comma) { + self.next_token(); + self.next_token(); + list.push(self.parse_expression(Precedence::Lowest)?); + } + if !self.expect_peek(end) { + return None; + } + Some(list) + } + + fn parse_index_expression( + &mut self, + left: ast::Expression, + ) -> Option { + let token = self.cur_token.clone(); + self.next_token(); + let index = self.parse_expression(Precedence::Lowest)?; + if !self.expect_peek(TokenType::Rbracket) { + return None; + } + Some(ast::Expression::IndexExpression(ast::IndexExpression { + token, + left: Box::new(left), + index: Box::new(index), + })) + } + fn parse_grouped_expression(&mut self) -> Option { self.next_token(); let exp = self.parse_expression(Precedence::Lowest)?; @@ -464,4 +547,5 @@ pub enum Precedence { Product, Prefix, Call, + Index, } diff --git a/src/token/mod.rs b/src/token/mod.rs index ed65960..fa17b54 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -7,6 +7,7 @@ pub enum TokenType { Eof, Ident, Int, + String, Assign, Plus, Minus, @@ -17,10 +18,13 @@ pub enum TokenType { Gt, Comma, Semicolon, + Colon, Lparen, Rparen, Lbrace, Rbrace, + Lbracket, + Rbracket, Function, Let, True, @@ -39,6 +43,7 @@ impl fmt::Display for TokenType { TokenType::Eof => write!(f, "EOF"), TokenType::Ident => write!(f, "IDENT"), TokenType::Int => write!(f, "INT"), + TokenType::String => write!(f, "STRING"), TokenType::Assign => write!(f, "="), TokenType::Plus => write!(f, "+"), TokenType::Minus => write!(f, "-"), @@ -49,10 +54,13 @@ impl fmt::Display for TokenType { TokenType::Gt => write!(f, ">"), TokenType::Comma => write!(f, ","), TokenType::Semicolon => write!(f, ";"), + TokenType::Colon => write!(f, ":"), TokenType::Lparen => write!(f, "("), TokenType::Rparen => write!(f, ")"), TokenType::Lbrace => write!(f, "{{"), TokenType::Rbrace => write!(f, "}}"), + TokenType::Lbracket => write!(f, "["), + TokenType::Rbracket => write!(f, "]"), TokenType::Function => write!(f, "FUNCTION"), TokenType::Let => write!(f, "LET"), TokenType::True => write!(f, "TRUE"),