Get variable scopes working

This commit is contained in:
Jeff 2024-09-11 03:10:12 -04:00
parent 8f58bf30a4
commit e4204c1b0d
6 changed files with 284 additions and 519 deletions

View File

@ -2,16 +2,14 @@ use std::fmt::{self, Debug, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
identifier_stack::Local, AnnotatedError, Identifier, IdentifierStack, Instruction, Span, Value,
ValueLocation,
};
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Chunk { pub struct Chunk {
code: Vec<(u8, Span)>, code: Vec<(u8, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
identifiers: IdentifierStack, identifiers: Vec<Local>,
scope_depth: usize,
} }
impl Chunk { impl Chunk {
@ -19,7 +17,8 @@ impl Chunk {
Self { Self {
code: Vec::new(), code: Vec::new(),
constants: Vec::new(), constants: Vec::new(),
identifiers: IdentifierStack::new(), identifiers: Vec::new(),
scope_depth: 0,
} }
} }
@ -31,7 +30,8 @@ impl Chunk {
Self { Self {
code, code,
constants, constants,
identifiers: IdentifierStack::with_data(identifiers, 0), identifiers,
scope_depth: 0,
} }
} }
@ -44,7 +44,7 @@ impl Chunk {
} }
pub fn scope_depth(&self) -> usize { pub fn scope_depth(&self) -> usize {
self.identifiers.scope_depth() self.scope_depth
} }
pub fn get_code(&self, offset: usize, position: Span) -> Result<&(u8, Span), ChunkError> { pub fn get_code(&self, offset: usize, position: Span) -> Result<&(u8, Span), ChunkError> {
@ -53,8 +53,8 @@ impl Chunk {
.ok_or(ChunkError::CodeIndexOfBounds { offset, position }) .ok_or(ChunkError::CodeIndexOfBounds { offset, position })
} }
pub fn push_code(&mut self, instruction: u8, position: Span) { pub fn push_code<T: Into<u8>>(&mut self, into_byte: T, position: Span) {
self.code.push((instruction, position)); self.code.push((into_byte.into(), position));
} }
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
@ -76,11 +76,11 @@ impl Chunk {
} }
} }
pub fn push_constant(&mut self, value: Value) -> Result<u8, ChunkError> { pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
let starting_length = self.constants.len(); let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) { if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::ConstantOverflow) Err(ChunkError::ConstantOverflow { position })
} else { } else {
self.constants.push(value); self.constants.push(value);
@ -89,28 +89,22 @@ impl Chunk {
} }
pub fn contains_identifier(&self, identifier: &Identifier) -> bool { pub fn contains_identifier(&self, identifier: &Identifier) -> bool {
self.identifiers.contains(identifier) self.identifiers
.iter()
.any(|local| &local.identifier == identifier)
} }
pub fn get_local(&self, index: u8) -> Result<&Local, ChunkError> { pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
self.identifiers self.identifiers
.get(index as usize) .get(index as usize)
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index)) .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
} }
pub fn resolve_local(&self, identifier: &Identifier) -> Option<u8> { pub fn get_identifier(&self, index: u8, position: Span) -> Result<&Identifier, ChunkError> {
self.identifiers.resolve(self, identifier)
}
pub fn resolve_local_index(&self, identifier: &Identifier) -> Option<u8> {
self.identifiers.resolve_index(identifier)
}
pub fn get_identifier(&self, index: u8) -> Result<&Identifier, ChunkError> {
self.identifiers self.identifiers
.get(index as usize) .get(index as usize)
.map(|local| &local.identifier) .map(|local| &local.identifier)
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index)) .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
} }
pub fn get_identifier_index( pub fn get_identifier_index(
@ -119,59 +113,47 @@ impl Chunk {
position: Span, position: Span,
) -> Result<u8, ChunkError> { ) -> Result<u8, ChunkError> {
self.identifiers self.identifiers
.get_index(identifier) .iter()
.map(|index| index as u8) .enumerate()
.rev()
.find_map(|(index, local)| {
if &local.identifier == identifier {
Some(index as u8)
} else {
None
}
})
.ok_or(ChunkError::IdentifierNotFound { .ok_or(ChunkError::IdentifierNotFound {
identifier: identifier.clone(), identifier: identifier.clone(),
position, position,
}) })
} }
pub fn push_constant_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> { pub fn declare_variable(
let starting_length = self.identifiers.local_count();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::IdentifierOverflow)
} else {
self.identifiers
.define(identifier, ValueLocation::ConstantStack);
Ok(starting_length as u8)
}
}
pub fn push_runtime_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
let starting_length = self.identifiers.local_count();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::IdentifierOverflow)
} else {
self.identifiers
.define(identifier, ValueLocation::RuntimeStack);
Ok(starting_length as u8)
}
}
pub fn redefine_as_runtime_identifier(
&mut self, &mut self,
identifier: &Identifier, identifier: Identifier,
position: Span, position: Span,
) -> Result<usize, ChunkError> { ) -> Result<u8, ChunkError> {
self.identifiers let starting_length = self.identifiers.len();
.redefine(identifier, ValueLocation::RuntimeStack)
.ok_or_else(|| ChunkError::IdentifierNotFound { if starting_length + 1 > (u8::MAX as usize) {
identifier: identifier.clone(), Err(ChunkError::IdentifierOverflow { position })
position, } else {
}) self.identifiers.push(Local {
identifier,
depth: self.scope_depth,
});
Ok(starting_length as u8)
}
} }
pub fn begin_scope(&mut self) { pub fn begin_scope(&mut self) {
self.identifiers.begin_scope(); self.scope_depth += 1;
} }
pub fn end_scope(&mut self) { pub fn end_scope(&mut self) {
self.identifiers.end_scope(); self.scope_depth -= 1;
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
@ -180,6 +162,14 @@ impl Chunk {
self.identifiers.clear(); self.identifiers.clear();
} }
pub fn identifiers(&self) -> &[Local] {
&self.identifiers
}
pub fn pop_identifier(&mut self) -> Option<Local> {
self.identifiers.pop()
}
pub fn disassemble(&self, name: &str) -> String { pub fn disassemble(&self, name: &str) -> String {
let mut output = String::new(); let mut output = String::new();
@ -199,7 +189,7 @@ impl Chunk {
for (offset, (byte, position)) in self.code.iter().enumerate() { for (offset, (byte, position)) in self.code.iter().enumerate() {
if let Some( if let Some(
Instruction::Constant Instruction::Constant
| Instruction::DefineVariable | Instruction::DeclareVariable
| Instruction::GetVariable | Instruction::GetVariable
| Instruction::SetVariable, | Instruction::SetVariable,
) = previous ) = previous
@ -237,24 +227,13 @@ impl Chunk {
output.push_str(&display); output.push_str(&display);
} }
output.push_str("\n Identifiers \n"); output.push_str("\n Identifiers \n");
output.push_str("----- ---------- -------- -----\n"); output.push_str("----- ---------- -----\n");
output.push_str("INDEX IDENTIFIER LOCATION DEPTH\n"); output.push_str("INDEX IDENTIFIER DEPTH\n");
output.push_str("----- ---------- -------- -----\n"); output.push_str("----- ---------- -----\n");
for ( for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() {
index, let display = format!("{index:3} {:10} {depth}\n", identifier.as_str());
Local {
identifier,
depth,
value_location,
},
) in self.identifiers.iter().enumerate()
{
let display = format!(
"{index:3} {:10} {value_location} {depth}\n",
identifier.as_str()
);
output.push_str(&display); output.push_str(&display);
} }
@ -280,19 +259,32 @@ impl Debug for Chunk {
} }
} }
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Local {
pub identifier: Identifier,
pub depth: usize,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ChunkError { pub enum ChunkError {
CodeIndexOfBounds { CodeIndexOfBounds {
offset: usize, offset: usize,
position: Span, position: Span,
}, },
ConstantOverflow, ConstantOverflow {
position: Span,
},
ConstantIndexOutOfBounds { ConstantIndexOutOfBounds {
index: u8, index: u8,
position: Span, position: Span,
}, },
IdentifierIndexOutOfBounds(u8), IdentifierIndexOutOfBounds {
IdentifierOverflow, index: u8,
position: Span,
},
IdentifierOverflow {
position: Span,
},
IdentifierNotFound { IdentifierNotFound {
identifier: Identifier, identifier: Identifier,
position: Span, position: Span,
@ -307,10 +299,10 @@ impl AnnotatedError for ChunkError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds", ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds",
ChunkError::ConstantOverflow => "Constant overflow", ChunkError::ConstantOverflow { .. } => "Constant overflow",
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
ChunkError::IdentifierIndexOutOfBounds(_) => "Identifier index out of bounds", ChunkError::IdentifierIndexOutOfBounds { .. } => "Identifier index out of bounds",
ChunkError::IdentifierOverflow => "Identifier overflow", ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
ChunkError::IdentifierNotFound { .. } => "Identifier not found", ChunkError::IdentifierNotFound { .. } => "Identifier not found",
} }
} }
@ -321,7 +313,7 @@ impl AnnotatedError for ChunkError {
ChunkError::ConstantIndexOutOfBounds { index, .. } => { ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index)) Some(format!("Constant index: {}", index))
} }
ChunkError::IdentifierIndexOutOfBounds(index) => { ChunkError::IdentifierIndexOutOfBounds { index, .. } => {
Some(format!("Identifier index: {}", index)) Some(format!("Identifier index: {}", index))
} }
ChunkError::IdentifierNotFound { identifier, .. } => { ChunkError::IdentifierNotFound { identifier, .. } => {
@ -340,24 +332,3 @@ impl AnnotatedError for ChunkError {
} }
} }
} }
impl Display for ChunkError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ChunkError::CodeIndexOfBounds { offset, .. } => {
write!(f, "Code index out of bounds: {}", offset)
}
ChunkError::ConstantOverflow => write!(f, "Constant overflow"),
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
write!(f, "Constant index out of bounds: {}", index)
}
ChunkError::IdentifierIndexOutOfBounds(index) => {
write!(f, "Identifier index out of bounds: {}", index)
}
ChunkError::IdentifierOverflow => write!(f, "Identifier overflow"),
ChunkError::IdentifierNotFound { identifier, .. } => {
write!(f, "Identifier not found: {}", identifier)
}
}
}
}

View File

@ -1,160 +0,0 @@
use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::{Chunk, Identifier};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdentifierStack {
locals: Vec<Local>,
scope_depth: usize,
}
impl IdentifierStack {
pub fn new() -> Self {
Self {
locals: Vec::new(),
scope_depth: 0,
}
}
pub fn with_data(locals: Vec<Local>, scope_depth: usize) -> Self {
Self {
locals,
scope_depth,
}
}
pub fn clear(&mut self) {
self.locals.clear();
self.scope_depth = 0;
}
pub fn local_count(&self) -> usize {
self.locals.len()
}
pub fn contains(&self, identifier: &Identifier) -> bool {
self.locals
.iter()
.rev()
.any(|local| &local.identifier == identifier)
}
pub fn resolve(&self, chunk: &Chunk, identifier: &Identifier) -> Option<u8> {
for (index, local) in self.locals.iter().rev().enumerate() {
if &local.identifier == identifier {
let offset = index;
return Some(offset as u8);
}
}
None
}
pub fn resolve_index(&self, identifier: &Identifier) -> Option<u8> {
self.locals.iter().enumerate().find_map(|(index, local)| {
if &local.identifier == identifier && local.depth <= self.scope_depth {
Some(index as u8)
} else {
None
}
})
}
pub fn get(&self, index: usize) -> Option<&Local> {
self.locals.get(index)
}
pub fn get_index(&self, identifier: &Identifier) -> Option<usize> {
self.locals.iter().enumerate().rev().find_map(
|(
index,
Local {
identifier: local, ..
},
)| {
if local == identifier {
Some(index)
} else {
None
}
},
)
}
pub fn scope_depth(&self) -> usize {
self.scope_depth
}
pub fn begin_scope(&mut self) {
self.scope_depth += 1;
}
pub fn end_scope(&mut self) {
self.scope_depth -= 1;
}
pub fn define(&mut self, identifier: Identifier, value_location: ValueLocation) {
self.locals.push(Local {
identifier,
depth: self.scope_depth,
value_location,
});
}
pub fn redefine(
&mut self,
identifier: &Identifier,
value_location: ValueLocation,
) -> Option<usize> {
if let Some(index) = self.get_index(identifier) {
self.locals[index].value_location = value_location;
Some(index)
} else {
None
}
}
pub fn iter(&self) -> impl Iterator<Item = &Local> {
self.locals.iter()
}
}
impl Default for IdentifierStack {
fn default() -> Self {
Self::new()
}
}
impl Eq for IdentifierStack {}
impl PartialEq for IdentifierStack {
fn eq(&self, other: &Self) -> bool {
self.locals == other.locals
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Local {
pub identifier: Identifier,
pub depth: usize,
pub value_location: ValueLocation,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum ValueLocation {
ConstantStack,
RuntimeStack,
}
impl Display for ValueLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueLocation::ConstantStack => write!(f, "constant"),
ValueLocation::RuntimeStack => write!(f, "runtime "),
}
}
}

View File

@ -11,7 +11,7 @@ pub enum Instruction {
Pop = 2, Pop = 2,
// Variables // Variables
DefineVariable = 3, DeclareVariable = 3,
GetVariable = 4, GetVariable = 4,
SetVariable = 5, SetVariable = 5,
@ -40,7 +40,7 @@ impl Instruction {
0 => Some(Instruction::Constant), 0 => Some(Instruction::Constant),
1 => Some(Instruction::Return), 1 => Some(Instruction::Return),
2 => Some(Instruction::Pop), 2 => Some(Instruction::Pop),
3 => Some(Instruction::DefineVariable), 3 => Some(Instruction::DeclareVariable),
4 => Some(Instruction::GetVariable), 4 => Some(Instruction::GetVariable),
5 => Some(Instruction::SetVariable), 5 => Some(Instruction::SetVariable),
6 => Some(Instruction::Negate), 6 => Some(Instruction::Negate),
@ -70,7 +70,7 @@ impl Instruction {
let value_display = chunk let value_display = chunk
.get_constant(argument, position) .get_constant(argument, position)
.map(|value| value.to_string()) .map(|value| value.to_string())
.unwrap_or_else(|error| error.to_string()); .unwrap_or_else(|error| format!("{error:?}"));
format!("CONSTANT {value_display}") format!("CONSTANT {value_display}")
} }
@ -78,20 +78,20 @@ impl Instruction {
Instruction::Pop => "POP".to_string(), Instruction::Pop => "POP".to_string(),
// Variables // Variables
Instruction::DefineVariable => { Instruction::DeclareVariable => {
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
let identifier_display = match chunk.get_identifier(*argument) { let identifier_display = match chunk.get_identifier(*argument, dummy_position) {
Ok(identifier) => identifier.to_string(), Ok(identifier) => identifier.to_string(),
Err(error) => error.to_string(), Err(error) => format!("{error:?}"),
}; };
format!("DEFINE_VARIABLE {identifier_display}") format!("DECLARE_VARIABLE {identifier_display}")
} }
Instruction::GetVariable => { Instruction::GetVariable => {
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
let identifier_display = match chunk.get_identifier(*argument) { let identifier_display = match chunk.get_identifier(*argument, dummy_position) {
Ok(identifier) => identifier.to_string(), Ok(identifier) => identifier.to_string(),
Err(error) => error.to_string(), Err(error) => format!("{error:?}"),
}; };
format!("GET_VARIABLE {identifier_display}") format!("GET_VARIABLE {identifier_display}")
@ -99,9 +99,9 @@ impl Instruction {
Instruction::SetVariable => { Instruction::SetVariable => {
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
let identifier_display = match chunk.get_identifier(*argument) { let identifier_display = match chunk.get_identifier(*argument, dummy_position) {
Ok(identifier) => identifier.to_string(), Ok(identifier) => identifier.to_string(),
Err(error) => error.to_string(), Err(error) => format!("{error:?}"),
}; };
format!("SET_VARIABLE {identifier_display}") format!("SET_VARIABLE {identifier_display}")

View File

@ -19,7 +19,6 @@ pub mod chunk;
pub mod constructor; pub mod constructor;
pub mod dust_error; pub mod dust_error;
pub mod identifier; pub mod identifier;
pub mod identifier_stack;
pub mod instruction; pub mod instruction;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
@ -28,11 +27,10 @@ pub mod r#type;
pub mod value; pub mod value;
pub mod vm; pub mod vm;
pub use chunk::{Chunk, ChunkError}; pub use chunk::{Chunk, ChunkError, Local};
pub use constructor::{ConstructError, Constructor}; pub use constructor::{ConstructError, Constructor};
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use identifier::Identifier; pub use identifier::Identifier;
pub use identifier_stack::{IdentifierStack, Local, ValueLocation};
pub use instruction::Instruction; pub use instruction::Instruction;
pub use lexer::{lex, LexError, Lexer}; pub use lexer::{lex, LexError, Lexer};
pub use parser::{parse, ParseError, Parser}; pub use parser::{parse, ParseError, Parser};

View File

@ -6,7 +6,7 @@ use std::{
use crate::{ use crate::{
dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError,
Lexer, Local, Span, Token, TokenKind, TokenOwned, Value, ValueLocation, Lexer, Span, Token, TokenKind, TokenOwned, Value,
}; };
pub fn parse(source: &str) -> Result<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -96,10 +96,7 @@ impl<'src> Parser<'src> {
fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
let position = self.previous_position; let position = self.previous_position;
let constant_index = self let constant_index = self.chunk.push_constant(value, position)?;
.chunk
.push_constant(value)
.map_err(|error| ParseError::Chunk { error, position })?;
self.emit_byte(Instruction::Constant, position); self.emit_byte(Instruction::Constant, position);
self.emit_byte(constant_index, position); self.emit_byte(constant_index, position);
@ -239,7 +236,7 @@ impl<'src> Parser<'src> {
fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
let token = self.previous_token.to_owned(); let token = self.previous_token.to_owned();
let identifier_index = self.parse_identifier_from(token)?; let identifier_index = self.parse_identifier_from(token, self.previous_position)?;
if allow_assignment && self.allow(TokenKind::Equal)? { if allow_assignment && self.allow(TokenKind::Equal)? {
self.parse_expression()?; self.parse_expression()?;
@ -253,24 +250,27 @@ impl<'src> Parser<'src> {
Ok(()) Ok(())
} }
fn parse_identifier_from(&mut self, token: TokenOwned) -> Result<u8, ParseError> { fn parse_identifier_from(
&mut self,
token: TokenOwned,
position: Span,
) -> Result<u8, ParseError> {
if let TokenOwned::Identifier(text) = token { if let TokenOwned::Identifier(text) = token {
let identifier = Identifier::new(text); let identifier = Identifier::new(text);
let identifier_index = if let Ok(identifier_index) = self.chunk.get_identifier_index(&identifier, position) {
self.chunk Ok(identifier_index)
.push_constant_identifier(identifier) } else {
.map_err(|error| ParseError::Chunk { Err(ParseError::UndefinedVariable {
error, identifier,
position: self.previous_position, position,
})?; })
}
Ok(identifier_index)
} else { } else {
Err(ParseError::ExpectedToken { Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier, expected: TokenKind::Identifier,
found: self.current_token.to_owned(), found: self.current_token.to_owned(),
position: self.current_position, position,
}) })
} }
} }
@ -284,6 +284,18 @@ impl<'src> Parser<'src> {
self.chunk.end_scope(); self.chunk.end_scope();
while self
.chunk
.identifiers()
.iter()
.rev()
.next()
.map_or(false, |local| local.depth > self.chunk.scope_depth())
{
self.emit_byte(Instruction::Pop, self.current_position);
self.chunk.pop_identifier();
}
Ok(()) Ok(())
} }
@ -295,7 +307,7 @@ impl<'src> Parser<'src> {
let start = self.current_position.0; let start = self.current_position.0;
let (is_expression_statement, contains_block) = match self.current_token { let (is_expression_statement, contains_block) = match self.current_token {
Token::Let => { Token::Let => {
self.parse_let_assignment(true)?; self.parse_let_statement(true)?;
(false, false) (false, false)
} }
@ -321,7 +333,7 @@ impl<'src> Parser<'src> {
Ok(()) Ok(())
} }
fn parse_let_assignment(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
self.expect(TokenKind::Let)?; self.expect(TokenKind::Let)?;
let position = self.current_position; let position = self.current_position;
@ -340,12 +352,9 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?; self.expect(TokenKind::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let identifier_index = self let identifier_index = self.chunk.declare_variable(identifier, position)?;
.chunk
.push_constant_identifier(identifier)
.map_err(|error| ParseError::Chunk { error, position })?;
self.emit_byte(Instruction::DefineVariable, position); self.emit_byte(Instruction::DeclareVariable, position);
self.emit_byte(identifier_index, position); self.emit_byte(identifier_index, position);
Ok(()) Ok(())
@ -504,7 +513,7 @@ impl From<&TokenKind> for ParseRule<'_> {
TokenKind::If => todo!(), TokenKind::If => todo!(),
TokenKind::Int => todo!(), TokenKind::Int => todo!(),
TokenKind::Let => ParseRule { TokenKind::Let => ParseRule {
prefix: Some(Parser::parse_let_assignment), prefix: Some(Parser::parse_let_statement),
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },
@ -606,12 +615,13 @@ pub enum ParseError {
found: TokenOwned, found: TokenOwned,
position: Span, position: Span,
}, },
UndefinedVariable {
// Wrappers around foreign errors identifier: Identifier,
Chunk {
error: ChunkError,
position: Span, position: Span,
}, },
// Wrappers around foreign errors
Chunk(ChunkError),
Lex(LexError), Lex(LexError),
ParseIntError { ParseIntError {
error: ParseIntError, error: ParseIntError,
@ -619,6 +629,12 @@ pub enum ParseError {
}, },
} }
impl From<ChunkError> for ParseError {
fn from(error: ChunkError) -> Self {
Self::Chunk(error)
}
}
impl AnnotatedError for ParseError { impl AnnotatedError for ParseError {
fn title() -> &'static str { fn title() -> &'static str {
"Parse Error" "Parse Error"
@ -630,6 +646,7 @@ impl AnnotatedError for ParseError {
Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedToken { .. } => "Expected a specific token",
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndefinedVariable { .. } => "Undefined variable",
Self::Chunk { .. } => "Chunk error", Self::Chunk { .. } => "Chunk error",
Self::Lex(_) => "Lex error", Self::Lex(_) => "Lex error",
Self::ParseIntError { .. } => "Failed to parse integer", Self::ParseIntError { .. } => "Failed to parse integer",
@ -650,7 +667,10 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { found, .. } => { Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target \"{found}\"")) Some(format!("Invalid assignment target \"{found}\""))
} }
Self::Chunk { error, .. } => Some(error.to_string()), Self::UndefinedVariable { identifier, .. } => {
Some(format!("Undefined variable \"{identifier}\""))
}
Self::Chunk(error) => error.details(),
Self::Lex(error) => Some(error.to_string()), Self::Lex(error) => Some(error.to_string()),
Self::ParseIntError { error, .. } => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()),
} }
@ -662,7 +682,8 @@ impl AnnotatedError for ParseError {
Self::ExpectedToken { position, .. } => *position, Self::ExpectedToken { position, .. } => *position,
Self::ExpectedTokenMultiple { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::Chunk { position, .. } => *position, Self::UndefinedVariable { position, .. } => *position,
Self::Chunk(error) => error.position(),
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
} }
@ -677,7 +698,7 @@ impl From<LexError> for ParseError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{identifier_stack::Local, ValueLocation}; use crate::Local;
use super::*; use super::*;
@ -711,14 +732,14 @@ mod tests {
test_chunk, test_chunk,
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::DefineVariable as u8, Span(4, 5)),
(0, Span(4, 5)),
(Instruction::Constant as u8, Span(8, 10)), (Instruction::Constant as u8, Span(8, 10)),
(0, Span(8, 10)), (0, Span(8, 10)),
(Instruction::DefineVariable as u8, Span(16, 17)), (Instruction::DeclareVariable as u8, Span(4, 5)),
(1, Span(16, 17)), (0, Span(4, 5)),
(Instruction::Constant as u8, Span(20, 22)), (Instruction::Constant as u8, Span(20, 22)),
(1, Span(20, 22)), (1, Span(20, 22)),
(Instruction::DeclareVariable as u8, Span(16, 17)),
(1, Span(16, 17)),
(Instruction::GetVariable as u8, Span(24, 25)), (Instruction::GetVariable as u8, Span(24, 25)),
(0, Span(24, 25)), (0, Span(24, 25)),
(Instruction::GetVariable as u8, Span(28, 29)), (Instruction::GetVariable as u8, Span(28, 29)),
@ -731,12 +752,10 @@ mod tests {
Local { Local {
identifier: Identifier::new("x"), identifier: Identifier::new("x"),
depth: 0, depth: 0,
value_location: ValueLocation::ConstantStack,
}, },
Local { Local {
identifier: Identifier::new("y"), identifier: Identifier::new("y"),
depth: 0, depth: 0,
value_location: ValueLocation::ConstantStack,
}, },
], ],
)) ))
@ -752,16 +771,15 @@ mod tests {
test_chunk, test_chunk,
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::DefineVariable as u8, Span(4, 5)),
(0, Span(4, 5)),
(Instruction::Constant as u8, Span(8, 10)), (Instruction::Constant as u8, Span(8, 10)),
(0, Span(8, 10)), (0, Span(8, 10)),
(Instruction::DeclareVariable as u8, Span(4, 5)),
(0, Span(4, 5)),
], ],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![Local { vec![Local {
identifier: Identifier::new("x"), identifier: Identifier::new("x"),
depth: 0, depth: 0,
value_location: ValueLocation::ConstantStack,
}], }],
)) ))
); );

View File

@ -1,8 +1,8 @@
use std::rc::Rc; use std::rc::{Rc, Weak};
use crate::{ use crate::{
dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span,
Value, ValueError, ValueLocation, Value, ValueError,
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
@ -18,7 +18,7 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
pub struct Vm { pub struct Vm {
chunk: Rc<Chunk>, chunk: Rc<Chunk>,
ip: usize, ip: usize,
stack: Vec<StackedValue>, stack: Vec<Value>,
} }
impl Vm { impl Vm {
@ -50,16 +50,10 @@ impl Vm {
log::trace!("Pushing constant {value}"); log::trace!("Pushing constant {value}");
self.push_runtime_value(value, position)?; self.push(value, position)?;
} }
Instruction::Return => { Instruction::Return => {
let stacked = self.pop(position)?; let value = self.pop(position)?;
let value = match stacked {
StackedValue::Runtime(value) => value,
StackedValue::Constant(index) => Rc::get_mut(&mut self.chunk)
.unwrap()
.remove_constant(index, position)?,
};
log::trace!("Returning {value}"); log::trace!("Returning {value}");
@ -72,221 +66,168 @@ impl Vm {
} }
// Variables // Variables
Instruction::DefineVariable => { Instruction::DeclareVariable => {
let (argument, _) = *self.read(position)?; let (argument, _) = *self.read(position)?;
let identifier = self.chunk.get_identifier(argument)?.clone(); let identifier = self.chunk.get_identifier(argument, position)?;
let stack_index_option = self.chunk.resolve_local(&identifier); let value = self.stack.remove(argument as usize);
if let Some(index) = stack_index_option { log::trace!("Declaring {identifier} as value {value}",);
let value = self.stack[index as usize]
.to_value(&self.chunk, position)?
.clone();
log::trace!("Defining {identifier} as value {value}"); self.push(value, position)?;
self.push_runtime_value(value, position)?;
} else {
return Err(VmError::UndefinedVariable {
identifier,
position,
});
}
} }
Instruction::GetVariable => { Instruction::GetVariable => {
let (argument, _) = *self.read(position)?; let (argument, _) = *self.read(position)?;
let value = self.pop(position)?.to_value(&self.chunk, position)?.clone(); let identifier = self.chunk.get_identifier(argument, position)?;
let value = self.stack.remove(argument as usize);
log::trace!( log::trace!("Getting {identifier} as value {value}",);
"Getting {} as value {value}",
self.chunk.get_identifier(argument)?,
);
self.push_runtime_value(value, position)?; self.push(value, position)?;
} }
Instruction::SetVariable => { Instruction::SetVariable => {
let (argument, _) = *self.read(position)?; let (argument, _) = *self.read(position)?;
let identifier = self.chunk.get_identifier(argument)?.clone(); let identifier = self.chunk.get_identifier(argument, position)?.clone();
if !self.chunk.contains_identifier(&identifier) { if !self.chunk.contains_identifier(&identifier) {
return Err(VmError::UndefinedVariable { return Err(VmError::UndefinedVariable {
identifier, identifier: identifier.clone(),
position, position,
}); });
} }
let value = self.stack[argument as usize] let value = self.pop(position)?;
.to_value(&self.chunk, position)?
.clone();
log::trace!("Setting {identifier} to {value}"); log::trace!("Setting {identifier} to {value}");
self.push_runtime_value(value, position)?; self.stack[argument as usize] = value;
} }
// Unary // Unary
Instruction::Negate => { Instruction::Negate => {
let negated = self let negated = self
.pop(position)? .pop(position)?
.to_value(&self.chunk, position)?
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(negated, position)?; self.push(negated, position)?;
} }
Instruction::Not => { Instruction::Not => {
let not = self let not = self
.pop(position)? .pop(position)?
.to_value(&self.chunk, position)?
.not() .not()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(not, position)?; self.push(not, position)?;
} }
// Binary // Binary
Instruction::Add => { Instruction::Add => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let sum = left let sum = left
.add(right) .add(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(sum, position)?; self.push(sum, position)?;
} }
Instruction::Subtract => { Instruction::Subtract => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let difference = left let difference = left
.subtract(right) .subtract(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(difference, position)?; self.push(difference, position)?;
} }
Instruction::Multiply => { Instruction::Multiply => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let product = left let product = left
.multiply(right) .multiply(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(product, position)?; self.push(product, position)?;
} }
Instruction::Divide => { Instruction::Divide => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let quotient = left let quotient = left
.divide(right) .divide(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(quotient, position)?; self.push(quotient, position)?;
} }
Instruction::Greater => { Instruction::Greater => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let greater = left let greater = left
.greater_than(right) .greater_than(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(greater, position)?; self.push(greater, position)?;
} }
Instruction::Less => { Instruction::Less => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let less = left let less = left
.less_than(right) .less_than(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(less, position)?; self.push(less, position)?;
} }
Instruction::GreaterEqual => { Instruction::GreaterEqual => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let greater_equal = left let greater_equal = left
.greater_than_or_equal(right) .greater_than_or_equal(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(greater_equal, position)?; self.push(greater_equal, position)?;
} }
Instruction::LessEqual => { Instruction::LessEqual => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let less_equal = left let less_equal = left
.less_than_or_equal(right) .less_than_or_equal(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(less_equal, position)?; self.push(less_equal, position)?;
} }
Instruction::Equal => { Instruction::Equal => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let equal = left let equal = left
.equal(right) .equal(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(equal, position)?; self.push(equal, position)?;
} }
Instruction::NotEqual => { Instruction::NotEqual => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let not_equal = left let not_equal = left
.not_equal(right) .not_equal(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(not_equal, position)?; self.push(not_equal, position)?;
} }
Instruction::And => { Instruction::And => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let and = left let and = left
.and(right) .and(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(and, position)?; self.push(and, position)?;
} }
Instruction::Or => { Instruction::Or => {
let chunk = self.chunk.clone(); let right = self.pop(position)?;
let right_stacked = self.pop(position)?; let left = self.pop(position)?;
let right = right_stacked.to_value(chunk.as_ref(), position)?;
let left_stacked = self.pop(position)?;
let left = left_stacked.to_value(&self.chunk, position)?;
let or = left let or = left
.or(right) .or(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.push_runtime_value(or, position)?; self.push(or, position)?;
} }
} }
} }
@ -294,7 +235,7 @@ impl Vm {
Ok(None) Ok(None)
} }
fn push_runtime_value(&mut self, value: Value, position: Span) -> Result<(), VmError> { fn push(&mut self, value: Value, position: Span) -> Result<(), VmError> {
if self.stack.len() == Self::STACK_SIZE { if self.stack.len() == Self::STACK_SIZE {
Err(VmError::StackOverflow(position)) Err(VmError::StackOverflow(position))
} else { } else {
@ -304,23 +245,13 @@ impl Vm {
value value
}; };
self.stack.push(StackedValue::Runtime(value)); self.stack.push(value);
Ok(()) Ok(())
} }
} }
fn push_constant_value(&mut self, index: u8, position: Span) -> Result<(), VmError> { fn pop(&mut self, position: Span) -> Result<Value, VmError> {
if self.stack.len() == Self::STACK_SIZE {
Err(VmError::StackOverflow(position))
} else {
self.stack.push(StackedValue::Constant(index));
Ok(())
}
}
fn pop(&mut self, position: Span) -> Result<StackedValue, VmError> {
if let Some(stacked) = self.stack.pop() { if let Some(stacked) = self.stack.pop() {
Ok(stacked) Ok(stacked)
} else { } else {
@ -337,21 +268,6 @@ impl Vm {
} }
} }
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum StackedValue {
Runtime(Value),
Constant(u8),
}
impl StackedValue {
fn to_value<'a>(&'a self, chunk: &'a Chunk, position: Span) -> Result<&'a Value, VmError> {
match self {
Self::Runtime(value) => Ok(value),
Self::Constant(index) => Ok(chunk.get_constant(*index, position)?),
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum VmError { pub enum VmError {
InvalidInstruction(u8, Span), InvalidInstruction(u8, Span),
@ -402,8 +318,7 @@ impl AnnotatedError for VmError {
Self::UndefinedVariable { identifier, .. } => { Self::UndefinedVariable { identifier, .. } => {
Some(format!("{identifier} is not in scope")) Some(format!("{identifier} is not in scope"))
} }
Self::Chunk(error) => error.details(),
Self::Chunk(error) => Some(error.to_string()),
Self::Value { error, .. } => Some(error.to_string()), Self::Value { error, .. } => Some(error.to_string()),
} }
} }
@ -427,12 +342,15 @@ pub mod tests {
#[test] #[test]
fn negation() { fn negation() {
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
let constant = chunk.push_constant(Value::integer(42)).unwrap(); let dummy_position = Span(0, 0);
let constant = chunk
.push_constant(Value::integer(42), dummy_position)
.unwrap();
chunk.push_code(Instruction::Constant as u8, Span(0, 1)); chunk.push_code(Instruction::Constant as u8, dummy_position);
chunk.push_code(constant, Span(2, 3)); chunk.push_code(constant, dummy_position);
chunk.push_code(Instruction::Negate as u8, Span(4, 5)); chunk.push_code(Instruction::Negate as u8, dummy_position);
chunk.push_code(Instruction::Return as u8, Span(2, 3)); chunk.push_code(Instruction::Return as u8, dummy_position);
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
let result = vm.run(); let result = vm.run();
@ -443,15 +361,20 @@ pub mod tests {
#[test] #[test]
fn addition() { fn addition() {
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
let left = chunk.push_constant(Value::integer(42)).unwrap(); let dummy_position = Span(0, 0);
let right = chunk.push_constant(Value::integer(23)).unwrap(); let left = chunk
.push_constant(Value::integer(42), dummy_position)
.unwrap();
let right = chunk
.push_constant(Value::integer(23), dummy_position)
.unwrap();
chunk.push_code(Instruction::Constant as u8, Span(0, 1)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(left, Span(2, 3)); chunk.push_code(left, dummy_position);
chunk.push_code(Instruction::Constant as u8, Span(4, 5)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(right, Span(6, 7)); chunk.push_code(right, dummy_position);
chunk.push_code(Instruction::Add as u8, Span(8, 9)); chunk.push_code(Instruction::Add, dummy_position);
chunk.push_code(Instruction::Return as u8, Span(10, 11)); chunk.push_code(Instruction::Return, dummy_position);
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
let result = vm.run(); let result = vm.run();
@ -462,15 +385,20 @@ pub mod tests {
#[test] #[test]
fn subtraction() { fn subtraction() {
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
let left = chunk.push_constant(Value::integer(42)).unwrap(); let dummy_position = Span(0, 0);
let right = chunk.push_constant(Value::integer(23)).unwrap(); let left = chunk
.push_constant(Value::integer(42), dummy_position)
.unwrap();
let right = chunk
.push_constant(Value::integer(23), dummy_position)
.unwrap();
chunk.push_code(Instruction::Constant as u8, Span(0, 1)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(left, Span(2, 3)); chunk.push_code(left, dummy_position);
chunk.push_code(Instruction::Constant as u8, Span(4, 5)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(right, Span(6, 7)); chunk.push_code(right, dummy_position);
chunk.push_code(Instruction::Subtract as u8, Span(8, 9)); chunk.push_code(Instruction::Subtract, dummy_position);
chunk.push_code(Instruction::Return as u8, Span(10, 11)); chunk.push_code(Instruction::Return, dummy_position);
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
let result = vm.run(); let result = vm.run();
@ -481,15 +409,20 @@ pub mod tests {
#[test] #[test]
fn multiplication() { fn multiplication() {
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
let left = chunk.push_constant(Value::integer(42)).unwrap(); let dummy_position = Span(0, 0);
let right = chunk.push_constant(Value::integer(23)).unwrap(); let left = chunk
.push_constant(Value::integer(42), dummy_position)
.unwrap();
let right = chunk
.push_constant(Value::integer(23), dummy_position)
.unwrap();
chunk.push_code(Instruction::Constant as u8, Span(0, 1)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(left, Span(2, 3)); chunk.push_code(left, dummy_position);
chunk.push_code(Instruction::Constant as u8, Span(4, 5)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(right, Span(6, 7)); chunk.push_code(right, dummy_position);
chunk.push_code(Instruction::Multiply as u8, Span(8, 9)); chunk.push_code(Instruction::Multiply, dummy_position);
chunk.push_code(Instruction::Return as u8, Span(10, 11)); chunk.push_code(Instruction::Return, dummy_position);
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
let result = vm.run(); let result = vm.run();
@ -501,15 +434,20 @@ pub mod tests {
fn division() { fn division() {
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
let left = chunk.push_constant(Value::integer(42)).unwrap(); let dummy_position = Span(0, 0);
let right = chunk.push_constant(Value::integer(23)).unwrap(); let left = chunk
.push_constant(Value::integer(42), dummy_position)
.unwrap();
let right = chunk
.push_constant(Value::integer(23), dummy_position)
.unwrap();
chunk.push_code(Instruction::Constant as u8, Span(0, 1)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(left, Span(2, 3)); chunk.push_code(left, dummy_position);
chunk.push_code(Instruction::Constant as u8, Span(4, 5)); chunk.push_code(Instruction::Constant, dummy_position);
chunk.push_code(right, Span(6, 7)); chunk.push_code(right, dummy_position);
chunk.push_code(Instruction::Divide as u8, Span(8, 9)); chunk.push_code(Instruction::Divide, dummy_position);
chunk.push_code(Instruction::Return as u8, Span(10, 11)); chunk.push_code(Instruction::Return, dummy_position);
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
let result = vm.run(); let result = vm.run();