From 1b055995d9a429b3190e7ffa980854823cf212eb Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Wed, 25 Oct 2023 13:47:49 +0100 Subject: [PATCH] feat: strings in compiler and VM --- compiler/src/lib.rs | 11 ++++++++++- compiler/tests/compiler_tests.rs | 31 +++++++++++++++++++++++++++++++ vm/src/lib.rs | 1 + vm/tests/vm_tests.rs | 22 ++++++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index ae99457..2ddc6db 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use anyhow::Error; use opcode::Opcode; use parser::ast::{ - BlockStatement, BooleanLiteral, Expression, IntegerLiteral, Literal, Node, Statement, + BlockStatement, BooleanLiteral, Expression, IntegerLiteral, Literal, Node, Statement, StringLiteral, }; use symbol_table::SymbolTable; @@ -303,6 +303,15 @@ impl Compiler { Ok(()) } + Literal::String(StringLiteral { value, .. }) => { + let string = object::Object::String(value.clone()); + + let constant = self.add_constant(string); + + self.emit(opcode::Opcode::OpConst, vec![constant]); + + Ok(()) + } _ => { return Err(Error::msg("compile_expression: unimplemented")); } diff --git a/compiler/tests/compiler_tests.rs b/compiler/tests/compiler_tests.rs index 21c271d..070e0fe 100644 --- a/compiler/tests/compiler_tests.rs +++ b/compiler/tests/compiler_tests.rs @@ -196,6 +196,37 @@ fn test_integer_arithmetic() -> Result<(), Error> { Ok(()) } +#[test] +fn test_string_expressions() -> Result<(), Error> { + let tests = vec![ + CompilerTestCase { + input: "\"hello\"".to_string(), + expected_constants: vec![Object::String("hello".to_string())], + expected_instructions: vec![ + opcode::make(opcode::Opcode::OpConst, &vec![0]), + opcode::make(opcode::Opcode::OpPop, &vec![]), + ], + }, + CompilerTestCase { + input: "\"hello\" + \"world\"".to_string(), + expected_constants: vec![ + Object::String("hello".to_string()), + Object::String("world".to_string()), + ], + expected_instructions: vec![ + opcode::make(opcode::Opcode::OpConst, &vec![0]), + opcode::make(opcode::Opcode::OpConst, &vec![1]), + opcode::make(opcode::Opcode::OpAdd, &vec![1]), + opcode::make(opcode::Opcode::OpPop, &vec![0]), + ] + } + ]; + + run_compiler_tests(tests)?; + + Ok(()) +} + fn run_compiler_tests(tests: Vec) -> Result<(), Error> { for test in tests { let mut parser = parser::Parser::new(Lexer::new(&test.input)); diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 86fd6cc..fe7d940 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -107,6 +107,7 @@ impl Vm { let result = match (&*left, &*right) { (Object::Integer(l), Object::Integer(r)) => Object::Integer(l + r), + (Object::String(l), Object::String(r)) => Object::String(format!("{}{}", l, r)), _ => { return Err(Error::msg(format!( "unsupported types for addition: {} + {}", diff --git a/vm/tests/vm_tests.rs b/vm/tests/vm_tests.rs index 0327ba4..8ccf3b2 100644 --- a/vm/tests/vm_tests.rs +++ b/vm/tests/vm_tests.rs @@ -249,3 +249,25 @@ fn test_global_dollar_statements() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_string_expressions() -> Result<(), Error> { + let tests = vec![ + VmTestCase { + input: r#""hello""#.to_string(), + expected: "hello".to_string(), + }, + VmTestCase { + input: r#""hello" + "world""#.to_string(), + expected: "helloworld".to_string(), + }, + VmTestCase { + input: r#""hello" + "world" + "!""#.to_string(), + expected: "helloworld!".to_string(), + }, + ]; + + run_vm_tests(tests)?; + + Ok(()) +} \ No newline at end of file