diff options
-rw-r--r-- | users/tazjin/rlox/src/bytecode/compiler.rs | 74 | ||||
-rw-r--r-- | users/tazjin/rlox/src/bytecode/interner/mod.rs | 2 | ||||
-rw-r--r-- | users/tazjin/rlox/src/bytecode/opcode.rs | 3 | ||||
-rw-r--r-- | users/tazjin/rlox/src/bytecode/value.rs | 2 | ||||
-rw-r--r-- | users/tazjin/rlox/src/bytecode/vm.rs | 14 |
5 files changed, 80 insertions, 15 deletions
diff --git a/users/tazjin/rlox/src/bytecode/compiler.rs b/users/tazjin/rlox/src/bytecode/compiler.rs index f2fdeb0e9feb..f993e35557ad 100644 --- a/users/tazjin/rlox/src/bytecode/compiler.rs +++ b/users/tazjin/rlox/src/bytecode/compiler.rs @@ -1,6 +1,6 @@ use super::chunk::Chunk; use super::errors::{Error, ErrorKind, LoxResult}; -use super::interner::Interner; +use super::interner::{InternedStr, Interner}; use super::opcode::OpCode; use super::value::Value; use crate::scanner::{self, Token, TokenKind}; @@ -182,8 +182,30 @@ impl<T: Iterator<Item = Token>> Compiler<T> { self.parse_precedence(Precedence::Assignment) } + fn var_declaration(&mut self) -> LoxResult<()> { + let global = self.parse_variable()?; + + if self.match_token(&TokenKind::Equal) { + self.expression()?; + } else { + self.emit_op(OpCode::OpNil); + } + + self.expect_semicolon("expect ';' after variable declaration")?; + self.define_variable(global) + } + + fn define_variable(&mut self, var: usize) -> LoxResult<()> { + self.emit_op(OpCode::OpDefineGlobal(var)); + Ok(()) + } + fn declaration(&mut self) -> LoxResult<()> { - self.statement()?; + if self.match_token(&TokenKind::Var) { + self.var_declaration()?; + } else { + self.statement()?; + } if self.panic { self.synchronise(); @@ -202,22 +224,14 @@ impl<T: Iterator<Item = Token>> Compiler<T> { fn print_statement(&mut self) -> LoxResult<()> { self.expression()?; - consume!( - self, - TokenKind::Semicolon, - ErrorKind::ExpectedToken("Expected ';' after value") - ); + self.expect_semicolon("expect ';' after print statement")?; self.emit_op(OpCode::OpPrint); Ok(()) } fn expression_statement(&mut self) -> LoxResult<()> { self.expression()?; - consume!( - self, - TokenKind::Semicolon, - ErrorKind::ExpectedToken("Expected ';' after expression") - ); + self.expect_semicolon("expect ';' after expression")?; self.emit_op(OpCode::OpPop); Ok(()) } @@ -346,6 +360,34 @@ impl<T: Iterator<Item = Token>> Compiler<T> { Ok(()) } + fn identifier_str( + &mut self, + token_fn: fn(&Self) -> &Token, + ) -> LoxResult<InternedStr> { + let ident = match &token_fn(self).kind { + TokenKind::Identifier(ident) => ident.to_string(), + _ => { + return Err(Error { + line: self.current().line, + kind: ErrorKind::ExpectedToken("Expected identifier"), + }) + } + }; + + Ok(self.strings.intern(ident)) + } + + fn parse_variable(&mut self) -> LoxResult<usize> { + consume!( + self, + TokenKind::Identifier(_), + ErrorKind::ExpectedToken("expected identifier") + ); + + let id = self.identifier_str(Self::previous)?; + Ok(self.emit_constant(Value::String(id.into()))) + } + fn current_chunk(&mut self) -> &mut Chunk { &mut self.chunk } @@ -367,9 +409,10 @@ impl<T: Iterator<Item = Token>> Compiler<T> { self.current_chunk().add_op(op, line); } - fn emit_constant(&mut self, val: Value) { + fn emit_constant(&mut self, val: Value) -> usize { let idx = self.chunk.add_constant(val); self.emit_op(OpCode::OpConstant(idx)); + idx } fn previous(&self) -> &Token { @@ -430,6 +473,11 @@ impl<T: Iterator<Item = Token>> Compiler<T> { } } } + + fn expect_semicolon(&mut self, msg: &'static str) -> LoxResult<()> { + consume!(self, TokenKind::Semicolon, ErrorKind::ExpectedToken(msg)); + Ok(()) + } } pub fn compile(code: &str) -> Result<(Interner, Chunk), Vec<Error>> { diff --git a/users/tazjin/rlox/src/bytecode/interner/mod.rs b/users/tazjin/rlox/src/bytecode/interner/mod.rs index f5f695904e85..1da1a24b2c5f 100644 --- a/users/tazjin/rlox/src/bytecode/interner/mod.rs +++ b/users/tazjin/rlox/src/bytecode/interner/mod.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; #[cfg(test)] mod tests; -#[derive(Clone, Copy, Debug, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InternedStr { id: usize, } diff --git a/users/tazjin/rlox/src/bytecode/opcode.rs b/users/tazjin/rlox/src/bytecode/opcode.rs index 37466b30564c..25ce03c3c182 100644 --- a/users/tazjin/rlox/src/bytecode/opcode.rs +++ b/users/tazjin/rlox/src/bytecode/opcode.rs @@ -29,4 +29,7 @@ pub enum OpCode { // Built in operations OpPrint, OpPop, + + // Variable definitions + OpDefineGlobal(usize), } diff --git a/users/tazjin/rlox/src/bytecode/value.rs b/users/tazjin/rlox/src/bytecode/value.rs index 19a4dcc92eac..4170efadf8fe 100644 --- a/users/tazjin/rlox/src/bytecode/value.rs +++ b/users/tazjin/rlox/src/bytecode/value.rs @@ -8,7 +8,7 @@ pub enum Value { String(LoxString), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum LoxString { Heap(String), Interned(InternedStr), diff --git a/users/tazjin/rlox/src/bytecode/vm.rs b/users/tazjin/rlox/src/bytecode/vm.rs index 9161bdfe53b8..53431a083785 100644 --- a/users/tazjin/rlox/src/bytecode/vm.rs +++ b/users/tazjin/rlox/src/bytecode/vm.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use super::chunk; use super::errors::*; use super::interner::Interner; @@ -14,6 +16,8 @@ pub struct VM { stack: Vec<Value>, strings: Interner, + globals: HashMap<LoxString, Value>, + // Operations that consume values from the stack without pushing // anything leave their last value in this slot, which makes it // possible to return values from interpreters that ran code which @@ -157,6 +161,15 @@ impl VM { OpCode::OpPop => { self.last_drop = Some(self.pop()); } + + OpCode::OpDefineGlobal(name_idx) => { + let name = self.chunk.constant(*name_idx); + with_type!(self, name, Value::String(name), { + let name = name.clone(); + let val = self.pop(); + self.globals.insert(name, val); + }); + } } #[cfg(feature = "disassemble")] @@ -197,6 +210,7 @@ pub fn interpret(strings: Interner, chunk: chunk::Chunk) -> LoxResult<Value> { let mut vm = VM { chunk, strings, + globals: HashMap::new(), ip: 0, stack: vec![], last_drop: None, |