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 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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,
|
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}")
|
||||||
|
@ -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};
|
||||||
|
@ -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,
|
|
||||||
}],
|
}],
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user