Get variable scopes working
This commit is contained in:
parent
8f58bf30a4
commit
e4204c1b0d
@ -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
|
||||
@ -237,24 +227,13 @@ impl Chunk {
|
||||
output.push_str(&display);
|
||||
}
|
||||
|
||||
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 Identifiers \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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 "),
|
||||
}
|
||||
}
|
||||
}
|
@ -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}")
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
})?;
|
||||
|
||||
Ok(identifier_index)
|
||||
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,
|
||||
}],
|
||||
))
|
||||
);
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user