1
0

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 crate::{
identifier_stack::Local, AnnotatedError, Identifier, IdentifierStack, Instruction, Span, Value,
ValueLocation,
};
use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Chunk {
code: Vec<(u8, Span)>,
constants: Vec<Value>,
identifiers: IdentifierStack,
identifiers: Vec<Local>,
scope_depth: usize,
}
impl Chunk {
@ -19,7 +17,8 @@ impl Chunk {
Self {
code: Vec::new(),
constants: Vec::new(),
identifiers: IdentifierStack::new(),
identifiers: Vec::new(),
scope_depth: 0,
}
}
@ -31,7 +30,8 @@ impl Chunk {
Self {
code,
constants,
identifiers: IdentifierStack::with_data(identifiers, 0),
identifiers,
scope_depth: 0,
}
}
@ -44,7 +44,7 @@ impl Chunk {
}
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> {
@ -53,8 +53,8 @@ impl Chunk {
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
}
pub fn push_code(&mut self, instruction: u8, position: Span) {
self.code.push((instruction, position));
pub fn push_code<T: Into<u8>>(&mut self, into_byte: T, position: Span) {
self.code.push((into_byte.into(), position));
}
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();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::ConstantOverflow)
Err(ChunkError::ConstantOverflow { position })
} else {
self.constants.push(value);
@ -89,28 +89,22 @@ impl Chunk {
}
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
.get(index as usize)
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
.ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
}
pub fn resolve_local(&self, identifier: &Identifier) -> Option<u8> {
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> {
pub fn get_identifier(&self, index: u8, position: Span) -> Result<&Identifier, ChunkError> {
self.identifiers
.get(index as usize)
.map(|local| &local.identifier)
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
.ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
}
pub fn get_identifier_index(
@ -119,59 +113,47 @@ impl Chunk {
position: Span,
) -> Result<u8, ChunkError> {
self.identifiers
.get_index(identifier)
.map(|index| index as u8)
.iter()
.enumerate()
.rev()
.find_map(|(index, local)| {
if &local.identifier == identifier {
Some(index as u8)
} else {
None
}
})
.ok_or(ChunkError::IdentifierNotFound {
identifier: identifier.clone(),
position,
})
}
pub fn push_constant_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::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(
pub fn declare_variable(
&mut self,
identifier: &Identifier,
identifier: Identifier,
position: Span,
) -> Result<usize, ChunkError> {
self.identifiers
.redefine(identifier, ValueLocation::RuntimeStack)
.ok_or_else(|| ChunkError::IdentifierNotFound {
identifier: identifier.clone(),
position,
})
) -> Result<u8, ChunkError> {
let starting_length = self.identifiers.len();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::IdentifierOverflow { position })
} else {
self.identifiers.push(Local {
identifier,
depth: self.scope_depth,
});
Ok(starting_length as u8)
}
}
pub fn begin_scope(&mut self) {
self.identifiers.begin_scope();
self.scope_depth += 1;
}
pub fn end_scope(&mut self) {
self.identifiers.end_scope();
self.scope_depth -= 1;
}
pub fn clear(&mut self) {
@ -180,6 +162,14 @@ impl Chunk {
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 {
let mut output = String::new();
@ -199,7 +189,7 @@ impl Chunk {
for (offset, (byte, position)) in self.code.iter().enumerate() {
if let Some(
Instruction::Constant
| Instruction::DefineVariable
| Instruction::DeclareVariable
| Instruction::GetVariable
| Instruction::SetVariable,
) = previous
@ -238,23 +228,12 @@ impl Chunk {
}
output.push_str("\n Identifiers \n");
output.push_str("----- ---------- -------- -----\n");
output.push_str("INDEX IDENTIFIER LOCATION DEPTH\n");
output.push_str("----- ---------- -------- -----\n");
output.push_str("----- ---------- -----\n");
output.push_str("INDEX IDENTIFIER DEPTH\n");
output.push_str("----- ---------- -----\n");
for (
index,
Local {
identifier,
depth,
value_location,
},
) in self.identifiers.iter().enumerate()
{
let display = format!(
"{index:3} {:10} {value_location} {depth}\n",
identifier.as_str()
);
for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() {
let display = format!("{index:3} {:10} {depth}\n", identifier.as_str());
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)]
pub enum ChunkError {
CodeIndexOfBounds {
offset: usize,
position: Span,
},
ConstantOverflow,
ConstantOverflow {
position: Span,
},
ConstantIndexOutOfBounds {
index: u8,
position: Span,
},
IdentifierIndexOutOfBounds(u8),
IdentifierOverflow,
IdentifierIndexOutOfBounds {
index: u8,
position: Span,
},
IdentifierOverflow {
position: Span,
},
IdentifierNotFound {
identifier: Identifier,
position: Span,
@ -307,10 +299,10 @@ impl AnnotatedError for ChunkError {
fn description(&self) -> &'static str {
match self {
ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds",
ChunkError::ConstantOverflow => "Constant overflow",
ChunkError::ConstantOverflow { .. } => "Constant overflow",
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
ChunkError::IdentifierIndexOutOfBounds(_) => "Identifier index out of bounds",
ChunkError::IdentifierOverflow => "Identifier overflow",
ChunkError::IdentifierIndexOutOfBounds { .. } => "Identifier index out of bounds",
ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
}
}
@ -321,7 +313,7 @@ impl AnnotatedError for ChunkError {
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index))
}
ChunkError::IdentifierIndexOutOfBounds(index) => {
ChunkError::IdentifierIndexOutOfBounds { index, .. } => {
Some(format!("Identifier index: {}", index))
}
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,
// Variables
DefineVariable = 3,
DeclareVariable = 3,
GetVariable = 4,
SetVariable = 5,
@ -40,7 +40,7 @@ impl Instruction {
0 => Some(Instruction::Constant),
1 => Some(Instruction::Return),
2 => Some(Instruction::Pop),
3 => Some(Instruction::DefineVariable),
3 => Some(Instruction::DeclareVariable),
4 => Some(Instruction::GetVariable),
5 => Some(Instruction::SetVariable),
6 => Some(Instruction::Negate),
@ -70,7 +70,7 @@ impl Instruction {
let value_display = chunk
.get_constant(argument, position)
.map(|value| value.to_string())
.unwrap_or_else(|error| error.to_string());
.unwrap_or_else(|error| format!("{error:?}"));
format!("CONSTANT {value_display}")
}
@ -78,20 +78,20 @@ impl Instruction {
Instruction::Pop => "POP".to_string(),
// Variables
Instruction::DefineVariable => {
Instruction::DeclareVariable => {
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(),
Err(error) => error.to_string(),
Err(error) => format!("{error:?}"),
};
format!("DEFINE_VARIABLE {identifier_display}")
format!("DECLARE_VARIABLE {identifier_display}")
}
Instruction::GetVariable => {
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(),
Err(error) => error.to_string(),
Err(error) => format!("{error:?}"),
};
format!("GET_VARIABLE {identifier_display}")
@ -99,9 +99,9 @@ impl Instruction {
Instruction::SetVariable => {
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(),
Err(error) => error.to_string(),
Err(error) => format!("{error:?}"),
};
format!("SET_VARIABLE {identifier_display}")

View File

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

View File

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

View File

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