diff --git a/rlox/src/bc.rs b/rlox/src/bc.rs index 370c2e6..dacbc1c 100644 --- a/rlox/src/bc.rs +++ b/rlox/src/bc.rs @@ -1,7 +1,7 @@ use crate::bc::Value::{Bool, Number}; use std::convert::From; use std::fmt; - +use std::fmt::{Debug}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Op { Return, @@ -19,28 +19,46 @@ pub enum Op { Greater, Less, } +#[derive(Clone, Debug, PartialEq)] +pub enum Object { + String(String) +} -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +#[derive(Clone, Debug, PartialEq)] pub enum Value { Nil, Bool(bool), Number(f64), + Obj(Box) } impl Value { - pub fn as_num(self) -> Option { + pub fn as_num(&self) -> Option { match self { - Number(val) => Some(val), + &Number(val) => Some(val), _ => None, } } - pub fn as_bool(self) -> Option { + pub fn as_bool(&self) -> Option { match self { - Bool(val) => Some(val), + &Bool(val) => Some(val), _ => None, } } + + pub fn as_str(&self) -> Option<&str> { + match self { + Value::Obj(obj) => { + match obj.as_ref() { + Object::String(string) => { + Some(string.as_str()) + } + } + }, + _ => None + } + } } impl From for Value { @@ -55,6 +73,18 @@ impl From for Value { } } +impl From<&str> for Value { + fn from(value: &str) -> Self { + Value::Obj(Box::from(Object::String(value.to_string()))) + } +} + +impl From for Value { + fn from(value: String) -> Self { + Value::Obj(Box::from(Object::String(value))) + } +} + pub struct Chunk { pub code: Vec, pub debug_info: Vec, @@ -164,3 +194,20 @@ impl fmt::Debug for TraceInfo<'_> { } } } + +mod tests { + use crate::bc::{Object, Value}; + + #[test] + fn string_value_equality() { + let s1 = "bla5"; + let s2 = "bla6"; + + let v1 = Value::from(s1); + let v2 = Value::from(s2); + let v3 = Value::from(s2); + + assert_ne!(v1, v2); + assert_eq!(v2, v3); + } +} diff --git a/rlox/src/lc.rs b/rlox/src/lc.rs index acbe501..5bc6d55 100644 --- a/rlox/src/lc.rs +++ b/rlox/src/lc.rs @@ -318,47 +318,39 @@ impl<'src> Parser<'src> { fn _expression(&mut self, chunk: &mut Chunk, min_prec: Precedence) { match self.scanner.next() { None => panic!("Expected further tokens"), - Some(token) => match token { - Token { - ttype: ttype@(TokenType::Minus | TokenType::Bang), - span: _, - } => { + Some(token) => match token.ttype { + TokenType::Minus | TokenType::Bang => { self._expression(chunk, Precedence::Unary); - let op = match ttype { + let op = match token.ttype { TokenType::Minus => Op::Negate, TokenType::Bang => Op::Not, _ => unreachable!(), }; chunk.add_op(op, 0); - } - Token { - ttype: TokenType::Number, - span, - } => { - match span.parse::() { + }, + TokenType::Number => { + match token.span.parse::() { Ok(c) => chunk.add_constant(c.into(), 0), _ => panic!("Could not parse number"), }; - } - Token { - ttype: ttype@(TokenType::Nil | TokenType::True | TokenType::False), - span: _, - } => { - let op = match ttype { + }, + TokenType::String => { + let without_quotes = &token.span[1..(token.span.len() - 1)]; + chunk.add_constant(without_quotes.into(), 0); + }, + TokenType::Nil | TokenType::True | TokenType::False => { + let op = match token.ttype { TokenType::Nil => Op::Nil, TokenType::True => Op::True, TokenType::False => Op::False, _ => unreachable!() }; chunk.add_op(op, 0); - } - Token { - ttype: TokenType::LeftParen, - span: _, - } => { + }, + TokenType::LeftParen => { self._expression(chunk, Precedence::None); assert_eq!(self.scanner.next().unwrap().ttype, TokenType::RightParen) - } + }, _ => panic!("Expected '-' or number"), }, }; @@ -463,6 +455,25 @@ mod tests { ); } + #[test] + fn string_scan() { + let source = "\"hello world\""; + let scanner = Scanner::new(source); + let tokens: Vec = scanner.collect(); + + assert_eq!( + tokens, + vec![ + Token { + ttype: TokenType::String, + span: &source[0..=12] + } + ] + ); + + assert_eq!(tokens[0].span, source); + } + fn test_parse_expression(source: &str, expected: &Chunk) { let scanner = Scanner::new(source); let mut parser = Parser::new(scanner); diff --git a/rlox/src/main.rs b/rlox/src/main.rs index 192f4db..45bc046 100644 --- a/rlox/src/main.rs +++ b/rlox/src/main.rs @@ -58,4 +58,14 @@ mod tests { let mut vm = VM::new(); vm.run(&chunk).unwrap(); } + + #[test] + fn string_handling() { + let source = "\"hello\" + \" \" + \"world\""; + let mut chunk = Chunk::new(); + compile(source, &mut chunk); + let mut vm = VM::new(); + let v = vm.run(&chunk).unwrap(); + assert_eq!(v, Some("hello world".into())); + } } diff --git a/rlox/src/vm.rs b/rlox/src/vm.rs index 5aa38f1..a43b0d9 100644 --- a/rlox/src/vm.rs +++ b/rlox/src/vm.rs @@ -1,4 +1,4 @@ -use crate::bc::{Chunk, Op, TraceInfo, Value}; +use crate::bc::{Object, Chunk, Op, TraceInfo, Value}; use std::ops::Not; use std::rc::Rc; @@ -55,13 +55,6 @@ impl VM { .ok_or(self.type_err("Number", top_of_stack)) } - fn pop_bool(&mut self) -> Result { - let top_of_stack = self.pop()?; - top_of_stack - .as_bool() - .ok_or(self.type_err("Boolean", top_of_stack)) - } - pub fn run(&mut self, chunk: &Chunk) -> Result, VMError> { while self.pc < chunk.code.len() { let instr = chunk.code[self.pc]; @@ -86,7 +79,7 @@ impl VM { match instr { Op::Return => print!("{:?}", self.pop()?), - Op::Constant { offset } => self.push(chunk.constants[offset]), + Op::Constant { offset } => self.push(chunk.constants[offset].clone()), Op::Nil => self.push(Value::Nil), Op::True => self.push(Value::Bool(true)), Op::False => self.push(Value::Bool(false)), @@ -103,11 +96,34 @@ impl VM { }?; self.push(new_val.into()); } - Op::Add | Op::Subtract | Op::Multiply | Op::Divide => { + Op::Add => { + let b = self.pop()?; + match b { + Value::Number(num) => { + let a = self.pop_num()?; + self.push(Value::from(num + a)); + } + Value::Obj(ref obj) => { + match b.as_str() { + None => Err(self.type_err("String", b)), + Some(str_b) => { + let a = self.pop()?; + match a.as_str() { + Some(str_a) => { + Ok(self.push(Value::from(str_a.to_owned() + str_b))) + }, + None => Err(self.type_err("String", a)) + } + } + }? + } + _ => todo!() + }; + } + Op::Subtract | Op::Multiply | Op::Divide => { let b = self.pop_num()?; let a = self.pop_num()?; let r = match instr { - Op::Add => a + b, Op::Subtract => a - b, Op::Multiply => a * b, Op::Divide => a / b, @@ -115,17 +131,22 @@ impl VM { }; self.push(r.into()) } - Op::Equal | Op::Greater | Op::Less => { - let b = self.pop()?; - let a = self.pop()?; + Op::Greater | Op::Less => { + let b = self.pop_num()?; + let a = self.pop_num()?; let r = match instr { - Op::Equal => a == b, Op::Greater => a > b, Op::Less => a < b, _ => unreachable!(), }; self.push(r.into()) } + Op::Equal => { + let b = self.pop()?; + let a = self.pop()?; + let r = a == b; + self.push(r.into()) + } } } @@ -133,7 +154,7 @@ impl VM { .stack .is_empty() .not() - .then_some(self.stack[self.stack.len() - 1])) + .then_some(self.stack[self.stack.len() - 1].clone())) } }