Compare commits
No commits in common. "dev" and "main" have entirely different histories.
@ -4,56 +4,58 @@
|
|||||||
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
||||||
//! belong to a named function.
|
//! belong to a named function.
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{value::ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
|
use crate::{Disassembler, Instruction, Operation, Span, Type, Value};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// In-memory representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
name: Option<String>, // TODO: Use a bool indicating whether the chunk is named. If it is, get
|
name: Option<String>,
|
||||||
r#type: FunctionType, // the name from C0.
|
pub is_poisoned: bool,
|
||||||
|
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
|
|
||||||
|
current_scope: Scope,
|
||||||
|
block_index: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(name: Option<String>) -> Self {
|
pub fn new(name: Option<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
is_poisoned: false,
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
constants: Vec::new(),
|
constants: Vec::new(),
|
||||||
locals: Vec::new(),
|
locals: Vec::new(),
|
||||||
r#type: FunctionType {
|
current_scope: Scope::default(),
|
||||||
type_parameters: None,
|
block_index: 0,
|
||||||
value_parameters: None,
|
|
||||||
return_type: Box::new(Type::None),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_data(
|
pub fn with_data(
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
is_poisoned: false,
|
||||||
instructions,
|
instructions,
|
||||||
constants,
|
constants,
|
||||||
locals,
|
locals,
|
||||||
r#type: FunctionType {
|
current_scope: Scope::default(),
|
||||||
type_parameters: None,
|
block_index: 0,
|
||||||
value_parameters: None,
|
|
||||||
return_type: Box::new(Type::None),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,10 +63,6 @@ impl Chunk {
|
|||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#type(&self) -> &FunctionType {
|
|
||||||
&self.r#type
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_name(&mut self, name: String) {
|
pub fn set_name(&mut self, name: String) {
|
||||||
self.name = Some(name);
|
self.name = Some(name);
|
||||||
}
|
}
|
||||||
@ -77,36 +75,16 @@ impl Chunk {
|
|||||||
self.instructions.is_empty()
|
self.instructions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants(&self) -> &Vec<ConcreteValue> {
|
pub fn constants(&self) -> &Vec<Value> {
|
||||||
&self.constants
|
&self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants_mut(&mut self) -> &mut Vec<ConcreteValue> {
|
pub fn constants_mut(&mut self) -> &mut Vec<Value> {
|
||||||
&mut self.constants
|
&mut self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: u8) -> Result<&ConcreteValue, ChunkError> {
|
pub fn take_constants(self) -> Vec<Value> {
|
||||||
self.constants
|
self.constants
|
||||||
.get(index as usize)
|
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
|
||||||
index: index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 {
|
|
||||||
if let Some(index) = self
|
|
||||||
.constants
|
|
||||||
.iter()
|
|
||||||
.position(|constant| *constant == value)
|
|
||||||
{
|
|
||||||
index as u8
|
|
||||||
} else {
|
|
||||||
let index = self.constants.len() as u8;
|
|
||||||
|
|
||||||
self.constants.push(value);
|
|
||||||
|
|
||||||
index
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
|
pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
|
||||||
@ -147,6 +125,32 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_scope(&self) -> Scope {
|
||||||
|
self.current_scope
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> {
|
||||||
|
self.constants
|
||||||
|
.get(index as usize)
|
||||||
|
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
||||||
|
index: index as usize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
|
||||||
|
if let Some(index) = self
|
||||||
|
.constants
|
||||||
|
.iter()
|
||||||
|
.position(|constant| constant == &value)
|
||||||
|
{
|
||||||
|
return index as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.constants.push(value);
|
||||||
|
|
||||||
|
(self.constants.len() - 1) as u8
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_identifier(&self, local_index: u8) -> Option<String> {
|
pub fn get_identifier(&self, local_index: u8) -> Option<String> {
|
||||||
self.locals.get(local_index as usize).and_then(|local| {
|
self.locals.get(local_index as usize).and_then(|local| {
|
||||||
self.constants
|
self.constants
|
||||||
@ -155,22 +159,112 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant_type(&self, constant_index: u8) -> Result<Type, ChunkError> {
|
pub fn begin_scope(&mut self) {
|
||||||
|
self.block_index += 1;
|
||||||
|
self.current_scope.block_index = self.block_index;
|
||||||
|
self.current_scope.depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_scope(&mut self) {
|
||||||
|
self.current_scope.depth -= 1;
|
||||||
|
|
||||||
|
if self.current_scope.depth == 0 {
|
||||||
|
self.current_scope.block_index = 0;
|
||||||
|
} else {
|
||||||
|
self.current_scope.block_index -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_not_poisoned(&self) -> Result<(), ChunkError> {
|
||||||
|
if self.is_poisoned {
|
||||||
|
Err(ChunkError::PoisonedChunk)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constant_type(&self, constant_index: u8) -> Option<Type> {
|
||||||
self.constants
|
self.constants
|
||||||
.get(constant_index as usize)
|
.get(constant_index as usize)
|
||||||
.map(|value| value.r#type())
|
.map(|value| value.r#type())
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
}
|
||||||
index: constant_index as usize,
|
|
||||||
|
pub fn get_local_type(&self, local_index: u8) -> Option<Type> {
|
||||||
|
self.locals.get(local_index as usize)?.r#type.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_register_type(&self, register_index: u8) -> Option<Type> {
|
||||||
|
let local_type_option = self
|
||||||
|
.locals
|
||||||
|
.iter()
|
||||||
|
.find(|local| local.register_index == register_index)
|
||||||
|
.map(|local| local.r#type.clone());
|
||||||
|
|
||||||
|
if let Some(local_type) = local_type_option {
|
||||||
|
return local_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.instructions
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(index, (instruction, _))| {
|
||||||
|
if let Operation::LoadList = instruction.operation() {
|
||||||
|
if instruction.a() == register_index {
|
||||||
|
let mut length = (instruction.c() - instruction.b() + 1) as usize;
|
||||||
|
let mut item_type = Type::Any;
|
||||||
|
let distance_to_end = self.len() - index;
|
||||||
|
|
||||||
|
for (instruction, _) in self
|
||||||
|
.instructions()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.skip(distance_to_end)
|
||||||
|
.take(length)
|
||||||
|
{
|
||||||
|
if let Operation::Close = instruction.operation() {
|
||||||
|
length -= (instruction.c() - instruction.b()) as usize;
|
||||||
|
} else if let Type::Any = item_type {
|
||||||
|
item_type = instruction.yielded_type(self).unwrap_or(Type::Any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(Type::List {
|
||||||
|
item_type: Box::new(item_type),
|
||||||
|
length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.yields_value() && instruction.a() == register_index {
|
||||||
|
instruction.yielded_type(self)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_type(&self, local_index: u8) -> Result<&Type, ChunkError> {
|
pub fn return_type(&self) -> Option<Type> {
|
||||||
self.locals
|
let returns_value = self
|
||||||
.get(local_index as usize)
|
.instructions()
|
||||||
.map(|local| &local.r#type)
|
.last()
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
.map(|(instruction, _)| {
|
||||||
index: local_index as usize,
|
debug_assert!(matches!(instruction.operation(), Operation::Return));
|
||||||
|
|
||||||
|
instruction.b_as_boolean()
|
||||||
})
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if returns_value {
|
||||||
|
self.instructions.iter().rev().find_map(|(instruction, _)| {
|
||||||
|
if instruction.yields_value() {
|
||||||
|
instruction.yielded_type(self)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassembler(&self) -> Disassembler {
|
pub fn disassembler(&self) -> Disassembler {
|
||||||
@ -215,34 +309,79 @@ pub struct Local {
|
|||||||
pub identifier_index: u8,
|
pub identifier_index: u8,
|
||||||
|
|
||||||
/// The expected type of the local's value.
|
/// The expected type of the local's value.
|
||||||
pub r#type: Type,
|
pub r#type: Option<Type>,
|
||||||
|
|
||||||
/// Whether the local is mutable.
|
/// Whether the local is mutable.
|
||||||
pub is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
|
|
||||||
/// Scope where the variable was declared.
|
/// Scope where the variable was declared.
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
|
|
||||||
|
/// Expected location of a local's value.
|
||||||
|
pub register_index: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
/// Creates a new Local instance.
|
/// Creates a new Local instance.
|
||||||
pub fn new(identifier_index: u8, r#type: Type, mutable: bool, scope: Scope) -> Self {
|
pub fn new(
|
||||||
|
identifier_index: u8,
|
||||||
|
r#type: Option<Type>,
|
||||||
|
mutable: bool,
|
||||||
|
scope: Scope,
|
||||||
|
register_index: u8,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier_index,
|
identifier_index,
|
||||||
r#type,
|
r#type,
|
||||||
is_mutable: mutable,
|
is_mutable: mutable,
|
||||||
scope,
|
scope,
|
||||||
|
register_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Variable locality, as defined by its depth and block index.
|
||||||
|
///
|
||||||
|
/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate
|
||||||
|
/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the
|
||||||
|
/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0
|
||||||
|
/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are
|
||||||
|
/// incremented and decremented.
|
||||||
|
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub struct Scope {
|
||||||
|
/// Level of block nesting.
|
||||||
|
pub depth: u8,
|
||||||
|
/// Index of the block in the chunk.
|
||||||
|
pub block_index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn new(depth: u8, block_index: u8) -> Self {
|
||||||
|
Self { depth, block_index }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, other: &Self) -> bool {
|
||||||
|
match self.depth.cmp(&other.depth) {
|
||||||
|
Ordering::Less => false,
|
||||||
|
Ordering::Greater => self.block_index >= other.block_index,
|
||||||
|
Ordering::Equal => self.block_index == other.block_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Scope {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.depth, self.block_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors that can occur when using a [`Chunk`].
|
/// Errors that can occur when using a [`Chunk`].
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum ChunkError {
|
pub enum ChunkError {
|
||||||
ConstantIndexOutOfBounds { index: usize },
|
ConstantIndexOutOfBounds { index: usize },
|
||||||
FunctionIndexOutOfBounds { index: usize },
|
|
||||||
InstructionIndexOutOfBounds { index: usize },
|
InstructionIndexOutOfBounds { index: usize },
|
||||||
LocalIndexOutOfBounds { index: usize },
|
LocalIndexOutOfBounds { index: usize },
|
||||||
|
PoisonedChunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ChunkError {
|
impl Display for ChunkError {
|
||||||
@ -251,15 +390,13 @@ impl Display for ChunkError {
|
|||||||
ChunkError::ConstantIndexOutOfBounds { index } => {
|
ChunkError::ConstantIndexOutOfBounds { index } => {
|
||||||
write!(f, "Constant index {} out of bounds", index)
|
write!(f, "Constant index {} out of bounds", index)
|
||||||
}
|
}
|
||||||
ChunkError::FunctionIndexOutOfBounds { index } => {
|
|
||||||
write!(f, "Function index {} out of bounds", index)
|
|
||||||
}
|
|
||||||
ChunkError::InstructionIndexOutOfBounds { index } => {
|
ChunkError::InstructionIndexOutOfBounds { index } => {
|
||||||
write!(f, "Instruction index {} out of bounds", index)
|
write!(f, "Instruction index {} out of bounds", index)
|
||||||
}
|
}
|
||||||
ChunkError::LocalIndexOutOfBounds { index } => {
|
ChunkError::LocalIndexOutOfBounds { index } => {
|
||||||
write!(f, "Local index {} out of bounds", index)
|
write!(f, "Local index {} out of bounds", index)
|
||||||
}
|
}
|
||||||
|
ChunkError::PoisonedChunk => write!(f, "Chunk is poisoned"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -45,13 +45,13 @@ use std::env::current_exe;
|
|||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
use crate::{value::ConcreteValue, Chunk, Local};
|
use crate::{Chunk, ConcreteValue, Local, Value};
|
||||||
|
|
||||||
const INSTRUCTION_HEADER: [&str; 4] = [
|
const INSTRUCTION_HEADER: [&str; 4] = [
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
" i BYTECODE OPERATION INFO POSITION ",
|
" i BYTECODE OPERATION INFO TYPE POSITION ",
|
||||||
"--- -------- ------------- ------------------------- ----------",
|
"--- -------- ------------- -------------------- ---------------- ----------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&str; 4] = [
|
const CONSTANT_HEADER: [&str; 4] = [
|
||||||
@ -64,8 +64,8 @@ const CONSTANT_HEADER: [&str; 4] = [
|
|||||||
const LOCAL_HEADER: [&str; 4] = [
|
const LOCAL_HEADER: [&str; 4] = [
|
||||||
"Locals",
|
"Locals",
|
||||||
"------",
|
"------",
|
||||||
" i IDENTIFIER TYPE MUTABLE SCOPE ",
|
" i IDENTIFIER TYPE MUTABLE SCOPE REGISTER",
|
||||||
"--- ---------- ---------------- ------- -------",
|
"--- ---------- ---------------- ------- ------- --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Builder that constructs a human-readable representation of a chunk.
|
/// Builder that constructs a human-readable representation of a chunk.
|
||||||
@ -222,16 +222,7 @@ impl<'a> Disassembler<'a> {
|
|||||||
.map(|identifier| identifier.to_string())
|
.map(|identifier| identifier.to_string())
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
current_exe()
|
current_exe()
|
||||||
.map(|path| {
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
let path_string = path.to_string_lossy();
|
|
||||||
let file_name = path_string
|
|
||||||
.split('/')
|
|
||||||
.last()
|
|
||||||
.map(|slice| slice.to_string())
|
|
||||||
.unwrap_or(path_string.to_string());
|
|
||||||
|
|
||||||
file_name
|
|
||||||
})
|
|
||||||
.unwrap_or("Chunk Disassembly".to_string())
|
.unwrap_or("Chunk Disassembly".to_string())
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -254,7 +245,10 @@ impl<'a> Disassembler<'a> {
|
|||||||
self.chunk.len(),
|
self.chunk.len(),
|
||||||
self.chunk.constants().len(),
|
self.chunk.constants().len(),
|
||||||
self.chunk.locals().len(),
|
self.chunk.locals().len(),
|
||||||
self.chunk.r#type().return_type
|
self.chunk
|
||||||
|
.return_type()
|
||||||
|
.map(|r#type| r#type.to_string())
|
||||||
|
.unwrap_or("none".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
self.push(&info_line, true, false, true, true);
|
self.push(&info_line, true, false, true, true);
|
||||||
@ -268,10 +262,23 @@ impl<'a> Disassembler<'a> {
|
|||||||
let bytecode = format!("{:02X}", u32::from(instruction));
|
let bytecode = format!("{:02X}", u32::from(instruction));
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info = instruction.disassembly_info(self.chunk);
|
let info = instruction.disassembly_info(self.chunk);
|
||||||
|
let type_display = instruction
|
||||||
|
.yielded_type(self.chunk)
|
||||||
|
.map(|r#type| {
|
||||||
|
let type_string = r#type.to_string();
|
||||||
|
|
||||||
|
if type_string.len() > 16 {
|
||||||
|
format!("{type_string:.13}...")
|
||||||
|
} else {
|
||||||
|
type_string
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(String::with_capacity(0));
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
|
|
||||||
let instruction_display =
|
let instruction_display = format!(
|
||||||
format!("{index:^3} {bytecode:>8} {operation:13} {info:<25} {position:10}");
|
"{index:^3} {bytecode:>8} {operation:13} {info:^20} {type_display:^16} {position:10}"
|
||||||
|
);
|
||||||
|
|
||||||
self.push_details(&instruction_display);
|
self.push_details(&instruction_display);
|
||||||
}
|
}
|
||||||
@ -288,6 +295,7 @@ impl<'a> Disassembler<'a> {
|
|||||||
identifier_index,
|
identifier_index,
|
||||||
r#type,
|
r#type,
|
||||||
scope,
|
scope,
|
||||||
|
register_index,
|
||||||
is_mutable: mutable,
|
is_mutable: mutable,
|
||||||
},
|
},
|
||||||
) in self.chunk.locals().iter().enumerate()
|
) in self.chunk.locals().iter().enumerate()
|
||||||
@ -298,9 +306,20 @@ impl<'a> Disassembler<'a> {
|
|||||||
.get(*identifier_index as usize)
|
.get(*identifier_index as usize)
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
let type_display = r#type.to_string();
|
let type_display = r#type
|
||||||
|
.as_ref()
|
||||||
|
.map(|r#type| {
|
||||||
|
let type_string = r#type.to_string();
|
||||||
|
|
||||||
|
if type_string.len() > 16 {
|
||||||
|
format!("{type_string:.13}...")
|
||||||
|
} else {
|
||||||
|
type_string
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or("unknown".to_string());
|
||||||
let local_display = format!(
|
let local_display = format!(
|
||||||
"{index:^3} {identifier_display:10} {type_display:16} {mutable:7} {scope:7}"
|
"{index:^3} {identifier_display:10} {type_display:16} {mutable:7} {scope:7} {register_index:8}"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.push_details(&local_display);
|
self.push_details(&local_display);
|
||||||
@ -313,19 +332,6 @@ impl<'a> Disassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants().iter().enumerate() {
|
for (index, value) in self.chunk.constants().iter().enumerate() {
|
||||||
if let ConcreteValue::Function(function) = value {
|
|
||||||
let function_disassembly = function
|
|
||||||
.chunk()
|
|
||||||
.disassembler()
|
|
||||||
.styled(self.styled)
|
|
||||||
.indent(self.indent + 1)
|
|
||||||
.disassemble();
|
|
||||||
|
|
||||||
self.output.push_str(&function_disassembly);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_display = {
|
let value_display = {
|
||||||
let value_string = value.to_string();
|
let value_string = value.to_string();
|
||||||
|
|
||||||
@ -338,6 +344,17 @@ impl<'a> Disassembler<'a> {
|
|||||||
let constant_display = format!("{index:^3} {value_display:^15}");
|
let constant_display = format!("{index:^3} {value_display:^15}");
|
||||||
|
|
||||||
self.push_details(&constant_display);
|
self.push_details(&constant_display);
|
||||||
|
|
||||||
|
if let Value::Concrete(ConcreteValue::Function(function)) = value {
|
||||||
|
let function_disassembly = function
|
||||||
|
.chunk()
|
||||||
|
.disassembler()
|
||||||
|
.styled(self.styled)
|
||||||
|
.indent(self.indent + 1)
|
||||||
|
.disassemble();
|
||||||
|
|
||||||
|
self.output.push_str(&function_disassembly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push_border(&bottom_border);
|
self.push_border(&bottom_border);
|
||||||
|
@ -101,13 +101,13 @@ impl<'src> Formatter<'src> {
|
|||||||
String(string) => {
|
String(string) => {
|
||||||
self.push_colored(string.magenta());
|
self.push_colored(string.magenta());
|
||||||
}
|
}
|
||||||
LeftBrace => {
|
LeftCurlyBrace => {
|
||||||
self.next_line.push_str(self.current_token.as_str());
|
self.next_line.push_str(self.current_token.as_str());
|
||||||
self.commit_line(LineKind::OpenBlock);
|
self.commit_line(LineKind::OpenBlock);
|
||||||
|
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
}
|
}
|
||||||
RightBrace => {
|
RightCurlyBrace => {
|
||||||
self.commit_line(LineKind::CloseBlock);
|
self.commit_line(LineKind::CloseBlock);
|
||||||
self.next_line.push_str(self.current_token.as_str());
|
self.next_line.push_str(self.current_token.as_str());
|
||||||
|
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{Chunk, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Function {
|
|
||||||
chunk: Chunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
|
||||||
Self { chunk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk(&self) -> &Chunk {
|
|
||||||
&self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
Type::Function(self.chunk.r#type().clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_borrowed(&self) -> FunctionBorrowed {
|
|
||||||
FunctionBorrowed::new(&self.chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Function {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.chunk.r#type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct FunctionBorrowed<'a> {
|
|
||||||
chunk: &'a Chunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FunctionBorrowed<'a> {
|
|
||||||
pub fn new(chunk: &'a Chunk) -> Self {
|
|
||||||
Self { chunk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk(&self) -> &Chunk {
|
|
||||||
self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
Type::Function(self.chunk.r#type().clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_owned(&self) -> Function {
|
|
||||||
Function::new(self.chunk.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Display for FunctionBorrowed<'a> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.chunk.r#type())
|
|
||||||
}
|
|
||||||
}
|
|
@ -362,18 +362,6 @@ impl Instruction {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn returns_or_panics(&self) -> bool {
|
|
||||||
match self.operation() {
|
|
||||||
Operation::Return => true,
|
|
||||||
Operation::CallNative => {
|
|
||||||
let native_function = NativeFunction::from(self.b());
|
|
||||||
|
|
||||||
matches!(native_function, NativeFunction::Panic)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn yields_value(&self) -> bool {
|
pub fn yields_value(&self) -> bool {
|
||||||
match self.operation() {
|
match self.operation() {
|
||||||
Operation::Add
|
Operation::Add
|
||||||
@ -392,12 +380,43 @@ impl Instruction {
|
|||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let native_function = NativeFunction::from(self.b());
|
let native_function = NativeFunction::from(self.b());
|
||||||
|
|
||||||
*native_function.r#type().return_type != Type::None
|
native_function.r#type().return_type.is_some()
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn yielded_type(&self, chunk: &Chunk) -> Option<Type> {
|
||||||
|
use Operation::*;
|
||||||
|
|
||||||
|
match self.operation() {
|
||||||
|
Add | Divide | Modulo | Multiply | Subtract => {
|
||||||
|
if self.b_is_constant() {
|
||||||
|
chunk.get_constant_type(self.b())
|
||||||
|
} else {
|
||||||
|
chunk.get_register_type(self.b())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LoadBoolean | Not => Some(Type::Boolean),
|
||||||
|
Negate => {
|
||||||
|
if self.b_is_constant() {
|
||||||
|
chunk.get_constant_type(self.b())
|
||||||
|
} else {
|
||||||
|
chunk.get_register_type(self.b())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LoadConstant => chunk.get_constant_type(self.b()),
|
||||||
|
LoadList => chunk.get_register_type(self.a()),
|
||||||
|
GetLocal => chunk.get_local_type(self.b()),
|
||||||
|
CallNative => {
|
||||||
|
let native_function = NativeFunction::from(self.b());
|
||||||
|
|
||||||
|
native_function.r#type().return_type.map(|boxed| *boxed)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
||||||
let format_arguments = || {
|
let format_arguments = || {
|
||||||
let first_argument = if self.b_is_constant() {
|
let first_argument = if self.b_is_constant() {
|
||||||
@ -610,7 +629,7 @@ impl Instruction {
|
|||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
let native_function_name = native_function.as_str();
|
let native_function_name = native_function.as_str();
|
||||||
|
|
||||||
if *native_function.r#type().return_type != Type::None {
|
if native_function.r#type().return_type.is_some() {
|
||||||
output.push_str(&format!("R{} = {}(", to_register, native_function_name));
|
output.push_str(&format!("R{} = {}(", to_register, native_function_name));
|
||||||
} else {
|
} else {
|
||||||
output.push_str(&format!("{}(", native_function_name));
|
output.push_str(&format!("{}(", native_function_name));
|
||||||
@ -657,13 +676,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#move() {
|
fn r#move() {
|
||||||
let mut instruction = Instruction::r#move(4, 1);
|
let mut instruction = Instruction::r#move(0, 1);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Move);
|
assert_eq!(instruction.operation(), Operation::Move);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -690,13 +709,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_constant() {
|
fn load_constant() {
|
||||||
let mut instruction = Instruction::load_constant(4, 1, true);
|
let mut instruction = Instruction::load_constant(0, 1, true);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -705,10 +724,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_list() {
|
fn load_list() {
|
||||||
let instruction = Instruction::load_list(4, 1);
|
let instruction = Instruction::load_list(0, 1);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::LoadList);
|
assert_eq!(instruction.operation(), Operation::LoadList);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,12 +741,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn declare_local() {
|
fn declare_local() {
|
||||||
let mut instruction = Instruction::define_local(4, 1, true);
|
let mut instruction = Instruction::define_local(0, 1, true);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), true as u8);
|
assert_eq!(instruction.c(), true as u8);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -735,26 +754,26 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let mut instruction = Instruction::add(1, 1, 4);
|
let mut instruction = Instruction::add(1, 1, 0);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Add);
|
assert_eq!(instruction.operation(), Operation::Add);
|
||||||
assert_eq!(instruction.a(), 1);
|
assert_eq!(instruction.a(), 1);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), 4);
|
assert_eq!(instruction.c(), 0);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subtract() {
|
fn subtract() {
|
||||||
let mut instruction = Instruction::subtract(4, 1, 2);
|
let mut instruction = Instruction::subtract(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Subtract);
|
assert_eq!(instruction.operation(), Operation::Subtract);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -763,13 +782,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiply() {
|
fn multiply() {
|
||||||
let mut instruction = Instruction::multiply(4, 1, 2);
|
let mut instruction = Instruction::multiply(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Multiply);
|
assert_eq!(instruction.operation(), Operation::Multiply);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -778,13 +797,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let mut instruction = Instruction::divide(4, 1, 2);
|
let mut instruction = Instruction::divide(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Divide);
|
assert_eq!(instruction.operation(), Operation::Divide);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -827,13 +846,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate() {
|
fn negate() {
|
||||||
let mut instruction = Instruction::negate(4, 1);
|
let mut instruction = Instruction::negate(0, 1);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Negate);
|
assert_eq!(instruction.operation(), Operation::Negate);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
@ -841,13 +860,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not() {
|
fn not() {
|
||||||
let mut instruction = Instruction::not(4, 1);
|
let mut instruction = Instruction::not(0, 1);
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_c_is_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Not);
|
assert_eq!(instruction.operation(), Operation::Not);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
|
@ -41,10 +41,11 @@ pub fn lex<'tokens, 'src: 'tokens>(
|
|||||||
error: CompileError::Lex(error),
|
error: CompileError::Lex(error),
|
||||||
source,
|
source,
|
||||||
})?;
|
})?;
|
||||||
|
let is_eof = matches!(token, Token::Eof);
|
||||||
|
|
||||||
tokens.push((token, span));
|
tokens.push((token, span));
|
||||||
|
|
||||||
if lexer.is_eof() {
|
if is_eof {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +60,6 @@ pub fn lex<'tokens, 'src: 'tokens>(
|
|||||||
pub struct Lexer<'src> {
|
pub struct Lexer<'src> {
|
||||||
source: &'src str,
|
source: &'src str,
|
||||||
position: usize,
|
position: usize,
|
||||||
is_eof: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Lexer<'src> {
|
impl<'src> Lexer<'src> {
|
||||||
@ -68,7 +68,6 @@ impl<'src> Lexer<'src> {
|
|||||||
Lexer {
|
Lexer {
|
||||||
source,
|
source,
|
||||||
position: 0,
|
position: 0,
|
||||||
is_eof: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
self.is_eof
|
self.position >= self.source.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_to(&mut self, position: usize) {
|
pub fn skip_to(&mut self, position: usize) {
|
||||||
@ -88,19 +87,272 @@ impl<'src> Lexer<'src> {
|
|||||||
pub fn next_token(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
pub fn next_token(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
let (token, span) = if let Some(character) = self.peek_char() {
|
let (token, span) = if let Some(c) = self.peek_char() {
|
||||||
let lexer = LexRule::from(&character).lexer;
|
match c {
|
||||||
|
'0'..='9' => self.lex_numeric()?,
|
||||||
|
'-' => {
|
||||||
|
let second_char = self.peek_second_char();
|
||||||
|
|
||||||
lexer(self)?
|
if let Some('=') = second_char {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::MinusEqual, Span(self.position - 2, self.position))
|
||||||
|
} else if let Some('>') = second_char {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::ArrowThin, Span(self.position - 2, self.position))
|
||||||
|
} else if let Some('0'..='9') = second_char {
|
||||||
|
self.lex_numeric()?
|
||||||
|
} else if "-Infinity" == self.peek_chars(9) {
|
||||||
|
self.position += 9;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::Float("-Infinity"),
|
||||||
|
Span(self.position - 9, self.position),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Minus, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'a'..='z' | 'A'..='Z' => self.lex_alphanumeric()?,
|
||||||
|
'"' => self.lex_string()?,
|
||||||
|
'\'' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
if let Some(c) = self.peek_char() {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
let peek = self.peek_char();
|
||||||
|
|
||||||
|
if let Some('\'') = peek {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Character(c), Span(self.position - 3, self.position))
|
||||||
|
} else {
|
||||||
|
return Err(LexError::ExpectedCharacter {
|
||||||
|
expected: '\'',
|
||||||
|
actual: peek.unwrap_or('\0'),
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(LexError::UnexpectedEndOfFile {
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'+' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::PlusEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Plus, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'*' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::StarEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Star, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::LeftParenthesis,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::RightParenthesis,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'=' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::DoubleEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Equal, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'[' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::LeftSquareBrace,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
']' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::RightSquareBrace,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
',' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Comma, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'.' => {
|
||||||
|
if let Some('.') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::DoubleDot, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Dot, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::GreaterEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Greater, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'<' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::LessEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Less, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'{' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::LeftCurlyBrace,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'}' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::RightCurlyBrace,
|
||||||
|
Span(self.position - 1, self.position),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'/' => {
|
||||||
|
if let Some('=') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::SlashEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Slash, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'%' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Percent, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'&' => {
|
||||||
|
if let Some('&') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(
|
||||||
|
Token::DoubleAmpersand,
|
||||||
|
Span(self.position - 2, self.position),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
actual: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
';' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Semicolon, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
if let Some('|') = self.peek_second_char() {
|
||||||
|
self.position += 2;
|
||||||
|
|
||||||
|
(Token::DoublePipe, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
actual: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'!' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
if let Some('=') = self.peek_char() {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::BangEqual, Span(self.position - 2, self.position))
|
||||||
|
} else {
|
||||||
|
(Token::Bang, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
':' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Colon, Span(self.position - 1, self.position))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(LexError::UnexpectedCharacter {
|
||||||
|
actual: c,
|
||||||
|
position: self.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.is_eof = true;
|
|
||||||
|
|
||||||
(Token::Eof, Span(self.position, self.position))
|
(Token::Eof, Span(self.position, self.position))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((token, span))
|
Ok((token, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Peek at the next token without consuming the source.
|
||||||
|
pub fn peek_token(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
||||||
|
let token = self.next_token()?;
|
||||||
|
|
||||||
|
self.position -= token.0.len();
|
||||||
|
|
||||||
|
Ok(token)
|
||||||
|
}
|
||||||
|
|
||||||
/// Progress to the next character.
|
/// Progress to the next character.
|
||||||
fn next_char(&mut self) -> Option<char> {
|
fn next_char(&mut self) -> Option<char> {
|
||||||
if let Some(c) = self.source[self.position..].chars().next() {
|
if let Some(c) = self.source[self.position..].chars().next() {
|
||||||
@ -255,7 +507,7 @@ impl<'src> Lexer<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lex an identifier token.
|
/// Lex an identifier token.
|
||||||
fn lex_keyword_or_identifier(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
fn lex_alphanumeric(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
||||||
let start_pos = self.position;
|
let start_pos = self.position;
|
||||||
|
|
||||||
while let Some(c) = self.peek_char() {
|
while let Some(c) = self.peek_char() {
|
||||||
@ -312,410 +564,6 @@ impl<'src> Lexer<'src> {
|
|||||||
|
|
||||||
Ok((Token::String(text), Span(start_pos, self.position)))
|
Ok((Token::String(text), Span(start_pos, self.position)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lex_char(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_position = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
let char = self.source[self.position..].chars().next().unwrap();
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if self.peek_char() == Some('\'') {
|
|
||||||
self.next_char();
|
|
||||||
} else {
|
|
||||||
return Err(LexError::ExpectedCharacter {
|
|
||||||
expected: '\'',
|
|
||||||
actual: self.peek_char().unwrap_or('\0'),
|
|
||||||
position: self.position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let end_position = self.position;
|
|
||||||
|
|
||||||
Ok((Token::Character(char), Span(start_position, end_position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_plus(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::PlusEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Plus, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_minus(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_position = self.position;
|
|
||||||
|
|
||||||
if self
|
|
||||||
.peek_second_char()
|
|
||||||
.is_some_and(|char| char.is_ascii_digit())
|
|
||||||
{
|
|
||||||
return self.lex_numeric();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
return Ok((Token::MinusEqual, Span(start_position, self.position)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some('>') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
return Ok((Token::ArrowThin, Span(start_position, self.position)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.peek_chars(8) == "Infinity" {
|
|
||||||
self.position += 8;
|
|
||||||
|
|
||||||
return Ok((
|
|
||||||
Token::Float("-Infinity"),
|
|
||||||
Span(start_position, self.position),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((Token::Minus, Span(start_position, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_star(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::StarEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Star, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_slash(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::SlashEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Slash, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_percent(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::PercentEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Percent, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_unexpected(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
Err(LexError::UnexpectedCharacter {
|
|
||||||
actual: self.peek_char().unwrap_or('\0'),
|
|
||||||
position: self.position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_exclamation_mark(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::BangEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Bang, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_equal(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::DoubleEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Equal, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_less_than(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::LessEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Less, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_greater_than(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('=') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::GreaterEqual, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Greater, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_ampersand(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
let peek_char = self.peek_char();
|
|
||||||
|
|
||||||
if let Some('&') = peek_char {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::DoubleAmpersand, Span(start_pos, self.position)))
|
|
||||||
} else if peek_char.is_none() {
|
|
||||||
Err(LexError::UnexpectedEndOfFile {
|
|
||||||
position: self.position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(LexError::ExpectedCharacter {
|
|
||||||
expected: '&',
|
|
||||||
actual: self.peek_char().unwrap(),
|
|
||||||
position: self.position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_pipe(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
let peek_char = self.peek_char();
|
|
||||||
|
|
||||||
if let Some('|') = peek_char {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::DoublePipe, Span(start_pos, self.position)))
|
|
||||||
} else if peek_char.is_none() {
|
|
||||||
Err(LexError::UnexpectedEndOfFile {
|
|
||||||
position: self.position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(LexError::ExpectedCharacter {
|
|
||||||
expected: '&',
|
|
||||||
actual: self.peek_char().unwrap(),
|
|
||||||
position: self.position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_left_parenthesis(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::LeftParenthesis, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_right_parenthesis(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::RightParenthesis, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_left_bracket(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::LeftBracket, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_right_bracket(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::RightBracket, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_left_brace(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::LeftBrace, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_right_brace(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::RightBrace, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_semicolon(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::Semicolon, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_colon(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::Colon, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_comma(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::Comma, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lex_dot(&mut self) -> Result<(Token<'src>, Span), LexError> {
|
|
||||||
let start_pos = self.position;
|
|
||||||
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
if let Some('.') = self.peek_char() {
|
|
||||||
self.next_char();
|
|
||||||
|
|
||||||
Ok((Token::DoubleDot, Span(start_pos, self.position)))
|
|
||||||
} else {
|
|
||||||
Ok((Token::Dot, Span(start_pos, self.position)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type LexerFn<'src> = fn(&mut Lexer<'src>) -> Result<(Token<'src>, Span), LexError>;
|
|
||||||
|
|
||||||
pub struct LexRule<'src> {
|
|
||||||
lexer: LexerFn<'src>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> From<&char> for LexRule<'src> {
|
|
||||||
fn from(char: &char) -> Self {
|
|
||||||
match char {
|
|
||||||
'0'..='9' => LexRule {
|
|
||||||
lexer: Lexer::lex_numeric,
|
|
||||||
},
|
|
||||||
char if char.is_alphabetic() => LexRule {
|
|
||||||
lexer: Lexer::lex_keyword_or_identifier,
|
|
||||||
},
|
|
||||||
'"' => LexRule {
|
|
||||||
lexer: Lexer::lex_string,
|
|
||||||
},
|
|
||||||
'\'' => LexRule {
|
|
||||||
lexer: Lexer::lex_char,
|
|
||||||
},
|
|
||||||
'+' => LexRule {
|
|
||||||
lexer: Lexer::lex_plus,
|
|
||||||
},
|
|
||||||
'-' => LexRule {
|
|
||||||
lexer: Lexer::lex_minus,
|
|
||||||
},
|
|
||||||
'*' => LexRule {
|
|
||||||
lexer: Lexer::lex_star,
|
|
||||||
},
|
|
||||||
'/' => LexRule {
|
|
||||||
lexer: Lexer::lex_slash,
|
|
||||||
},
|
|
||||||
'%' => LexRule {
|
|
||||||
lexer: Lexer::lex_percent,
|
|
||||||
},
|
|
||||||
'!' => LexRule {
|
|
||||||
lexer: Lexer::lex_exclamation_mark,
|
|
||||||
},
|
|
||||||
'=' => LexRule {
|
|
||||||
lexer: Lexer::lex_equal,
|
|
||||||
},
|
|
||||||
'<' => LexRule {
|
|
||||||
lexer: Lexer::lex_less_than,
|
|
||||||
},
|
|
||||||
'>' => LexRule {
|
|
||||||
lexer: Lexer::lex_greater_than,
|
|
||||||
},
|
|
||||||
'&' => LexRule {
|
|
||||||
lexer: Lexer::lex_ampersand,
|
|
||||||
},
|
|
||||||
'|' => LexRule {
|
|
||||||
lexer: Lexer::lex_pipe,
|
|
||||||
},
|
|
||||||
'(' => LexRule {
|
|
||||||
lexer: Lexer::lex_left_parenthesis,
|
|
||||||
},
|
|
||||||
')' => LexRule {
|
|
||||||
lexer: Lexer::lex_right_parenthesis,
|
|
||||||
},
|
|
||||||
'[' => LexRule {
|
|
||||||
lexer: Lexer::lex_left_bracket,
|
|
||||||
},
|
|
||||||
']' => LexRule {
|
|
||||||
lexer: Lexer::lex_right_bracket,
|
|
||||||
},
|
|
||||||
'{' => LexRule {
|
|
||||||
lexer: Lexer::lex_left_brace,
|
|
||||||
},
|
|
||||||
'}' => LexRule {
|
|
||||||
lexer: Lexer::lex_right_brace,
|
|
||||||
},
|
|
||||||
';' => LexRule {
|
|
||||||
lexer: Lexer::lex_semicolon,
|
|
||||||
},
|
|
||||||
':' => LexRule {
|
|
||||||
lexer: Lexer::lex_colon,
|
|
||||||
},
|
|
||||||
',' => LexRule {
|
|
||||||
lexer: Lexer::lex_comma,
|
|
||||||
},
|
|
||||||
'.' => LexRule {
|
|
||||||
lexer: Lexer::lex_dot,
|
|
||||||
},
|
|
||||||
_ => LexRule {
|
|
||||||
lexer: Lexer::lex_unexpected,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@ -845,7 +693,7 @@ mod tests {
|
|||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::Map, Span(0, 3)),
|
(Token::Map, Span(0, 3)),
|
||||||
(Token::LeftBrace, Span(4, 5)),
|
(Token::LeftCurlyBrace, Span(4, 5)),
|
||||||
(Token::Identifier("x"), Span(6, 7)),
|
(Token::Identifier("x"), Span(6, 7)),
|
||||||
(Token::Equal, Span(8, 9)),
|
(Token::Equal, Span(8, 9)),
|
||||||
(Token::String("1"), Span(10, 13)),
|
(Token::String("1"), Span(10, 13)),
|
||||||
@ -857,7 +705,7 @@ mod tests {
|
|||||||
(Token::Identifier("z"), Span(22, 23)),
|
(Token::Identifier("z"), Span(22, 23)),
|
||||||
(Token::Equal, Span(24, 25)),
|
(Token::Equal, Span(24, 25)),
|
||||||
(Token::Float("3.0"), Span(26, 29)),
|
(Token::Float("3.0"), Span(26, 29)),
|
||||||
(Token::RightBrace, Span(30, 31)),
|
(Token::RightCurlyBrace, Span(30, 31)),
|
||||||
(Token::Eof, Span(31, 31)),
|
(Token::Eof, Span(31, 31)),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
@ -921,7 +769,7 @@ mod tests {
|
|||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::Struct, Span(0, 6)),
|
(Token::Struct, Span(0, 6)),
|
||||||
(Token::Identifier("FooBar"), Span(7, 13)),
|
(Token::Identifier("FooBar"), Span(7, 13)),
|
||||||
(Token::LeftBrace, Span(14, 15)),
|
(Token::LeftCurlyBrace, Span(14, 15)),
|
||||||
(Token::Identifier("foo"), Span(16, 19)),
|
(Token::Identifier("foo"), Span(16, 19)),
|
||||||
(Token::Colon, Span(19, 20)),
|
(Token::Colon, Span(19, 20)),
|
||||||
(Token::Int, Span(21, 24)),
|
(Token::Int, Span(21, 24)),
|
||||||
@ -929,7 +777,7 @@ mod tests {
|
|||||||
(Token::Identifier("bar"), Span(26, 29)),
|
(Token::Identifier("bar"), Span(26, 29)),
|
||||||
(Token::Colon, Span(29, 30)),
|
(Token::Colon, Span(29, 30)),
|
||||||
(Token::FloatKeyword, Span(31, 36)),
|
(Token::FloatKeyword, Span(31, 36)),
|
||||||
(Token::RightBrace, Span(37, 38)),
|
(Token::RightCurlyBrace, Span(37, 38)),
|
||||||
(Token::Eof, Span(38, 38))
|
(Token::Eof, Span(38, 38))
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
@ -942,16 +790,16 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::LeftBracket, Span(0, 1)),
|
(Token::LeftSquareBrace, Span(0, 1)),
|
||||||
(Token::Integer("1"), Span(1, 2)),
|
(Token::Integer("1"), Span(1, 2)),
|
||||||
(Token::Comma, Span(2, 3)),
|
(Token::Comma, Span(2, 3)),
|
||||||
(Token::Integer("2"), Span(4, 5)),
|
(Token::Integer("2"), Span(4, 5)),
|
||||||
(Token::Comma, Span(5, 6)),
|
(Token::Comma, Span(5, 6)),
|
||||||
(Token::Integer("3"), Span(7, 8)),
|
(Token::Integer("3"), Span(7, 8)),
|
||||||
(Token::RightBracket, Span(8, 9)),
|
(Token::RightSquareBrace, Span(8, 9)),
|
||||||
(Token::LeftBracket, Span(9, 10)),
|
(Token::LeftSquareBrace, Span(9, 10)),
|
||||||
(Token::Integer("1"), Span(10, 11)),
|
(Token::Integer("1"), Span(10, 11)),
|
||||||
(Token::RightBracket, Span(11, 12)),
|
(Token::RightSquareBrace, Span(11, 12)),
|
||||||
(Token::Eof, Span(12, 12)),
|
(Token::Eof, Span(12, 12)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -964,13 +812,13 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::LeftBracket, Span(0, 1)),
|
(Token::LeftSquareBrace, Span(0, 1)),
|
||||||
(Token::Integer("1"), Span(1, 2)),
|
(Token::Integer("1"), Span(1, 2)),
|
||||||
(Token::Comma, Span(2, 3)),
|
(Token::Comma, Span(2, 3)),
|
||||||
(Token::Integer("2"), Span(4, 5)),
|
(Token::Integer("2"), Span(4, 5)),
|
||||||
(Token::Comma, Span(5, 6)),
|
(Token::Comma, Span(5, 6)),
|
||||||
(Token::Integer("3"), Span(7, 8)),
|
(Token::Integer("3"), Span(7, 8)),
|
||||||
(Token::RightBracket, Span(8, 9)),
|
(Token::RightSquareBrace, Span(8, 9)),
|
||||||
(Token::Eof, Span(9, 9)),
|
(Token::Eof, Span(9, 9)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -983,7 +831,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::LeftBrace, Span(0, 1)),
|
(Token::LeftCurlyBrace, Span(0, 1)),
|
||||||
(Token::Identifier("a"), Span(1, 2)),
|
(Token::Identifier("a"), Span(1, 2)),
|
||||||
(Token::Equal, Span(3, 4)),
|
(Token::Equal, Span(3, 4)),
|
||||||
(Token::Integer("1"), Span(5, 6)),
|
(Token::Integer("1"), Span(5, 6)),
|
||||||
@ -995,7 +843,7 @@ mod tests {
|
|||||||
(Token::Identifier("c"), Span(15, 16)),
|
(Token::Identifier("c"), Span(15, 16)),
|
||||||
(Token::Equal, Span(17, 18)),
|
(Token::Equal, Span(17, 18)),
|
||||||
(Token::Integer("3"), Span(19, 20)),
|
(Token::Integer("3"), Span(19, 20)),
|
||||||
(Token::RightBrace, Span(20, 21)),
|
(Token::RightCurlyBrace, Span(20, 21)),
|
||||||
(Token::Dot, Span(21, 22)),
|
(Token::Dot, Span(21, 22)),
|
||||||
(Token::Identifier("c"), Span(22, 23)),
|
(Token::Identifier("c"), Span(22, 23)),
|
||||||
(Token::Eof, Span(23, 23)),
|
(Token::Eof, Span(23, 23)),
|
||||||
@ -1064,15 +912,15 @@ mod tests {
|
|||||||
(Token::Identifier("x"), Span(3, 4)),
|
(Token::Identifier("x"), Span(3, 4)),
|
||||||
(Token::Less, Span(5, 6)),
|
(Token::Less, Span(5, 6)),
|
||||||
(Token::Integer("10"), Span(7, 9)),
|
(Token::Integer("10"), Span(7, 9)),
|
||||||
(Token::LeftBrace, Span(10, 11)),
|
(Token::LeftCurlyBrace, Span(10, 11)),
|
||||||
(Token::Identifier("x"), Span(12, 13)),
|
(Token::Identifier("x"), Span(12, 13)),
|
||||||
(Token::Plus, Span(14, 15)),
|
(Token::Plus, Span(14, 15)),
|
||||||
(Token::Integer("1"), Span(16, 17)),
|
(Token::Integer("1"), Span(16, 17)),
|
||||||
(Token::RightBrace, Span(18, 19)),
|
(Token::RightCurlyBrace, Span(18, 19)),
|
||||||
(Token::Else, Span(20, 24)),
|
(Token::Else, Span(20, 24)),
|
||||||
(Token::LeftBrace, Span(25, 26)),
|
(Token::LeftCurlyBrace, Span(25, 26)),
|
||||||
(Token::Identifier("x"), Span(27, 28)),
|
(Token::Identifier("x"), Span(27, 28)),
|
||||||
(Token::RightBrace, Span(29, 30)),
|
(Token::RightCurlyBrace, Span(29, 30)),
|
||||||
(Token::Eof, Span(30, 30)),
|
(Token::Eof, Span(30, 30)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -1089,11 +937,11 @@ mod tests {
|
|||||||
(Token::Identifier("x"), Span(6, 7)),
|
(Token::Identifier("x"), Span(6, 7)),
|
||||||
(Token::Less, Span(8, 9)),
|
(Token::Less, Span(8, 9)),
|
||||||
(Token::Integer("10"), Span(10, 12)),
|
(Token::Integer("10"), Span(10, 12)),
|
||||||
(Token::LeftBrace, Span(13, 14)),
|
(Token::LeftCurlyBrace, Span(13, 14)),
|
||||||
(Token::Identifier("x"), Span(15, 16)),
|
(Token::Identifier("x"), Span(15, 16)),
|
||||||
(Token::PlusEqual, Span(17, 19)),
|
(Token::PlusEqual, Span(17, 19)),
|
||||||
(Token::Integer("1"), Span(20, 21)),
|
(Token::Integer("1"), Span(20, 21)),
|
||||||
(Token::RightBrace, Span(22, 23)),
|
(Token::RightCurlyBrace, Span(22, 23)),
|
||||||
(Token::Eof, Span(23, 23)),
|
(Token::Eof, Span(23, 23)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -1136,7 +984,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::LeftBrace, Span(0, 1)),
|
(Token::LeftCurlyBrace, Span(0, 1)),
|
||||||
(Token::Identifier("x"), Span(2, 3)),
|
(Token::Identifier("x"), Span(2, 3)),
|
||||||
(Token::Equal, Span(4, 5)),
|
(Token::Equal, Span(4, 5)),
|
||||||
(Token::Integer("42"), Span(6, 8)),
|
(Token::Integer("42"), Span(6, 8)),
|
||||||
@ -1144,7 +992,7 @@ mod tests {
|
|||||||
(Token::Identifier("y"), Span(10, 11)),
|
(Token::Identifier("y"), Span(10, 11)),
|
||||||
(Token::Equal, Span(12, 13)),
|
(Token::Equal, Span(12, 13)),
|
||||||
(Token::String("foobar"), Span(14, 22)),
|
(Token::String("foobar"), Span(14, 22)),
|
||||||
(Token::RightBrace, Span(23, 24)),
|
(Token::RightCurlyBrace, Span(23, 24)),
|
||||||
(Token::Eof, Span(24, 24)),
|
(Token::Eof, Span(24, 24)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -1485,8 +1333,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
lex(input),
|
lex(input),
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(Token::LeftBracket, Span(0, 1)),
|
(Token::LeftSquareBrace, Span(0, 1)),
|
||||||
(Token::RightBracket, Span(1, 2)),
|
(Token::RightSquareBrace, Span(1, 2)),
|
||||||
(Token::Eof, Span(2, 2)),
|
(Token::Eof, Span(2, 2)),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -5,33 +5,29 @@ pub mod compiler;
|
|||||||
pub mod disassembler;
|
pub mod disassembler;
|
||||||
pub mod dust_error;
|
pub mod dust_error;
|
||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
pub mod function;
|
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod native_function;
|
pub mod native_function;
|
||||||
pub mod operation;
|
pub mod operation;
|
||||||
pub mod optimizer;
|
pub mod optimizer;
|
||||||
pub mod scope;
|
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub use crate::chunk::{Chunk, ChunkError, Local};
|
pub use crate::chunk::{Chunk, ChunkError, Local, Scope};
|
||||||
pub use crate::compiler::{compile, CompileError, Compiler};
|
pub use crate::compiler::{compile, CompileError, Compiler};
|
||||||
pub use crate::disassembler::Disassembler;
|
pub use crate::disassembler::Disassembler;
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||||
pub use crate::formatter::{format, Formatter};
|
pub use crate::formatter::{format, Formatter};
|
||||||
pub use crate::function::{Function, FunctionBorrowed};
|
|
||||||
pub use crate::instruction::Instruction;
|
pub use crate::instruction::Instruction;
|
||||||
pub use crate::lexer::{lex, LexError, Lexer};
|
pub use crate::lexer::{lex, LexError, Lexer};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::operation::Operation;
|
pub use crate::operation::Operation;
|
||||||
pub use crate::optimizer::Optimizer;
|
pub use crate::optimizer::{optimize, Optimizer};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||||
pub use crate::scope::Scope;
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned};
|
pub use crate::value::{ConcreteValue, Function, Value, ValueError};
|
||||||
pub use crate::value::{ConcreteValue, Value, ValueError};
|
|
||||||
pub use crate::vm::{run, Vm, VmError};
|
pub use crate::vm::{run, Vm, VmError};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -2,20 +2,20 @@
|
|||||||
//!
|
//!
|
||||||
//! Native functions are used either to implement features that are not possible to implement in
|
//! Native functions are used either to implement features that are not possible to implement in
|
||||||
//! Dust itself or that are more efficient to implement in Rust.
|
//! Dust itself or that are more efficient to implement in Rust.
|
||||||
mod logic;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
io::{self},
|
io::{self, stdin, stdout, Write},
|
||||||
string::{self},
|
string::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Vm, VmError};
|
use crate::{
|
||||||
|
AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Value, Vm, VmError,
|
||||||
|
};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $byte:literal, $str:expr, $type:expr)),*) => {
|
||||||
/// A dust-native function.
|
/// A dust-native function.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
@ -27,19 +27,6 @@ macro_rules! define_native_function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NativeFunction {
|
impl NativeFunction {
|
||||||
pub fn call(
|
|
||||||
&self,
|
|
||||||
vm: &mut Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
span: Span
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
match self {
|
|
||||||
$(
|
|
||||||
NativeFunction::$name => $function(vm, instruction, span),
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
@ -69,7 +56,7 @@ macro_rules! define_native_function {
|
|||||||
pub fn returns_value(&self) -> bool {
|
pub fn returns_value(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
NativeFunction::$name => *$type.return_type != Type::None,
|
NativeFunction::$name => $type.return_type.is_some(),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,25 +91,18 @@ macro_rules! define_native_function {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for NativeFunction {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_native_function! {
|
define_native_function! {
|
||||||
// Assertion
|
// Assertion
|
||||||
// (
|
(
|
||||||
// Assert,
|
Assert,
|
||||||
// 0_u8,
|
0_u8,
|
||||||
// "assert",
|
"assert",
|
||||||
// FunctionType {
|
FunctionType {
|
||||||
// type_parameters: None,
|
type_parameters: None,
|
||||||
// value_parameters: None,
|
value_parameters: None,
|
||||||
// return_type: Box::new(Type::None)
|
return_type: None
|
||||||
// },
|
}
|
||||||
// assert
|
),
|
||||||
// ),
|
|
||||||
// (AssertEqual, 1_u8, "assert_equal", false),
|
// (AssertEqual, 1_u8, "assert_equal", false),
|
||||||
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
|
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
|
||||||
(
|
(
|
||||||
@ -132,9 +112,8 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::None)
|
return_type: Some(Box::new(Type::Any))
|
||||||
},
|
}
|
||||||
logic::panic
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// // Type conversion
|
// // Type conversion
|
||||||
@ -149,9 +128,8 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::Any)]),
|
value_parameters: Some(vec![(0, Type::Any)]),
|
||||||
return_type: Box::new(Type::String { length: None })
|
return_type: Some(Box::new(Type::String { length: None }))
|
||||||
},
|
}
|
||||||
logic::to_string
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// // List and string
|
// // List and string
|
||||||
@ -210,9 +188,8 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::String { length: None })
|
return_type: Some(Box::new(Type::String { length: None }))
|
||||||
},
|
}
|
||||||
logic::read_line
|
|
||||||
),
|
),
|
||||||
// (ReadTo, 51_u8, "read_to", false),
|
// (ReadTo, 51_u8, "read_to", false),
|
||||||
// (ReadUntil, 52_u8, "read_until", true),
|
// (ReadUntil, 52_u8, "read_until", true),
|
||||||
@ -226,9 +203,8 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
||||||
return_type: Box::new(Type::None)
|
return_type: None
|
||||||
},
|
}
|
||||||
logic::write
|
|
||||||
),
|
),
|
||||||
// (WriteFile, 56_u8, "write_file", false),
|
// (WriteFile, 56_u8, "write_file", false),
|
||||||
(
|
(
|
||||||
@ -238,9 +214,8 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
||||||
return_type: Box::new(Type::None)
|
return_type: None
|
||||||
},
|
}
|
||||||
logic::write_line
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// // Random
|
// // Random
|
||||||
@ -248,6 +223,142 @@ define_native_function! {
|
|||||||
// (RandomInRange, 59_u8, "random_in_range", true)
|
// (RandomInRange, 59_u8, "random_in_range", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NativeFunction {
|
||||||
|
pub fn call(
|
||||||
|
&self,
|
||||||
|
instruction: Instruction,
|
||||||
|
vm: &Vm,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<Option<Value>, VmError> {
|
||||||
|
let to_register = instruction.a();
|
||||||
|
let argument_count = instruction.c();
|
||||||
|
|
||||||
|
let return_value = match self {
|
||||||
|
NativeFunction::Panic => {
|
||||||
|
let message = if argument_count == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mut message = String::new();
|
||||||
|
|
||||||
|
for argument_index in 0..argument_count {
|
||||||
|
if argument_index != 0 {
|
||||||
|
message.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
let argument = vm.open_register(argument_index, position)?;
|
||||||
|
|
||||||
|
message.push_str(&argument.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(message)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(VmError::NativeFunction(NativeFunctionError::Panic {
|
||||||
|
message,
|
||||||
|
position,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type conversion
|
||||||
|
NativeFunction::ToString => {
|
||||||
|
let mut string = String::new();
|
||||||
|
|
||||||
|
for argument_index in 0..argument_count {
|
||||||
|
let argument = vm.open_register(argument_index, position)?;
|
||||||
|
|
||||||
|
string.push_str(&argument.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Value::Concrete(ConcreteValue::String(string)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// I/O
|
||||||
|
NativeFunction::ReadLine => {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
|
stdin().read_line(&mut buffer).map_err(|io_error| {
|
||||||
|
VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
|
error: io_error.kind(),
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
buffer = buffer.trim_end_matches('\n').to_string();
|
||||||
|
|
||||||
|
Some(Value::Concrete(ConcreteValue::String(buffer)))
|
||||||
|
}
|
||||||
|
NativeFunction::Write => {
|
||||||
|
let to_register = instruction.a();
|
||||||
|
let mut stdout = stdout();
|
||||||
|
let map_err = |io_error: io::Error| {
|
||||||
|
VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
|
error: io_error.kind(),
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_argument = to_register.saturating_sub(argument_count);
|
||||||
|
let last_argument = to_register.saturating_sub(1);
|
||||||
|
|
||||||
|
for argument_index in first_argument..=last_argument {
|
||||||
|
if argument_index != first_argument {
|
||||||
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let argument_string = vm.open_register(argument_index, position)?.to_string();
|
||||||
|
|
||||||
|
stdout
|
||||||
|
.write_all(argument_string.as_bytes())
|
||||||
|
.map_err(map_err)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
NativeFunction::WriteLine => {
|
||||||
|
let mut stdout = stdout();
|
||||||
|
let map_err = |io_error: io::Error| {
|
||||||
|
VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
|
error: io_error.kind(),
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_index = to_register.saturating_sub(argument_count);
|
||||||
|
let arguments = vm.open_nonempty_registers(first_index..to_register, position)?;
|
||||||
|
|
||||||
|
for (index, argument) in arguments.into_iter().enumerate() {
|
||||||
|
if index != 0 {
|
||||||
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Value::Concrete(ConcreteValue::String(string)) = argument {
|
||||||
|
let bytes = string.as_bytes();
|
||||||
|
|
||||||
|
stdout.write_all(bytes).map_err(map_err)?;
|
||||||
|
} else {
|
||||||
|
let bytes = argument.to_string().into_bytes();
|
||||||
|
|
||||||
|
stdout.write_all(&bytes).map_err(map_err)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout.write(b"\n").map_err(map_err)?;
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(return_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for NativeFunction {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum NativeFunctionError {
|
pub enum NativeFunctionError {
|
||||||
ExpectedArgumentCount {
|
ExpectedArgumentCount {
|
@ -1,157 +0,0 @@
|
|||||||
use std::io::{self, stdout, Write};
|
|
||||||
|
|
||||||
use crate::{ConcreteValue, Instruction, NativeFunctionError, Span, Value, Vm, VmError};
|
|
||||||
|
|
||||||
pub fn panic(
|
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
|
||||||
let message = if argument_count == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut message = String::new();
|
|
||||||
|
|
||||||
for argument_index in 0..argument_count {
|
|
||||||
if argument_index != 0 {
|
|
||||||
message.push(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
let argument = vm.open_register(argument_index, position)?;
|
|
||||||
|
|
||||||
message.push_str(&argument.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(message)
|
|
||||||
};
|
|
||||||
|
|
||||||
Err(VmError::NativeFunction(NativeFunctionError::Panic {
|
|
||||||
message,
|
|
||||||
position,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(
|
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
|
||||||
|
|
||||||
if argument_count != 1 {
|
|
||||||
return Err(VmError::NativeFunction(
|
|
||||||
NativeFunctionError::ExpectedArgumentCount {
|
|
||||||
expected: 1,
|
|
||||||
found: argument_count as usize,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut string = String::new();
|
|
||||||
|
|
||||||
for argument_index in 0..argument_count {
|
|
||||||
let argument = vm.open_register(argument_index, position)?;
|
|
||||||
|
|
||||||
string.push_str(&argument.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(ConcreteValue::String(string)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_line(
|
|
||||||
_: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
|
||||||
|
|
||||||
if argument_count != 0 {
|
|
||||||
return Err(VmError::NativeFunction(
|
|
||||||
NativeFunctionError::ExpectedArgumentCount {
|
|
||||||
expected: 0,
|
|
||||||
found: argument_count as usize,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer = String::new();
|
|
||||||
|
|
||||||
match io::stdin().read_line(&mut buffer) {
|
|
||||||
Ok(_) => Ok(Some(ConcreteValue::String(
|
|
||||||
buffer.trim_end_matches('\n').to_string(),
|
|
||||||
))),
|
|
||||||
Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io {
|
|
||||||
error: error.kind(),
|
|
||||||
position,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(
|
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let to_register = instruction.a();
|
|
||||||
let argument_count = instruction.c();
|
|
||||||
let mut stdout = stdout();
|
|
||||||
let map_err = |io_error: io::Error| {
|
|
||||||
VmError::NativeFunction(NativeFunctionError::Io {
|
|
||||||
error: io_error.kind(),
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_argument = to_register.saturating_sub(argument_count);
|
|
||||||
|
|
||||||
for argument_index in first_argument..to_register {
|
|
||||||
if argument_index != first_argument {
|
|
||||||
stdout.write(b" ").map_err(map_err)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let argument_string = vm.open_register(argument_index, position)?.to_string();
|
|
||||||
|
|
||||||
stdout
|
|
||||||
.write_all(argument_string.as_bytes())
|
|
||||||
.map_err(map_err)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_line(
|
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let to_register = instruction.a();
|
|
||||||
let argument_count = instruction.c();
|
|
||||||
let mut stdout = stdout();
|
|
||||||
let map_err = |io_error: io::Error| {
|
|
||||||
VmError::NativeFunction(NativeFunctionError::Io {
|
|
||||||
error: io_error.kind(),
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_argument = to_register.saturating_sub(argument_count);
|
|
||||||
|
|
||||||
for argument_index in first_argument..to_register {
|
|
||||||
if argument_index != first_argument {
|
|
||||||
stdout.write(b" ").map_err(map_err)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let argument_string = vm.open_register(argument_index, position)?.to_string();
|
|
||||||
|
|
||||||
stdout
|
|
||||||
.write_all(argument_string.as_bytes())
|
|
||||||
.map_err(map_err)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout.write(b"\n").map_err(map_err)?;
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
@ -1,41 +1,34 @@
|
|||||||
//! Tool used by the compiler to optimize a chunk's bytecode.
|
//! Tools used by the compiler to optimize a chunk's bytecode.
|
||||||
|
use std::{iter::Map, slice::Iter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation, Span};
|
use crate::{Instruction, Operation, Span};
|
||||||
|
|
||||||
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
type MapToOperation = fn(&(Instruction, Span)) -> Operation;
|
||||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct Optimizer<'a> {
|
type OperationIter<'iter> = Map<Iter<'iter, (Instruction, Span)>, MapToOperation>;
|
||||||
instructions: &'a mut Vec<(Instruction, Span)>,
|
|
||||||
|
/// Performs optimizations on a subset of instructions.
|
||||||
|
pub fn optimize(instructions: &mut [(Instruction, Span)]) -> usize {
|
||||||
|
Optimizer::new(instructions).optimize()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Optimizer<'a> {
|
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
||||||
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Optimizer<'chunk> {
|
||||||
|
instructions: &'chunk mut [(Instruction, Span)],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'chunk> Optimizer<'chunk> {
|
||||||
/// Creates a new optimizer with a mutable reference to some of a chunk's instructions.
|
/// Creates a new optimizer with a mutable reference to some of a chunk's instructions.
|
||||||
pub fn new(instructions: &'a mut Vec<(Instruction, Span)>) -> Self {
|
pub fn new(instructions: &'chunk mut [(Instruction, Span)]) -> Self {
|
||||||
Self { instructions }
|
Self { instructions }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimizes a comparison operation.
|
/// Potentially mutates the instructions to optimize them.
|
||||||
///
|
pub fn optimize(&mut self) -> usize {
|
||||||
/// Comparison instructions (which are always followed by a JUMP) can be optimized when the
|
let mut optimizations = 0;
|
||||||
/// next instructions are two constant or boolean loaders. The first loader is set to skip an
|
|
||||||
/// instruction if it is run while the second loader is modified to use the first's register.
|
if matches!(
|
||||||
/// This makes the following two code snippets compile to the same bytecode:
|
|
||||||
///
|
|
||||||
/// ```dust
|
|
||||||
/// 4 == 4
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```dust
|
|
||||||
/// if 4 == 4 { true } else { false }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The instructions must be in the following order:
|
|
||||||
/// - `Operation::Equal | Operation::Less | Operation::LessEqual`
|
|
||||||
/// - `Operation::Jump`
|
|
||||||
/// - `Operation::LoadBoolean | Operation::LoadConstant`
|
|
||||||
/// - `Operation::LoadBoolean | Operation::LoadConstant`
|
|
||||||
pub fn optimize_comparison(&mut self) -> bool {
|
|
||||||
if !matches!(
|
|
||||||
self.get_operations(),
|
self.get_operations(),
|
||||||
Some([
|
Some([
|
||||||
Operation::Equal | Operation::Less | Operation::LessEqual,
|
Operation::Equal | Operation::Less | Operation::LessEqual,
|
||||||
@ -44,9 +37,22 @@ impl<'a> Optimizer<'a> {
|
|||||||
Operation::LoadBoolean | Operation::LoadConstant,
|
Operation::LoadBoolean | Operation::LoadConstant,
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
return false;
|
self.optimize_comparison();
|
||||||
|
|
||||||
|
optimizations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optimizations
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optimizes a comparison operation.
|
||||||
|
///
|
||||||
|
/// The instructions must be in the following order:
|
||||||
|
/// - `Operation::Equal | Operation::Less | Operation::LessEqual`
|
||||||
|
/// - `Operation::Jump`
|
||||||
|
/// - `Operation::LoadBoolean | Operation::LoadConstant`
|
||||||
|
/// - `Operation::LoadBoolean | Operation::LoadConstant`
|
||||||
|
fn optimize_comparison(&mut self) {
|
||||||
log::debug!("Optimizing comparison");
|
log::debug!("Optimizing comparison");
|
||||||
|
|
||||||
let first_loader_register = {
|
let first_loader_register = {
|
||||||
@ -66,30 +72,12 @@ impl<'a> Optimizer<'a> {
|
|||||||
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
|
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
|
||||||
|
|
||||||
*second_loader = second_loader_new;
|
*second_loader = second_loader_new;
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn optimize_set_local(&mut self) -> bool {
|
fn operations_iter(&self) -> OperationIter {
|
||||||
if !matches!(
|
self.instructions
|
||||||
self.get_operations(),
|
.iter()
|
||||||
Some([
|
.map(|(instruction, _)| instruction.operation())
|
||||||
Operation::Add
|
|
||||||
| Operation::Subtract
|
|
||||||
| Operation::Multiply
|
|
||||||
| Operation::Divide
|
|
||||||
| Operation::Modulo,
|
|
||||||
Operation::SetLocal,
|
|
||||||
])
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("Optimizing set local");
|
|
||||||
|
|
||||||
self.instructions.pop();
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
fn get_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
||||||
@ -99,12 +87,7 @@ impl<'a> Optimizer<'a> {
|
|||||||
|
|
||||||
let mut n_operations = [Operation::Return; COUNT];
|
let mut n_operations = [Operation::Return; COUNT];
|
||||||
|
|
||||||
for (nth, operation) in n_operations.iter_mut().rev().zip(
|
for (nth, operation) in n_operations.iter_mut().zip(self.operations_iter()) {
|
||||||
self.instructions
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|(instruction, _)| instruction.operation()),
|
|
||||||
) {
|
|
||||||
*nth = operation;
|
*nth = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
//! Scope, a type that represents the locality of a variable.
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Variable locality, as defined by its depth and block index.
|
|
||||||
///
|
|
||||||
/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate
|
|
||||||
/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the
|
|
||||||
/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0
|
|
||||||
/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are
|
|
||||||
/// incremented and decremented.
|
|
||||||
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Scope {
|
|
||||||
/// Level of block nesting.
|
|
||||||
pub depth: u8,
|
|
||||||
/// Index of the block in the chunk.
|
|
||||||
pub block_index: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Scope {
|
|
||||||
pub fn new(depth: u8, block_index: u8) -> Self {
|
|
||||||
Self { depth, block_index }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, other: &Self) -> bool {
|
|
||||||
match self.depth.cmp(&other.depth) {
|
|
||||||
Ordering::Less => false,
|
|
||||||
Ordering::Greater => self.block_index >= other.block_index,
|
|
||||||
Ordering::Equal => self.block_index == other.block_index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn begin(&mut self, block_index: u8) {
|
|
||||||
self.block_index = block_index;
|
|
||||||
self.depth += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn end(&mut self) {
|
|
||||||
self.depth -= 1;
|
|
||||||
|
|
||||||
if self.depth == 0 {
|
|
||||||
self.block_index = 0;
|
|
||||||
} else {
|
|
||||||
self.block_index -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Scope {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "({}, {})", self.depth, self.block_index)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +1,8 @@
|
|||||||
//! Token, TokenOwned and TokenKind types.
|
//! Token, TokenOwned and TokenKind types.
|
||||||
use std::{
|
use std::fmt::{self, Display, Formatter};
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
io::Write,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Span;
|
|
||||||
|
|
||||||
pub fn output_token_list<W: Write>(tokens: &[(Token, Span)], writer: &mut W) {
|
|
||||||
const HEADER: [&str; 2] = [
|
|
||||||
"TOKEN KIND POSITION ",
|
|
||||||
"------------ ---------- ----------",
|
|
||||||
];
|
|
||||||
|
|
||||||
writeln!(writer, "{}", HEADER[0]).unwrap();
|
|
||||||
writeln!(writer, "{}", HEADER[1]).unwrap();
|
|
||||||
|
|
||||||
for (token, position) in tokens {
|
|
||||||
let kind = token.kind().to_string();
|
|
||||||
let token = token.to_string();
|
|
||||||
|
|
||||||
writeln!(writer, "{token:<12} {kind:<10} {position}").unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_tokens {
|
macro_rules! define_tokens {
|
||||||
($($variant:ident $(($data_type:ty))?),+ $(,)?) => {
|
($($variant:ident $(($data_type:ty))?),+ $(,)?) => {
|
||||||
/// Source token.
|
/// Source token.
|
||||||
@ -94,9 +72,9 @@ define_tokens! {
|
|||||||
Equal,
|
Equal,
|
||||||
Greater,
|
Greater,
|
||||||
GreaterEqual,
|
GreaterEqual,
|
||||||
LeftBrace,
|
LeftCurlyBrace,
|
||||||
LeftBracket,
|
|
||||||
LeftParenthesis,
|
LeftParenthesis,
|
||||||
|
LeftSquareBrace,
|
||||||
Less,
|
Less,
|
||||||
LessEqual,
|
LessEqual,
|
||||||
Minus,
|
Minus,
|
||||||
@ -105,9 +83,9 @@ define_tokens! {
|
|||||||
PercentEqual,
|
PercentEqual,
|
||||||
Plus,
|
Plus,
|
||||||
PlusEqual,
|
PlusEqual,
|
||||||
RightBrace,
|
RightCurlyBrace,
|
||||||
RightBracket,
|
|
||||||
RightParenthesis,
|
RightParenthesis,
|
||||||
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Slash,
|
Slash,
|
||||||
SlashEqual,
|
SlashEqual,
|
||||||
@ -155,9 +133,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Equal => 1,
|
Token::Equal => 1,
|
||||||
Token::Greater => 1,
|
Token::Greater => 1,
|
||||||
Token::GreaterEqual => 2,
|
Token::GreaterEqual => 2,
|
||||||
Token::LeftBrace => 1,
|
Token::LeftCurlyBrace => 1,
|
||||||
Token::LeftParenthesis => 1,
|
Token::LeftParenthesis => 1,
|
||||||
Token::LeftBracket => 1,
|
Token::LeftSquareBrace => 1,
|
||||||
Token::Less => 1,
|
Token::Less => 1,
|
||||||
Token::LessEqual => 2,
|
Token::LessEqual => 2,
|
||||||
Token::Minus => 1,
|
Token::Minus => 1,
|
||||||
@ -167,9 +145,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Plus => 1,
|
Token::Plus => 1,
|
||||||
Token::PlusEqual => 2,
|
Token::PlusEqual => 2,
|
||||||
Token::Return => 6,
|
Token::Return => 6,
|
||||||
Token::RightBrace => 1,
|
Token::RightCurlyBrace => 1,
|
||||||
Token::RightParenthesis => 1,
|
Token::RightParenthesis => 1,
|
||||||
Token::RightBracket => 1,
|
Token::RightSquareBrace => 1,
|
||||||
Token::Semicolon => 1,
|
Token::Semicolon => 1,
|
||||||
Token::Slash => 1,
|
Token::Slash => 1,
|
||||||
Token::SlashEqual => 2,
|
Token::SlashEqual => 2,
|
||||||
@ -216,9 +194,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Equal => "=",
|
Token::Equal => "=",
|
||||||
Token::Greater => ">",
|
Token::Greater => ">",
|
||||||
Token::GreaterEqual => ">=",
|
Token::GreaterEqual => ">=",
|
||||||
Token::LeftBrace => "{",
|
Token::LeftCurlyBrace => "{",
|
||||||
Token::LeftParenthesis => "(",
|
Token::LeftParenthesis => "(",
|
||||||
Token::LeftBracket => "[",
|
Token::LeftSquareBrace => "[",
|
||||||
Token::Less => "<",
|
Token::Less => "<",
|
||||||
Token::LessEqual => "<=",
|
Token::LessEqual => "<=",
|
||||||
Token::Minus => "-",
|
Token::Minus => "-",
|
||||||
@ -228,9 +206,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Plus => "+",
|
Token::Plus => "+",
|
||||||
Token::PlusEqual => "+=",
|
Token::PlusEqual => "+=",
|
||||||
Token::Return => "return",
|
Token::Return => "return",
|
||||||
Token::RightBrace => "}",
|
Token::RightCurlyBrace => "}",
|
||||||
Token::RightParenthesis => ")",
|
Token::RightParenthesis => ")",
|
||||||
Token::RightBracket => "]",
|
Token::RightSquareBrace => "]",
|
||||||
Token::Semicolon => ";",
|
Token::Semicolon => ";",
|
||||||
Token::Slash => "/",
|
Token::Slash => "/",
|
||||||
Token::SlashEqual => "/=",
|
Token::SlashEqual => "/=",
|
||||||
@ -269,9 +247,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::If => TokenOwned::If,
|
Token::If => TokenOwned::If,
|
||||||
Token::Int => TokenOwned::Int,
|
Token::Int => TokenOwned::Int,
|
||||||
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
||||||
Token::LeftBrace => TokenOwned::LeftCurlyBrace,
|
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
|
||||||
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
|
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
|
||||||
Token::LeftBracket => TokenOwned::LeftSquareBrace,
|
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
|
||||||
Token::Let => TokenOwned::Let,
|
Token::Let => TokenOwned::Let,
|
||||||
Token::Less => TokenOwned::Less,
|
Token::Less => TokenOwned::Less,
|
||||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
Token::LessEqual => TokenOwned::LessOrEqual,
|
||||||
@ -285,9 +263,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Plus => TokenOwned::Plus,
|
Token::Plus => TokenOwned::Plus,
|
||||||
Token::PlusEqual => TokenOwned::PlusEqual,
|
Token::PlusEqual => TokenOwned::PlusEqual,
|
||||||
Token::Return => TokenOwned::Return,
|
Token::Return => TokenOwned::Return,
|
||||||
Token::RightBrace => TokenOwned::RightCurlyBrace,
|
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
||||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
||||||
Token::RightBracket => TokenOwned::RightSquareBrace,
|
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||||
Token::Semicolon => TokenOwned::Semicolon,
|
Token::Semicolon => TokenOwned::Semicolon,
|
||||||
Token::Star => TokenOwned::Star,
|
Token::Star => TokenOwned::Star,
|
||||||
Token::StarEqual => TokenOwned::StarEqual,
|
Token::StarEqual => TokenOwned::StarEqual,
|
||||||
@ -330,9 +308,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::If => TokenKind::If,
|
Token::If => TokenKind::If,
|
||||||
Token::Int => TokenKind::Int,
|
Token::Int => TokenKind::Int,
|
||||||
Token::Integer(_) => TokenKind::Integer,
|
Token::Integer(_) => TokenKind::Integer,
|
||||||
Token::LeftBrace => TokenKind::LeftBrace,
|
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
|
||||||
Token::LeftParenthesis => TokenKind::LeftParenthesis,
|
Token::LeftParenthesis => TokenKind::LeftParenthesis,
|
||||||
Token::LeftBracket => TokenKind::LeftBracket,
|
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
|
||||||
Token::Let => TokenKind::Let,
|
Token::Let => TokenKind::Let,
|
||||||
Token::Less => TokenKind::Less,
|
Token::Less => TokenKind::Less,
|
||||||
Token::LessEqual => TokenKind::LessEqual,
|
Token::LessEqual => TokenKind::LessEqual,
|
||||||
@ -346,9 +324,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Plus => TokenKind::Plus,
|
Token::Plus => TokenKind::Plus,
|
||||||
Token::PlusEqual => TokenKind::PlusEqual,
|
Token::PlusEqual => TokenKind::PlusEqual,
|
||||||
Token::Return => TokenKind::Return,
|
Token::Return => TokenKind::Return,
|
||||||
Token::RightBrace => TokenKind::RightBrace,
|
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
|
||||||
Token::RightParenthesis => TokenKind::RightParenthesis,
|
Token::RightParenthesis => TokenKind::RightParenthesis,
|
||||||
Token::RightBracket => TokenKind::RightBracket,
|
Token::RightSquareBrace => TokenKind::RightSquareBrace,
|
||||||
Token::Semicolon => TokenKind::Semicolon,
|
Token::Semicolon => TokenKind::Semicolon,
|
||||||
Token::Star => TokenKind::Star,
|
Token::Star => TokenKind::Star,
|
||||||
Token::StarEqual => TokenKind::StarEqual,
|
Token::StarEqual => TokenKind::StarEqual,
|
||||||
@ -385,9 +363,9 @@ impl<'src> Token<'src> {
|
|||||||
| Token::Equal
|
| Token::Equal
|
||||||
| Token::Greater
|
| Token::Greater
|
||||||
| Token::GreaterEqual
|
| Token::GreaterEqual
|
||||||
| Token::LeftBrace
|
| Token::LeftCurlyBrace
|
||||||
| Token::LeftParenthesis
|
| Token::LeftParenthesis
|
||||||
| Token::LeftBracket
|
| Token::LeftSquareBrace
|
||||||
| Token::Less
|
| Token::Less
|
||||||
| Token::LessEqual
|
| Token::LessEqual
|
||||||
| Token::Minus
|
| Token::Minus
|
||||||
@ -435,9 +413,9 @@ impl<'src> Display for Token<'src> {
|
|||||||
Token::If => write!(f, "if"),
|
Token::If => write!(f, "if"),
|
||||||
Token::Int => write!(f, "int"),
|
Token::Int => write!(f, "int"),
|
||||||
Token::Integer(value) => write!(f, "{value}"),
|
Token::Integer(value) => write!(f, "{value}"),
|
||||||
Token::LeftBrace => write!(f, "{{"),
|
Token::LeftCurlyBrace => write!(f, "{{"),
|
||||||
Token::LeftParenthesis => write!(f, "("),
|
Token::LeftParenthesis => write!(f, "("),
|
||||||
Token::LeftBracket => write!(f, "["),
|
Token::LeftSquareBrace => write!(f, "["),
|
||||||
Token::Let => write!(f, "let"),
|
Token::Let => write!(f, "let"),
|
||||||
Token::Less => write!(f, "<"),
|
Token::Less => write!(f, "<"),
|
||||||
Token::LessEqual => write!(f, "<="),
|
Token::LessEqual => write!(f, "<="),
|
||||||
@ -451,9 +429,9 @@ impl<'src> Display for Token<'src> {
|
|||||||
Token::Plus => write!(f, "+"),
|
Token::Plus => write!(f, "+"),
|
||||||
Token::PlusEqual => write!(f, "+="),
|
Token::PlusEqual => write!(f, "+="),
|
||||||
Token::Return => write!(f, "return"),
|
Token::Return => write!(f, "return"),
|
||||||
Token::RightBrace => write!(f, "}}"),
|
Token::RightCurlyBrace => write!(f, "}}"),
|
||||||
Token::RightParenthesis => write!(f, ")"),
|
Token::RightParenthesis => write!(f, ")"),
|
||||||
Token::RightBracket => write!(f, "]"),
|
Token::RightSquareBrace => write!(f, "]"),
|
||||||
Token::Semicolon => write!(f, ";"),
|
Token::Semicolon => write!(f, ";"),
|
||||||
Token::Slash => write!(f, "/"),
|
Token::Slash => write!(f, "/"),
|
||||||
Token::SlashEqual => write!(f, "/="),
|
Token::SlashEqual => write!(f, "/="),
|
||||||
@ -568,9 +546,9 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::If => Token::If.fmt(f),
|
TokenOwned::If => Token::If.fmt(f),
|
||||||
TokenOwned::Int => Token::Int.fmt(f),
|
TokenOwned::Int => Token::Int.fmt(f),
|
||||||
TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
|
TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
|
||||||
TokenOwned::LeftCurlyBrace => Token::LeftBrace.fmt(f),
|
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
||||||
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||||
TokenOwned::LeftSquareBrace => Token::LeftBracket.fmt(f),
|
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||||
TokenOwned::Let => Token::Let.fmt(f),
|
TokenOwned::Let => Token::Let.fmt(f),
|
||||||
TokenOwned::Less => Token::Less.fmt(f),
|
TokenOwned::Less => Token::Less.fmt(f),
|
||||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
||||||
@ -584,9 +562,9 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||||
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
|
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
|
||||||
TokenOwned::Return => Token::Return.fmt(f),
|
TokenOwned::Return => Token::Return.fmt(f),
|
||||||
TokenOwned::RightCurlyBrace => Token::RightBrace.fmt(f),
|
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||||
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
|
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||||
TokenOwned::RightSquareBrace => Token::RightBracket.fmt(f),
|
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenOwned::Star => Token::Star.fmt(f),
|
TokenOwned::Star => Token::Star.fmt(f),
|
||||||
TokenOwned::StarEqual => Token::StarEqual.fmt(f),
|
TokenOwned::StarEqual => Token::StarEqual.fmt(f),
|
||||||
@ -608,10 +586,10 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Bang => Token::Bang.fmt(f),
|
TokenKind::Bang => Token::Bang.fmt(f),
|
||||||
TokenKind::BangEqual => Token::BangEqual.fmt(f),
|
TokenKind::BangEqual => Token::BangEqual.fmt(f),
|
||||||
TokenKind::Bool => Token::Bool.fmt(f),
|
TokenKind::Bool => Token::Bool.fmt(f),
|
||||||
TokenKind::Boolean => write!(f, "boolean"),
|
TokenKind::Boolean => write!(f, "boolean value"),
|
||||||
TokenKind::Break => Token::Break.fmt(f),
|
TokenKind::Break => Token::Break.fmt(f),
|
||||||
TokenKind::Byte => write!(f, "byte"),
|
TokenKind::Byte => write!(f, "byte value"),
|
||||||
TokenKind::Character => write!(f, "character"),
|
TokenKind::Character => write!(f, "character value"),
|
||||||
TokenKind::Colon => Token::Colon.fmt(f),
|
TokenKind::Colon => Token::Colon.fmt(f),
|
||||||
TokenKind::Comma => Token::Comma.fmt(f),
|
TokenKind::Comma => Token::Comma.fmt(f),
|
||||||
TokenKind::Dot => Token::Dot.fmt(f),
|
TokenKind::Dot => Token::Dot.fmt(f),
|
||||||
@ -622,7 +600,7 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Else => Token::Else.fmt(f),
|
TokenKind::Else => Token::Else.fmt(f),
|
||||||
TokenKind::Eof => Token::Eof.fmt(f),
|
TokenKind::Eof => Token::Eof.fmt(f),
|
||||||
TokenKind::Equal => Token::Equal.fmt(f),
|
TokenKind::Equal => Token::Equal.fmt(f),
|
||||||
TokenKind::Float => write!(f, "float"),
|
TokenKind::Float => write!(f, "float value"),
|
||||||
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
|
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
|
||||||
TokenKind::Fn => Token::Fn.fmt(f),
|
TokenKind::Fn => Token::Fn.fmt(f),
|
||||||
TokenKind::Greater => Token::Greater.fmt(f),
|
TokenKind::Greater => Token::Greater.fmt(f),
|
||||||
@ -630,10 +608,10 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Identifier => write!(f, "identifier"),
|
TokenKind::Identifier => write!(f, "identifier"),
|
||||||
TokenKind::If => Token::If.fmt(f),
|
TokenKind::If => Token::If.fmt(f),
|
||||||
TokenKind::Int => Token::Int.fmt(f),
|
TokenKind::Int => Token::Int.fmt(f),
|
||||||
TokenKind::Integer => write!(f, "integer"),
|
TokenKind::Integer => write!(f, "integer value"),
|
||||||
TokenKind::LeftBrace => Token::LeftBrace.fmt(f),
|
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
||||||
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
|
||||||
TokenKind::LeftBracket => Token::LeftBracket.fmt(f),
|
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||||
TokenKind::Let => Token::Let.fmt(f),
|
TokenKind::Let => Token::Let.fmt(f),
|
||||||
TokenKind::Less => Token::Less.fmt(f),
|
TokenKind::Less => Token::Less.fmt(f),
|
||||||
TokenKind::LessEqual => Token::LessEqual.fmt(f),
|
TokenKind::LessEqual => Token::LessEqual.fmt(f),
|
||||||
@ -647,16 +625,16 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Plus => Token::Plus.fmt(f),
|
TokenKind::Plus => Token::Plus.fmt(f),
|
||||||
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
|
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
|
||||||
TokenKind::Return => Token::Return.fmt(f),
|
TokenKind::Return => Token::Return.fmt(f),
|
||||||
TokenKind::RightBrace => Token::RightBrace.fmt(f),
|
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||||
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
|
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
|
||||||
TokenKind::RightBracket => Token::RightBracket.fmt(f),
|
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenKind::Star => Token::Star.fmt(f),
|
TokenKind::Star => Token::Star.fmt(f),
|
||||||
TokenKind::StarEqual => Token::StarEqual.fmt(f),
|
TokenKind::StarEqual => Token::StarEqual.fmt(f),
|
||||||
TokenKind::Str => Token::Str.fmt(f),
|
TokenKind::Str => Token::Str.fmt(f),
|
||||||
TokenKind::Slash => Token::Slash.fmt(f),
|
TokenKind::Slash => Token::Slash.fmt(f),
|
||||||
TokenKind::SlashEqual => Token::SlashEqual.fmt(f),
|
TokenKind::SlashEqual => Token::SlashEqual.fmt(f),
|
||||||
TokenKind::String => write!(f, "string"),
|
TokenKind::String => write!(f, "string value"),
|
||||||
TokenKind::Struct => Token::Struct.fmt(f),
|
TokenKind::Struct => Token::Struct.fmt(f),
|
||||||
TokenKind::While => Token::While.fmt(f),
|
TokenKind::While => Token::While.fmt(f),
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,10 @@ pub enum Type {
|
|||||||
Map {
|
Map {
|
||||||
pairs: HashMap<u8, Type>,
|
pairs: HashMap<u8, Type>,
|
||||||
},
|
},
|
||||||
None,
|
|
||||||
Number,
|
Number,
|
||||||
Range {
|
Range {
|
||||||
r#type: Box<Type>,
|
r#type: RangeableType,
|
||||||
},
|
},
|
||||||
SelfChunk,
|
|
||||||
String {
|
String {
|
||||||
length: Option<usize>,
|
length: Option<usize>,
|
||||||
},
|
},
|
||||||
@ -72,7 +70,6 @@ impl Type {
|
|||||||
| (Type::Character, Type::Character)
|
| (Type::Character, Type::Character)
|
||||||
| (Type::Float, Type::Float)
|
| (Type::Float, Type::Float)
|
||||||
| (Type::Integer, Type::Integer)
|
| (Type::Integer, Type::Integer)
|
||||||
| (Type::None, Type::None)
|
|
||||||
| (Type::String { .. }, Type::String { .. }) => return Ok(()),
|
| (Type::String { .. }, Type::String { .. }) => return Ok(()),
|
||||||
(
|
(
|
||||||
Type::Generic {
|
Type::Generic {
|
||||||
@ -260,10 +257,8 @@ impl Display for Type {
|
|||||||
|
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
Type::None => write!(f, "none"),
|
|
||||||
Type::Number => write!(f, "num"),
|
Type::Number => write!(f, "num"),
|
||||||
Type::Range { r#type } => write!(f, "{type} range"),
|
Type::Range { r#type } => write!(f, "{type} range"),
|
||||||
Type::SelfChunk => write!(f, "self"),
|
|
||||||
Type::String { .. } => write!(f, "str"),
|
Type::String { .. } => write!(f, "str"),
|
||||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||||
Type::Tuple { fields } => {
|
Type::Tuple { fields } => {
|
||||||
@ -348,16 +343,12 @@ impl Ord for Type {
|
|||||||
left_pairs.iter().cmp(right_pairs.iter())
|
left_pairs.iter().cmp(right_pairs.iter())
|
||||||
}
|
}
|
||||||
(Type::Map { .. }, _) => Ordering::Greater,
|
(Type::Map { .. }, _) => Ordering::Greater,
|
||||||
(Type::None, Type::None) => Ordering::Equal,
|
|
||||||
(Type::None, _) => Ordering::Greater,
|
|
||||||
(Type::Number, Type::Number) => Ordering::Equal,
|
(Type::Number, Type::Number) => Ordering::Equal,
|
||||||
(Type::Number, _) => Ordering::Greater,
|
(Type::Number, _) => Ordering::Greater,
|
||||||
(Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => {
|
(Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => {
|
||||||
left_type.cmp(right_type)
|
left_type.cmp(right_type)
|
||||||
}
|
}
|
||||||
(Type::Range { .. }, _) => Ordering::Greater,
|
(Type::Range { .. }, _) => Ordering::Greater,
|
||||||
(Type::SelfChunk, Type::SelfChunk) => Ordering::Equal,
|
|
||||||
(Type::SelfChunk, _) => Ordering::Greater,
|
|
||||||
(Type::String { length: left }, Type::String { length: right }) => left.cmp(right),
|
(Type::String { length: left }, Type::String { length: right }) => left.cmp(right),
|
||||||
(Type::String { .. }, _) => Ordering::Greater,
|
(Type::String { .. }, _) => Ordering::Greater,
|
||||||
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
||||||
@ -375,7 +366,7 @@ impl Ord for Type {
|
|||||||
pub struct FunctionType {
|
pub struct FunctionType {
|
||||||
pub type_parameters: Option<Vec<u8>>,
|
pub type_parameters: Option<Vec<u8>>,
|
||||||
pub value_parameters: Option<Vec<(u8, Type)>>,
|
pub value_parameters: Option<Vec<(u8, Type)>>,
|
||||||
pub return_type: Box<Type>,
|
pub return_type: Option<Box<Type>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for FunctionType {
|
impl Display for FunctionType {
|
||||||
@ -410,8 +401,8 @@ impl Display for FunctionType {
|
|||||||
|
|
||||||
write!(f, ")")?;
|
write!(f, ")")?;
|
||||||
|
|
||||||
if *self.return_type != Type::None {
|
if let Some(return_type) = &self.return_type {
|
||||||
write!(f, " -> {}", self.return_type)?;
|
write!(f, " -> {return_type}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -554,8 +545,115 @@ impl Display for EnumType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub enum RangeableType {
|
||||||
|
Byte,
|
||||||
|
Character,
|
||||||
|
Float,
|
||||||
|
Integer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RangeableType {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
RangeableType::Byte => Type::Byte.fmt(f),
|
||||||
|
RangeableType::Character => Type::Character.fmt(f),
|
||||||
|
RangeableType::Float => Type::Float.fmt(f),
|
||||||
|
RangeableType::Integer => Type::Integer.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct TypeConflict {
|
pub struct TypeConflict {
|
||||||
pub expected: Type,
|
pub expected: Type,
|
||||||
pub actual: Type,
|
pub actual: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_type_any() {
|
||||||
|
let foo = Type::Any;
|
||||||
|
let bar = Type::Any;
|
||||||
|
|
||||||
|
foo.check(&bar).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_type_boolean() {
|
||||||
|
let foo = Type::Boolean;
|
||||||
|
let bar = Type::Boolean;
|
||||||
|
|
||||||
|
foo.check(&bar).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_type_byte() {
|
||||||
|
let foo = Type::Byte;
|
||||||
|
let bar = Type::Byte;
|
||||||
|
|
||||||
|
foo.check(&bar).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_type_character() {
|
||||||
|
let foo = Type::Character;
|
||||||
|
let bar = Type::Character;
|
||||||
|
|
||||||
|
foo.check(&bar).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn errors() {
|
||||||
|
let foo = Type::Integer;
|
||||||
|
let bar = Type::String { length: None };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
foo.check(&bar),
|
||||||
|
Err(TypeConflict {
|
||||||
|
actual: bar.clone(),
|
||||||
|
expected: foo.clone()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
bar.check(&foo),
|
||||||
|
Err(TypeConflict {
|
||||||
|
actual: foo.clone(),
|
||||||
|
expected: bar.clone()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let types = [
|
||||||
|
Type::Boolean,
|
||||||
|
Type::Float,
|
||||||
|
Type::Integer,
|
||||||
|
Type::List {
|
||||||
|
item_type: Box::new(Type::Integer),
|
||||||
|
length: 42,
|
||||||
|
},
|
||||||
|
Type::Range {
|
||||||
|
r#type: RangeableType::Integer,
|
||||||
|
},
|
||||||
|
Type::String { length: None },
|
||||||
|
];
|
||||||
|
|
||||||
|
for left in types.clone() {
|
||||||
|
for right in types.clone() {
|
||||||
|
if left == right {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
left.check(&right),
|
||||||
|
Err(TypeConflict {
|
||||||
|
actual: right.clone(),
|
||||||
|
expected: left.clone()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,22 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
mem::replace,
|
ops::Range,
|
||||||
rc::Weak,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionBorrowed,
|
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
||||||
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
Instruction, Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value,
|
||||||
|
ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let mut chunk = compile(source)?;
|
||||||
let mut vm = Vm::new(&chunk, None);
|
let mut vm = Vm::new(&mut chunk, None);
|
||||||
|
|
||||||
vm.run()
|
vm.run()
|
||||||
|
.map(|option| option.cloned())
|
||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,30 +32,48 @@ pub fn run_and_display_output(source: &str) {
|
|||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Vm<'chunk, 'parent, 'stack> {
|
pub struct Vm<'chunk, 'parent> {
|
||||||
ip: usize,
|
ip: usize,
|
||||||
chunk: &'chunk Chunk,
|
chunk: &'chunk mut Chunk,
|
||||||
stack: Vec<Register<'stack>>,
|
stack: Vec<Register>,
|
||||||
local_definitions: HashMap<u8, u8>,
|
|
||||||
last_assigned_register: Option<u8>,
|
last_assigned_register: Option<u8>,
|
||||||
parent: Option<&'parent Vm<'chunk, 'stack, 'parent>>,
|
parent: Option<&'parent Vm<'chunk, 'parent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
impl<'chunk, 'parent> Vm<'chunk, 'parent> {
|
||||||
const STACK_LIMIT: usize = u16::MAX as usize;
|
const STACK_LIMIT: usize = u16::MAX as usize;
|
||||||
|
|
||||||
pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent, 'stack>>) -> Self {
|
pub fn new(chunk: &'chunk mut Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ip: 0,
|
ip: 0,
|
||||||
chunk,
|
chunk,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
local_definitions: HashMap::new(),
|
|
||||||
last_assigned_register: None,
|
last_assigned_register: None,
|
||||||
parent,
|
parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&'stack mut self) -> Result<Option<ConcreteValue>, VmError> {
|
pub fn run(&mut self) -> Result<Option<&Value>, VmError> {
|
||||||
|
// DRY helper to get constant or register values for binary operations
|
||||||
|
fn get_arguments<'a>(
|
||||||
|
vm: &'a mut Vm,
|
||||||
|
instruction: Instruction,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(&'a Value, &'a Value), VmError> {
|
||||||
|
let left = if instruction.b_is_constant() {
|
||||||
|
vm.get_constant(instruction.b(), position)?
|
||||||
|
} else {
|
||||||
|
vm.open_register(instruction.b(), position)?
|
||||||
|
};
|
||||||
|
let right = if instruction.c_is_constant() {
|
||||||
|
vm.get_constant(instruction.c(), position)?
|
||||||
|
} else {
|
||||||
|
vm.open_register(instruction.c(), position)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((left, right))
|
||||||
|
}
|
||||||
|
|
||||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"{} | {} | {} | {}",
|
"{} | {} | {} | {}",
|
||||||
@ -98,9 +116,9 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let boolean = instruction.b_as_boolean();
|
let boolean = instruction.b_as_boolean();
|
||||||
let jump = instruction.c_as_boolean();
|
let jump = instruction.c_as_boolean();
|
||||||
let boolean = ConcreteValue::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(boolean), position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -124,103 +142,96 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let start_register = instruction.b();
|
let start_register = instruction.b();
|
||||||
let mut list = Vec::new();
|
let item_type = (start_register..to_register)
|
||||||
|
.find_map(|register_index| {
|
||||||
|
if let Ok(value) = self.open_register(register_index, position) {
|
||||||
|
Some(value.r#type())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(Type::Any);
|
||||||
|
let value = Value::abstract_list(start_register, to_register, item_type);
|
||||||
|
|
||||||
for register_index in start_register..to_register {
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
let value = self.open_register(register_index, position)?;
|
|
||||||
|
|
||||||
list.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_register(to_register, Register::List(list), position)?;
|
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let function = Value::FunctionBorrowed(FunctionBorrowed::new(self.chunk));
|
let value = Value::function(
|
||||||
|
self.chunk.clone(),
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters: None,
|
||||||
|
return_type: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// self.set_register(to_register, Register::Value(function), position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
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.define_local(to_local, from_register)?;
|
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_register = self.local_definitions.get(&local_index).copied().ok_or(
|
let local = self.get_local(local_index, position)?;
|
||||||
VmError::UndefinedLocal {
|
|
||||||
local_index,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.set_register(
|
self.set_register(
|
||||||
to_register,
|
to_register,
|
||||||
Register::StackPointer(local_register),
|
Register::StackPointer(local.register_index),
|
||||||
position,
|
position,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let from_register = instruction.a();
|
let register = instruction.a();
|
||||||
let to_local = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
|
|
||||||
VmError::UndefinedLocal {
|
|
||||||
local_index: to_local,
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.set_register(
|
self.define_local(local_index, register, position)?;
|
||||||
local_register,
|
|
||||||
Register::StackPointer(from_register),
|
|
||||||
position,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let sum = left
|
let sum = left
|
||||||
.add(&right)
|
.add(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(sum), position)?;
|
self.set_register(to_register, Register::Value(sum), position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let difference = left
|
let difference = left
|
||||||
.subtract(&right)
|
.subtract(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(difference), position)?;
|
self.set_register(to_register, Register::Value(difference), position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let product = left
|
let product = left
|
||||||
.multiply(&right)
|
.multiply(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(product), position)?;
|
self.set_register(to_register, Register::Value(product), position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let quotient = left
|
let quotient = left
|
||||||
.divide(&right)
|
.divide(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(quotient), position)?;
|
self.set_register(to_register, Register::Value(quotient), position)?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let remainder = left
|
let remainder = left
|
||||||
.modulo(&right)
|
.modulo(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(remainder), position)?;
|
self.set_register(to_register, Register::Value(remainder), position)?;
|
||||||
@ -229,11 +240,11 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
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.open_register(register, position)?;
|
let value = self.open_register(register, position)?;
|
||||||
let boolean = if let Some(boolean) = value.as_boolean() {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
found: value.into_concrete(),
|
found: value.clone(),
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -249,26 +260,34 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
|
||||||
let equal_result = left
|
let equal_result = left
|
||||||
.equal(&right)
|
.equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result {
|
let boolean =
|
||||||
boolean
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
|
||||||
} else {
|
boolean
|
||||||
return Err(VmError::ExpectedBoolean {
|
} else {
|
||||||
found: equal_result.clone(),
|
return Err(VmError::ExpectedBoolean {
|
||||||
position,
|
found: equal_result.clone(),
|
||||||
});
|
position,
|
||||||
};
|
});
|
||||||
|
};
|
||||||
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if is_equal == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.get_instruction(self.ip, position)?.0;
|
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 {
|
||||||
|
self.ip + jump_distance as usize
|
||||||
|
} else {
|
||||||
|
self.ip - jump_distance as usize
|
||||||
|
};
|
||||||
|
|
||||||
self.jump(jump);
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
@ -277,26 +296,34 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
|
||||||
let less_result = left
|
let less_result = left
|
||||||
.less_than(&right)
|
.less_than(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result {
|
let boolean =
|
||||||
boolean
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result {
|
||||||
} else {
|
boolean
|
||||||
return Err(VmError::ExpectedBoolean {
|
} else {
|
||||||
found: less_result.clone(),
|
return Err(VmError::ExpectedBoolean {
|
||||||
position,
|
found: less_result.clone(),
|
||||||
});
|
position,
|
||||||
};
|
});
|
||||||
|
};
|
||||||
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if is_less_than == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.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 {
|
||||||
|
self.ip + jump_distance as usize
|
||||||
|
} else {
|
||||||
|
self.ip - jump_distance as usize
|
||||||
|
};
|
||||||
|
|
||||||
self.jump(jump);
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
@ -305,32 +332,43 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
|
||||||
let less_or_equal_result = left
|
let less_or_equal_result = left
|
||||||
.less_than_or_equal(&right)
|
.less_than_or_equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let is_less_than_or_equal =
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
|
||||||
if let ConcreteValue::Boolean(boolean) = less_or_equal_result {
|
less_or_equal_result
|
||||||
boolean
|
{
|
||||||
} else {
|
boolean
|
||||||
return Err(VmError::ExpectedBoolean {
|
} else {
|
||||||
found: less_or_equal_result.clone(),
|
return Err(VmError::ExpectedBoolean {
|
||||||
position,
|
found: less_or_equal_result.clone(),
|
||||||
});
|
position,
|
||||||
};
|
});
|
||||||
|
};
|
||||||
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if is_less_than_or_equal == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.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 {
|
||||||
|
self.ip + jump_distance as usize
|
||||||
|
} else {
|
||||||
|
self.ip - jump_distance as usize
|
||||||
|
};
|
||||||
|
|
||||||
self.jump(jump);
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value =
|
let value = if instruction.b_is_constant() {
|
||||||
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
self.get_constant(instruction.b(), position)?
|
||||||
|
} else {
|
||||||
|
self.open_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 })?;
|
||||||
@ -338,30 +376,42 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
self.set_register(instruction.a(), Register::Value(negated), position)?;
|
self.set_register(instruction.a(), Register::Value(negated), position)?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value =
|
let value = if instruction.b_is_constant() {
|
||||||
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
self.get_constant(instruction.b(), position)?
|
||||||
|
} else {
|
||||||
|
self.open_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_register(instruction.a(), Register::Value(not), position)?;
|
self.set_register(instruction.a(), Register::Value(not), position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => self.jump(instruction),
|
Operation::Jump => {
|
||||||
|
let jump_distance = instruction.b();
|
||||||
|
let is_positive = instruction.c_as_boolean();
|
||||||
|
let new_ip = if is_positive {
|
||||||
|
self.ip + jump_distance as usize
|
||||||
|
} else {
|
||||||
|
self.ip - jump_distance as usize - 1
|
||||||
|
};
|
||||||
|
self.ip = new_ip;
|
||||||
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
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.open_register(function_register, position)?;
|
let value = self.open_register(function_register, position)?.clone();
|
||||||
let function = if let Some(function) = value.as_function() {
|
let mut function =
|
||||||
function
|
if let Value::Concrete(ConcreteValue::Function(function)) = value {
|
||||||
} else {
|
function
|
||||||
return Err(VmError::ExpectedFunction {
|
} else {
|
||||||
found: value.into_concrete(),
|
return Err(VmError::ExpectedFunction {
|
||||||
position,
|
found: value,
|
||||||
});
|
position,
|
||||||
};
|
});
|
||||||
let mut function_vm = Vm::new(function.chunk(), Some(self));
|
};
|
||||||
|
let mut function_vm = Vm::new(function.chunk_mut(), Some(self));
|
||||||
let first_argument_index = function_register + 1;
|
let first_argument_index = function_register + 1;
|
||||||
|
|
||||||
for argument_index in
|
for argument_index in
|
||||||
@ -376,7 +426,7 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = function_vm.run()?;
|
let return_value = function_vm.run()?.cloned();
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
self.set_register(to_register, Register::Value(value), position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
@ -384,12 +434,12 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
}
|
}
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let native_function = NativeFunction::from(instruction.b());
|
let native_function = NativeFunction::from(instruction.b());
|
||||||
let return_value = native_function.call(self, instruction, position)?;
|
let return_value = native_function.call(instruction, self, position)?;
|
||||||
|
|
||||||
if let Some(concrete_value) = return_value {
|
if let Some(value) = return_value {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(concrete_value), position)?;
|
self.set_register(to_register, Register::Value(value), position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
@ -399,12 +449,13 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
return if let Some(register_index) = self.last_assigned_register {
|
let return_value = if let Some(register_index) = self.last_assigned_register {
|
||||||
self.open_register(register_index, position)
|
self.open_register(register_index, position)?
|
||||||
.map(|value| Some(value.into_concrete()))
|
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::StackUnderflow { position })
|
return Err(VmError::StackUnderflow { position });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return Ok(Some(return_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,149 +463,10 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_register(
|
|
||||||
&'stack self,
|
|
||||||
register_index: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<&'stack ConcreteValue, VmError> {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
let register =
|
|
||||||
self.stack
|
|
||||||
.get(register_index)
|
|
||||||
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
log::trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => Ok(value),
|
|
||||||
Register::List(list) => Ok(ConcreteValue::List(
|
|
||||||
list.into_iter()
|
|
||||||
.map(|concrete_value| (*concrete_value).clone())
|
|
||||||
.collect(),
|
|
||||||
)),
|
|
||||||
Register::StackPointer(register_index) => self.open_register(*register_index, position),
|
|
||||||
Register::ConstantPointer(constant_index) => {
|
|
||||||
self.get_constant(*constant_index, position)
|
|
||||||
}
|
|
||||||
Register::ParentStackPointer(register_index) => {
|
|
||||||
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.open_register(*register_index, position)
|
|
||||||
}
|
|
||||||
Register::ParentConstantPointer(constant_index) => {
|
|
||||||
let parent = self
|
|
||||||
.parent
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
let constant = parent.get_constant(*constant_index, position)?;
|
|
||||||
|
|
||||||
Ok(constant)
|
|
||||||
}
|
|
||||||
Register::Empty => Err(VmError::EmptyRegister {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_concrete_from_register(
|
|
||||||
&'stack self,
|
|
||||||
register_index: u8,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<ConcreteValue, VmError> {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
let register =
|
|
||||||
self.stack
|
|
||||||
.get(register_index)
|
|
||||||
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let value = match register {
|
|
||||||
Register::Value(concrete_value) => concrete_value.clone(),
|
|
||||||
Register::List(list) => {
|
|
||||||
let items = list.into_iter().map(|value| (*value).clone()).collect();
|
|
||||||
|
|
||||||
ConcreteValue::List(items)
|
|
||||||
}
|
|
||||||
Register::StackPointer(register_index) => {
|
|
||||||
self.get_concrete_from_register(*register_index, position)?
|
|
||||||
}
|
|
||||||
Register::ConstantPointer(constant_pointer) => {
|
|
||||||
self.get_constant(*constant_pointer, position)?.clone()
|
|
||||||
}
|
|
||||||
Register::ParentStackPointer(register_index) => {
|
|
||||||
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.get_concrete_from_register(*register_index, position)?
|
|
||||||
}
|
|
||||||
Register::ParentConstantPointer(constant_index) => {
|
|
||||||
let parent = self
|
|
||||||
.parent
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.get_constant(*constant_index, position)?.clone()
|
|
||||||
}
|
|
||||||
Register::Empty => {
|
|
||||||
return Err(VmError::EmptyRegister {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper for handling JUMP instructions
|
|
||||||
fn jump(&mut self, jump: Instruction) {
|
|
||||||
let jump_distance = jump.b();
|
|
||||||
let is_positive = jump.c_as_boolean();
|
|
||||||
let new_ip = if is_positive {
|
|
||||||
self.ip + jump_distance as usize
|
|
||||||
} else {
|
|
||||||
self.ip - jump_distance as usize - 1
|
|
||||||
};
|
|
||||||
self.ip = new_ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get a constant or register values
|
|
||||||
fn get_argument(
|
|
||||||
&'stack self,
|
|
||||||
index: u8,
|
|
||||||
is_constant: bool,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Value<'stack>, VmError> {
|
|
||||||
let argument = if is_constant {
|
|
||||||
self.get_constant(index, position)?.as_reference_value()
|
|
||||||
} else {
|
|
||||||
self.open_register(index, position)?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get two arguments for binary operations
|
|
||||||
fn get_arguments(
|
|
||||||
&'stack self,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(Value<'stack>, Value<'stack>), VmError> {
|
|
||||||
let left = self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
|
||||||
let right = self.get_argument(instruction.c(), instruction.c_is_constant(), position)?;
|
|
||||||
|
|
||||||
Ok((left, right))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_register(
|
fn set_register(
|
||||||
&mut self,
|
&mut self,
|
||||||
to_register: u8,
|
to_register: u8,
|
||||||
register: Register<'stack>,
|
register: Register,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(), VmError> {
|
) -> Result<(), VmError> {
|
||||||
self.last_assigned_register = Some(to_register);
|
self.last_assigned_register = Some(to_register);
|
||||||
@ -599,18 +511,145 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_constant(&self, index: u8, position: Span) -> Result<&'chunk ConcreteValue, VmError> {
|
fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_constant(index)
|
.get_constant(index)
|
||||||
.map_err(|error| VmError::Chunk { error, position })
|
.map_err(|error| VmError::Chunk { error, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
let register =
|
||||||
|
self.stack
|
||||||
|
.get(register_index)
|
||||||
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
||||||
|
index: register_index,
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => Ok(value),
|
||||||
|
Register::StackPointer(register_index) => self.open_register(*register_index, position),
|
||||||
|
Register::ConstantPointer(constant_index) => {
|
||||||
|
self.get_constant(*constant_index, position)
|
||||||
|
}
|
||||||
|
Register::ParentStackPointer(register_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
|
parent.open_register(*register_index, position)
|
||||||
|
}
|
||||||
|
Register::ParentConstantPointer(constant_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
|
parent.get_constant(*constant_index, position)
|
||||||
|
}
|
||||||
|
Register::Empty => Err(VmError::EmptyRegister {
|
||||||
|
index: register_index,
|
||||||
|
position,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_nonempty_registers(
|
||||||
|
&self,
|
||||||
|
register_index_range: Range<u8>,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<Vec<&Value>, VmError> {
|
||||||
|
let mut values = Vec::with_capacity(register_index_range.len());
|
||||||
|
|
||||||
|
for register_index in register_index_range.clone() {
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
let register = self.stack.get(register_index).ok_or_else(|| {
|
||||||
|
VmError::RegisterIndexOutOfBounds {
|
||||||
|
index: register_index,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let value = match register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::StackPointer(register_index) => {
|
||||||
|
self.open_register(*register_index, position)?
|
||||||
|
}
|
||||||
|
Register::ConstantPointer(constant_index) => {
|
||||||
|
self.get_constant(*constant_index, position)?
|
||||||
|
}
|
||||||
|
Register::ParentStackPointer(register_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
|
parent.open_register(*register_index, position)?
|
||||||
|
}
|
||||||
|
Register::ParentConstantPointer(constant_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(VmError::ExpectedParent { position })?;
|
||||||
|
|
||||||
|
parent.get_constant(*constant_index, position)?
|
||||||
|
}
|
||||||
|
Register::Empty => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() {
|
||||||
|
Err(VmError::EmptyRegisters {
|
||||||
|
indexes: register_index_range,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
||||||
let ip = self.ip;
|
self.chunk
|
||||||
|
.expect_not_poisoned()
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?;
|
||||||
|
|
||||||
self.ip += 1;
|
let max_ip = self.chunk.len() - 1;
|
||||||
|
|
||||||
self.get_instruction(ip, position)
|
if self.ip > max_ip {
|
||||||
|
return self.get_instruction(max_ip, position);
|
||||||
|
} else {
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_instruction(self.ip - 1, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define_local(
|
||||||
|
&mut self,
|
||||||
|
local_index: u8,
|
||||||
|
register_index: u8,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let local = self
|
||||||
|
.chunk
|
||||||
|
.get_local_mut(local_index)
|
||||||
|
.map_err(|error| VmError::Chunk { error, 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> {
|
||||||
|
self.chunk
|
||||||
|
.get_local(local_index)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_instruction(
|
fn get_instruction(
|
||||||
@ -622,45 +661,23 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
.get_instruction(index)
|
.get_instruction(index)
|
||||||
.map_err(|error| VmError::Chunk { error, position })
|
.map_err(|error| VmError::Chunk { error, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
|
|
||||||
log::debug!("Define local L{}", local_index);
|
|
||||||
|
|
||||||
self.local_definitions.insert(local_index, register_index);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum Register<'stack> {
|
enum Register {
|
||||||
Empty,
|
Empty,
|
||||||
Value(ConcreteValue),
|
Value(Value),
|
||||||
List(Vec<&'stack ConcreteValue>),
|
|
||||||
StackPointer(u8),
|
StackPointer(u8),
|
||||||
ConstantPointer(u8),
|
ConstantPointer(u8),
|
||||||
ParentStackPointer(u8),
|
ParentStackPointer(u8),
|
||||||
ParentConstantPointer(u8),
|
ParentConstantPointer(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stack> Display for Register<'stack> {
|
impl Display for Register {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => write!(f, "empty"),
|
Self::Empty => write!(f, "empty"),
|
||||||
Self::Value(value) => write!(f, "{}", value),
|
Self::Value(value) => write!(f, "{}", value),
|
||||||
Self::List(values) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (index, value) in values.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{}", value)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
Self::StackPointer(index) => write!(f, "R{}", index),
|
Self::StackPointer(index) => write!(f, "R{}", index),
|
||||||
Self::ConstantPointer(index) => write!(f, "C{}", index),
|
Self::ConstantPointer(index) => write!(f, "C{}", index),
|
||||||
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
|
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
|
||||||
@ -672,52 +689,23 @@ impl<'stack> Display for Register<'stack> {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
// Stack errors
|
// Stack errors
|
||||||
StackOverflow {
|
StackOverflow { position: Span },
|
||||||
position: Span,
|
StackUnderflow { position: Span },
|
||||||
},
|
|
||||||
StackUnderflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Register errors
|
// Register errors
|
||||||
EmptyRegister {
|
EmptyRegister { index: usize, position: Span },
|
||||||
index: usize,
|
EmptyRegisters { indexes: Range<u8>, position: Span },
|
||||||
position: Span,
|
RegisterIndexOutOfBounds { index: usize, position: Span },
|
||||||
},
|
|
||||||
RegisterIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Local errors
|
|
||||||
UndefinedLocal {
|
|
||||||
local_index: u8,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Execution errors
|
// Execution errors
|
||||||
ExpectedBoolean {
|
ExpectedBoolean { found: Value, position: Span },
|
||||||
found: ConcreteValue,
|
ExpectedFunction { found: Value, position: Span },
|
||||||
position: Span,
|
ExpectedParent { position: Span },
|
||||||
},
|
|
||||||
ExpectedFunction {
|
|
||||||
found: ConcreteValue,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedParent {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
Chunk {
|
Chunk { error: ChunkError, position: Span },
|
||||||
error: ChunkError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
NativeFunction(NativeFunctionError),
|
NativeFunction(NativeFunctionError),
|
||||||
Value {
|
Value { error: ValueError, position: Span },
|
||||||
error: ValueError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnotatedError for VmError {
|
impl AnnotatedError for VmError {
|
||||||
@ -729,6 +717,7 @@ impl AnnotatedError for VmError {
|
|||||||
match self {
|
match self {
|
||||||
Self::Chunk { .. } => "Chunk error",
|
Self::Chunk { .. } => "Chunk error",
|
||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
|
Self::EmptyRegisters { .. } => "Empty registers",
|
||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
Self::ExpectedParent { .. } => "Expected parent",
|
Self::ExpectedParent { .. } => "Expected parent",
|
||||||
@ -736,7 +725,6 @@ impl AnnotatedError for VmError {
|
|||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
Self::StackUnderflow { .. } => "Stack underflow",
|
Self::StackUnderflow { .. } => "Stack underflow",
|
||||||
Self::UndefinedLocal { .. } => "Undefined local",
|
|
||||||
Self::Value { .. } => "Value error",
|
Self::Value { .. } => "Value error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -745,8 +733,11 @@ impl AnnotatedError for VmError {
|
|||||||
match self {
|
match self {
|
||||||
Self::Chunk { error, .. } => Some(error.to_string()),
|
Self::Chunk { error, .. } => Some(error.to_string()),
|
||||||
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
||||||
|
Self::EmptyRegisters { indexes: range, .. } => Some(format!(
|
||||||
|
"Registers R{} to R{} are empty",
|
||||||
|
range.start, range.end
|
||||||
|
)),
|
||||||
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"))
|
||||||
}
|
}
|
||||||
@ -760,6 +751,7 @@ impl AnnotatedError for VmError {
|
|||||||
match self {
|
match self {
|
||||||
Self::Chunk { position, .. } => *position,
|
Self::Chunk { position, .. } => *position,
|
||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
|
Self::EmptyRegisters { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::ExpectedParent { position } => *position,
|
Self::ExpectedParent { position } => *position,
|
||||||
@ -767,7 +759,6 @@ impl AnnotatedError for VmError {
|
|||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
Self::StackUnderflow { position } => *position,
|
Self::StackUnderflow { position } => *position,
|
||||||
Self::UndefinedLocal { position, .. } => *position,
|
|
||||||
Self::Value { position, .. } => *position,
|
Self::Value { position, .. } => *position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,12 @@ fn constant() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(0, 2)),
|
(Instruction::load_constant(0, 0, false), Span(0, 2)),
|
||||||
(Instruction::r#return(true), Span(2, 2))
|
(Instruction::r#return(true), Span(2, 2))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::Primitive(Primitive::Integer(42))],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -57,14 +57,10 @@ fn parentheses_precedence() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(11, 11)),
|
(Instruction::r#return(true), Span(11, 11)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||||
ValueOwned::integer(1),
|
|
||||||
ValueOwned::integer(2),
|
|
||||||
ValueOwned::integer(3)
|
|
||||||
],
|
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(9))));
|
assert_eq!(run(source), Ok(Some(Value::integer(9))));
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ fn equal() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -48,12 +48,12 @@ fn greater() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -76,12 +76,12 @@ fn greater_than_or_equal() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -104,12 +104,12 @@ fn less_than() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -132,12 +132,12 @@ fn less_than_or_equal() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -160,10 +160,10 @@ fn not_equal() {
|
|||||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||||
(Instruction::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,21 @@ fn equality_assignment_long() {
|
|||||||
(Instruction::get_local(1, 0), Span(43, 44)),
|
(Instruction::get_local(1, 0), Span(43, 44)),
|
||||||
(Instruction::r#return(true), Span(44, 44)),
|
(Instruction::r#return(true), Span(44, 44)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(4), ValueOwned::string("a")],
|
vec![Value::integer(4), Value::string("a")],
|
||||||
vec![Local::new(1, Type::Boolean, false, Scope::default(),)]
|
vec![Local::new(
|
||||||
|
1,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Scope {
|
||||||
|
depth: 0,
|
||||||
|
block_index: 0
|
||||||
|
},
|
||||||
|
0
|
||||||
|
)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -52,12 +61,12 @@ fn equality_assignment_short() {
|
|||||||
(Instruction::get_local(1, 0), Span(15, 16)),
|
(Instruction::get_local(1, 0), Span(15, 16)),
|
||||||
(Instruction::r#return(true), Span(16, 16)),
|
(Instruction::r#return(true), Span(16, 16)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(4), ValueOwned::string("a")],
|
vec![Value::integer(4), Value::string("a")],
|
||||||
vec![Local::new(1, Type::Boolean, false, Scope::default())]
|
vec![Local::new(1, None, false, Scope::default(), 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -104,18 +113,18 @@ fn if_else_assigment_false() {
|
|||||||
(Instruction::r#return(true), Span(149, 149)),
|
(Instruction::r#return(true), Span(149, 149)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(42),
|
Value::integer(42),
|
||||||
ValueOwned::string("a")
|
Value::string("a")
|
||||||
],
|
],
|
||||||
vec![Local::new(5, Type::Integer, false, Scope::default())]
|
vec![Local::new(5, None, false, Scope::default(), 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -162,18 +171,18 @@ fn if_else_assigment_true() {
|
|||||||
(Instruction::r#return(true), Span(149, 149)),
|
(Instruction::r#return(true), Span(149, 149)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(42),
|
Value::integer(42),
|
||||||
ValueOwned::string("a")
|
Value::string("a")
|
||||||
],
|
],
|
||||||
vec![Local::new(5, Type::Integer, false, Scope::default())]
|
vec![Local::new(5, None, false, Scope::default(), 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -210,10 +219,10 @@ fn if_else_complex() {
|
|||||||
(Instruction::r#return(false), Span(95, 95)),
|
(Instruction::r#return(false), Span(95, 95)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
],
|
],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
@ -275,26 +284,26 @@ fn if_else_complex() {
|
|||||||
// (Instruction::r#return(true), Span(146, 146)),
|
// (Instruction::r#return(true), Span(146, 146)),
|
||||||
// ],
|
// ],
|
||||||
// vec![
|
// vec![
|
||||||
// ValueOwned::integer(0),
|
// Value::integer(0),
|
||||||
// ValueOwned::integer(1),
|
// Value::integer(1),
|
||||||
// ValueOwned::integer(0),
|
// Value::integer(0),
|
||||||
// ValueOwned::integer(2),
|
// Value::integer(2),
|
||||||
// ValueOwned::integer(1),
|
// Value::integer(1),
|
||||||
// ValueOwned::integer(0),
|
// Value::integer(0),
|
||||||
// ValueOwned::integer(3),
|
// Value::integer(3),
|
||||||
// ValueOwned::integer(3),
|
// Value::integer(3),
|
||||||
// ValueOwned::integer(4)
|
// Value::integer(4)
|
||||||
// ],
|
// ],
|
||||||
// vec![]
|
// vec![]
|
||||||
// ))
|
// ))
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// assert_eq!(run(source), Ok(Some(ValueOwned::integer(4))));
|
// assert_eq!(run(source), Ok(Some(Value::integer(4))));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_false() {
|
fn if_else_false() {
|
||||||
let source = "if 1 == 2 { panic(); 0 } else { 42 }";
|
let source = "if 1 == 2 { panic() } else { 42 }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compile(source),
|
compile(source),
|
||||||
@ -316,21 +325,17 @@ fn if_else_false() {
|
|||||||
(Instruction::r#move(1, 0), Span(33, 33)),
|
(Instruction::r#move(1, 0), Span(33, 33)),
|
||||||
(Instruction::r#return(true), Span(33, 33)),
|
(Instruction::r#return(true), Span(33, 33)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(1), Value::integer(2), Value::integer(42)],
|
||||||
ValueOwned::integer(1),
|
|
||||||
ValueOwned::integer(2),
|
|
||||||
ValueOwned::integer(42)
|
|
||||||
],
|
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_true() {
|
fn if_else_true() {
|
||||||
let source = "if 1 == 1 { 42 } else { panic(); 0 }";
|
let source = "if 1 == 1 { 42 } else { panic() }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compile(source),
|
compile(source),
|
||||||
@ -352,17 +357,17 @@ fn if_else_true() {
|
|||||||
(Instruction::r#move(1, 0), Span(33, 33)),
|
(Instruction::r#move(1, 0), Span(33, 33)),
|
||||||
(Instruction::r#return(true), Span(33, 33))
|
(Instruction::r#return(true), Span(33, 33))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(42)],
|
vec![Value::integer(1), Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_false() {
|
fn if_false() {
|
||||||
let source = "if 1 == 2 { panic() }";
|
let source = "if 1 == 2 { 2 }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compile(source),
|
compile(source),
|
||||||
@ -376,13 +381,10 @@ fn if_false() {
|
|||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(10, 11)),
|
(Instruction::jump(1, true), Span(10, 11)),
|
||||||
(
|
(Instruction::load_constant(0, 1, false), Span(12, 13)),
|
||||||
Instruction::call_native(0, NativeFunction::Panic, 0),
|
(Instruction::r#return(false), Span(15, 15))
|
||||||
Span(12, 19)
|
|
||||||
),
|
|
||||||
(Instruction::r#return(false), Span(21, 21))
|
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -392,7 +394,7 @@ fn if_false() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_true() {
|
fn if_true() {
|
||||||
let source = "if 1 == 1 { panic() }";
|
let source = "if 1 == 1 { 2 }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compile(source),
|
compile(source),
|
||||||
@ -406,25 +408,13 @@ fn if_true() {
|
|||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(10, 11)),
|
(Instruction::jump(1, true), Span(10, 11)),
|
||||||
(
|
(Instruction::load_constant(0, 1, false), Span(12, 13)),
|
||||||
Instruction::call_native(0, NativeFunction::Panic, 0),
|
(Instruction::r#return(false), Span(15, 15))
|
||||||
Span(12, 19)
|
|
||||||
),
|
|
||||||
(Instruction::r#return(false), Span(21, 21))
|
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(run(source), Ok(None));
|
||||||
run(source),
|
|
||||||
Err(DustError::Runtime {
|
|
||||||
error: VmError::NativeFunction(NativeFunctionError::Panic {
|
|
||||||
message: None,
|
|
||||||
position: Span(12, 19)
|
|
||||||
}),
|
|
||||||
source
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,25 @@ fn function() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(ValueOwned::function(Chunk::with_data(
|
Ok(Some(Value::function(
|
||||||
None,
|
Chunk::with_data(
|
||||||
vec![
|
None,
|
||||||
(Instruction::add(2, 0, 1), Span(30, 31)),
|
vec![
|
||||||
(Instruction::r#return(true), Span(35, 35)),
|
(Instruction::add(2, 0, 1), Span(30, 31)),
|
||||||
],
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
vec![ValueOwned::string("a"), ValueOwned::string("b"),],
|
],
|
||||||
vec![
|
vec![Value::string("a"), Value::string("b"),],
|
||||||
Local::new(0, Type::Integer, false, Scope::default()),
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::default())
|
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
|
||||||
]
|
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
|
||||||
))))
|
]
|
||||||
|
),
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
|
||||||
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
|
}
|
||||||
|
)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,26 +44,33 @@ fn function_call() {
|
|||||||
(Instruction::r#return(true), Span(41, 41)),
|
(Instruction::r#return(true), Span(41, 41)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::function(Chunk::with_data(
|
Value::function(
|
||||||
None,
|
Chunk::with_data(
|
||||||
vec![
|
None,
|
||||||
(Instruction::add(2, 0, 1), Span(30, 31)),
|
vec![
|
||||||
(Instruction::r#return(true), Span(35, 36)),
|
(Instruction::add(2, 0, 1), Span(30, 31)),
|
||||||
],
|
(Instruction::r#return(true), Span(35, 36)),
|
||||||
vec![ValueOwned::string("a"), ValueOwned::string("b"),],
|
],
|
||||||
vec![
|
vec![Value::string("a"), Value::string("b"),],
|
||||||
Local::new(0, Type::Integer, false, Scope::default()),
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::default())
|
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
|
||||||
]
|
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
|
||||||
)),
|
]
|
||||||
ValueOwned::integer(1),
|
),
|
||||||
ValueOwned::integer(2)
|
FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
|
||||||
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2)
|
||||||
],
|
],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -73,30 +87,38 @@ fn function_declaration() {
|
|||||||
(Instruction::r#return(false), Span(40, 40))
|
(Instruction::r#return(false), Span(40, 40))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::string("add"),
|
Value::string("add"),
|
||||||
ValueOwned::function(Chunk::with_data(
|
Value::function(
|
||||||
None,
|
Chunk::with_data(
|
||||||
vec![
|
None,
|
||||||
(Instruction::add(2, 0, 1), Span(35, 36)),
|
vec![
|
||||||
(Instruction::r#return(true), Span(40, 40)),
|
(Instruction::add(2, 0, 1), Span(35, 36)),
|
||||||
],
|
(Instruction::r#return(true), Span(40, 40)),
|
||||||
vec![ValueOwned::string("a"), ValueOwned::string("b")],
|
],
|
||||||
vec![
|
vec![Value::string("a"), Value::string("b")],
|
||||||
Local::new(0, Type::Integer, false, Scope::default()),
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::default())
|
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
|
||||||
]
|
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
|
||||||
),)
|
]
|
||||||
|
),
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
|
||||||
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
vec![Local::new(
|
vec![Local::new(
|
||||||
0,
|
0,
|
||||||
Type::Function(FunctionType {
|
Some(Type::Function(FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
|
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
|
||||||
return_type: Box::new(Type::Integer),
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
}),
|
})),
|
||||||
false,
|
false,
|
||||||
Scope::default(),
|
Scope::default(),
|
||||||
)],
|
0
|
||||||
|
),],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ fn empty_list() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::list([]))));
|
assert_eq!(run(source), Ok(Some(Value::abstract_list(0, 0, Type::Any))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -35,22 +35,14 @@ fn list() {
|
|||||||
(Instruction::load_list(3, 0), Span(0, 9)),
|
(Instruction::load_list(3, 0), Span(0, 9)),
|
||||||
(Instruction::r#return(true), Span(9, 9)),
|
(Instruction::r#return(true), Span(9, 9)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||||
ValueOwned::integer(1),
|
|
||||||
ValueOwned::integer(2),
|
|
||||||
ValueOwned::integer(3)
|
|
||||||
],
|
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(ValueOwned::list([
|
Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
|
||||||
ValueOwned::integer(1),
|
|
||||||
ValueOwned::integer(2),
|
|
||||||
ValueOwned::integer(3)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +74,11 @@ fn list_with_complex_expression() {
|
|||||||
(Instruction::r#return(true), Span(18, 18)),
|
(Instruction::r#return(true), Span(18, 18)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
ValueOwned::integer(5)
|
Value::integer(5)
|
||||||
],
|
],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
@ -94,10 +86,7 @@ fn list_with_complex_expression() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(ValueOwned::list([
|
Ok(Some(Value::abstract_list(0, 4, Type::Integer)))
|
||||||
ValueOwned::integer(0),
|
|
||||||
ValueOwned::integer(4)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,10 +111,10 @@ fn list_with_simple_expression() {
|
|||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
],
|
],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
@ -133,9 +122,6 @@ fn list_with_simple_expression() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(source),
|
run(source),
|
||||||
Ok(Some(ValueOwned::list([
|
Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
|
||||||
ValueOwned::integer(0),
|
|
||||||
ValueOwned::integer(3)
|
|
||||||
])))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ fn and() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -43,7 +43,7 @@ fn or() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -65,13 +65,13 @@ fn variable_and() {
|
|||||||
(Instruction::get_local(3, 1), Span(34, 35)),
|
(Instruction::get_local(3, 1), Span(34, 35)),
|
||||||
(Instruction::r#return(true), Span(35, 35)),
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::string("a"), ValueOwned::string("b"),],
|
vec![Value::string("a"), Value::string("b"),],
|
||||||
vec![
|
vec![
|
||||||
Local::new(0, Type::Boolean, false, Scope::default()),
|
Local::new(0, None, false, Scope::default(), 0),
|
||||||
Local::new(1, Type::Boolean, false, Scope::default()),
|
Local::new(1, None, false, Scope::default(), 1),
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
@ -16,20 +16,20 @@ fn r#while() {
|
|||||||
Span(23, 24)
|
Span(23, 24)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(41, 42)),
|
(Instruction::jump(2, true), Span(41, 42)),
|
||||||
(*Instruction::add(0, 0, 3).set_c_is_constant(), Span(35, 36)),
|
(*Instruction::add(0, 0, 3).set_c_is_constant(), Span(39, 40)),
|
||||||
(Instruction::jump(3, false), Span(41, 42)),
|
(Instruction::jump(3, false), Span(41, 42)),
|
||||||
(Instruction::get_local(1, 0), Span(41, 42)),
|
(Instruction::get_local(1, 0), Span(41, 42)),
|
||||||
(Instruction::r#return(true), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(0),
|
Value::integer(0),
|
||||||
ValueOwned::string("x"),
|
Value::string("x"),
|
||||||
ValueOwned::integer(5),
|
Value::integer(5),
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
],
|
],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
vec![Local::new(1, None, true, Scope::default(), 0),]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(5))));
|
assert_eq!(run(source), Ok(Some(Value::integer(5))));
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,12 @@ fn add() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -40,16 +40,12 @@ fn add_assign() {
|
|||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(1), Value::string("a"), Value::integer(2)],
|
||||||
ValueOwned::integer(1),
|
vec![Local::new(1, None, true, Scope::default(), 0)]
|
||||||
ValueOwned::string("a"),
|
|
||||||
ValueOwned::integer(2)
|
|
||||||
],
|
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -101,12 +97,12 @@ fn divide() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(2)],
|
vec![Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -127,12 +123,12 @@ fn divide_assign() {
|
|||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(2), ValueOwned::string("a")],
|
vec![Value::integer(2), Value::string("a")],
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
vec![Local::new(1, None, true, Scope::default(), 0)]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -180,17 +176,17 @@ fn math_operator_precedence() {
|
|||||||
(Instruction::r#return(true), Span(17, 17)),
|
(Instruction::r#return(true), Span(17, 17)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::integer(3),
|
Value::integer(3),
|
||||||
ValueOwned::integer(4),
|
Value::integer(4),
|
||||||
ValueOwned::integer(5),
|
Value::integer(5),
|
||||||
],
|
],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -210,12 +206,12 @@ fn multiply() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(2))));
|
assert_eq!(run(source), Ok(Some(Value::integer(2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -236,16 +232,12 @@ fn multiply_assign() {
|
|||||||
(Instruction::get_local(1, 0), Span(22, 23)),
|
(Instruction::get_local(1, 0), Span(22, 23)),
|
||||||
(Instruction::r#return(true), Span(23, 23))
|
(Instruction::r#return(true), Span(23, 23))
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(2), Value::string("a"), Value::integer(3)],
|
||||||
ValueOwned::integer(2),
|
vec![Local::new(1, None, true, Scope::default(), 0),]
|
||||||
ValueOwned::string("a"),
|
|
||||||
ValueOwned::integer(3)
|
|
||||||
],
|
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(6))));
|
assert_eq!(run(source), Ok(Some(Value::integer(6))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -281,12 +273,12 @@ fn subtract() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(1), ValueOwned::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(-1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(-1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -307,16 +299,12 @@ fn subtract_assign() {
|
|||||||
(Instruction::get_local(1, 0), Span(24, 25)),
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(42), Value::string("x"), Value::integer(2)],
|
||||||
ValueOwned::integer(42),
|
vec![Local::new(1, None, true, Scope::default(), 0)]
|
||||||
ValueOwned::string("x"),
|
|
||||||
ValueOwned::integer(2)
|
|
||||||
],
|
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(40))));
|
assert_eq!(run(source), Ok(Some(Value::integer(40))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -17,10 +17,7 @@ fn panic() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(27, 27))
|
(Instruction::r#return(true), Span(27, 27))
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::string("Goodbye world!"), Value::integer(42)],
|
||||||
ValueOwned::string("Goodbye world!"),
|
|
||||||
ValueOwned::integer(42)
|
|
||||||
],
|
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -53,10 +50,10 @@ fn to_string() {
|
|||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(13, 13))
|
(Instruction::r#return(true), Span(13, 13))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::string("42"))))
|
assert_eq!(run(source), Ok(Some(Value::string("42"))))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ fn allow_access_to_parent_scope() {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
|
assert_eq!(run(source), Ok(Some(Value::integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -44,22 +44,22 @@ fn block_scope() {
|
|||||||
(Instruction::r#return(false), Span(165, 165))
|
(Instruction::r#return(false), Span(165, 165))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(0),
|
Value::integer(0),
|
||||||
ValueOwned::string("a"),
|
Value::string("a"),
|
||||||
ValueOwned::integer(42),
|
Value::integer(42),
|
||||||
ValueOwned::string("b"),
|
Value::string("b"),
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::string("c"),
|
Value::string("c"),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::string("d"),
|
Value::string("d"),
|
||||||
ValueOwned::string("e"),
|
Value::string("e"),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::new(0, 0)),
|
Local::new(1, None, false, Scope::new(0, 0), 0),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 1)),
|
Local::new(3, None, false, Scope::new(1, 1), 1),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 2)),
|
Local::new(5, None, false, Scope::new(2, 2), 2),
|
||||||
Local::new(7, Type::Integer, false, Scope::new(1, 1)),
|
Local::new(7, None, false, Scope::new(1, 1), 3),
|
||||||
Local::new(8, Type::Integer, false, Scope::new(0, 0)),
|
Local::new(8, None, false, Scope::new(0, 0), 4),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -115,27 +115,27 @@ fn multiple_block_scopes() {
|
|||||||
(Instruction::r#return(false), Span(307, 307))
|
(Instruction::r#return(false), Span(307, 307))
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
ValueOwned::integer(0),
|
Value::integer(0),
|
||||||
ValueOwned::string("a"),
|
Value::string("a"),
|
||||||
ValueOwned::integer(42),
|
Value::integer(42),
|
||||||
ValueOwned::string("b"),
|
Value::string("b"),
|
||||||
ValueOwned::integer(1),
|
Value::integer(1),
|
||||||
ValueOwned::string("c"),
|
Value::string("c"),
|
||||||
ValueOwned::integer(2),
|
Value::integer(2),
|
||||||
ValueOwned::string("d"),
|
Value::string("d"),
|
||||||
ValueOwned::string("q"),
|
Value::string("q"),
|
||||||
ValueOwned::string("e"),
|
Value::string("e"),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Local::new(1, Type::Integer, false, Scope::new(0, 0)),
|
Local::new(1, None, false, Scope::new(0, 0), 0),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 1)),
|
Local::new(3, None, false, Scope::new(1, 1), 1),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 2)),
|
Local::new(5, None, false, Scope::new(2, 2), 2),
|
||||||
Local::new(7, Type::Integer, false, Scope::new(1, 1)),
|
Local::new(7, None, false, Scope::new(1, 1), 3),
|
||||||
Local::new(8, Type::Integer, false, Scope::new(0, 0)),
|
Local::new(8, None, false, Scope::new(0, 0), 4),
|
||||||
Local::new(3, Type::Integer, false, Scope::new(1, 3)),
|
Local::new(3, None, false, Scope::new(1, 3), 5),
|
||||||
Local::new(5, Type::Integer, false, Scope::new(2, 4)),
|
Local::new(5, None, false, Scope::new(2, 4), 6),
|
||||||
Local::new(7, Type::Integer, false, Scope::new(1, 3)),
|
Local::new(7, None, false, Scope::new(1, 3), 7),
|
||||||
Local::new(9, Type::Integer, false, Scope::new(0, 0)),
|
Local::new(9, None, false, Scope::new(0, 0), 8),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
@ -12,12 +12,12 @@ fn negate() {
|
|||||||
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
|
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(-42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(-42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -38,5 +38,5 @@ fn not() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
|
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ fn define_local() {
|
|||||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
(Instruction::r#return(false), Span(11, 11))
|
(Instruction::r#return(false), Span(11, 11))
|
||||||
],
|
],
|
||||||
vec![ValueOwned::integer(42), ValueOwned::string("x")],
|
vec![Value::integer(42), Value::string("x")],
|
||||||
vec![Local::new(1, Type::Integer, false, Scope::default())]
|
vec![Local::new(1, None, false, Scope::default(), 0)]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -54,14 +54,10 @@ fn set_local() {
|
|||||||
(Instruction::get_local(2, 0), Span(24, 25)),
|
(Instruction::get_local(2, 0), Span(24, 25)),
|
||||||
(Instruction::r#return(true), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![Value::integer(41), Value::string("x"), Value::integer(42)],
|
||||||
ValueOwned::integer(41),
|
vec![Local::new(1, None, true, Scope::default(), 0)]
|
||||||
ValueOwned::string("x"),
|
|
||||||
ValueOwned::integer(42)
|
|
||||||
],
|
|
||||||
vec![Local::new(1, Type::Integer, true, Scope::default())]
|
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
|
assert_eq!(run(source), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
use std::{
|
use std::{fs::read_to_string, io::Write};
|
||||||
fs::read_to_string,
|
|
||||||
io::{stdout, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use dust_lang::{compile, format, lex, output_token_list, run};
|
use dust_lang::{compile, format, run};
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@ -14,7 +11,7 @@ struct Cli {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
command: Option<String>,
|
command: Option<String>,
|
||||||
|
|
||||||
/// Whether to output formatted source code instead of running the program
|
/// Whether to output formatted source code
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
format: bool,
|
format: bool,
|
||||||
|
|
||||||
@ -26,7 +23,7 @@ struct Cli {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
format_colored: Option<bool>,
|
format_colored: Option<bool>,
|
||||||
|
|
||||||
/// Whether to output the disassembled chunk instead of running the program
|
/// Whether to output the disassembled chunk
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
parse: bool,
|
parse: bool,
|
||||||
|
|
||||||
@ -34,10 +31,6 @@ struct Cli {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
style_disassembly: Option<bool>,
|
style_disassembly: Option<bool>,
|
||||||
|
|
||||||
/// Whether to tokenize the source code instead of running the program
|
|
||||||
#[arg(short, long)]
|
|
||||||
tokenize: bool,
|
|
||||||
|
|
||||||
/// Log level
|
/// Log level
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
log: Option<LevelFilter>,
|
log: Option<LevelFilter>,
|
||||||
@ -85,11 +78,11 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if args.format {
|
if args.format {
|
||||||
log::info!("Formatting source");
|
|
||||||
|
|
||||||
let line_numbers = args.format_line_numbers.unwrap_or(true);
|
let line_numbers = args.format_line_numbers.unwrap_or(true);
|
||||||
let colored = args.format_colored.unwrap_or(true);
|
let colored = args.format_colored.unwrap_or(true);
|
||||||
|
|
||||||
|
log::info!("Formatting source");
|
||||||
|
|
||||||
match format(source, line_numbers, colored) {
|
match format(source, line_numbers, colored) {
|
||||||
Ok(formatted) => println!("{}", formatted),
|
Ok(formatted) => println!("{}", formatted),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@ -98,20 +91,11 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.tokenize {
|
|
||||||
log::info!("Tokenizing source");
|
|
||||||
|
|
||||||
match lex(source) {
|
|
||||||
Ok(tokens) => output_token_list(&tokens, &mut stdout()),
|
|
||||||
Err(error) => eprintln!("{}", error.report()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.parse {
|
if args.parse {
|
||||||
log::info!("Parsing source");
|
|
||||||
|
|
||||||
let styled = args.style_disassembly.unwrap_or(true);
|
let styled = args.style_disassembly.unwrap_or(true);
|
||||||
|
|
||||||
|
log::info!("Parsing source");
|
||||||
|
|
||||||
match compile(source) {
|
match compile(source) {
|
||||||
Ok(chunk) => {
|
Ok(chunk) => {
|
||||||
let disassembly = chunk
|
let disassembly = chunk
|
||||||
@ -128,7 +112,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.format || args.tokenize || args.parse {
|
if args.format || args.parse {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user