diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 09f3912..aad21d9 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -102,28 +102,35 @@ impl Chunk { output.push_str(name); output.push_str(" ==\n"); - let mut next_is_index = false; + let mut previous = None; for (offset, (byte, position)) in self.code.iter().enumerate() { - if next_is_index { - let index_display = format!("{position} {offset:04} INDEX {byte}\n"); + if let Some(Instruction::Constant) = previous { + let display = format!("{position} {offset:04} CONSTANT_INDEX {byte}\n"); + previous = None; - output.push_str(&index_display); + output.push_str(&display); - next_is_index = false; + continue; + } + + if let Some( + Instruction::DefineGlobal | Instruction::GetGlobal | Instruction::SetGlobal, + ) = previous + { + let display = format!("{position} {offset:04} IDENTIFIER_INDEX {byte}\n"); + previous = None; + + output.push_str(&display); continue; } let instruction = Instruction::from_byte(*byte).unwrap(); - let instruction_display = - format!("{} {}\n", position, instruction.disassemble(self, offset)); + let display = format!("{} {}\n", position, instruction.disassemble(self, offset)); + previous = Some(instruction); - output.push_str(&instruction_display); - - if let Instruction::Constant = instruction { - next_is_index = true; - } + output.push_str(&display); } output diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 78d0765..8fb9234 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -100,7 +100,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_boolean(&mut self) -> Result<(), ParseError> { + fn parse_boolean(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Boolean(text) = self.previous_token { let boolean = text.parse::().unwrap(); let value = Value::boolean(boolean); @@ -111,7 +111,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_float(&mut self) -> Result<(), ParseError> { + fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Float(text) = self.previous_token { let float = text.parse::().unwrap(); let value = Value::float(float); @@ -122,7 +122,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_integer(&mut self) -> Result<(), ParseError> { + fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Integer(text) = self.previous_token { let integer = text.parse::().unwrap(); let value = Value::integer(integer); @@ -133,7 +133,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_string(&mut self) -> Result<(), ParseError> { + fn parse_string(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::String(text) = self.previous_token { let value = Value::string(text); @@ -143,12 +143,12 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_grouped(&mut self) -> Result<(), ParseError> { + fn parse_grouped(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { self.parse_expression()?; self.expect(TokenKind::RightParenthesis) } - fn parse_unary(&mut self) -> Result<(), ParseError> { + fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { let operator_position = self.previous_position; let byte = match self.previous_token.kind() { TokenKind::Minus => Instruction::Negate as u8, @@ -198,15 +198,25 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_variable(&mut self) -> Result<(), ParseError> { - self.parse_named_variable_from(self.previous_token.to_owned()) + fn parse_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { + self.parse_named_variable_from(self.previous_token.to_owned(), allow_assignment) } - fn parse_named_variable_from(&mut self, token: TokenOwned) -> Result<(), ParseError> { + fn parse_named_variable_from( + &mut self, + token: TokenOwned, + allow_assignment: bool, + ) -> Result<(), ParseError> { let identifier_index = self.parse_identifier_from(token)?; - self.emit_byte(Instruction::GetGlobal as u8, self.previous_position); - self.emit_byte(identifier_index, self.previous_position); + if allow_assignment && self.allow(TokenKind::Equal)? { + self.parse_expression()?; + self.emit_byte(Instruction::SetGlobal as u8, self.previous_position); + self.emit_byte(identifier_index, self.previous_position); + } else { + self.emit_byte(Instruction::GetGlobal as u8, self.previous_position); + self.emit_byte(identifier_index, self.previous_position); + } Ok(()) } @@ -268,7 +278,7 @@ impl<'src> Parser<'src> { } fn define_variable(&mut self, identifier_index: u8, position: Span) -> Result<(), ParseError> { - self.emit_byte(Instruction::SetGlobal as u8, position); + self.emit_byte(Instruction::DefineGlobal as u8, position); self.emit_byte(identifier_index, position); Ok(()) @@ -277,19 +287,24 @@ impl<'src> Parser<'src> { fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> { self.advance()?; - if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix { + let prefix_rule = if let Some(prefix) = ParseRule::from(&self.previous_token.kind()).prefix + { log::trace!( "Parsing {} as prefix with precedence {precedence}", self.previous_token, ); - prefix(self)?; + prefix } else { return Err(ParseError::ExpectedExpression { found: self.previous_token.to_owned(), position: self.previous_position, }); - } + }; + + let allow_assignment = precedence <= Precedence::Assignment; + + prefix_rule(self, allow_assignment)?; while precedence < ParseRule::from(&self.current_token.kind()).precedence { self.advance()?; @@ -302,6 +317,13 @@ impl<'src> Parser<'src> { self.previous_token, ); + if allow_assignment && self.allow(TokenKind::Equal)? { + return Err(ParseError::InvalidAssignmentTarget { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + } + infix(self)?; } else { break; @@ -357,12 +379,13 @@ impl Display for Precedence { } } -type ParserFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>; +type PrefixFunction<'a> = fn(&mut Parser<'a>, bool) -> Result<(), ParseError>; +type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>; #[derive(Debug, Clone, Copy)] pub struct ParseRule<'a> { - pub prefix: Option>, - pub infix: Option>, + pub prefix: Option>, + pub infix: Option>, pub precedence: Precedence, } @@ -490,6 +513,10 @@ pub enum ParseError { found: TokenOwned, position: Span, }, + InvalidAssignmentTarget { + found: TokenOwned, + position: Span, + }, // Wrappers around foreign errors Chunk(ChunkError), @@ -530,7 +557,7 @@ mod tests { vec![ (Instruction::Constant as u8, Span(8, 10)), (0, Span(8, 10)), - (Instruction::SetGlobal as u8, Span(4, 5)), + (Instruction::DefineGlobal as u8, Span(4, 5)), (0, Span(4, 5)) ], vec![Value::integer(42)], diff --git a/dust-lang/src/run.rs b/dust-lang/src/run.rs index 85e42de..ad99885 100644 --- a/dust-lang/src/run.rs +++ b/dust-lang/src/run.rs @@ -5,7 +5,7 @@ pub fn run(source: &str) -> Result, DustError> { let mut vm = Vm::new(chunk); - vm.interpret() + vm.run() .map_err(|error| DustError::Runtime { error, source }) } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index e91e473..e37a0fa 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -24,7 +24,7 @@ impl Vm { } } - pub fn interpret(&mut self) -> Result, VmError> { + pub fn run(&mut self) -> Result, VmError> { while let Ok((byte, position)) = self.read().copied() { let instruction = Instruction::from_byte(byte) .ok_or_else(|| VmError::InvalidInstruction(byte, position))?; @@ -44,7 +44,9 @@ impl Vm { Instruction::Pop => { self.pop()?; } - Instruction::SetGlobal => { + + // Variables + Instruction::DefineGlobal => { let (index, _) = self.read().copied()?; let identifier = self.chunk.get_identifier(index as usize)?.clone(); let value = self.pop()?; @@ -61,6 +63,18 @@ impl Vm { self.push(value)?; } + Instruction::SetGlobal => { + let (index, _) = self.read().copied()?; + let identifier = self.chunk.get_identifier(index as usize)?.clone(); + + if !self.globals.contains_key(&identifier) { + return Err(VmError::UndefinedGlobal(identifier, position)); + } + + let value = self.pop()?; + + self.globals.insert(identifier, value); + } // Unary Instruction::Negate => { @@ -225,26 +239,29 @@ pub enum Instruction { Constant = 0, Return = 1, Pop = 2, - SetGlobal = 3, + + // Variables + DefineGlobal = 3, GetGlobal = 4, + SetGlobal = 5, // Unary - Negate = 5, - Not = 6, + Negate = 6, + Not = 7, // Binary - Add = 7, - Subtract = 8, - Multiply = 9, - Divide = 10, - Greater = 11, - Less = 12, - GreaterEqual = 13, - LessEqual = 14, - Equal = 15, - NotEqual = 16, - And = 17, - Or = 18, + Add = 8, + Subtract = 9, + Multiply = 10, + Divide = 11, + Greater = 12, + Less = 13, + GreaterEqual = 14, + LessEqual = 15, + Equal = 16, + NotEqual = 17, + And = 18, + Or = 19, } impl Instruction { @@ -253,22 +270,23 @@ impl Instruction { 0 => Some(Instruction::Constant), 1 => Some(Instruction::Return), 2 => Some(Instruction::Pop), - 3 => Some(Instruction::SetGlobal), + 3 => Some(Instruction::DefineGlobal), 4 => Some(Instruction::GetGlobal), - 5 => Some(Instruction::Negate), - 6 => Some(Instruction::Not), - 7 => Some(Instruction::Add), - 8 => Some(Instruction::Subtract), - 9 => Some(Instruction::Multiply), - 10 => Some(Instruction::Divide), - 11 => Some(Instruction::Greater), - 12 => Some(Instruction::Less), - 13 => Some(Instruction::GreaterEqual), - 14 => Some(Instruction::LessEqual), - 15 => Some(Instruction::Equal), - 16 => Some(Instruction::NotEqual), - 17 => Some(Instruction::And), - 18 => Some(Instruction::Or), + 5 => Some(Instruction::SetGlobal), + 6 => Some(Instruction::Negate), + 7 => Some(Instruction::Not), + 8 => Some(Instruction::Add), + 9 => Some(Instruction::Subtract), + 10 => Some(Instruction::Multiply), + 11 => Some(Instruction::Divide), + 12 => Some(Instruction::Greater), + 13 => Some(Instruction::Less), + 14 => Some(Instruction::GreaterEqual), + 15 => Some(Instruction::LessEqual), + 16 => Some(Instruction::Equal), + 17 => Some(Instruction::NotEqual), + 18 => Some(Instruction::And), + 19 => Some(Instruction::Or), _ => None, } } @@ -286,17 +304,32 @@ impl Instruction { } Instruction::Return => format!("{offset:04} RETURN"), Instruction::Pop => format!("{offset:04} POP"), - Instruction::SetGlobal => { - let (index, _) = chunk.read(offset + 1).unwrap(); - let identifier = chunk.get_identifier(*index as usize).unwrap(); - format!("{offset:04} DEFINE_GLOBAL {identifier}") + // Variables + Instruction::DefineGlobal => { + let (index, _) = chunk.read(offset + 1).unwrap(); + let index = *index as usize; + let identifier = chunk.get_identifier(index).unwrap(); + let value = chunk.get_constant(index).unwrap(); + + format!("{offset:04} DEFINE_GLOBAL {identifier} {value}") } Instruction::GetGlobal => { let (index, _) = chunk.read(offset + 1).unwrap(); - let identifier = chunk.get_identifier(*index as usize).unwrap(); + let index = *index as usize; + let identifier = chunk.get_identifier(index).unwrap(); + let value = chunk.get_constant(index).unwrap(); - format!("{offset:04} GET_GLOBAL {identifier}") + format!("{offset:04} GET_GLOBAL {identifier} {value}") + } + + Instruction::SetGlobal => { + let (index, _) = chunk.read(offset + 1).unwrap(); + let index = *index as usize; + let identifier = chunk.get_identifier(index).unwrap(); + let value = chunk.get_constant(index).unwrap(); + + format!("{offset:04} SET_GLOBAL {identifier} {value}") } // Unary @@ -335,7 +368,7 @@ pub mod tests { chunk.write(Instruction::Return as u8, Span(2, 3)); let mut vm = Vm::new(chunk); - let result = vm.interpret(); + let result = vm.run(); assert_eq!(result, Ok(Some(Value::integer(-42)))); } @@ -354,7 +387,7 @@ pub mod tests { chunk.write(Instruction::Return as u8, Span(10, 11)); let mut vm = Vm::new(chunk); - let result = vm.interpret(); + let result = vm.run(); assert_eq!(result, Ok(Some(Value::integer(65)))); } @@ -373,7 +406,7 @@ pub mod tests { chunk.write(Instruction::Return as u8, Span(10, 11)); let mut vm = Vm::new(chunk); - let result = vm.interpret(); + let result = vm.run(); assert_eq!(result, Ok(Some(Value::integer(19)))); } @@ -392,7 +425,7 @@ pub mod tests { chunk.write(Instruction::Return as u8, Span(10, 11)); let mut vm = Vm::new(chunk); - let result = vm.interpret(); + let result = vm.run(); assert_eq!(result, Ok(Some(Value::integer(966)))); } @@ -412,7 +445,7 @@ pub mod tests { chunk.write(Instruction::Return as u8, Span(10, 11)); let mut vm = Vm::new(chunk); - let result = vm.interpret(); + let result = vm.run(); assert_eq!(result, Ok(Some(Value::integer(1)))); }