diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index f640a38..28fe286 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -54,6 +54,7 @@ impl Environment { #[derive(Debug, Clone, PartialEq)] pub enum Object { Integer(i64), + String(String), Boolean(bool), ReturnValue(Box), Error(String), @@ -69,6 +70,7 @@ impl Object { pub fn inspect(&self) -> String { match self { Object::Integer(v) => v.to_string(), + Object::String(s) => s.to_string(), Object::Boolean(v) => v.to_string(), Object::ReturnValue(ret) => ret.inspect(), Object::Error(s) => s.to_string(), @@ -79,7 +81,7 @@ impl Object { out.write_fmt(format_args!( "fn({}) {{ {} }}", s.parameters.iter().map(|x| x.to_string()).join(", "), - s.body.to_string() + s.body )) .unwrap(); @@ -93,6 +95,7 @@ impl Display for Object { fn fmt(&self, f: &mut Formatter) -> FmtResult { f.write_str(match self { Object::Integer(_) => "INTEGER", + Object::String(_) => "STRING", Object::Boolean(_) => "BOOLEAN", Object::ReturnValue(_) => "RETURN_VALUE", Object::Error(_) => "ERROR", @@ -195,6 +198,23 @@ mod tests { run_test_cases(&test_cases); } + #[test] + fn eval_string_literal_expression() { + let test_cases = [("\"Hello \"", Some(Object::String("Hello ".to_owned())))]; + + run_test_cases(&test_cases); + } + + #[test] + fn eval_string_concatenation() { + let test_cases = [( + "\"Hello \" + \"World\"", + Some(Object::String("Hello World".to_owned())), + )]; + + run_test_cases(&test_cases); + } + #[test] fn eval_if_else_expression() { let test_cases = [ @@ -280,6 +300,10 @@ mod tests { "foobar", Some(Object::Error("identifier not found: foobar".into())), ), + ( + "\"Hello\" - \"World\"", + Some(Object::Error("unknown operator: STRING - STRING".into())), + ), ]; run_test_cases(&test_cases); @@ -304,7 +328,7 @@ mod tests { fn test_function_object() { let test_case = "fn(x) { x + 2;};"; - let lexer = Lexer::new(&test_case); + let lexer = Lexer::new(test_case); let mut parser = Parser::new(lexer); let program = parser.parse_program(); assert!(program.is_some()); @@ -373,7 +397,7 @@ mod tests { // even though it is never used. let test_cases = [( "let counter = fn(x) { - if (x > 100 ) { + if (x > 30 ) { return true; } else { let foobar = 9999; diff --git a/src/evaluator/tree_walker.rs b/src/evaluator/tree_walker.rs index bd21491..377c7b5 100644 --- a/src/evaluator/tree_walker.rs +++ b/src/evaluator/tree_walker.rs @@ -46,6 +46,7 @@ impl Evaluator for TreeWalker { Node::Expression(expr) => match expr { 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::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::PrefixExpression(p) => { let expr = self.eval(Node::Expression(*p.right), env)?; @@ -56,13 +57,11 @@ impl Evaluator for TreeWalker { let right = self.eval(Node::Expression(*ie.right), env)?; self.eval_infix_expression(left, ie.operator, right) } - Expression::FunctionExpression(fnl) => { - return Some(Object::Function(Function { - body: fnl.body, - parameters: fnl.parameters, - env: env, - })); - } + Expression::FunctionExpression(fnl) => Some(Object::Function(Function { + body: fnl.body, + parameters: fnl.parameters, + env, + })), Expression::CallExpression(v) => { let function = self.eval(Node::Expression(*v.function), env.clone())?; // Resolve function arguments and update the environment @@ -179,6 +178,9 @@ impl TreeWalker { TokenType::LessThan => Object::Boolean(l < r), _ => Object::Error(format!("unknown operator: {} {} {}", l, operator, r)), }, + (Object::String(l), Object::String(r)) if operator == TokenType::Plus => { + Object::String(l.to_owned() + &r) + } (o1, o2) if o1.to_string() != o2.to_string() => { Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2)) } diff --git a/src/lexer.rs b/src/lexer.rs index d5b81db..873c96e 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -31,6 +31,7 @@ pub enum TokenType { // by other variants of this enum. Ident, Int, + String, // Operators Assign, Plus, @@ -91,6 +92,7 @@ impl Display for TokenType { TokenType::Illegal => "illegal", TokenType::Ident => "ident", TokenType::Int => "int", + TokenType::String => "string", }) } } @@ -199,6 +201,19 @@ impl<'a> Lexer<'a> { } number.into_iter().collect() } + + fn read_string(&mut self) -> String { + let mut out = String::new(); + + while let Some(c) = self.read_char() { + if c == '"' { + break; + } + out.push(c); + } + + out + } } impl<'a> Iterator for Lexer<'a> { @@ -210,10 +225,7 @@ impl<'a> Iterator for Lexer<'a> { match ch { Some('=') => { - let is_e = match self.input.peek() { - Some(v) if *v == '=' => true, - _ => false, - }; + let is_e = matches!(self.input.peek(), Some(v) if *v == '='); if is_e { self.read_char(); Some(token!(TokenType::Equals)) @@ -232,10 +244,7 @@ impl<'a> Iterator for Lexer<'a> { Some('{') => Some(token!(TokenType::LBrace)), Some('}') => Some(token!(TokenType::RBrace)), Some('!') => { - let is_ne = match self.input.peek() { - Some(v) if *v == '=' => true, - _ => false, - }; + let is_ne = matches!(self.input.peek(), Some(v) if *v == '='); if is_ne { self.read_char(); Some(token!(TokenType::NotEquals)) @@ -245,6 +254,10 @@ impl<'a> Iterator for Lexer<'a> { } Some('>') => Some(token!(TokenType::GreaterThan)), Some('<') => Some(token!(TokenType::LessThan)), + Some('"') => { + let str = self.read_string(); + Some(token!(TokenType::String, &str)) + } Some(ch) if is_letter(ch) => { let ident = self.read_identifier(ch); Some(lookup_ident(&ident)) @@ -364,6 +377,9 @@ mod tests { 10 == 10; 9 != 10; + \"foobar\" + \"foo bar\" + " ) .collect::>(), @@ -415,6 +431,8 @@ mod tests { token!(TokenType::NotEquals), token!(TokenType::Int, "10"), token!(TokenType::Semicolon), + token!(TokenType::String, "foobar"), + token!(TokenType::String, "foo bar"), token!(TokenType::EOF), ], ); diff --git a/src/main.rs b/src/main.rs index 0708c1e..2621f72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(assert_matches)] #[macro_use] extern crate lazy_static; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index be6cf9c..e137e6c 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -102,7 +102,7 @@ impl LetStatement { impl Display for LetStatement { fn fmt(&self, f: &mut Formatter) -> FmtResult { - let mut out = format!("{} {} = ", TokenType::Let.to_string(), self.name.value); + let mut out = format!("{} {} = ", TokenType::Let, self.name.value); if let Some(v) = &self.value { let a: String = v.into(); @@ -188,6 +188,7 @@ pub enum ExpressionPriority { pub enum Expression { Identifier(Identifier), IntegerLiteral(IntegerLiteral), + StringLiteral(StringLiteral), PrefixExpression(PrefixExpression), InfixExpression(InfixExpression), BooleanExpression(BooleanExpression), @@ -242,6 +243,7 @@ impl Display for Expression { let value = match self { Expression::Identifier(v) => v.to_string(), Expression::IntegerLiteral(v) => v.value.to_string(), + Expression::StringLiteral(v) => v.value.to_string(), Expression::PrefixExpression(v) => v.to_string(), Expression::InfixExpression(v) => v.to_string(), Expression::BooleanExpression(v) => v.to_string(), @@ -313,6 +315,24 @@ impl IntegerLiteral { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct StringLiteral { + pub value: String, +} + +impl StringLiteral { + pub fn new(v: &str) -> Self { + Self { + value: v.to_string(), + } + } + pub fn parse(_parser: &mut Parser, token: Token) -> Option { + Some(Expression::StringLiteral(StringLiteral::new( + &token.literal?, + ))) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct PrefixExpression { pub operator: TokenType, @@ -339,11 +359,7 @@ impl PrefixExpression { impl Display for PrefixExpression { fn fmt(&self, f: &mut Formatter) -> FmtResult { - f.write_fmt(format_args!( - "({}{})", - self.operator, - self.right.to_string() - )) + f.write_fmt(format_args!("({}{})", self.operator, self.right)) } } @@ -376,9 +392,7 @@ impl Display for InfixExpression { fn fmt(&self, f: &mut Formatter) -> FmtResult { f.write_fmt(format_args!( "({} {} {})", - self.left.to_string(), - self.operator, - self.right.to_string(), + self.left, self.operator, self.right, )) } } @@ -449,13 +463,9 @@ impl IfExpression { impl Display for IfExpression { fn fmt(&self, f: &mut Formatter) -> FmtResult { - let mut out = format!( - "if {} {{ {} }}", - self.condition.to_string(), - self.consequence.to_string() - ); + let mut out = format!("if {} {{ {} }}", self.condition, self.consequence); if let Some(alternative) = &self.alternative { - out += &format!(" else {{ {} }}", alternative.to_string()); + out += &format!(" else {{ {} }}", alternative); } f.write_str(&out) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9cab234..2ffd219 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -48,6 +48,7 @@ impl<'a> Parser<'a> { infix_parse_fns: HashMap::new(), }; + parser.register_prefix(TokenType::String, StringLiteral::parse); parser.register_prefix(TokenType::Ident, Identifier::parse); parser.register_prefix(TokenType::Int, IntegerLiteral::parse); parser.register_prefix(TokenType::Bang, PrefixExpression::parse); @@ -101,10 +102,7 @@ impl<'a> Parser<'a> { if self.peek_token_is(token) { self.lexer.next() } else { - let got_token = match self.lexer.peek() { - Some(v) => Some(v.name), - None => None, - }; + let got_token = self.lexer.peek().map(|v| v.name); self.peek_error(token, got_token); None } @@ -113,10 +111,7 @@ impl<'a> Parser<'a> { fn peek_error(&mut self, et: TokenType, gt: Option) { let msg = match gt { Some(v) => format!("expected next token to be {:?}, Got {:?} instead", et, v), - None => format!( - "expected next token to be {}, Got None instead", - et.to_string() - ), + None => format!("expected next token to be {}, Got None instead", et), }; self.errors.push(Error { reason: msg }); } @@ -131,7 +126,7 @@ impl<'a> Parser<'a> { fn no_prefix_parse_fn_error(&mut self, token: TokenType) { self.errors.push(Error { - reason: format!("no prefix parse function for {} found", token.to_string()), + reason: format!("no prefix parse function for {} found", token), }); } @@ -288,6 +283,24 @@ mod tests { ); } + #[test] + fn string_literal_expression() { + let lexer = Lexer::new("\"hello world\";"); + let mut parser = Parser::new(lexer); + let program = parser.parse_program(); + check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); + assert!(program.is_some()); + + assert_eq!( + program.unwrap().statements, + vec![Statement::ExpressionStatement(ExpressionStatement::new( + token!(TokenType::String, "hello world"), + Expression::StringLiteral(StringLiteral::new("hello world")) + ))] + ); + } + #[test] fn prefix_expressions() { let test_cases = [