Begin removing chunk errors; Use constants for identifiers
This commit is contained in:
parent
a9e675cb4f
commit
a2e7a4e73e
@ -6,11 +6,11 @@ use std::{
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value};
|
use crate::{Instruction, Span, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
name: Option<Identifier>,
|
name: Option<String>,
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<Value>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
@ -18,7 +18,7 @@ pub struct Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(name: Option<Identifier>) -> Self {
|
pub fn new(name: Option<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
@ -29,7 +29,7 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_data(
|
pub fn with_data(
|
||||||
name: Option<Identifier>,
|
name: Option<String>,
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<Value>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
@ -43,11 +43,11 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> Option<&Identifier> {
|
pub fn name(&self) -> Option<&String> {
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_name(&mut self, name: Identifier) {
|
pub fn set_name(&mut self, name: String) {
|
||||||
self.name = Some(name);
|
self.name = Some(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,19 @@ impl Chunk {
|
|||||||
self.instructions.is_empty()
|
self.instructions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions(&self) -> &[(Instruction, Span)] {
|
pub fn constants(&self) -> &Vec<Value> {
|
||||||
|
&self.constants
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constants_mut(&mut self) -> &mut Vec<Value> {
|
||||||
|
&mut self.constants
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_constants(self) -> Vec<Value> {
|
||||||
|
self.constants
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
|
||||||
&self.instructions
|
&self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,151 +79,42 @@ impl Chunk {
|
|||||||
&mut self.instructions
|
&mut self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_instruction(
|
pub fn locals(&self) -> &Vec<Local> {
|
||||||
&self,
|
&self.locals
|
||||||
offset: usize,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<&(Instruction, Span), ChunkError> {
|
|
||||||
self.instructions
|
|
||||||
.get(offset)
|
|
||||||
.ok_or(ChunkError::InstructionIndexOfBounds { offset, position })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
|
pub fn locals_mut(&mut self) -> &mut Vec<Local> {
|
||||||
self.instructions.push((instruction, position));
|
&mut self.locals
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_instruction(
|
pub fn scope_depth(&self) -> usize {
|
||||||
&mut self,
|
self.scope_depth
|
||||||
index: usize,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), ChunkError> {
|
|
||||||
if index > self.instructions.len() {
|
|
||||||
Err(ChunkError::InstructionIndexOfBounds {
|
|
||||||
offset: index,
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.instructions.insert(index, (instruction, position));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_constants(self) -> Vec<Value> {
|
pub fn get_constant(&self, index: u8) -> Option<&Value> {
|
||||||
self.constants
|
self.constants.get(index as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
|
||||||
let index = index as usize;
|
|
||||||
|
|
||||||
self.constants
|
|
||||||
.get(index)
|
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_or_get_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
|
|
||||||
if let Some(index) = self
|
if let Some(index) = self
|
||||||
.constants
|
.constants
|
||||||
.iter()
|
.iter()
|
||||||
.position(|constant| constant == &value)
|
.position(|constant| constant == &value)
|
||||||
{
|
{
|
||||||
return Ok(index as u8);
|
return index as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
let starting_length = self.constants.len();
|
|
||||||
|
|
||||||
if starting_length + 1 > (u8::MAX as usize) {
|
|
||||||
Err(ChunkError::ConstantOverflow { position })
|
|
||||||
} else {
|
|
||||||
self.constants.push(value);
|
self.constants.push(value);
|
||||||
|
|
||||||
Ok(starting_length as u8)
|
(self.constants.len() - 1) as u8
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locals(&self) -> &[Local] {
|
pub fn get_identifier(&self, local_index: u8) -> Option<String> {
|
||||||
&self.locals
|
self.locals.get(local_index as usize).and_then(|local| {
|
||||||
}
|
self.constants
|
||||||
|
.get(local.identifier_index as usize)
|
||||||
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
|
.map(|value| value.to_string())
|
||||||
let index = index as usize;
|
|
||||||
|
|
||||||
self.locals
|
|
||||||
.get(index)
|
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_identifier(&self, index: u8) -> Option<&Identifier> {
|
|
||||||
self.locals
|
|
||||||
.get(index as usize)
|
|
||||||
.map(|local| &local.identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result<u8, ChunkError> {
|
|
||||||
self.locals
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.rev()
|
|
||||||
.find_map(|(index, local)| {
|
|
||||||
if local.identifier.as_str() == identifier_text {
|
|
||||||
Some(index as u8)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.ok_or(ChunkError::IdentifierNotFound {
|
|
||||||
identifier: Identifier::new(identifier_text),
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn declare_local(
|
|
||||||
&mut self,
|
|
||||||
identifier: Identifier,
|
|
||||||
r#type: Option<Type>,
|
|
||||||
is_mutable: bool,
|
|
||||||
register_index: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<u8, ChunkError> {
|
|
||||||
log::debug!("Declare local {identifier}");
|
|
||||||
|
|
||||||
let starting_length = self.locals.len();
|
|
||||||
|
|
||||||
if starting_length + 1 > (u8::MAX as usize) {
|
|
||||||
Err(ChunkError::LocalOverflow { position })
|
|
||||||
} else {
|
|
||||||
self.locals.push(Local::new(
|
|
||||||
identifier,
|
|
||||||
r#type,
|
|
||||||
is_mutable,
|
|
||||||
self.scope_depth,
|
|
||||||
register_index,
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(starting_length as u8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn define_local(
|
|
||||||
&mut self,
|
|
||||||
local_index: u8,
|
|
||||||
register_index: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), ChunkError> {
|
|
||||||
let local = self.locals.get_mut(local_index as usize).ok_or_else(|| {
|
|
||||||
ChunkError::LocalIndexOutOfBounds {
|
|
||||||
index: local_index as usize,
|
|
||||||
position,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
log::debug!("Define local {}", local.identifier);
|
|
||||||
|
|
||||||
local.register_index = register_index;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_scope(&mut self) {
|
pub fn begin_scope(&mut self) {
|
||||||
@ -257,7 +160,7 @@ impl PartialEq for Chunk {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
pub identifier: Identifier,
|
pub identifier_index: u8,
|
||||||
pub r#type: Option<Type>,
|
pub r#type: Option<Type>,
|
||||||
pub is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
@ -266,14 +169,14 @@ pub struct Local {
|
|||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
identifier: Identifier,
|
identifier_index: u8,
|
||||||
r#type: Option<Type>,
|
r#type: Option<Type>,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
register_index: u8,
|
register_index: u8,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier,
|
identifier_index,
|
||||||
r#type,
|
r#type,
|
||||||
is_mutable: mutable,
|
is_mutable: mutable,
|
||||||
depth,
|
depth,
|
||||||
@ -524,7 +427,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
for (
|
for (
|
||||||
index,
|
index,
|
||||||
Local {
|
Local {
|
||||||
identifier,
|
identifier_index,
|
||||||
r#type,
|
r#type,
|
||||||
depth,
|
depth,
|
||||||
register_index,
|
register_index,
|
||||||
@ -532,7 +435,12 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
},
|
},
|
||||||
) in self.chunk.locals.iter().enumerate()
|
) in self.chunk.locals.iter().enumerate()
|
||||||
{
|
{
|
||||||
let identifier_display = identifier.as_str();
|
let identifier_display = self
|
||||||
|
.chunk
|
||||||
|
.constants
|
||||||
|
.get(*identifier_index as usize)
|
||||||
|
.map(|value| value.to_string())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
let type_display = r#type
|
let type_display = r#type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r#type| r#type.to_string())
|
.map(|r#type| r#type.to_string())
|
||||||
@ -577,76 +485,3 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
disassembly
|
disassembly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum ChunkError {
|
|
||||||
InstructionIndexOfBounds {
|
|
||||||
offset: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ConstantOverflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ConstantIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
LocalIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
LocalOverflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
IdentifierNotFound {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnnotatedError for ChunkError {
|
|
||||||
fn title() -> &'static str {
|
|
||||||
"Chunk Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
|
|
||||||
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
|
||||||
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
|
||||||
ChunkError::LocalOverflow { .. } => "Local overflow",
|
|
||||||
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn details(&self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
ChunkError::InstructionIndexOfBounds { offset, .. } => {
|
|
||||||
Some(format!("Instruction index: {}", offset))
|
|
||||||
}
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
|
||||||
Some(format!("Constant index: {}", index))
|
|
||||||
}
|
|
||||||
ChunkError::LocalIndexOutOfBounds { index, .. } => {
|
|
||||||
Some(format!("Local index: {}", index))
|
|
||||||
}
|
|
||||||
ChunkError::IdentifierNotFound { identifier, .. } => {
|
|
||||||
Some(format!("Identifier: {}", identifier))
|
|
||||||
}
|
|
||||||
ChunkError::LocalOverflow { .. } => None,
|
|
||||||
ChunkError::ConstantOverflow { .. } => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn position(&self) -> Span {
|
|
||||||
match self {
|
|
||||||
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
|
||||||
ChunkError::IdentifierNotFound { position, .. } => *position,
|
|
||||||
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
|
|
||||||
ChunkError::LocalOverflow { position, .. } => *position,
|
|
||||||
ChunkError::ConstantOverflow { position, .. } => *position,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
//! Key used to identify a value or type.
|
|
||||||
//!
|
|
||||||
//! Identifiers are used to uniquely identify values and types in Dust programs.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//! ```
|
|
||||||
//! # use dust_lang::Identifier;
|
|
||||||
//! let foo = Identifier::new("foo");
|
|
||||||
//! let also_foo = foo.clone();
|
|
||||||
//! let another_foo = also_foo.clone();
|
|
||||||
//!
|
|
||||||
//! assert_eq!(foo.strong_count(), 3); // One for each of the above.
|
|
||||||
//! ```
|
|
||||||
use std::{
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{de::Visitor, Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Key used to identify a value or type.
|
|
||||||
///
|
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct Identifier(Arc<String>);
|
|
||||||
|
|
||||||
impl Identifier {
|
|
||||||
pub fn new<T: ToString>(text: T) -> Self {
|
|
||||||
let string = text.to_string();
|
|
||||||
|
|
||||||
Identifier(Arc::new(string.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
self.0.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn strong_count(&self) -> usize {
|
|
||||||
Arc::strong_count(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Identifier {
|
|
||||||
fn from(string: String) -> Self {
|
|
||||||
Identifier::new(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Identifier {
|
|
||||||
fn from(slice: &str) -> Self {
|
|
||||||
Identifier::new(slice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Identifier {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for Identifier {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.0.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Identifier {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_identifier(IdentifierVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IdentifierVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for IdentifierVisitor {
|
|
||||||
type Value = Identifier;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a UTF-8 string")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_str(v.encode_utf8(&mut [0u8; 4]))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(Identifier::new(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_str(&v)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
mod chunk;
|
mod chunk;
|
||||||
mod dust_error;
|
mod dust_error;
|
||||||
mod formatter;
|
mod formatter;
|
||||||
mod identifier;
|
|
||||||
mod instruction;
|
mod instruction;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod native_function;
|
mod native_function;
|
||||||
@ -12,10 +11,9 @@ mod r#type;
|
|||||||
mod value;
|
mod value;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|
||||||
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
|
pub use chunk::{Chunk, ChunkDisassembler, Local};
|
||||||
pub use dust_error::{AnnotatedError, DustError};
|
pub use dust_error::{AnnotatedError, DustError};
|
||||||
pub use formatter::{format, Formatter};
|
pub use formatter::{format, Formatter};
|
||||||
pub use identifier::Identifier;
|
|
||||||
pub use instruction::Instruction;
|
pub use instruction::Instruction;
|
||||||
pub use lexer::{lex, LexError, Lexer};
|
pub use lexer::{lex, LexError, Lexer};
|
||||||
pub use native_function::{NativeFunction, NativeFunctionError};
|
pub use native_function::{NativeFunction, NativeFunctionError};
|
||||||
|
@ -174,7 +174,7 @@ impl NativeFunction {
|
|||||||
message.push(' ');
|
message.push(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument = vm.get(argument_index, position)?;
|
let argument = vm.get_register(argument_index, position)?;
|
||||||
|
|
||||||
message.push_str(&argument.to_string());
|
message.push_str(&argument.to_string());
|
||||||
}
|
}
|
||||||
@ -197,7 +197,7 @@ impl NativeFunction {
|
|||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
|
|
||||||
for argument_index in 0..argument_count {
|
for argument_index in 0..argument_count {
|
||||||
let argument = vm.get(argument_index, position)?;
|
let argument = vm.get_register(argument_index, position)?;
|
||||||
|
|
||||||
string.push_str(&argument.to_string());
|
string.push_str(&argument.to_string());
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ impl NativeFunction {
|
|||||||
stdout.write(b" ").map_err(map_err)?;
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument_string = vm.get(argument_index, position)?.to_string();
|
let argument_string = vm.get_register(argument_index, position)?.to_string();
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(argument_string.as_bytes())
|
.write_all(argument_string.as_bytes())
|
||||||
@ -264,7 +264,7 @@ impl NativeFunction {
|
|||||||
stdout.write(b" ").map_err(map_err)?;
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument_string = vm.get(argument_index, position)?.to_string();
|
let argument_string = vm.get_register(argument_index, position)?.to_string();
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(argument_string.as_bytes())
|
.write_all(argument_string.as_bytes())
|
||||||
|
@ -9,8 +9,8 @@ use colored::Colorize;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError,
|
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
|
||||||
Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
|
NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||||
@ -107,6 +107,67 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_local(&self, index: u8) -> Result<&Local, ParseError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
self.chunk
|
||||||
|
.locals()
|
||||||
|
.get(index)
|
||||||
|
.ok_or(ParseError::LocalIndexOutOfBounds {
|
||||||
|
index,
|
||||||
|
position: self.current_position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_local_index(&self, identifier_text: &str) -> Result<u8, ParseError> {
|
||||||
|
self.chunk
|
||||||
|
.locals()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find_map(|(index, local)| {
|
||||||
|
let identifier = self
|
||||||
|
.chunk
|
||||||
|
.constants()
|
||||||
|
.get(local.identifier_index as usize)?
|
||||||
|
.as_string()?;
|
||||||
|
|
||||||
|
if identifier == identifier_text {
|
||||||
|
Some(index as u8)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(ParseError::UndeclaredVariable {
|
||||||
|
identifier: identifier_text.to_string(),
|
||||||
|
position: self.current_position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_local(
|
||||||
|
&mut self,
|
||||||
|
identifier: &str,
|
||||||
|
r#type: Option<Type>,
|
||||||
|
is_mutable: bool,
|
||||||
|
register_index: u8,
|
||||||
|
) -> (u8, u8) {
|
||||||
|
log::debug!("Declare local {identifier}");
|
||||||
|
|
||||||
|
let scope_depth = self.chunk.scope_depth();
|
||||||
|
let identifier = Value::string(identifier);
|
||||||
|
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
||||||
|
|
||||||
|
self.chunk.locals_mut().push(Local::new(
|
||||||
|
identifier_index,
|
||||||
|
r#type,
|
||||||
|
is_mutable,
|
||||||
|
scope_depth,
|
||||||
|
register_index,
|
||||||
|
));
|
||||||
|
|
||||||
|
(self.chunk.locals().len() as u8 - 1, identifier_index)
|
||||||
|
}
|
||||||
|
|
||||||
fn allow(&mut self, allowed: Token) -> Result<bool, ParseError> {
|
fn allow(&mut self, allowed: Token) -> Result<bool, ParseError> {
|
||||||
if self.current_token == allowed {
|
if self.current_token == allowed {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
@ -138,7 +199,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.current_statement_length += 1;
|
self.current_statement_length += 1;
|
||||||
|
|
||||||
self.chunk.push_instruction(instruction, position);
|
self.chunk.instructions_mut().push((instruction, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimize_statement(&mut self) {
|
fn optimize_statement(&mut self) {
|
||||||
@ -236,7 +297,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
||||||
let constant_index = self.chunk.push_or_get_constant(value, position)?;
|
let constant_index = self.chunk.push_or_get_constant(value);
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
@ -476,7 +537,7 @@ impl<'src> Parser<'src> {
|
|||||||
let argument = match instruction.operation() {
|
let argument = match instruction.operation() {
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, self.current_position)?;
|
let local = self.get_local(local_index)?;
|
||||||
is_mutable_local = local.is_mutable;
|
is_mutable_local = local.is_mutable;
|
||||||
|
|
||||||
local.register_index
|
local.register_index
|
||||||
@ -754,25 +815,17 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
let token = self.current_token;
|
|
||||||
let start_position = self.current_position;
|
let start_position = self.current_position;
|
||||||
|
|
||||||
let local_index = if let Token::Identifier(text) = token {
|
let local_index = if let Token::Identifier(text) = self.current_token {
|
||||||
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) {
|
if let Ok(local_index) = self.get_local_index(text) {
|
||||||
local_index
|
local_index
|
||||||
} else if let Some(name) = self.chunk.name() {
|
} else if let Some(name) = self.chunk.name() {
|
||||||
if name.as_str() == text {
|
if name.as_str() == text {
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
self.emit_instruction(Instruction::load_self(register), start_position);
|
self.emit_instruction(Instruction::load_self(register), start_position);
|
||||||
|
self.declare_local(text, None, false, register);
|
||||||
self.chunk.declare_local(
|
|
||||||
Identifier::new(text),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
register,
|
|
||||||
start_position,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.current_is_expression = true;
|
self.current_is_expression = true;
|
||||||
|
|
||||||
@ -783,7 +836,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_native_call(allowed)
|
self.parse_native_call(allowed)
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::UndeclaredVariable {
|
Err(ParseError::UndeclaredVariable {
|
||||||
identifier: Identifier::new(text),
|
identifier: text.to_string(),
|
||||||
position: start_position,
|
position: start_position,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -792,7 +845,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_native_call(allowed)
|
self.parse_native_call(allowed)
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::UndeclaredVariable {
|
Err(ParseError::UndeclaredVariable {
|
||||||
identifier: Identifier::new(text),
|
identifier: text.to_string(),
|
||||||
position: start_position,
|
position: start_position,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -807,12 +860,9 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let is_mutable = self
|
|
||||||
.chunk
|
|
||||||
.get_local(local_index, start_position)?
|
|
||||||
.is_mutable;
|
|
||||||
|
|
||||||
if self.allow(Token::Equal)? {
|
if self.allow(Token::Equal)? {
|
||||||
|
let is_mutable = self.get_local(local_index)?.is_mutable;
|
||||||
|
|
||||||
if !allowed.assignment {
|
if !allowed.assignment {
|
||||||
return Err(ParseError::InvalidAssignmentTarget {
|
return Err(ParseError::InvalidAssignmentTarget {
|
||||||
found: self.current_token.to_owned(),
|
found: self.current_token.to_owned(),
|
||||||
@ -822,7 +872,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
if !is_mutable {
|
if !is_mutable {
|
||||||
return Err(ParseError::CannotMutateImmutableVariable {
|
return Err(ParseError::CannotMutateImmutableVariable {
|
||||||
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
|
identifier: self.chunk.get_identifier(local_index).unwrap(),
|
||||||
position: start_position,
|
position: start_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -838,10 +888,7 @@ impl<'src> Parser<'src> {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
if previous_instruction.operation().is_math() {
|
if previous_instruction.operation().is_math() {
|
||||||
let register_index = self
|
let register_index = self.get_local(local_index)?.register_index;
|
||||||
.chunk
|
|
||||||
.get_local(local_index, start_position)?
|
|
||||||
.register_index;
|
|
||||||
|
|
||||||
log::trace!("Condensing SET_LOCAL to binary math expression");
|
log::trace!("Condensing SET_LOCAL to binary math expression");
|
||||||
|
|
||||||
@ -1019,18 +1066,22 @@ impl<'src> Parser<'src> {
|
|||||||
if let Some(skippable) = self.get_last_jumpable_mut() {
|
if let Some(skippable) = self.get_last_jumpable_mut() {
|
||||||
skippable.set_c_to_boolean(true);
|
skippable.set_c_to_boolean(true);
|
||||||
} else {
|
} else {
|
||||||
self.chunk.insert_instruction(
|
self.chunk.instructions_mut().insert(
|
||||||
else_start,
|
else_start,
|
||||||
|
(
|
||||||
Instruction::jump(jump_distance, true),
|
Instruction::jump(jump_distance, true),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
)?;
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.chunk.insert_instruction(
|
self.chunk.instructions_mut().insert(
|
||||||
else_start,
|
else_start,
|
||||||
|
(
|
||||||
Instruction::jump(jump_distance, true),
|
Instruction::jump(jump_distance, true),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
)?;
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.current_is_expression = false;
|
self.current_is_expression = false;
|
||||||
@ -1054,11 +1105,13 @@ impl<'src> Parser<'src> {
|
|||||||
if else_end - if_block_end > 1 {
|
if else_end - if_block_end > 1 {
|
||||||
let jump_distance = (else_end - if_block_end) as u8;
|
let jump_distance = (else_end - if_block_end) as u8;
|
||||||
|
|
||||||
self.chunk.insert_instruction(
|
self.chunk.instructions_mut().insert(
|
||||||
if_block_end,
|
if_block_end,
|
||||||
|
(
|
||||||
Instruction::jump(jump_distance, true),
|
Instruction::jump(jump_distance, true),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
)?;
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
@ -1096,11 +1149,13 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let block_end = self.chunk.len() as u8;
|
let block_end = self.chunk.len() as u8;
|
||||||
|
|
||||||
self.chunk.insert_instruction(
|
self.chunk.instructions_mut().insert(
|
||||||
block_start,
|
block_start,
|
||||||
|
(
|
||||||
Instruction::jump(block_end - block_start as u8 + 1, true),
|
Instruction::jump(block_end - block_start as u8 + 1, true),
|
||||||
self.current_position,
|
self.current_position,
|
||||||
)?;
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let jump_back_distance = block_end - expression_start + 1;
|
let jump_back_distance = block_end - expression_start + 1;
|
||||||
let jump_back = Instruction::jump(jump_back_distance, false);
|
let jump_back = Instruction::jump(jump_back_distance, false);
|
||||||
@ -1267,7 +1322,7 @@ impl<'src> Parser<'src> {
|
|||||||
let identifier = if let Token::Identifier(text) = self.current_token {
|
let identifier = if let Token::Identifier(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
Identifier::new(text)
|
text
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenKind::Identifier,
|
expected: TokenKind::Identifier,
|
||||||
@ -1289,9 +1344,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.expect(Token::Equal)?;
|
self.expect(Token::Equal)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let local_index = self
|
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register);
|
||||||
.chunk
|
|
||||||
.declare_local(identifier, r#type, is_mutable, register, position)?;
|
|
||||||
let register = self.next_register().saturating_sub(1);
|
let register = self.next_register().saturating_sub(1);
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
@ -1310,27 +1363,25 @@ impl<'src> Parser<'src> {
|
|||||||
let mut function_parser = Parser::new(self.lexer)?;
|
let mut function_parser = Parser::new(self.lexer)?;
|
||||||
let identifier = if let Token::Identifier(text) = function_parser.current_token {
|
let identifier = if let Token::Identifier(text) = function_parser.current_token {
|
||||||
let position = function_parser.current_position;
|
let position = function_parser.current_position;
|
||||||
let identifier = Identifier::new(text);
|
|
||||||
|
|
||||||
function_parser.advance()?;
|
function_parser.advance()?;
|
||||||
function_parser.chunk.set_name(identifier.clone());
|
function_parser.chunk.set_name(text.to_string());
|
||||||
|
|
||||||
Some((identifier, position))
|
Some((text, position))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
function_parser.expect(Token::LeftParenthesis)?;
|
function_parser.expect(Token::LeftParenthesis)?;
|
||||||
|
|
||||||
let mut value_parameters: Option<Vec<(Identifier, Type)>> = None;
|
let mut value_parameters: Option<Vec<(u8, Type)>> = None;
|
||||||
|
|
||||||
while function_parser.current_token != Token::RightParenthesis {
|
while function_parser.current_token != Token::RightParenthesis {
|
||||||
let start = function_parser.current_position.0;
|
|
||||||
let is_mutable = function_parser.allow(Token::Mut)?;
|
let is_mutable = function_parser.allow(Token::Mut)?;
|
||||||
let parameter = if let Token::Identifier(text) = function_parser.current_token {
|
let parameter = if let Token::Identifier(text) = function_parser.current_token {
|
||||||
function_parser.advance()?;
|
function_parser.advance()?;
|
||||||
|
|
||||||
Identifier::new(text)
|
text
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenKind::Identifier,
|
expected: TokenKind::Identifier,
|
||||||
@ -1348,26 +1399,22 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
function_parser.advance()?;
|
function_parser.advance()?;
|
||||||
|
|
||||||
let end = function_parser.current_position.1;
|
|
||||||
|
|
||||||
if let Some(value_parameters) = value_parameters.as_mut() {
|
|
||||||
value_parameters.push((parameter.clone(), r#type.clone()));
|
|
||||||
} else {
|
|
||||||
value_parameters = Some(vec![(parameter.clone(), r#type.clone())]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let register = value_parameters
|
let register = value_parameters
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|values| values.len() as u8 - 1)
|
.map(|values| values.len() as u8 - 1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
let (_, identifier_index) = function_parser.declare_local(
|
||||||
function_parser.chunk.declare_local(
|
|
||||||
parameter,
|
parameter,
|
||||||
Some(r#type),
|
Some(r#type.clone()),
|
||||||
is_mutable,
|
is_mutable,
|
||||||
register,
|
register,
|
||||||
Span(start, end),
|
);
|
||||||
)?;
|
|
||||||
|
if let Some(value_parameters) = value_parameters.as_mut() {
|
||||||
|
value_parameters.push((identifier_index, r#type));
|
||||||
|
} else {
|
||||||
|
value_parameters = Some(vec![(identifier_index, r#type)]);
|
||||||
|
};
|
||||||
|
|
||||||
function_parser.minimum_register += 1;
|
function_parser.minimum_register += 1;
|
||||||
|
|
||||||
@ -1409,13 +1456,12 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
if let Some((identifier, identifier_position)) = identifier {
|
if let Some((identifier, identifier_position)) = identifier {
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
let local_index = self.chunk.declare_local(
|
let (local_index, _) = self.declare_local(
|
||||||
identifier,
|
identifier,
|
||||||
Some(Type::Function(function_type)),
|
Some(Type::Function(function_type)),
|
||||||
false,
|
false,
|
||||||
register,
|
register,
|
||||||
Span(function_start, function_end),
|
);
|
||||||
)?;
|
|
||||||
|
|
||||||
self.emit_constant(function, Span(function_start, function_end))?;
|
self.emit_constant(function, Span(function_start, function_end))?;
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
@ -1867,17 +1913,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
CannotChainComparison {
|
// Token errors
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
CannotMutateImmutableVariable {
|
|
||||||
identifier: Identifier,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedExpression {
|
|
||||||
found: TokenOwned,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedToken {
|
ExpectedToken {
|
||||||
expected: TokenKind,
|
expected: TokenKind,
|
||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
@ -1888,19 +1924,42 @@ pub enum ParseError {
|
|||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Expression errors
|
||||||
|
CannotChainComparison {
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
ExpectedExpression {
|
||||||
|
found: TokenOwned,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Variable errors
|
||||||
|
CannotMutateImmutableVariable {
|
||||||
|
identifier: String,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
ExpectedMutableVariable {
|
ExpectedMutableVariable {
|
||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
UndeclaredVariable {
|
||||||
|
identifier: String,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Statement errors
|
||||||
InvalidAssignmentTarget {
|
InvalidAssignmentTarget {
|
||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
UndeclaredVariable {
|
UnexpectedReturn {
|
||||||
identifier: Identifier,
|
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
UnexpectedReturn {
|
|
||||||
|
// Chunk errors
|
||||||
|
LocalIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
RegisterOverflow {
|
RegisterOverflow {
|
||||||
@ -1911,7 +1970,6 @@ pub enum ParseError {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Wrappers around foreign errors
|
// Wrappers around foreign errors
|
||||||
Chunk(ChunkError),
|
|
||||||
Lex(LexError),
|
Lex(LexError),
|
||||||
ParseFloatError {
|
ParseFloatError {
|
||||||
error: ParseFloatError,
|
error: ParseFloatError,
|
||||||
@ -1923,12 +1981,6 @@ 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"
|
||||||
@ -1939,18 +1991,18 @@ impl AnnotatedError for ParseError {
|
|||||||
Self::CannotChainComparison { .. } => "Cannot chain comparison",
|
Self::CannotChainComparison { .. } => "Cannot chain comparison",
|
||||||
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
||||||
Self::ExpectedExpression { .. } => "Expected an expression",
|
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||||
|
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
|
||||||
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::ExpectedMutableVariable { .. } => "Expected a mutable variable",
|
|
||||||
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||||
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
Self::Lex(error) => error.description(),
|
||||||
Self::UnexpectedReturn { .. } => "Unexpected return",
|
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
Self::RegisterOverflow { .. } => "Register overflow",
|
|
||||||
Self::RegisterUnderflow { .. } => "Register underflow",
|
|
||||||
Self::ParseFloatError { .. } => "Failed to parse float",
|
Self::ParseFloatError { .. } => "Failed to parse float",
|
||||||
Self::ParseIntError { .. } => "Failed to parse integer",
|
Self::ParseIntError { .. } => "Failed to parse integer",
|
||||||
Self::Chunk(error) => error.description(),
|
Self::RegisterOverflow { .. } => "Register overflow",
|
||||||
Self::Lex(error) => error.description(),
|
Self::RegisterUnderflow { .. } => "Register underflow",
|
||||||
|
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
||||||
|
Self::UnexpectedReturn { .. } => "Unexpected return",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1993,16 +2045,18 @@ impl AnnotatedError for ParseError {
|
|||||||
Self::InvalidAssignmentTarget { found, .. } => {
|
Self::InvalidAssignmentTarget { found, .. } => {
|
||||||
Some(format!("Invalid assignment target, found {found}"))
|
Some(format!("Invalid assignment target, found {found}"))
|
||||||
}
|
}
|
||||||
|
Self::Lex(error) => error.details(),
|
||||||
|
Self::LocalIndexOutOfBounds { index, .. } => {
|
||||||
|
Some(format!("Local index {index} out of bounds"))
|
||||||
|
}
|
||||||
|
Self::ParseFloatError { error, .. } => Some(error.to_string()),
|
||||||
|
Self::ParseIntError { error, .. } => Some(error.to_string()),
|
||||||
|
Self::RegisterOverflow { .. } => None,
|
||||||
|
Self::RegisterUnderflow { .. } => None,
|
||||||
Self::UndeclaredVariable { identifier, .. } => {
|
Self::UndeclaredVariable { identifier, .. } => {
|
||||||
Some(format!("Undeclared variable {identifier}"))
|
Some(format!("Undeclared variable {identifier}"))
|
||||||
}
|
}
|
||||||
Self::UnexpectedReturn { .. } => None,
|
Self::UnexpectedReturn { .. } => None,
|
||||||
Self::RegisterOverflow { .. } => None,
|
|
||||||
Self::RegisterUnderflow { .. } => None,
|
|
||||||
Self::ParseFloatError { error, .. } => Some(error.to_string()),
|
|
||||||
Self::ParseIntError { error, .. } => Some(error.to_string()),
|
|
||||||
Self::Chunk(error) => error.details(),
|
|
||||||
Self::Lex(error) => error.details(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2011,18 +2065,18 @@ impl AnnotatedError for ParseError {
|
|||||||
Self::CannotChainComparison { position } => *position,
|
Self::CannotChainComparison { position } => *position,
|
||||||
Self::CannotMutateImmutableVariable { position, .. } => *position,
|
Self::CannotMutateImmutableVariable { position, .. } => *position,
|
||||||
Self::ExpectedExpression { position, .. } => *position,
|
Self::ExpectedExpression { position, .. } => *position,
|
||||||
|
Self::ExpectedMutableVariable { position, .. } => *position,
|
||||||
Self::ExpectedToken { position, .. } => *position,
|
Self::ExpectedToken { position, .. } => *position,
|
||||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||||
Self::ExpectedMutableVariable { position, .. } => *position,
|
|
||||||
Self::InvalidAssignmentTarget { position, .. } => *position,
|
Self::InvalidAssignmentTarget { position, .. } => *position,
|
||||||
Self::UndeclaredVariable { position, .. } => *position,
|
Self::Lex(error) => error.position(),
|
||||||
Self::UnexpectedReturn { position } => *position,
|
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::RegisterOverflow { position } => *position,
|
|
||||||
Self::RegisterUnderflow { position } => *position,
|
|
||||||
Self::ParseFloatError { position, .. } => *position,
|
Self::ParseFloatError { position, .. } => *position,
|
||||||
Self::ParseIntError { position, .. } => *position,
|
Self::ParseIntError { position, .. } => *position,
|
||||||
Self::Chunk(error) => error.position(),
|
Self::RegisterOverflow { position } => *position,
|
||||||
Self::Lex(error) => error.position(),
|
Self::RegisterUnderflow { position } => *position,
|
||||||
|
Self::UndeclaredVariable { position, .. } => *position,
|
||||||
|
Self::UnexpectedReturn { position } => *position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Identifier;
|
|
||||||
|
|
||||||
/// Description of a kind of value.
|
/// Description of a kind of value.
|
||||||
///
|
///
|
||||||
/// See the [module documentation](index.html) for more information.
|
/// See the [module documentation](index.html) for more information.
|
||||||
@ -32,7 +30,7 @@ pub enum Type {
|
|||||||
Float,
|
Float,
|
||||||
Function(FunctionType),
|
Function(FunctionType),
|
||||||
Generic {
|
Generic {
|
||||||
identifier: Identifier,
|
identifier_index: u8,
|
||||||
concrete_type: Option<Box<Type>>,
|
concrete_type: Option<Box<Type>>,
|
||||||
},
|
},
|
||||||
Integer,
|
Integer,
|
||||||
@ -45,7 +43,7 @@ pub enum Type {
|
|||||||
item_type: Box<Type>,
|
item_type: Box<Type>,
|
||||||
},
|
},
|
||||||
Map {
|
Map {
|
||||||
pairs: HashMap<Identifier, Type>,
|
pairs: HashMap<u8, Type>,
|
||||||
},
|
},
|
||||||
Number,
|
Number,
|
||||||
Range {
|
Range {
|
||||||
@ -230,47 +228,6 @@ impl Type {
|
|||||||
expected: self.clone(),
|
expected: self.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_field(&self, field: &Identifier) -> bool {
|
|
||||||
match field.as_str() {
|
|
||||||
"to_string" => true,
|
|
||||||
"length" => {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Type::List { .. }
|
|
||||||
| Type::ListOf { .. }
|
|
||||||
| Type::ListEmpty
|
|
||||||
| Type::Map { .. }
|
|
||||||
| Type::String { .. }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"is_even" | "is_odd" => matches!(self, Type::Integer | Type::Float),
|
|
||||||
_ => match self {
|
|
||||||
Type::Struct(StructType::Fields { fields, .. }) => fields.contains_key(field),
|
|
||||||
Type::Map { pairs } => pairs.contains_key(field),
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_field_type(&self, field: &Identifier) -> Option<Type> {
|
|
||||||
match field.as_str() {
|
|
||||||
"length" => match self {
|
|
||||||
Type::List { .. } => Some(Type::Integer),
|
|
||||||
Type::ListOf { .. } => Some(Type::Integer),
|
|
||||||
Type::ListEmpty => Some(Type::Integer),
|
|
||||||
Type::Map { .. } => Some(Type::Integer),
|
|
||||||
Type::String { .. } => Some(Type::Integer),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
"is_even" | "is_odd" => Some(Type::Boolean),
|
|
||||||
_ => match self {
|
|
||||||
Type::Struct(StructType::Fields { fields, .. }) => fields.get(field).cloned(),
|
|
||||||
Type::Map { pairs } => pairs.get(field).cloned(),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Type {
|
impl Display for Type {
|
||||||
@ -285,7 +242,10 @@ impl Display for Type {
|
|||||||
Type::Function(function_type) => write!(f, "{function_type}"),
|
Type::Function(function_type) => write!(f, "{function_type}"),
|
||||||
Type::Generic { concrete_type, .. } => {
|
Type::Generic { concrete_type, .. } => {
|
||||||
match concrete_type.clone().map(|r#box| *r#box) {
|
match concrete_type.clone().map(|r#box| *r#box) {
|
||||||
Some(Type::Generic { identifier, .. }) => write!(f, "{identifier}"),
|
Some(Type::Generic {
|
||||||
|
identifier_index: identifier,
|
||||||
|
..
|
||||||
|
}) => write!(f, "{identifier}"),
|
||||||
Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
|
Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
|
||||||
None => write!(f, "unknown"),
|
None => write!(f, "unknown"),
|
||||||
}
|
}
|
||||||
@ -416,8 +376,8 @@ impl Ord for Type {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct FunctionType {
|
pub struct FunctionType {
|
||||||
pub type_parameters: Option<Vec<Identifier>>,
|
pub type_parameters: Option<Vec<u8>>,
|
||||||
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
pub value_parameters: Option<Vec<(u8, Type)>>,
|
||||||
pub return_type: Option<Box<Type>>,
|
pub return_type: Option<Box<Type>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,25 +423,17 @@ impl Display for FunctionType {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum StructType {
|
pub enum StructType {
|
||||||
Unit {
|
Unit { name: u8 },
|
||||||
name: Identifier,
|
Tuple { name: u8, fields: Vec<Type> },
|
||||||
},
|
Fields { name: u8, fields: HashMap<u8, Type> },
|
||||||
Tuple {
|
|
||||||
name: Identifier,
|
|
||||||
fields: Vec<Type>,
|
|
||||||
},
|
|
||||||
Fields {
|
|
||||||
name: Identifier,
|
|
||||||
fields: HashMap<Identifier, Type>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructType {
|
impl StructType {
|
||||||
pub fn name(&self) -> &Identifier {
|
pub fn name(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
StructType::Unit { name } => name,
|
StructType::Unit { name } => *name,
|
||||||
StructType::Tuple { name, .. } => name,
|
StructType::Tuple { name, .. } => *name,
|
||||||
StructType::Fields { name, .. } => name,
|
StructType::Fields { name, .. } => *name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,7 +535,7 @@ impl Ord for StructType {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct EnumType {
|
pub struct EnumType {
|
||||||
pub name: Identifier,
|
pub name: u8,
|
||||||
pub variants: Vec<StructType>,
|
pub variants: Vec<StructType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,14 @@ impl Value {
|
|||||||
Value::Primitive(Primitive::String(to_string.to_string()))
|
Value::Primitive(Primitive::String(to_string.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_string(&self) -> Option<&String> {
|
||||||
|
if let Value::Primitive(Primitive::String(string)) = self {
|
||||||
|
Some(string)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_function(&self) -> bool {
|
pub fn is_function(&self) -> bool {
|
||||||
matches!(self, Value::Function(_))
|
matches!(self, Value::Function(_))
|
||||||
}
|
}
|
||||||
@ -894,7 +902,7 @@ impl Object {
|
|||||||
display.push_str(", ");
|
display.push_str(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let value_display = match vm.get(register, position) {
|
let value_display = match vm.get_register(register, position) {
|
||||||
Ok(value) => value.display(vm, position)?,
|
Ok(value) => value.display(vm, position)?,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(ValueError::CannotDisplay {
|
return Err(ValueError::CannotDisplay {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use std::{cmp::Ordering, mem::replace};
|
use std::{cmp::Ordering, mem::replace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local,
|
||||||
Identifier, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value,
|
NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
||||||
ValueError,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
@ -42,14 +41,24 @@ impl Vm {
|
|||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(&Value, &Value), VmError> {
|
) -> Result<(&Value, &Value), VmError> {
|
||||||
let left = if instruction.b_is_constant() {
|
let left = if instruction.b_is_constant() {
|
||||||
vm.chunk.get_constant(instruction.b(), position)?
|
vm.chunk.get_constant(instruction.b()).ok_or_else(|| {
|
||||||
|
VmError::ConstantIndexOutOfBounds {
|
||||||
|
index: instruction.b() as usize,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
vm.get(instruction.b(), position)?
|
vm.get_register(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let right = if instruction.c_is_constant() {
|
let right = if instruction.c_is_constant() {
|
||||||
vm.chunk.get_constant(instruction.c(), position)?
|
vm.chunk.get_constant(instruction.c()).ok_or_else(|| {
|
||||||
|
VmError::ConstantIndexOutOfBounds {
|
||||||
|
index: instruction.c() as usize,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
vm.get(instruction.c(), position)?
|
vm.get_register(instruction.c(), position)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((left, right))
|
Ok((left, right))
|
||||||
@ -89,7 +98,7 @@ impl Vm {
|
|||||||
let jump = instruction.c_as_boolean();
|
let jump = instruction.c_as_boolean();
|
||||||
let value = Value::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
self.set(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -115,11 +124,11 @@ impl Vm {
|
|||||||
let item_type = if is_empty {
|
let item_type = if is_empty {
|
||||||
Type::Any
|
Type::Any
|
||||||
} else {
|
} else {
|
||||||
self.get(first_register, position)?.r#type()
|
self.get_register(first_register, position)?.r#type()
|
||||||
};
|
};
|
||||||
let value = Value::list(first_register, last_register, item_type);
|
let value = Value::list(first_register, last_register, item_type);
|
||||||
|
|
||||||
self.set(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
@ -132,34 +141,37 @@ impl Vm {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.set(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
let to_local = instruction.b();
|
let to_local = instruction.b();
|
||||||
|
|
||||||
self.chunk.define_local(to_local, from_register, position)?;
|
self.define_local(to_local, from_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.get_local(local_index, position)?;
|
||||||
|
|
||||||
self.set_pointer(to_register, local.register_index, position)?;
|
self.set_pointer(to_register, local.register_index, position)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.get_local(local_index, position)?;
|
||||||
|
|
||||||
if !local.is_mutable {
|
if !local.is_mutable {
|
||||||
return Err(VmError::CannotMutateImmutableLocal {
|
return Err(VmError::CannotMutateImmutableLocal {
|
||||||
identifier: local.identifier.clone(),
|
identifier: self
|
||||||
|
.chunk
|
||||||
|
.get_identifier(local.identifier_index)
|
||||||
|
.unwrap_or_else(|| "unknown".to_string()),
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chunk.define_local(local_index, register, position)?;
|
self.define_local(local_index, register, position)?;
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||||
@ -167,7 +179,7 @@ impl Vm {
|
|||||||
.add(right)
|
.add(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), sum, position)?;
|
self.set_register(instruction.a(), sum, position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||||
@ -175,7 +187,7 @@ impl Vm {
|
|||||||
.subtract(right)
|
.subtract(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), difference, position)?;
|
self.set_register(instruction.a(), difference, position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||||
@ -183,7 +195,7 @@ impl Vm {
|
|||||||
.multiply(right)
|
.multiply(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), product, position)?;
|
self.set_register(instruction.a(), product, position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||||
@ -191,7 +203,7 @@ impl Vm {
|
|||||||
.divide(right)
|
.divide(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), quotient, position)?;
|
self.set_register(instruction.a(), quotient, position)?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||||
@ -199,12 +211,12 @@ impl Vm {
|
|||||||
.modulo(right)
|
.modulo(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), remainder, position)?;
|
self.set_register(instruction.a(), remainder, position)?;
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
let test_value = instruction.c_as_boolean();
|
let test_value = instruction.c_as_boolean();
|
||||||
let value = self.get(register, position)?;
|
let value = self.get_register(register, position)?;
|
||||||
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value {
|
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
@ -221,7 +233,7 @@ impl Vm {
|
|||||||
Operation::TestSet => todo!(),
|
Operation::TestSet => todo!(),
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip, position)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -243,7 +255,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
let (jump, _) = self.get_instruction(self.ip, position)?;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -257,7 +269,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip, position)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -279,7 +291,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
let jump = self.get_instruction(self.ip, position)?.0;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -293,7 +305,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip, position)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -316,7 +328,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
let jump = self.get_instruction(self.ip, position)?.0;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -330,27 +342,37 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value = if instruction.b_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
self.chunk.get_constant(instruction.b(), position)?
|
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
|
||||||
|
VmError::ConstantIndexOutOfBounds {
|
||||||
|
index: instruction.b() as usize,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
self.get(instruction.b(), position)?
|
self.get_register(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let negated = value
|
let negated = value
|
||||||
.negate()
|
.negate()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), negated, position)?;
|
self.set_register(instruction.a(), negated, position)?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value = if instruction.b_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
self.chunk.get_constant(instruction.b(), position)?
|
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
|
||||||
|
VmError::ConstantIndexOutOfBounds {
|
||||||
|
index: instruction.b() as usize,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
self.get(instruction.b(), position)?
|
self.get_register(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let not = value
|
let not = value
|
||||||
.not()
|
.not()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set(instruction.a(), not, position)?;
|
self.set_register(instruction.a(), not, position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let jump_distance = instruction.b();
|
let jump_distance = instruction.b();
|
||||||
@ -366,7 +388,7 @@ impl Vm {
|
|||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let function_register = instruction.b();
|
let function_register = instruction.b();
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let value = self.get(function_register, position)?.clone();
|
let value = self.get_register(function_register, position)?.clone();
|
||||||
let function = if let Value::Function(function) = value {
|
let function = if let Value::Function(function) = value {
|
||||||
function
|
function
|
||||||
} else {
|
} else {
|
||||||
@ -381,20 +403,20 @@ impl Vm {
|
|||||||
for argument_index in
|
for argument_index in
|
||||||
first_argument_index..first_argument_index + argument_count
|
first_argument_index..first_argument_index + argument_count
|
||||||
{
|
{
|
||||||
let argument = match self.get(argument_index, position) {
|
let argument = match self.get_register(argument_index, position) {
|
||||||
Ok(value) => value.clone(),
|
Ok(value) => value.clone(),
|
||||||
Err(VmError::EmptyRegister { .. }) => continue,
|
Err(VmError::EmptyRegister { .. }) => continue,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let top_of_stack = function_vm.stack.len() as u8;
|
let top_of_stack = function_vm.stack.len() as u8;
|
||||||
|
|
||||||
function_vm.set(top_of_stack, argument, position)?;
|
function_vm.set_register(top_of_stack, argument, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = function_vm.run()?;
|
let return_value = function_vm.run()?;
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
self.set(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
@ -404,7 +426,7 @@ impl Vm {
|
|||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
|
||||||
self.set(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
@ -415,7 +437,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(register) = self.last_assigned_register {
|
if let Some(register) = self.last_assigned_register {
|
||||||
let value = self.empty(register, position)?;
|
let value = self.empty_register(register, position)?;
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(Some(value));
|
||||||
} else {
|
} else {
|
||||||
@ -428,7 +450,12 @@ impl Vm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> {
|
fn set_register(
|
||||||
|
&mut self,
|
||||||
|
to_register: u8,
|
||||||
|
value: Value,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
let length = self.stack.len();
|
let length = self.stack.len();
|
||||||
self.last_assigned_register = Some(to_register);
|
self.last_assigned_register = Some(to_register);
|
||||||
let to_register = to_register as usize;
|
let to_register = to_register as usize;
|
||||||
@ -564,7 +591,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
let register = self
|
let register = self
|
||||||
.stack
|
.stack
|
||||||
@ -573,21 +600,19 @@ impl Vm {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => Ok(value),
|
Register::Value(value) => Ok(value),
|
||||||
Register::Pointer(register_index) => {
|
Register::Pointer(register_index) => self.get_register(*register_index, position),
|
||||||
let value = self.get(*register_index, position)?;
|
Register::Constant(constant_index) => self
|
||||||
|
.chunk
|
||||||
Ok(value)
|
.get_constant(*constant_index)
|
||||||
}
|
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
|
||||||
Register::Constant(constant_index) => {
|
index: *constant_index as usize,
|
||||||
let value = self.chunk.get_constant(*constant_index, position)?;
|
position,
|
||||||
|
}),
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty(mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
fn empty_register(mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
|
||||||
if index >= self.stack.len() {
|
if index >= self.stack.len() {
|
||||||
@ -599,7 +624,7 @@ impl Vm {
|
|||||||
match register {
|
match register {
|
||||||
Register::Value(value) => Ok(value),
|
Register::Value(value) => Ok(value),
|
||||||
Register::Pointer(register_index) => {
|
Register::Pointer(register_index) => {
|
||||||
let value = self.empty(register_index, position)?;
|
let value = self.empty_register(register_index, position)?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
@ -617,12 +642,62 @@ impl Vm {
|
|||||||
self.ip = self.chunk.len() - 1;
|
self.ip = self.chunk.len() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let current = self.chunk.get_instruction(self.ip, position)?;
|
let current = self.chunk.instructions().get(self.ip).ok_or_else(|| {
|
||||||
|
VmError::InstructionIndexOutOfBounds {
|
||||||
|
index: self.ip,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
|
||||||
Ok(current)
|
Ok(current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn define_local(
|
||||||
|
&mut self,
|
||||||
|
local_index: u8,
|
||||||
|
register_index: u8,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let local = self
|
||||||
|
.chunk
|
||||||
|
.locals_mut()
|
||||||
|
.get_mut(local_index as usize)
|
||||||
|
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
|
||||||
|
index: local_index as usize,
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
log::debug!("Define local L{}", local_index);
|
||||||
|
|
||||||
|
local.register_index = register_index;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> {
|
||||||
|
let local_index = local_index as usize;
|
||||||
|
|
||||||
|
self.chunk
|
||||||
|
.locals()
|
||||||
|
.get(local_index)
|
||||||
|
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
|
||||||
|
index: local_index,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_instruction(
|
||||||
|
&self,
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<&(Instruction, Span), VmError> {
|
||||||
|
self.chunk
|
||||||
|
.instructions()
|
||||||
|
.get(index)
|
||||||
|
.ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -636,7 +711,7 @@ enum Register {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
CannotMutateImmutableLocal {
|
CannotMutateImmutableLocal {
|
||||||
identifier: Identifier,
|
identifier: String,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
EmptyRegister {
|
EmptyRegister {
|
||||||
@ -651,10 +726,22 @@ pub enum VmError {
|
|||||||
found: Value,
|
found: Value,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
ConstantIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
RegisterIndexOutOfBounds {
|
RegisterIndexOutOfBounds {
|
||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
InstructionIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
LocalIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
InvalidInstruction {
|
InvalidInstruction {
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -666,25 +753,18 @@ pub enum VmError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
UndefinedVariable {
|
UndefinedVariable {
|
||||||
identifier: Identifier,
|
identifier: String,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
NativeFunction(NativeFunctionError),
|
NativeFunction(NativeFunctionError),
|
||||||
Chunk(ChunkError),
|
|
||||||
Value {
|
Value {
|
||||||
error: ValueError,
|
error: ValueError,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChunkError> for VmError {
|
|
||||||
fn from(error: ChunkError) -> Self {
|
|
||||||
Self::Chunk(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnnotatedError for VmError {
|
impl AnnotatedError for VmError {
|
||||||
fn title() -> &'static str {
|
fn title() -> &'static str {
|
||||||
"Runtime Error"
|
"Runtime Error"
|
||||||
@ -693,15 +773,17 @@ impl AnnotatedError for VmError {
|
|||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
|
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
|
||||||
|
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
|
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
||||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||||
|
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
Self::StackUnderflow { .. } => "Stack underflow",
|
Self::StackUnderflow { .. } => "Stack underflow",
|
||||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||||
Self::Chunk(error) => error.description(),
|
|
||||||
Self::NativeFunction(error) => error.description(),
|
Self::NativeFunction(error) => error.description(),
|
||||||
Self::Value { .. } => "Value error",
|
Self::Value { .. } => "Value error",
|
||||||
}
|
}
|
||||||
@ -709,15 +791,23 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn details(&self) -> Option<String> {
|
fn details(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::ConstantIndexOutOfBounds { index, .. } => {
|
||||||
|
Some(format!("Constant C{index} does not exist"))
|
||||||
|
}
|
||||||
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
||||||
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
||||||
Self::RegisterIndexOutOfBounds { index, .. } => {
|
Self::RegisterIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Register {index} does not exist"))
|
Some(format!("Register {index} does not exist"))
|
||||||
}
|
}
|
||||||
|
Self::InstructionIndexOutOfBounds { index, .. } => {
|
||||||
|
Some(format!("Instruction {index} does not exist"))
|
||||||
|
}
|
||||||
|
Self::LocalIndexOutOfBounds { index, .. } => {
|
||||||
|
Some(format!("Local L{index} does not exist"))
|
||||||
|
}
|
||||||
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::NativeFunction(error) => error.details(),
|
Self::NativeFunction(error) => error.details(),
|
||||||
Self::Value { error, .. } => Some(error.to_string()),
|
Self::Value { error, .. } => Some(error.to_string()),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -727,15 +817,17 @@ impl AnnotatedError for VmError {
|
|||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::CannotMutateImmutableLocal { position, .. } => *position,
|
Self::CannotMutateImmutableLocal { position, .. } => *position,
|
||||||
|
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
|
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::InvalidInstruction { position, .. } => *position,
|
Self::InvalidInstruction { position, .. } => *position,
|
||||||
|
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackUnderflow { position } => *position,
|
Self::StackUnderflow { position } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
Self::UndefinedVariable { position, .. } => *position,
|
Self::UndefinedVariable { position, .. } => *position,
|
||||||
Self::Chunk(error) => error.position(),
|
|
||||||
Self::NativeFunction(error) => error.position(),
|
Self::NativeFunction(error) => error.position(),
|
||||||
Self::Value { position, .. } => *position,
|
Self::Value { position, .. } => *position,
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ fn equality_assignment_long() {
|
|||||||
(Instruction::r#return(true), Span(44, 44)),
|
(Instruction::r#return(true), Span(44, 44)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4)],
|
vec![Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
|
vec![Local::new(0, None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ fn equality_assignment_short() {
|
|||||||
(Instruction::r#return(true), Span(16, 16)),
|
(Instruction::r#return(true), Span(16, 16)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4)],
|
vec![Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
|
vec![Local::new(0, None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ fn if_else_assigment() {
|
|||||||
Value::integer(3),
|
Value::integer(3),
|
||||||
Value::integer(42),
|
Value::integer(42),
|
||||||
],
|
],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
|
vec![Local::new(0, None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ fn add_assign() {
|
|||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
|
vec![Local::new(0, None, true, 0, 0)]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ fn define_local() {
|
|||||||
(Instruction::r#return(false), Span(11, 11))
|
(Instruction::r#return(false), Span(11, 11))
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), None, false, 0, 0)]
|
vec![Local::new(0, None, false, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ fn divide_assign() {
|
|||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2)],
|
vec![Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
|
vec![Local::new(0, None, true, 0, 0)]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ fn multiply_assign() {
|
|||||||
(Instruction::r#return(true), Span(23, 23))
|
(Instruction::r#return(true), Span(23, 23))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(3)],
|
vec![Value::integer(2), Value::integer(3)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, 0),]
|
vec![Local::new(0, None, true, 0, 0),]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -453,7 +453,7 @@ fn set_local() {
|
|||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(41), Value::integer(42)],
|
vec![Value::integer(41), Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
|
vec![Local::new(0, None, true, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -504,7 +504,7 @@ fn subtract_assign() {
|
|||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(2)],
|
vec![Value::integer(42), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
|
vec![Local::new(0, None, true, 0, 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -532,8 +532,8 @@ fn variable_and() {
|
|||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), None, false, 0, 0),
|
Local::new(0, None, false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), None, false, 0, 1),
|
Local::new(0, None, false, 0, 1),
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
@ -563,7 +563,7 @@ fn r#while() {
|
|||||||
(Instruction::r#return(true), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, 0),]
|
vec![Local::new(0, None, true, 0, 0),]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -15,16 +15,13 @@ fn function() {
|
|||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
|
Local::new(0, Some(Type::Integer), false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
|
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![
|
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||||
(Identifier::new("a"), Type::Integer),
|
|
||||||
(Identifier::new("b"), Type::Integer)
|
|
||||||
]),
|
|
||||||
return_type: Some(Box::new(Type::Integer)),
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
}
|
}
|
||||||
)))
|
)))
|
||||||
@ -53,27 +50,21 @@ fn function_declaration() {
|
|||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
|
Local::new(0, Some(Type::Integer), false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
|
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![
|
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||||
(Identifier::new("a"), Type::Integer),
|
|
||||||
(Identifier::new("b"), Type::Integer)
|
|
||||||
]),
|
|
||||||
return_type: Some(Box::new(Type::Integer)),
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
vec![Local::new(
|
vec![Local::new(
|
||||||
Identifier::new("add"),
|
0,
|
||||||
Some(Type::Function(FunctionType {
|
Some(Type::Function(FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![
|
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||||
(Identifier::new("a"), Type::Integer),
|
|
||||||
(Identifier::new("b"), Type::Integer)
|
|
||||||
]),
|
|
||||||
return_type: Some(Box::new(Type::Integer)),
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
})),
|
})),
|
||||||
false,
|
false,
|
||||||
@ -111,16 +102,13 @@ fn function_call() {
|
|||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
|
Local::new(0, Some(Type::Integer), false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
|
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![
|
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||||
(Identifier::new("a"), Type::Integer),
|
|
||||||
(Identifier::new("b"), Type::Integer)
|
|
||||||
]),
|
|
||||||
return_type: Some(Box::new(Type::Integer)),
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -50,11 +50,11 @@ fn block_scope() {
|
|||||||
Value::integer(2),
|
Value::integer(2),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(Identifier::new("a"), None, false, 0, 0),
|
Local::new(0, None, false, 0, 0),
|
||||||
Local::new(Identifier::new("b"), None, false, 1, 1),
|
Local::new(0, None, false, 1, 1),
|
||||||
Local::new(Identifier::new("c"), None, false, 2, 2),
|
Local::new(0, None, false, 2, 2),
|
||||||
Local::new(Identifier::new("d"), None, false, 1, 3),
|
Local::new(0, None, false, 1, 3),
|
||||||
Local::new(Identifier::new("e"), None, false, 0, 4),
|
Local::new(0, None, false, 0, 4),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user