From 299900eda8bb59f929f7b4cb9623c31eb3c0ed82 Mon Sep 17 00:00:00 2001 From: Patric Bucher Date: Mon, 27 Nov 2023 23:36:14 +0100 Subject: [PATCH] Added value support for booleans and nil --- src/compiler/parser/mod.rs | 37 +++++++++++--------- src/compiler/parser/rules.rs | 6 ++-- src/vm/block/block.rs | 19 +++++------ src/vm/block/disassembler.rs | 7 +++- src/vm/mod.rs | 35 +++++++++++++++++-- src/vm/opcodes.rs | 3 ++ src/vm/value.rs | 31 +++++++++++++++++ src/vm/virtual_machine.rs | 65 ++++++++++++++++++++++++++++++++---- 8 files changed, 165 insertions(+), 38 deletions(-) create mode 100644 src/vm/value.rs diff --git a/src/compiler/parser/mod.rs b/src/compiler/parser/mod.rs index 790895f..5ecc92a 100644 --- a/src/compiler/parser/mod.rs +++ b/src/compiler/parser/mod.rs @@ -4,6 +4,7 @@ use crate::compiler::{Parser, Scanner, Token}; use crate::vm::opcodes::OpCode; use crate::vm::{Block, Value}; use rules::{ParseRule, Precedence}; +use std::str::FromStr; use tracing_attributes::instrument; mod rules; @@ -60,8 +61,8 @@ impl Parser { #[cfg_attr(feature = "disassemble", instrument(skip(self)))] fn number(&mut self) { - let value = self.previous_token.token.parse::().unwrap(); - self.emit_constant(value); + let value = f64::from_str(&*self.previous_token.token).unwrap(); + self.emit_constant(Value::from_number(value)); } #[cfg_attr(feature = "disassemble", instrument(skip(self)))] @@ -77,10 +78,19 @@ impl Parser { self.parse_precedence(Precedence::from_u8(rule.precedence as u8 + 1)); match operator_type { - token_type if token_type == TokenType::Plus => self.emit_byte(OpCode::Add as u8), - token_type if token_type == TokenType::Minus => self.emit_byte(OpCode::Subtract as u8), - token_type if token_type == TokenType::Star => self.emit_byte(OpCode::Multiply as u8), - token_type if token_type == TokenType::Slash => self.emit_byte(OpCode::Divide as u8), + token_type if token_type == TokenType::Plus => self.emit_op_code(OpCode::Add), + token_type if token_type == TokenType::Minus => self.emit_op_code(OpCode::Subtract), + token_type if token_type == TokenType::Star => self.emit_op_code(OpCode::Multiply), + token_type if token_type == TokenType::Slash => self.emit_op_code(OpCode::Divide), + _ => return, // Unreachable. + } + } + + fn literal(&mut self) { + match self.previous_token.token_type { + TokenType::False => self.emit_op_code(OpCode::False), + TokenType::Nil => self.emit_op_code(OpCode::Nil), + TokenType::True => self.emit_op_code(OpCode::True), _ => return, // Unreachable. } } @@ -94,7 +104,7 @@ impl Parser { // Emit the operator instruction. match operator_type { - TokenType::Minus => self.emit_byte(OpCode::Negate as u8), + TokenType::Minus => self.emit_op_code(OpCode::Negate), _ => return, // Unreachable. } } @@ -120,6 +130,7 @@ impl Parser { if self.panic_mode { return; } + self.had_error = true; self.panic_mode = true; @@ -149,19 +160,15 @@ impl Parser { } fn emit_return(&mut self) { - self.emit_byte(OpCode::Return as u8); + self.emit_op_code(OpCode::Return); } fn emit_constant(&mut self, value: Value) { self.current_block().write_constant(value, 0) } - fn emit_byte(&mut self, byte: u8) { - self.current_block().write_u8(byte); - } - - fn emit_bytes(&mut self, byte1: u8, byte2: u8) { - self.current_block().write_u8(byte1); - self.current_block().write_u8(byte2); + fn emit_op_code(&mut self, op_code: OpCode) { + let line = self.previous_token.line; + self.current_block().write_op_code(op_code, line); } } diff --git a/src/compiler/parser/rules.rs b/src/compiler/parser/rules.rs index c4dd54c..66f97dd 100644 --- a/src/compiler/parser/rules.rs +++ b/src/compiler/parser/rules.rs @@ -73,17 +73,17 @@ lazy_static! { (TokenType::And, ParseRule::new(None, None, Precedence::None)), (TokenType::Class, ParseRule::new(None, None, Precedence::None)), (TokenType::Else, ParseRule::new(None, None, Precedence::None)), - (TokenType::False, ParseRule::new(None, None, Precedence::None)), + (TokenType::False, ParseRule::new(Some(Parser::literal), None, Precedence::None)), (TokenType::For, ParseRule::new(None, None, Precedence::None)), (TokenType::Fun, ParseRule::new(None, None, Precedence::None)), (TokenType::If, ParseRule::new(None, None, Precedence::None)), - (TokenType::Nil, ParseRule::new(None, None, Precedence::None)), + (TokenType::Nil, ParseRule::new(Some(Parser::literal), None, Precedence::None)), (TokenType::Or, ParseRule::new(None, None, Precedence::None)), (TokenType::Print, ParseRule::new(None, None, Precedence::None)), (TokenType::Return, ParseRule::new(None, None, Precedence::None)), (TokenType::Super, ParseRule::new(None, None, Precedence::None)), (TokenType::This, ParseRule::new(None, None, Precedence::None)), - (TokenType::True, ParseRule::new(None, None, Precedence::None)), + (TokenType::True, ParseRule::new(Some(Parser::literal), None, Precedence::None)), (TokenType::Var, ParseRule::new(None, None, Precedence::None)), (TokenType::While, ParseRule::new(None, None, Precedence::None)), (TokenType::Error, ParseRule::new(None, None, Precedence::None)), diff --git a/src/vm/block/block.rs b/src/vm/block/block.rs index 9199375..e3eaa39 100644 --- a/src/vm/block/block.rs +++ b/src/vm/block/block.rs @@ -1,5 +1,5 @@ use crate::vm::opcodes::OpCode; -use crate::vm::{Block, Constants, Line}; +use crate::vm::{Block, Constants, Line, Value}; impl Block { pub(crate) fn new(name: &str) -> Self { @@ -13,21 +13,20 @@ impl Block { pub(crate) fn new_no_opt() -> Self { let mut block = Block::new("NO OPT BLOCK"); - block.write_constant(0.0, 0); + block.write_constant(Value::from_number(0.0), 0); block.write_op_code(OpCode::Return, 0); block } } impl Block { - pub(crate) fn write_op_code(&mut self, op_code: OpCode, line: usize) { + pub(crate) fn write_op_code(&mut self, op_code: OpCode, line: u32) { self.add_line(self.instructions.len(), line); self.instructions.push(op_code as u8) } - pub(crate) fn write_constant(&mut self, value: f64, line: usize) { + pub(crate) fn write_constant(&mut self, value: Value, line: u32) { let constant_index = self.constants.write_value(value); - if constant_index <= 0xFF { self.write_op_code(OpCode::Constant, line); self.write_u8(constant_index as u8) @@ -55,7 +54,7 @@ impl Block { } #[inline(always)] - pub(in crate::vm) fn read_constant(&mut self, index: usize) -> f64 { + pub(in crate::vm) fn read_constant(&mut self, index: usize) -> Value { self.constants.read_value(index) } @@ -82,11 +81,11 @@ impl Block { } impl Block { - fn add_line(&mut self, offset: usize, line: usize) { + fn add_line(&mut self, offset: usize, line: u32) { self.lines.push(Line { offset, line }); } - pub(in crate::vm) fn get_line(&self, offset: usize) -> Option { + pub(in crate::vm) fn get_line(&self, offset: usize) -> Option { let mut result = 0; let mut low = 0; let mut high = self.lines.len() - 1; @@ -112,7 +111,7 @@ impl Block { #[cfg(test)] mod tests { use crate::vm::opcodes::OpCode; - use crate::vm::Block; + use crate::vm::{Block, Value}; #[test] fn new_block_is_empty() { @@ -136,7 +135,7 @@ mod tests { fn can_write_more_then_256_constants() { let mut block = Block::new("maggie"); for i in 0..258 { - block.write_constant(i as f64, i); + block.write_constant(Value::from_number(i as f64), i); } assert_eq!(2 * 256 + 6, block.instructions.len()); diff --git a/src/vm/block/disassembler.rs b/src/vm/block/disassembler.rs index 4007381..3830e3d 100644 --- a/src/vm/block/disassembler.rs +++ b/src/vm/block/disassembler.rs @@ -37,6 +37,9 @@ impl Block { OpCode::Subtract => self.simple_instruction(OpCode::Subtract, offset), OpCode::Multiply => self.simple_instruction(OpCode::Multiply, offset), OpCode::Divide => self.simple_instruction(OpCode::Divide, offset), + OpCode::Nil => self.simple_instruction(OpCode::Nil, offset), + OpCode::True => self.simple_instruction(OpCode::True, offset), + OpCode::False => self.simple_instruction(OpCode::False, offset), }; } @@ -57,7 +60,9 @@ impl Block { let (index, offset_shift) = get_constant_index(self, &op_code, offset + 1); let constant = self.constants.read_value(index); - println!("{:?} {:02} '{}'", op_code, index, constant); + unsafe { + println!("{:?} {:02} '{}'", op_code, index, constant.value.number); + } offset + 1 + offset_shift } } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index ad16a6d..e802fa1 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,8 +1,39 @@ +use std::fmt::{Debug, Formatter}; + mod block; pub(crate) mod opcodes; +mod value; mod virtual_machine; -pub type Value = f64; +// pub type Value = f64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ValueType { + Number, + Bool, + String, + Nil, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union ValueUnion { + number: f64, + boolean: bool, + string: *const String, +} + +impl Debug for ValueUnion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ValueUnion") + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Value { + pub value_type: ValueType, + pub value: ValueUnion, +} #[derive(Debug, PartialEq)] pub enum Result { @@ -31,6 +62,6 @@ struct Constants { #[derive(Debug)] struct Line { - pub line: usize, pub offset: usize, + pub line: u32, } diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index 1709f37..856108b 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -12,6 +12,9 @@ pub(crate) enum OpCode { Subtract = 0x06, Multiply = 0x07, Divide = 0x08, + Nil = 0x09, + True = 0x0A, + False = 0x0B, } impl OpCode { diff --git a/src/vm/value.rs b/src/vm/value.rs new file mode 100644 index 0000000..4ee9538 --- /dev/null +++ b/src/vm/value.rs @@ -0,0 +1,31 @@ +use crate::vm::{Value, ValueType, ValueUnion}; + +impl Value { + pub fn from_number(value: f64) -> Self { + Value { + value_type: ValueType::Number, + value: ValueUnion { number: value }, + } + } + + pub fn from_bool(value: bool) -> Self { + Value { + value_type: ValueType::Bool, + value: ValueUnion { boolean: value }, + } + } + + pub fn from_string(value: *const String) -> Self { + Value { + value_type: ValueType::Bool, + value: ValueUnion { string: value }, + } + } + + pub fn nil() -> Value { + Value { + value_type: ValueType::Nil, + value: ValueUnion { number: 0.0 }, + } + } +} diff --git a/src/vm/virtual_machine.rs b/src/vm/virtual_machine.rs index 1a47b60..ec2d664 100644 --- a/src/vm/virtual_machine.rs +++ b/src/vm/virtual_machine.rs @@ -1,6 +1,7 @@ use crate::compiler::Compiler; use crate::vm::opcodes::OpCode; -use crate::vm::{Block, Result, Value, VirtualMachine}; +use crate::vm::{Block, Result, Value, ValueType, VirtualMachine}; +use tracing::error; impl VirtualMachine { pub fn new() -> Self { @@ -57,13 +58,28 @@ impl VirtualMachine { self.ip += 4; } OpCode::Negate => { + if self.peek(0).value_type != ValueType::Number { + self.runtime_error("Operand must be a number", block); + return Result::RuntimeError; + } let value = self.pop(); - self.push(-value); + unsafe { + self.push(Value::from_number(-value.value.number)); + } } OpCode::Add => self.addition(), OpCode::Subtract => self.subtraction(), OpCode::Multiply => self.multiplication(), OpCode::Divide => self.division(), + OpCode::Nil => { + self.push(Value::nil()); + } + OpCode::True => { + self.push(Value::from_bool(true)); + } + OpCode::False => { + self.push(Value::from_bool(false)); + } } self.ip += 1; } @@ -72,29 +88,55 @@ impl VirtualMachine { fn addition(&mut self) { let b = self.pop(); let a = self.pop(); - self.push(a + b); + unsafe { + self.push(Value::from_number(a.value.number + b.value.number)); + } } fn subtraction(&mut self) { let b = self.pop(); let a = self.pop(); - self.push(a - b); + unsafe { + self.push(Value::from_number(a.value.number - b.value.number)); + } } fn multiplication(&mut self) { let b = self.pop(); let a = self.pop(); - self.push(a * b); + unsafe { + self.push(Value::from_number(a.value.number * b.value.number)); + } } fn division(&mut self) { let b = self.pop(); let a = self.pop(); - self.push(a / b); + unsafe { + self.push(Value::from_number(a.value.number / b.value.number)); + } } fn print(value: Value) { - print!("{}", value); + pub(crate) fn print_nil() { + print!("nil") + } + pub(crate) fn print_string(value: Value) { + print!("{}", unsafe { &*value.value.string }) + } + pub(crate) fn print_bool(value: Value) { + print!("{}", unsafe { value.value.boolean }); + } + pub(crate) fn print_number(value: Value) { + print!("{}", unsafe { value.value.number }); + } + + match value.value_type { + ValueType::Number => print_number(value), + ValueType::Bool => print_bool(value), + ValueType::String => print_string(value), + ValueType::Nil => print_nil(), + } } fn push(&mut self, value: Value) { @@ -104,6 +146,15 @@ impl VirtualMachine { fn pop(&mut self) -> Value { self.stack.pop().unwrap() } + + fn peek(&mut self, distance: usize) -> Value { + self.stack[self.stack.len() - 1 - distance] + } + fn runtime_error(&mut self, error: &str, block: Block) { + eprint!("{} ", error); + let line = block.get_line(self.ip).unwrap(); + eprintln!("[line {}] in script", line); + } } #[cfg(test)]