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 serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value};
|
||||
use crate::{Instruction, Span, Type, Value};
|
||||
|
||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Chunk {
|
||||
name: Option<Identifier>,
|
||||
name: Option<String>,
|
||||
instructions: Vec<(Instruction, Span)>,
|
||||
constants: Vec<Value>,
|
||||
locals: Vec<Local>,
|
||||
@ -18,7 +18,7 @@ pub struct Chunk {
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub fn new(name: Option<Identifier>) -> Self {
|
||||
pub fn new(name: Option<String>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
instructions: Vec::new(),
|
||||
@ -29,7 +29,7 @@ impl Chunk {
|
||||
}
|
||||
|
||||
pub fn with_data(
|
||||
name: Option<Identifier>,
|
||||
name: Option<String>,
|
||||
instructions: Vec<(Instruction, Span)>,
|
||||
constants: Vec<Value>,
|
||||
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()
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, name: Identifier) {
|
||||
pub fn set_name(&mut self, name: String) {
|
||||
self.name = Some(name);
|
||||
}
|
||||
|
||||
@ -59,7 +59,19 @@ impl Chunk {
|
||||
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
|
||||
}
|
||||
|
||||
@ -67,151 +79,42 @@ impl Chunk {
|
||||
&mut self.instructions
|
||||
}
|
||||
|
||||
pub fn get_instruction(
|
||||
&self,
|
||||
offset: usize,
|
||||
position: Span,
|
||||
) -> Result<&(Instruction, Span), ChunkError> {
|
||||
self.instructions
|
||||
.get(offset)
|
||||
.ok_or(ChunkError::InstructionIndexOfBounds { offset, position })
|
||||
pub fn locals(&self) -> &Vec<Local> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
|
||||
self.instructions.push((instruction, position));
|
||||
pub fn locals_mut(&mut self) -> &mut Vec<Local> {
|
||||
&mut self.locals
|
||||
}
|
||||
|
||||
pub fn insert_instruction(
|
||||
&mut self,
|
||||
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 scope_depth(&self) -> usize {
|
||||
self.scope_depth
|
||||
}
|
||||
|
||||
pub fn take_constants(self) -> Vec<Value> {
|
||||
self.constants
|
||||
pub fn get_constant(&self, index: u8) -> Option<&Value> {
|
||||
self.constants.get(index as usize)
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
||||
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> {
|
||||
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
|
||||
if let Some(index) = self
|
||||
.constants
|
||||
.iter()
|
||||
.position(|constant| constant == &value)
|
||||
{
|
||||
return Ok(index as u8);
|
||||
return index as u8;
|
||||
}
|
||||
|
||||
let starting_length = self.constants.len();
|
||||
self.constants.push(value);
|
||||
|
||||
if starting_length + 1 > (u8::MAX as usize) {
|
||||
Err(ChunkError::ConstantOverflow { position })
|
||||
} else {
|
||||
self.constants.push(value);
|
||||
|
||||
Ok(starting_length as u8)
|
||||
}
|
||||
(self.constants.len() - 1) as u8
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &[Local] {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
|
||||
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 get_identifier(&self, local_index: u8) -> Option<String> {
|
||||
self.locals.get(local_index as usize).and_then(|local| {
|
||||
self.constants
|
||||
.get(local.identifier_index as usize)
|
||||
.map(|value| value.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn begin_scope(&mut self) {
|
||||
@ -257,7 +160,7 @@ impl PartialEq for Chunk {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Local {
|
||||
pub identifier: Identifier,
|
||||
pub identifier_index: u8,
|
||||
pub r#type: Option<Type>,
|
||||
pub is_mutable: bool,
|
||||
pub depth: usize,
|
||||
@ -266,14 +169,14 @@ pub struct Local {
|
||||
|
||||
impl Local {
|
||||
pub fn new(
|
||||
identifier: Identifier,
|
||||
identifier_index: u8,
|
||||
r#type: Option<Type>,
|
||||
mutable: bool,
|
||||
depth: usize,
|
||||
register_index: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
identifier,
|
||||
identifier_index,
|
||||
r#type,
|
||||
is_mutable: mutable,
|
||||
depth,
|
||||
@ -524,7 +427,7 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
for (
|
||||
index,
|
||||
Local {
|
||||
identifier,
|
||||
identifier_index,
|
||||
r#type,
|
||||
depth,
|
||||
register_index,
|
||||
@ -532,7 +435,12 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
},
|
||||
) 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
|
||||
.as_ref()
|
||||
.map(|r#type| r#type.to_string())
|
||||
@ -577,76 +485,3 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
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 dust_error;
|
||||
mod formatter;
|
||||
mod identifier;
|
||||
mod instruction;
|
||||
mod lexer;
|
||||
mod native_function;
|
||||
@ -12,10 +11,9 @@ mod r#type;
|
||||
mod value;
|
||||
mod vm;
|
||||
|
||||
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
|
||||
pub use chunk::{Chunk, ChunkDisassembler, Local};
|
||||
pub use dust_error::{AnnotatedError, DustError};
|
||||
pub use formatter::{format, Formatter};
|
||||
pub use identifier::Identifier;
|
||||
pub use instruction::Instruction;
|
||||
pub use lexer::{lex, LexError, Lexer};
|
||||
pub use native_function::{NativeFunction, NativeFunctionError};
|
||||
|
@ -174,7 +174,7 @@ impl NativeFunction {
|
||||
message.push(' ');
|
||||
}
|
||||
|
||||
let argument = vm.get(argument_index, position)?;
|
||||
let argument = vm.get_register(argument_index, position)?;
|
||||
|
||||
message.push_str(&argument.to_string());
|
||||
}
|
||||
@ -197,7 +197,7 @@ impl NativeFunction {
|
||||
let mut string = String::new();
|
||||
|
||||
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());
|
||||
}
|
||||
@ -238,7 +238,7 @@ impl NativeFunction {
|
||||
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
|
||||
.write_all(argument_string.as_bytes())
|
||||
@ -264,7 +264,7 @@ impl NativeFunction {
|
||||
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
|
||||
.write_all(argument_string.as_bytes())
|
||||
|
@ -9,8 +9,8 @@ use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError,
|
||||
Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
|
||||
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
|
||||
NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
|
||||
};
|
||||
|
||||
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
@ -107,6 +107,67 @@ impl<'src> Parser<'src> {
|
||||
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> {
|
||||
if self.current_token == allowed {
|
||||
self.advance()?;
|
||||
@ -138,7 +199,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
self.current_statement_length += 1;
|
||||
|
||||
self.chunk.push_instruction(instruction, position);
|
||||
self.chunk.instructions_mut().push((instruction, position));
|
||||
}
|
||||
|
||||
fn optimize_statement(&mut self) {
|
||||
@ -236,7 +297,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
self.emit_instruction(
|
||||
@ -476,7 +537,7 @@ impl<'src> Parser<'src> {
|
||||
let argument = match instruction.operation() {
|
||||
Operation::GetLocal => {
|
||||
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;
|
||||
|
||||
local.register_index
|
||||
@ -754,25 +815,17 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||
let token = self.current_token;
|
||||
let start_position = self.current_position;
|
||||
|
||||
let local_index = if let Token::Identifier(text) = token {
|
||||
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) {
|
||||
let local_index = if let Token::Identifier(text) = self.current_token {
|
||||
if let Ok(local_index) = self.get_local_index(text) {
|
||||
local_index
|
||||
} else if let Some(name) = self.chunk.name() {
|
||||
if name.as_str() == text {
|
||||
let register = self.next_register();
|
||||
|
||||
self.emit_instruction(Instruction::load_self(register), start_position);
|
||||
|
||||
self.chunk.declare_local(
|
||||
Identifier::new(text),
|
||||
None,
|
||||
false,
|
||||
register,
|
||||
start_position,
|
||||
)?;
|
||||
self.declare_local(text, None, false, register);
|
||||
|
||||
self.current_is_expression = true;
|
||||
|
||||
@ -783,7 +836,7 @@ impl<'src> Parser<'src> {
|
||||
self.parse_native_call(allowed)
|
||||
} else {
|
||||
Err(ParseError::UndeclaredVariable {
|
||||
identifier: Identifier::new(text),
|
||||
identifier: text.to_string(),
|
||||
position: start_position,
|
||||
})
|
||||
};
|
||||
@ -792,7 +845,7 @@ impl<'src> Parser<'src> {
|
||||
self.parse_native_call(allowed)
|
||||
} else {
|
||||
Err(ParseError::UndeclaredVariable {
|
||||
identifier: Identifier::new(text),
|
||||
identifier: text.to_string(),
|
||||
position: start_position,
|
||||
})
|
||||
};
|
||||
@ -807,12 +860,9 @@ impl<'src> Parser<'src> {
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let is_mutable = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
.is_mutable;
|
||||
|
||||
if self.allow(Token::Equal)? {
|
||||
let is_mutable = self.get_local(local_index)?.is_mutable;
|
||||
|
||||
if !allowed.assignment {
|
||||
return Err(ParseError::InvalidAssignmentTarget {
|
||||
found: self.current_token.to_owned(),
|
||||
@ -822,7 +872,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
if !is_mutable {
|
||||
return Err(ParseError::CannotMutateImmutableVariable {
|
||||
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
|
||||
identifier: self.chunk.get_identifier(local_index).unwrap(),
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
@ -838,10 +888,7 @@ impl<'src> Parser<'src> {
|
||||
})?;
|
||||
|
||||
if previous_instruction.operation().is_math() {
|
||||
let register_index = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
.register_index;
|
||||
let register_index = self.get_local(local_index)?.register_index;
|
||||
|
||||
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() {
|
||||
skippable.set_c_to_boolean(true);
|
||||
} else {
|
||||
self.chunk.insert_instruction(
|
||||
self.chunk.instructions_mut().insert(
|
||||
else_start,
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
)?;
|
||||
(
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.chunk.insert_instruction(
|
||||
self.chunk.instructions_mut().insert(
|
||||
else_start,
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
)?;
|
||||
(
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.current_is_expression = false;
|
||||
@ -1054,11 +1105,13 @@ impl<'src> Parser<'src> {
|
||||
if else_end - if_block_end > 1 {
|
||||
let jump_distance = (else_end - if_block_end) as u8;
|
||||
|
||||
self.chunk.insert_instruction(
|
||||
self.chunk.instructions_mut().insert(
|
||||
if_block_end,
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
)?;
|
||||
(
|
||||
Instruction::jump(jump_distance, true),
|
||||
self.current_position,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
@ -1096,11 +1149,13 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let block_end = self.chunk.len() as u8;
|
||||
|
||||
self.chunk.insert_instruction(
|
||||
self.chunk.instructions_mut().insert(
|
||||
block_start,
|
||||
Instruction::jump(block_end - block_start as u8 + 1, true),
|
||||
self.current_position,
|
||||
)?;
|
||||
(
|
||||
Instruction::jump(block_end - block_start as u8 + 1, true),
|
||||
self.current_position,
|
||||
),
|
||||
);
|
||||
|
||||
let jump_back_distance = block_end - expression_start + 1;
|
||||
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 {
|
||||
self.advance()?;
|
||||
|
||||
Identifier::new(text)
|
||||
text
|
||||
} else {
|
||||
return Err(ParseError::ExpectedToken {
|
||||
expected: TokenKind::Identifier,
|
||||
@ -1289,9 +1344,7 @@ impl<'src> Parser<'src> {
|
||||
self.expect(Token::Equal)?;
|
||||
self.parse_expression()?;
|
||||
|
||||
let local_index = self
|
||||
.chunk
|
||||
.declare_local(identifier, r#type, is_mutable, register, position)?;
|
||||
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register);
|
||||
let register = self.next_register().saturating_sub(1);
|
||||
|
||||
self.emit_instruction(
|
||||
@ -1310,27 +1363,25 @@ impl<'src> Parser<'src> {
|
||||
let mut function_parser = Parser::new(self.lexer)?;
|
||||
let identifier = if let Token::Identifier(text) = function_parser.current_token {
|
||||
let position = function_parser.current_position;
|
||||
let identifier = Identifier::new(text);
|
||||
|
||||
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 {
|
||||
None
|
||||
};
|
||||
|
||||
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 {
|
||||
let start = function_parser.current_position.0;
|
||||
let is_mutable = function_parser.allow(Token::Mut)?;
|
||||
let parameter = if let Token::Identifier(text) = function_parser.current_token {
|
||||
function_parser.advance()?;
|
||||
|
||||
Identifier::new(text)
|
||||
text
|
||||
} else {
|
||||
return Err(ParseError::ExpectedToken {
|
||||
expected: TokenKind::Identifier,
|
||||
@ -1348,26 +1399,22 @@ impl<'src> Parser<'src> {
|
||||
|
||||
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
|
||||
.as_ref()
|
||||
.map(|values| values.len() as u8 - 1)
|
||||
.unwrap_or(0);
|
||||
|
||||
function_parser.chunk.declare_local(
|
||||
let (_, identifier_index) = function_parser.declare_local(
|
||||
parameter,
|
||||
Some(r#type),
|
||||
Some(r#type.clone()),
|
||||
is_mutable,
|
||||
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;
|
||||
|
||||
@ -1409,13 +1456,12 @@ impl<'src> Parser<'src> {
|
||||
|
||||
if let Some((identifier, identifier_position)) = identifier {
|
||||
let register = self.next_register();
|
||||
let local_index = self.chunk.declare_local(
|
||||
let (local_index, _) = self.declare_local(
|
||||
identifier,
|
||||
Some(Type::Function(function_type)),
|
||||
false,
|
||||
register,
|
||||
Span(function_start, function_end),
|
||||
)?;
|
||||
);
|
||||
|
||||
self.emit_constant(function, Span(function_start, function_end))?;
|
||||
self.emit_instruction(
|
||||
@ -1867,17 +1913,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ParseError {
|
||||
CannotChainComparison {
|
||||
position: Span,
|
||||
},
|
||||
CannotMutateImmutableVariable {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedExpression {
|
||||
found: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
// Token errors
|
||||
ExpectedToken {
|
||||
expected: TokenKind,
|
||||
found: TokenOwned,
|
||||
@ -1888,19 +1924,42 @@ pub enum ParseError {
|
||||
found: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Expression errors
|
||||
CannotChainComparison {
|
||||
position: Span,
|
||||
},
|
||||
ExpectedExpression {
|
||||
found: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Variable errors
|
||||
CannotMutateImmutableVariable {
|
||||
identifier: String,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedMutableVariable {
|
||||
found: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
UndeclaredVariable {
|
||||
identifier: String,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Statement errors
|
||||
InvalidAssignmentTarget {
|
||||
found: TokenOwned,
|
||||
position: Span,
|
||||
},
|
||||
UndeclaredVariable {
|
||||
identifier: Identifier,
|
||||
UnexpectedReturn {
|
||||
position: Span,
|
||||
},
|
||||
UnexpectedReturn {
|
||||
|
||||
// Chunk errors
|
||||
LocalIndexOutOfBounds {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
RegisterOverflow {
|
||||
@ -1911,7 +1970,6 @@ pub enum ParseError {
|
||||
},
|
||||
|
||||
// Wrappers around foreign errors
|
||||
Chunk(ChunkError),
|
||||
Lex(LexError),
|
||||
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 {
|
||||
fn title() -> &'static str {
|
||||
"Parse Error"
|
||||
@ -1939,18 +1991,18 @@ impl AnnotatedError for ParseError {
|
||||
Self::CannotChainComparison { .. } => "Cannot chain comparison",
|
||||
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
||||
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
|
||||
Self::ExpectedToken { .. } => "Expected a specific token",
|
||||
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
|
||||
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
||||
Self::UnexpectedReturn { .. } => "Unexpected return",
|
||||
Self::RegisterOverflow { .. } => "Register overflow",
|
||||
Self::RegisterUnderflow { .. } => "Register underflow",
|
||||
Self::Lex(error) => error.description(),
|
||||
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||
Self::ParseFloatError { .. } => "Failed to parse float",
|
||||
Self::ParseIntError { .. } => "Failed to parse integer",
|
||||
Self::Chunk(error) => error.description(),
|
||||
Self::Lex(error) => error.description(),
|
||||
Self::RegisterOverflow { .. } => "Register overflow",
|
||||
Self::RegisterUnderflow { .. } => "Register underflow",
|
||||
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
||||
Self::UnexpectedReturn { .. } => "Unexpected return",
|
||||
}
|
||||
}
|
||||
|
||||
@ -1993,16 +2045,18 @@ impl AnnotatedError for ParseError {
|
||||
Self::InvalidAssignmentTarget { 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, .. } => {
|
||||
Some(format!("Undeclared variable {identifier}"))
|
||||
}
|
||||
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::CannotMutateImmutableVariable { position, .. } => *position,
|
||||
Self::ExpectedExpression { position, .. } => *position,
|
||||
Self::ExpectedMutableVariable { position, .. } => *position,
|
||||
Self::ExpectedToken { position, .. } => *position,
|
||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||
Self::ExpectedMutableVariable { position, .. } => *position,
|
||||
Self::InvalidAssignmentTarget { position, .. } => *position,
|
||||
Self::UndeclaredVariable { position, .. } => *position,
|
||||
Self::UnexpectedReturn { position } => *position,
|
||||
Self::RegisterOverflow { position } => *position,
|
||||
Self::RegisterUnderflow { position } => *position,
|
||||
Self::Lex(error) => error.position(),
|
||||
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||
Self::ParseFloatError { position, .. } => *position,
|
||||
Self::ParseIntError { position, .. } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Lex(error) => error.position(),
|
||||
Self::RegisterOverflow { position } => *position,
|
||||
Self::RegisterUnderflow { position } => *position,
|
||||
Self::UndeclaredVariable { position, .. } => *position,
|
||||
Self::UnexpectedReturn { position } => *position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,6 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Identifier;
|
||||
|
||||
/// Description of a kind of value.
|
||||
///
|
||||
/// See the [module documentation](index.html) for more information.
|
||||
@ -32,7 +30,7 @@ pub enum Type {
|
||||
Float,
|
||||
Function(FunctionType),
|
||||
Generic {
|
||||
identifier: Identifier,
|
||||
identifier_index: u8,
|
||||
concrete_type: Option<Box<Type>>,
|
||||
},
|
||||
Integer,
|
||||
@ -45,7 +43,7 @@ pub enum Type {
|
||||
item_type: Box<Type>,
|
||||
},
|
||||
Map {
|
||||
pairs: HashMap<Identifier, Type>,
|
||||
pairs: HashMap<u8, Type>,
|
||||
},
|
||||
Number,
|
||||
Range {
|
||||
@ -230,47 +228,6 @@ impl Type {
|
||||
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 {
|
||||
@ -285,7 +242,10 @@ impl Display for Type {
|
||||
Type::Function(function_type) => write!(f, "{function_type}"),
|
||||
Type::Generic { concrete_type, .. } => {
|
||||
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}"),
|
||||
None => write!(f, "unknown"),
|
||||
}
|
||||
@ -416,8 +376,8 @@ impl Ord for Type {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct FunctionType {
|
||||
pub type_parameters: Option<Vec<Identifier>>,
|
||||
pub value_parameters: Option<Vec<(Identifier, Type)>>,
|
||||
pub type_parameters: Option<Vec<u8>>,
|
||||
pub value_parameters: Option<Vec<(u8, Type)>>,
|
||||
pub return_type: Option<Box<Type>>,
|
||||
}
|
||||
|
||||
@ -463,25 +423,17 @@ impl Display for FunctionType {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum StructType {
|
||||
Unit {
|
||||
name: Identifier,
|
||||
},
|
||||
Tuple {
|
||||
name: Identifier,
|
||||
fields: Vec<Type>,
|
||||
},
|
||||
Fields {
|
||||
name: Identifier,
|
||||
fields: HashMap<Identifier, Type>,
|
||||
},
|
||||
Unit { name: u8 },
|
||||
Tuple { name: u8, fields: Vec<Type> },
|
||||
Fields { name: u8, fields: HashMap<u8, Type> },
|
||||
}
|
||||
|
||||
impl StructType {
|
||||
pub fn name(&self) -> &Identifier {
|
||||
pub fn name(&self) -> u8 {
|
||||
match self {
|
||||
StructType::Unit { name } => name,
|
||||
StructType::Tuple { name, .. } => name,
|
||||
StructType::Fields { name, .. } => name,
|
||||
StructType::Unit { name } => *name,
|
||||
StructType::Tuple { name, .. } => *name,
|
||||
StructType::Fields { name, .. } => *name,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -583,7 +535,7 @@ impl Ord for StructType {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct EnumType {
|
||||
pub name: Identifier,
|
||||
pub name: u8,
|
||||
pub variants: Vec<StructType>,
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,14 @@ impl Value {
|
||||
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 {
|
||||
matches!(self, Value::Function(_))
|
||||
}
|
||||
@ -894,7 +902,7 @@ impl Object {
|
||||
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)?,
|
||||
Err(error) => {
|
||||
return Err(ValueError::CannotDisplay {
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::{cmp::Ordering, mem::replace};
|
||||
|
||||
use crate::{
|
||||
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
||||
Identifier, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value,
|
||||
ValueError,
|
||||
parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local,
|
||||
NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
@ -42,14 +41,24 @@ impl Vm {
|
||||
position: Span,
|
||||
) -> Result<(&Value, &Value), VmError> {
|
||||
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 {
|
||||
vm.get(instruction.b(), position)?
|
||||
vm.get_register(instruction.b(), position)?
|
||||
};
|
||||
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 {
|
||||
vm.get(instruction.c(), position)?
|
||||
vm.get_register(instruction.c(), position)?
|
||||
};
|
||||
|
||||
Ok((left, right))
|
||||
@ -89,7 +98,7 @@ impl Vm {
|
||||
let jump = instruction.c_as_boolean();
|
||||
let value = Value::boolean(boolean);
|
||||
|
||||
self.set(to_register, value, position)?;
|
||||
self.set_register(to_register, value, position)?;
|
||||
|
||||
if jump {
|
||||
self.ip += 1;
|
||||
@ -115,11 +124,11 @@ impl Vm {
|
||||
let item_type = if is_empty {
|
||||
Type::Any
|
||||
} 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);
|
||||
|
||||
self.set(to_register, value, position)?;
|
||||
self.set_register(to_register, value, position)?;
|
||||
}
|
||||
Operation::LoadSelf => {
|
||||
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 => {
|
||||
let from_register = instruction.a();
|
||||
let to_local = instruction.b();
|
||||
|
||||
self.chunk.define_local(to_local, from_register, position)?;
|
||||
self.define_local(to_local, from_register, position)?;
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
let to_register = instruction.a();
|
||||
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)?;
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
let register = instruction.a();
|
||||
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 {
|
||||
return Err(VmError::CannotMutateImmutableLocal {
|
||||
identifier: local.identifier.clone(),
|
||||
identifier: self
|
||||
.chunk
|
||||
.get_identifier(local.identifier_index)
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
self.chunk.define_local(local_index, register, position)?;
|
||||
self.define_local(local_index, register, position)?;
|
||||
}
|
||||
Operation::Add => {
|
||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||
@ -167,7 +179,7 @@ impl Vm {
|
||||
.add(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), sum, position)?;
|
||||
self.set_register(instruction.a(), sum, position)?;
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||
@ -175,7 +187,7 @@ impl Vm {
|
||||
.subtract(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), difference, position)?;
|
||||
self.set_register(instruction.a(), difference, position)?;
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||
@ -183,7 +195,7 @@ impl Vm {
|
||||
.multiply(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), product, position)?;
|
||||
self.set_register(instruction.a(), product, position)?;
|
||||
}
|
||||
Operation::Divide => {
|
||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||
@ -191,7 +203,7 @@ impl Vm {
|
||||
.divide(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), quotient, position)?;
|
||||
self.set_register(instruction.a(), quotient, position)?;
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let (left, right) = get_arguments(&mut self, instruction, position)?;
|
||||
@ -199,12 +211,12 @@ impl Vm {
|
||||
.modulo(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), remainder, position)?;
|
||||
self.set_register(instruction.a(), remainder, position)?;
|
||||
}
|
||||
Operation::Test => {
|
||||
let register = instruction.a();
|
||||
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 {
|
||||
*boolean
|
||||
} else {
|
||||
@ -221,7 +233,7 @@ impl Vm {
|
||||
Operation::TestSet => todo!(),
|
||||
Operation::Equal => {
|
||||
debug_assert_eq!(
|
||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||
self.get_instruction(self.ip, position)?.0.operation(),
|
||||
Operation::Jump
|
||||
);
|
||||
|
||||
@ -243,7 +255,7 @@ impl Vm {
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} else {
|
||||
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
||||
let (jump, _) = self.get_instruction(self.ip, position)?;
|
||||
let jump_distance = jump.a();
|
||||
let is_positive = jump.b_as_boolean();
|
||||
let new_ip = if is_positive {
|
||||
@ -257,7 +269,7 @@ impl Vm {
|
||||
}
|
||||
Operation::Less => {
|
||||
debug_assert_eq!(
|
||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||
self.get_instruction(self.ip, position)?.0.operation(),
|
||||
Operation::Jump
|
||||
);
|
||||
|
||||
@ -279,7 +291,7 @@ impl Vm {
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} 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 is_positive = jump.b_as_boolean();
|
||||
let new_ip = if is_positive {
|
||||
@ -293,7 +305,7 @@ impl Vm {
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
debug_assert_eq!(
|
||||
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||
self.get_instruction(self.ip, position)?.0.operation(),
|
||||
Operation::Jump
|
||||
);
|
||||
|
||||
@ -316,7 +328,7 @@ impl Vm {
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} 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 is_positive = jump.b_as_boolean();
|
||||
let new_ip = if is_positive {
|
||||
@ -330,27 +342,37 @@ impl Vm {
|
||||
}
|
||||
Operation::Negate => {
|
||||
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 {
|
||||
self.get(instruction.b(), position)?
|
||||
self.get_register(instruction.b(), position)?
|
||||
};
|
||||
let negated = value
|
||||
.negate()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), negated, position)?;
|
||||
self.set_register(instruction.a(), negated, position)?;
|
||||
}
|
||||
Operation::Not => {
|
||||
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 {
|
||||
self.get(instruction.b(), position)?
|
||||
self.get_register(instruction.b(), position)?
|
||||
};
|
||||
let not = value
|
||||
.not()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.set(instruction.a(), not, position)?;
|
||||
self.set_register(instruction.a(), not, position)?;
|
||||
}
|
||||
Operation::Jump => {
|
||||
let jump_distance = instruction.b();
|
||||
@ -366,7 +388,7 @@ impl Vm {
|
||||
let to_register = instruction.a();
|
||||
let function_register = instruction.b();
|
||||
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 {
|
||||
function
|
||||
} else {
|
||||
@ -381,20 +403,20 @@ impl Vm {
|
||||
for argument_index in
|
||||
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(),
|
||||
Err(VmError::EmptyRegister { .. }) => continue,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
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()?;
|
||||
|
||||
if let Some(value) = return_value {
|
||||
self.set(to_register, value, position)?;
|
||||
self.set_register(to_register, value, position)?;
|
||||
}
|
||||
}
|
||||
Operation::CallNative => {
|
||||
@ -404,7 +426,7 @@ impl Vm {
|
||||
if let Some(value) = return_value {
|
||||
let to_register = instruction.a();
|
||||
|
||||
self.set(to_register, value, position)?;
|
||||
self.set_register(to_register, value, position)?;
|
||||
}
|
||||
}
|
||||
Operation::Return => {
|
||||
@ -415,7 +437,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
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));
|
||||
} else {
|
||||
@ -428,7 +450,12 @@ impl Vm {
|
||||
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();
|
||||
self.last_assigned_register = Some(to_register);
|
||||
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 register = self
|
||||
.stack
|
||||
@ -573,21 +600,19 @@ impl Vm {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => Ok(value),
|
||||
Register::Pointer(register_index) => {
|
||||
let value = self.get(*register_index, position)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
Register::Constant(constant_index) => {
|
||||
let value = self.chunk.get_constant(*constant_index, position)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
Register::Pointer(register_index) => self.get_register(*register_index, position),
|
||||
Register::Constant(constant_index) => self
|
||||
.chunk
|
||||
.get_constant(*constant_index)
|
||||
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
|
||||
index: *constant_index as usize,
|
||||
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;
|
||||
|
||||
if index >= self.stack.len() {
|
||||
@ -599,7 +624,7 @@ impl Vm {
|
||||
match register {
|
||||
Register::Value(value) => Ok(value),
|
||||
Register::Pointer(register_index) => {
|
||||
let value = self.empty(register_index, position)?;
|
||||
let value = self.empty_register(register_index, position)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
@ -617,12 +642,62 @@ impl Vm {
|
||||
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;
|
||||
|
||||
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)]
|
||||
@ -636,7 +711,7 @@ enum Register {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum VmError {
|
||||
CannotMutateImmutableLocal {
|
||||
identifier: Identifier,
|
||||
identifier: String,
|
||||
position: Span,
|
||||
},
|
||||
EmptyRegister {
|
||||
@ -651,10 +726,22 @@ pub enum VmError {
|
||||
found: Value,
|
||||
position: Span,
|
||||
},
|
||||
ConstantIndexOutOfBounds {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
RegisterIndexOutOfBounds {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
InstructionIndexOutOfBounds {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
LocalIndexOutOfBounds {
|
||||
index: usize,
|
||||
position: Span,
|
||||
},
|
||||
InvalidInstruction {
|
||||
instruction: Instruction,
|
||||
position: Span,
|
||||
@ -666,25 +753,18 @@ pub enum VmError {
|
||||
position: Span,
|
||||
},
|
||||
UndefinedVariable {
|
||||
identifier: Identifier,
|
||||
identifier: String,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Wrappers for foreign errors
|
||||
NativeFunction(NativeFunctionError),
|
||||
Chunk(ChunkError),
|
||||
Value {
|
||||
error: ValueError,
|
||||
position: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ChunkError> for VmError {
|
||||
fn from(error: ChunkError) -> Self {
|
||||
Self::Chunk(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl AnnotatedError for VmError {
|
||||
fn title() -> &'static str {
|
||||
"Runtime Error"
|
||||
@ -693,15 +773,17 @@ impl AnnotatedError for VmError {
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
|
||||
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||
Self::EmptyRegister { .. } => "Empty register",
|
||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||
Self::ExpectedFunction { .. } => "Expected function",
|
||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||
Self::StackOverflow { .. } => "Stack overflow",
|
||||
Self::StackUnderflow { .. } => "Stack underflow",
|
||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||
Self::Chunk(error) => error.description(),
|
||||
Self::NativeFunction(error) => error.description(),
|
||||
Self::Value { .. } => "Value error",
|
||||
}
|
||||
@ -709,15 +791,23 @@ impl AnnotatedError for VmError {
|
||||
|
||||
fn details(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::ConstantIndexOutOfBounds { index, .. } => {
|
||||
Some(format!("Constant C{index} does not exist"))
|
||||
}
|
||||
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
||||
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
||||
Self::RegisterIndexOutOfBounds { index, .. } => {
|
||||
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, .. } => {
|
||||
Some(format!("{identifier} is not in scope"))
|
||||
}
|
||||
Self::Chunk(error) => error.details(),
|
||||
Self::NativeFunction(error) => error.details(),
|
||||
Self::Value { error, .. } => Some(error.to_string()),
|
||||
_ => None,
|
||||
@ -727,15 +817,17 @@ impl AnnotatedError for VmError {
|
||||
fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::CannotMutateImmutableLocal { position, .. } => *position,
|
||||
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||
Self::EmptyRegister { position, .. } => *position,
|
||||
Self::ExpectedBoolean { position, .. } => *position,
|
||||
Self::ExpectedFunction { position, .. } => *position,
|
||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
||||
Self::InvalidInstruction { position, .. } => *position,
|
||||
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||
Self::StackUnderflow { position } => *position,
|
||||
Self::StackOverflow { position } => *position,
|
||||
Self::UndefinedVariable { position, .. } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::NativeFunction(error) => error.position(),
|
||||
Self::Value { position, .. } => *position,
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ fn equality_assignment_long() {
|
||||
(Instruction::r#return(true), Span(44, 44)),
|
||||
],
|
||||
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)),
|
||||
],
|
||||
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(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))
|
||||
],
|
||||
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))
|
||||
],
|
||||
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))
|
||||
],
|
||||
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))
|
||||
],
|
||||
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)),
|
||||
],
|
||||
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)),
|
||||
],
|
||||
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![
|
||||
Local::new(Identifier::new("a"), None, false, 0, 0),
|
||||
Local::new(Identifier::new("b"), None, false, 0, 1),
|
||||
Local::new(0, None, false, 0, 0),
|
||||
Local::new(0, None, false, 0, 1),
|
||||
]
|
||||
))
|
||||
);
|
||||
@ -563,7 +563,7 @@ fn r#while() {
|
||||
(Instruction::r#return(true), Span(42, 42)),
|
||||
],
|
||||
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![
|
||||
Local::new(Identifier::new("a"), 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, 0),
|
||||
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||
]
|
||||
),
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(vec![
|
||||
(Identifier::new("a"), Type::Integer),
|
||||
(Identifier::new("b"), Type::Integer)
|
||||
]),
|
||||
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||
return_type: Some(Box::new(Type::Integer)),
|
||||
}
|
||||
)))
|
||||
@ -53,27 +50,21 @@ fn function_declaration() {
|
||||
],
|
||||
vec![],
|
||||
vec![
|
||||
Local::new(Identifier::new("a"), 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, 0),
|
||||
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||
]
|
||||
),
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(vec![
|
||||
(Identifier::new("a"), Type::Integer),
|
||||
(Identifier::new("b"), Type::Integer)
|
||||
]),
|
||||
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||
return_type: Some(Box::new(Type::Integer)),
|
||||
},
|
||||
)],
|
||||
vec![Local::new(
|
||||
Identifier::new("add"),
|
||||
0,
|
||||
Some(Type::Function(FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(vec![
|
||||
(Identifier::new("a"), Type::Integer),
|
||||
(Identifier::new("b"), Type::Integer)
|
||||
]),
|
||||
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||
return_type: Some(Box::new(Type::Integer)),
|
||||
})),
|
||||
false,
|
||||
@ -111,16 +102,13 @@ fn function_call() {
|
||||
],
|
||||
vec![],
|
||||
vec![
|
||||
Local::new(Identifier::new("a"), 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, 0),
|
||||
Local::new(0, Some(Type::Integer), false, 0, 1)
|
||||
]
|
||||
),
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: Some(vec![
|
||||
(Identifier::new("a"), Type::Integer),
|
||||
(Identifier::new("b"), Type::Integer)
|
||||
]),
|
||||
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
|
||||
return_type: Some(Box::new(Type::Integer)),
|
||||
}
|
||||
),
|
||||
|
@ -50,11 +50,11 @@ fn block_scope() {
|
||||
Value::integer(2),
|
||||
],
|
||||
vec![
|
||||
Local::new(Identifier::new("a"), None, false, 0, 0),
|
||||
Local::new(Identifier::new("b"), None, false, 1, 1),
|
||||
Local::new(Identifier::new("c"), None, false, 2, 2),
|
||||
Local::new(Identifier::new("d"), None, false, 1, 3),
|
||||
Local::new(Identifier::new("e"), None, false, 0, 4),
|
||||
Local::new(0, None, false, 0, 0),
|
||||
Local::new(0, None, false, 1, 1),
|
||||
Local::new(0, None, false, 2, 2),
|
||||
Local::new(0, None, false, 1, 3),
|
||||
Local::new(0, None, false, 0, 4),
|
||||
]
|
||||
)),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user