Begin VM conversion to multi-thread
This commit is contained in:
parent
b59d51d620
commit
bd590e0643
@ -18,7 +18,7 @@ serde_json = "1.0.117"
|
|||||||
getrandom = { version = "0.2", features = [
|
getrandom = { version = "0.2", features = [
|
||||||
"js",
|
"js",
|
||||||
] } # Indirect dependency, for wasm builds
|
] } # Indirect dependency, for wasm builds
|
||||||
smallvec = { version = "1.13.2", features = ["serde"] }
|
smallvec = { version = "1.13.2", features = ["const_generics", "serde"] }
|
||||||
smartstring = { version = "1.0.1", features = [
|
smartstring = { version = "1.0.1", features = [
|
||||||
"serde",
|
"serde",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
|
@ -46,7 +46,7 @@ use std::{
|
|||||||
|
|
||||||
use colored::{ColoredString, Colorize};
|
use colored::{ColoredString, Colorize};
|
||||||
|
|
||||||
use crate::{value::ConcreteValue, Chunk, Local, Value};
|
use crate::{Chunk, Local};
|
||||||
|
|
||||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
||||||
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 24)];
|
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 24)];
|
||||||
@ -93,7 +93,8 @@ pub struct Disassembler<'a, W> {
|
|||||||
// Options
|
// Options
|
||||||
style: bool,
|
style: bool,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
output_width: usize,
|
width: usize,
|
||||||
|
show_type: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W: Write> Disassembler<'a, W> {
|
impl<'a, W: Write> Disassembler<'a, W> {
|
||||||
@ -104,7 +105,8 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
source: None,
|
source: None,
|
||||||
style: false,
|
style: false,
|
||||||
indent: 0,
|
indent: 0,
|
||||||
output_width: Self::content_length(),
|
width: Self::content_length(),
|
||||||
|
show_type: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +122,18 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn width(mut self, width: usize) -> Self {
|
||||||
|
self.width = width.max(Self::content_length());
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_type(mut self, show_type: bool) -> Self {
|
||||||
|
self.show_type = show_type;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn indent(mut self, indent: usize) -> Self {
|
fn indent(mut self, indent: usize) -> Self {
|
||||||
self.indent = indent;
|
self.indent = indent;
|
||||||
|
|
||||||
@ -135,7 +149,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
fn line_length(&self) -> usize {
|
fn line_length(&self) -> usize {
|
||||||
let indentation_length = INDENTATION.chars().count();
|
let indentation_length = INDENTATION.chars().count();
|
||||||
|
|
||||||
self.output_width + (indentation_length * self.indent) + 2 // Left and right border
|
self.width + (indentation_length * self.indent) + 2 // Left and right border
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_char(&mut self, c: char) -> Result<(), io::Error> {
|
fn write_char(&mut self, c: char) -> Result<(), io::Error> {
|
||||||
@ -159,10 +173,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
add_border: bool,
|
add_border: bool,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
let (line_content, overflow) = {
|
let (line_content, overflow) = {
|
||||||
if text.len() > self.output_width {
|
if text.len() > self.width {
|
||||||
let split_index = text
|
let split_index = text
|
||||||
.char_indices()
|
.char_indices()
|
||||||
.nth(self.output_width)
|
.nth(self.width)
|
||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
.unwrap_or_else(|| text.len());
|
.unwrap_or_else(|| text.len());
|
||||||
|
|
||||||
@ -226,15 +240,15 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_centered_with_border(&mut self, text: &str) -> Result<(), io::Error> {
|
fn write_center_border(&mut self, text: &str) -> Result<(), io::Error> {
|
||||||
self.write_content(text, true, false, false, true)
|
self.write_content(text, true, false, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_centered_with_border_dim(&mut self, text: &str) -> Result<(), io::Error> {
|
fn write_center_border_dim(&mut self, text: &str) -> Result<(), io::Error> {
|
||||||
self.write_content(text, true, false, self.style, true)
|
self.write_content(text, true, false, self.style, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_centered_with_border_bold(&mut self, text: &str) -> Result<(), io::Error> {
|
fn write_center_border_bold(&mut self, text: &str) -> Result<(), io::Error> {
|
||||||
self.write_content(text, true, self.style, false, true)
|
self.write_content(text, true, self.style, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +263,8 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self.write_char(border[1])?;
|
self.write_char(border[1])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_char(border[2])
|
self.write_char(border[2]);
|
||||||
|
self.write_char('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_instruction_section(&mut self) -> Result<(), io::Error> {
|
fn write_instruction_section(&mut self) -> Result<(), io::Error> {
|
||||||
@ -260,21 +275,26 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
column_name_line.push('│');
|
column_name_line.push('│');
|
||||||
self.write_centered_with_border_bold("Instructions")?;
|
self.write_center_border_bold("Instructions")?;
|
||||||
self.write_centered_with_border(INSTRUCTION_BORDERS[0])?;
|
self.write_center_border(INSTRUCTION_BORDERS[0])?;
|
||||||
self.write_centered_with_border(&column_name_line)?;
|
self.write_center_border(&column_name_line)?;
|
||||||
self.write_centered_with_border(INSTRUCTION_BORDERS[1])?;
|
self.write_center_border(INSTRUCTION_BORDERS[1])?;
|
||||||
|
|
||||||
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
for (index, instruction) in self.chunk.instructions().iter().enumerate() {
|
||||||
let position = position.to_string();
|
let position = self
|
||||||
|
.chunk
|
||||||
|
.positions()
|
||||||
|
.get(index)
|
||||||
|
.map(|position| position.to_string())
|
||||||
|
.unwrap_or("stripped".to_string());
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info = instruction.disassembly_info();
|
let info = instruction.disassembly_info();
|
||||||
let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^24}│");
|
let row = format!("│{index:^5}│{position:^12}│{operation:^17}│{info:^24}│");
|
||||||
|
|
||||||
self.write_centered_with_border(&row)?;
|
self.write_center_border(&row)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_centered_with_border(INSTRUCTION_BORDERS[2])?;
|
self.write_center_border(INSTRUCTION_BORDERS[2])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -287,10 +307,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
column_name_line.push('│');
|
column_name_line.push('│');
|
||||||
self.write_centered_with_border_bold("Locals")?;
|
self.write_center_border_bold("Locals")?;
|
||||||
self.write_centered_with_border(LOCAL_BORDERS[0])?;
|
self.write_center_border(LOCAL_BORDERS[0])?;
|
||||||
self.write_centered_with_border(&column_name_line)?;
|
self.write_center_border(&column_name_line)?;
|
||||||
self.write_centered_with_border(LOCAL_BORDERS[1])?;
|
self.write_center_border(LOCAL_BORDERS[1])?;
|
||||||
|
|
||||||
for (
|
for (
|
||||||
index,
|
index,
|
||||||
@ -314,10 +334,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
"│{index:^5}│{identifier_display:^16}│{register_display:^10}│{scope:^7}│{is_mutable:^7}│"
|
"│{index:^5}│{identifier_display:^16}│{register_display:^10}│{scope:^7}│{is_mutable:^7}│"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.write_centered_with_border(&row)?;
|
self.write_center_border(&row)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_centered_with_border(LOCAL_BORDERS[2])?;
|
self.write_center_border(LOCAL_BORDERS[2])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -330,17 +350,14 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
column_name_line.push('│');
|
column_name_line.push('│');
|
||||||
self.write_centered_with_border_bold("Constants")?;
|
self.write_center_border_bold("Constants")?;
|
||||||
self.write_centered_with_border(CONSTANT_BORDERS[0])?;
|
self.write_center_border(CONSTANT_BORDERS[0])?;
|
||||||
self.write_centered_with_border(&column_name_line)?;
|
self.write_center_border(&column_name_line)?;
|
||||||
self.write_centered_with_border(CONSTANT_BORDERS[1])?;
|
self.write_center_border(CONSTANT_BORDERS[1])?;
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants().iter().enumerate() {
|
for (index, value) in self.chunk.constants().iter().enumerate() {
|
||||||
let is_function = matches!(value, Value::Concrete(ConcreteValue::Function(_)));
|
|
||||||
let type_display = value.r#type().to_string();
|
let type_display = value.r#type().to_string();
|
||||||
let value_display = if is_function {
|
let value_display = {
|
||||||
"Function displayed below".to_string()
|
|
||||||
} else {
|
|
||||||
let mut value_string = value.to_string();
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
if value_string.len() > 26 {
|
if value_string.len() > 26 {
|
||||||
@ -351,29 +368,35 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
};
|
};
|
||||||
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
self.write_centered_with_border(&constant_display)?;
|
self.write_center_border(&constant_display)?;
|
||||||
|
|
||||||
if let Value::Concrete(ConcreteValue::Function(chunk)) = value {
|
|
||||||
let function_disassembler = chunk
|
|
||||||
.disassembler(self.writer)
|
|
||||||
.style(self.style)
|
|
||||||
.indent(self.indent + 1);
|
|
||||||
let original_output_width = self.output_width;
|
|
||||||
self.output_width = function_disassembler.output_width;
|
|
||||||
|
|
||||||
function_disassembler.disassemble()?;
|
|
||||||
self.write_char('\n')?;
|
|
||||||
|
|
||||||
self.output_width = original_output_width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_centered_with_border(CONSTANT_BORDERS[2])?;
|
self.write_center_border(CONSTANT_BORDERS[2])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble(mut self) -> Result<(), io::Error> {
|
pub fn write_prototype_section(&mut self) -> Result<(), io::Error> {
|
||||||
|
self.write_center_border_bold("Functions");
|
||||||
|
|
||||||
|
for chunk in &self.chunk.prototypes {
|
||||||
|
chunk
|
||||||
|
.disassembler(self.writer)
|
||||||
|
.indent(self.indent + 1)
|
||||||
|
.width(self.width)
|
||||||
|
.style(true)
|
||||||
|
.show_type(true)
|
||||||
|
.disassemble()?;
|
||||||
|
|
||||||
|
self.write_center_border("")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disassemble(&mut self) -> Result<(), io::Error> {
|
||||||
|
self.write_page_border(TOP_BORDER)?;
|
||||||
|
|
||||||
let name_display = self
|
let name_display = self
|
||||||
.chunk
|
.chunk
|
||||||
.name()
|
.name()
|
||||||
@ -393,16 +416,20 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
.unwrap_or("Dust Chunk Disassembly".to_string())
|
.unwrap_or("Dust Chunk Disassembly".to_string())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.write_page_border(TOP_BORDER)?;
|
self.write_center_border_bold(&name_display)?;
|
||||||
self.write_char('\n')?;
|
|
||||||
self.write_centered_with_border_bold(&name_display)?;
|
if self.show_type {
|
||||||
|
let type_display = self.chunk.r#type.to_string();
|
||||||
|
|
||||||
|
self.write_center_border(&type_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(source) = self.source {
|
if let Some(source) = self.source {
|
||||||
let lazily_formatted = source.split_whitespace().collect::<Vec<&str>>().join(" ");
|
let lazily_formatted = source.split_whitespace().collect::<Vec<&str>>().join(" ");
|
||||||
|
|
||||||
self.write_centered_with_border("")?;
|
self.write_center_border("")?;
|
||||||
self.write_centered_with_border(&lazily_formatted)?;
|
self.write_center_border(&lazily_formatted)?;
|
||||||
self.write_centered_with_border("")?;
|
self.write_center_border("")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let info_line = format!(
|
let info_line = format!(
|
||||||
@ -413,21 +440,25 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self.chunk.r#type.return_type
|
self.chunk.r#type.return_type
|
||||||
);
|
);
|
||||||
|
|
||||||
self.write_centered_with_border_dim(&info_line)?;
|
self.write_center_border_dim(&info_line)?;
|
||||||
self.write_centered_with_border("")?;
|
self.write_center_border("")?;
|
||||||
|
|
||||||
if !self.chunk.is_empty() {
|
if !self.chunk.instructions.is_empty() {
|
||||||
self.write_instruction_section()?;
|
self.write_instruction_section()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.chunk.locals().is_empty() {
|
if !self.chunk.locals.is_empty() {
|
||||||
self.write_local_section()?;
|
self.write_local_section()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.chunk.constants().is_empty() {
|
if !self.chunk.constants.is_empty() {
|
||||||
self.write_constant_section()?;
|
self.write_constant_section()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.chunk.prototypes.is_empty() {
|
||||||
|
self.write_prototype_section()?;
|
||||||
|
}
|
||||||
|
|
||||||
self.write_page_border(BOTTOM_BORDER)
|
self.write_page_border(BOTTOM_BORDER)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
//! In-memory representation of a Dust program or function.
|
//! Representation of a Dust program or function.
|
||||||
//!
|
//!
|
||||||
//! A chunk consists of a sequence of instructions and their positions, a list of constants, and a
|
//! A chunk is output by the compiler to represent all of the information needed to execute a Dust
|
||||||
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
//! program. In addition to the program itself, each function in the source is compiled into its own
|
||||||
//! belong to a named function.
|
//! chunk and stored in the `prototypes` field of its parent. Thus, a chunk is also the
|
||||||
|
//! representation of a function prototype, i.e. a function declaration as opposed to an individual
|
||||||
|
//! instance.
|
||||||
|
//!
|
||||||
|
//! Chunks have a name when they belong to a named function. They also have a type, so the input
|
||||||
|
//! parameters and the type of the return value are statically known. The [`Chunk::stack_size`]
|
||||||
|
//! method can provide the necessary stack size that will be needed by the virtual machine. Chunks
|
||||||
|
//! cannot be instantiated directly and must be created by the compiler. However, when the Rust
|
||||||
|
//! compiler is in the "test" configuration (used for all types of test), [`Chunk::with_data`] can
|
||||||
|
//! be used to create a chunk for comparison to the compiler output. Do not try to run these chunks
|
||||||
|
//! in a virtual machine. Due to their missing stack size, they will cause a panic.
|
||||||
mod disassembler;
|
mod disassembler;
|
||||||
mod local;
|
mod local;
|
||||||
mod scope;
|
mod scope;
|
||||||
@ -19,7 +29,7 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
use crate::{DustString, FunctionType, Instruction, Span, Value};
|
use crate::{DustString, FunctionType, Instruction, Span, Value};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// 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, Serialize, Deserialize)]
|
#[derive(Clone, PartialOrd, Serialize, Deserialize)]
|
||||||
@ -27,49 +37,84 @@ pub struct Chunk {
|
|||||||
name: Option<DustString>,
|
name: Option<DustString>,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
|
|
||||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
instructions: SmallVec<[Instruction; 32]>,
|
||||||
|
positions: SmallVec<[Span; 32]>,
|
||||||
constants: SmallVec<[Value; 16]>,
|
constants: SmallVec<[Value; 16]>,
|
||||||
locals: SmallVec<[Local; 8]>,
|
locals: SmallVec<[Local; 8]>,
|
||||||
|
prototypes: Vec<Chunk>,
|
||||||
|
|
||||||
stack_size: usize,
|
stack_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
name: Option<DustString>,
|
name: Option<DustString>,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
instructions: impl Into<SmallVec<[Instruction; 32]>>,
|
||||||
constants: SmallVec<[Value; 16]>,
|
positions: impl Into<SmallVec<[Span; 32]>>,
|
||||||
locals: SmallVec<[Local; 8]>,
|
constants: impl Into<SmallVec<[Value; 16]>>,
|
||||||
|
locals: impl Into<SmallVec<[Local; 8]>>,
|
||||||
|
prototypes: impl Into<Vec<Chunk>>,
|
||||||
stack_size: usize,
|
stack_size: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
r#type,
|
r#type,
|
||||||
instructions,
|
instructions: instructions.into(),
|
||||||
constants,
|
positions: positions.into(),
|
||||||
locals,
|
constants: constants.into(),
|
||||||
|
locals: locals.into(),
|
||||||
|
prototypes: prototypes.into(),
|
||||||
stack_size,
|
stack_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn with_data(
|
pub fn with_data(
|
||||||
name: Option<DustString>,
|
name: Option<DustString>,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
instructions: impl Into<SmallVec<[(Instruction, Span); 32]>>,
|
instructions: impl Into<SmallVec<[Instruction; 32]>>,
|
||||||
|
positions: impl Into<SmallVec<[Span; 32]>>,
|
||||||
constants: impl Into<SmallVec<[Value; 16]>>,
|
constants: impl Into<SmallVec<[Value; 16]>>,
|
||||||
locals: impl Into<SmallVec<[Local; 8]>>,
|
locals: impl Into<SmallVec<[Local; 8]>>,
|
||||||
|
prototypes: Vec<Chunk>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
r#type,
|
r#type,
|
||||||
instructions: instructions.into(),
|
instructions: instructions.into(),
|
||||||
|
positions: positions.into(),
|
||||||
constants: constants.into(),
|
constants: constants.into(),
|
||||||
locals: locals.into(),
|
locals: locals.into(),
|
||||||
|
prototypes,
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take_data(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Option<DustString>,
|
||||||
|
FunctionType,
|
||||||
|
SmallVec<[Instruction; 32]>,
|
||||||
|
SmallVec<[Span; 32]>,
|
||||||
|
SmallVec<[Value; 16]>,
|
||||||
|
SmallVec<[Local; 8]>,
|
||||||
|
Vec<Chunk>,
|
||||||
|
usize,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
self.name,
|
||||||
|
self.r#type,
|
||||||
|
self.instructions,
|
||||||
|
self.positions,
|
||||||
|
self.constants,
|
||||||
|
self.locals,
|
||||||
|
self.prototypes,
|
||||||
|
self.stack_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> Option<&DustString> {
|
pub fn name(&self) -> Option<&DustString> {
|
||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
@ -78,22 +123,26 @@ impl Chunk {
|
|||||||
&self.r#type
|
&self.r#type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> {
|
||||||
self.instructions.is_empty()
|
&self.instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positions(&self) -> &SmallVec<[Span; 32]> {
|
||||||
|
&self.positions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants(&self) -> &SmallVec<[Value; 16]> {
|
pub fn constants(&self) -> &SmallVec<[Value; 16]> {
|
||||||
&self.constants
|
&self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions(&self) -> &SmallVec<[(Instruction, Span); 32]> {
|
|
||||||
&self.instructions
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn locals(&self) -> &SmallVec<[Local; 8]> {
|
pub fn locals(&self) -> &SmallVec<[Local; 8]> {
|
||||||
&self.locals
|
&self.locals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prototypes(&self) -> &Vec<Chunk> {
|
||||||
|
&self.prototypes
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stack_size(&self) -> usize {
|
pub fn stack_size(&self) -> usize {
|
||||||
self.stack_size
|
self.stack_size
|
||||||
}
|
}
|
||||||
@ -141,8 +190,14 @@ impl Eq for Chunk {}
|
|||||||
|
|
||||||
impl PartialEq for Chunk {
|
impl PartialEq for Chunk {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.instructions == other.instructions
|
// Do not compare stack size because the chunks created for testing will not have one due to
|
||||||
|
// not being compiled.
|
||||||
|
|
||||||
|
self.name == other.name
|
||||||
|
&& self.r#type == other.r#type
|
||||||
|
&& self.instructions == other.instructions
|
||||||
&& self.constants == other.constants
|
&& self.constants == other.constants
|
||||||
&& self.locals == other.locals
|
&& self.locals == other.locals
|
||||||
|
&& self.prototypes == other.prototypes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ use smallvec::{smallvec, SmallVec};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
instruction::{
|
instruction::{
|
||||||
Call, CallNative, Close, GetLocal, Jump, LoadConstant, LoadList, LoadSelf, Move, Negate,
|
CallNative, Close, GetLocal, Jump, LoadList, LoadSelf, Negate, Not, Point, Return,
|
||||||
Not, Return, SetLocal, Test,
|
SetLocal, Test,
|
||||||
},
|
},
|
||||||
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
||||||
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
||||||
@ -56,23 +56,58 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
|||||||
/// See the [`compile`] function an example of how to create and use a Compiler.
|
/// See the [`compile`] function an example of how to create and use a Compiler.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Compiler<'src> {
|
pub struct Compiler<'src> {
|
||||||
self_name: Option<DustString>,
|
/// Used to get tokens for the compiler.
|
||||||
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
|
|
||||||
constants: SmallVec<[Value; 16]>,
|
|
||||||
locals: SmallVec<[(Local, Type); 8]>,
|
|
||||||
stack_size: usize,
|
|
||||||
|
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
|
|
||||||
|
/// Name of the function or program being compiled. This is assigned to the chunk when
|
||||||
|
/// [`Compiler::finish`] is called.
|
||||||
|
self_name: Option<DustString>,
|
||||||
|
|
||||||
|
/// Return type of the function being compiled. This is assigned to the chunk when
|
||||||
|
/// [`Compiler::finish`] is called.
|
||||||
|
return_type: Option<Type>,
|
||||||
|
|
||||||
|
/// Instructions, along with their types and positions, that have been compiled. The
|
||||||
|
/// instructions and positions are assigned to the chunk when [`Compiler::finish`] is called.
|
||||||
|
/// The types are discarded after compilation.
|
||||||
|
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
|
||||||
|
|
||||||
|
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
|
||||||
|
/// is called.
|
||||||
|
constants: SmallVec<[Value; 16]>,
|
||||||
|
|
||||||
|
/// Locals that have been compiled. These are assigned to the chunk when [`Compiler::finish`] is
|
||||||
|
/// called.
|
||||||
|
locals: SmallVec<[(Local, Type); 8]>,
|
||||||
|
|
||||||
|
/// Prototypes that have been compiled. These are assigned to the chunk when
|
||||||
|
/// [`Compiler::finish`] is called.
|
||||||
|
prototypes: Vec<Chunk>,
|
||||||
|
|
||||||
|
/// Maximum stack size required by the chunk. This is assigned to the chunk when
|
||||||
|
/// [`Compiler::finish`] is called.
|
||||||
|
stack_size: usize,
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
current_position: Span,
|
current_position: Span,
|
||||||
previous_token: Token<'src>,
|
previous_token: Token<'src>,
|
||||||
previous_position: Span,
|
previous_position: Span,
|
||||||
|
|
||||||
return_type: Option<Type>,
|
/// The first register index that the compiler should use. This is used to avoid reusing the
|
||||||
|
/// registers that are used for the function's arguments, thus it is zero in the program's main
|
||||||
|
/// chunk.
|
||||||
minimum_register: u8,
|
minimum_register: u8,
|
||||||
|
|
||||||
|
/// Index of the current block. This is used to determine the scope of the locals and is
|
||||||
|
/// incremented when a new block is entered.
|
||||||
block_index: u8,
|
block_index: u8,
|
||||||
|
|
||||||
|
/// The current scope of the compiler. This is used to test if a variable is in scope.
|
||||||
current_scope: Scope,
|
current_scope: Scope,
|
||||||
|
|
||||||
|
/// Index of the record (i.e. runtime data) that the VM will use when calling the function. This
|
||||||
|
/// is a depth-first index.
|
||||||
|
record_index: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Compiler<'src> {
|
impl<'src> Compiler<'src> {
|
||||||
@ -90,6 +125,7 @@ impl<'src> Compiler<'src> {
|
|||||||
instructions: SmallVec::new(),
|
instructions: SmallVec::new(),
|
||||||
constants: SmallVec::new(),
|
constants: SmallVec::new(),
|
||||||
locals: SmallVec::new(),
|
locals: SmallVec::new(),
|
||||||
|
prototypes: Vec::new(),
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
lexer,
|
lexer,
|
||||||
current_token,
|
current_token,
|
||||||
@ -100,6 +136,7 @@ impl<'src> Compiler<'src> {
|
|||||||
minimum_register: 0,
|
minimum_register: 0,
|
||||||
block_index: 0,
|
block_index: 0,
|
||||||
current_scope: Scope::default(),
|
current_scope: Scope::default(),
|
||||||
|
record_index: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +152,11 @@ impl<'src> Compiler<'src> {
|
|||||||
value_parameters,
|
value_parameters,
|
||||||
return_type: self.return_type.unwrap_or(Type::None),
|
return_type: self.return_type.unwrap_or(Type::None),
|
||||||
};
|
};
|
||||||
let instructions = self
|
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
|
||||||
.instructions
|
.instructions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(instruction, _, position)| (instruction, position))
|
.map(|(instruction, _, position)| (instruction, position))
|
||||||
.collect::<SmallVec<[(Instruction, Span); 32]>>();
|
.unzip();
|
||||||
let locals = self
|
let locals = self
|
||||||
.locals
|
.locals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -130,8 +167,10 @@ impl<'src> Compiler<'src> {
|
|||||||
self.self_name,
|
self.self_name,
|
||||||
r#type,
|
r#type,
|
||||||
instructions,
|
instructions,
|
||||||
|
positions,
|
||||||
self.constants,
|
self.constants,
|
||||||
locals,
|
locals,
|
||||||
|
self.prototypes,
|
||||||
self.stack_size,
|
self.stack_size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -347,7 +386,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Operation::LOAD_SELF = operation {
|
if let Operation::LOAD_SELF = operation {
|
||||||
return Ok(Type::SelfChunk);
|
return Ok(Type::SelfFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
@ -989,7 +1028,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let load_self = Instruction::from(LoadSelf { destination });
|
let load_self = Instruction::from(LoadSelf { destination });
|
||||||
|
|
||||||
self.emit_instruction(load_self, Type::SelfChunk, start_position);
|
self.emit_instruction(load_self, Type::SelfFunction, start_position);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
@ -1264,7 +1303,7 @@ impl<'src> Compiler<'src> {
|
|||||||
optimize_test_with_loader_arguments(self);
|
optimize_test_with_loader_arguments(self);
|
||||||
|
|
||||||
let else_last_register = self.next_register().saturating_sub(1);
|
let else_last_register = self.next_register().saturating_sub(1);
|
||||||
let r#move = Instruction::from(Move {
|
let r#move = Instruction::from(Point {
|
||||||
from: else_last_register,
|
from: else_last_register,
|
||||||
to: if_last_register,
|
to: if_last_register,
|
||||||
});
|
});
|
||||||
@ -1532,6 +1571,10 @@ impl<'src> Compiler<'src> {
|
|||||||
fn parse_function(&mut self) -> Result<(), CompileError> {
|
fn parse_function(&mut self) -> Result<(), CompileError> {
|
||||||
let function_start = self.current_position.0;
|
let function_start = self.current_position.0;
|
||||||
let mut function_compiler = Compiler::new(self.lexer)?;
|
let mut function_compiler = Compiler::new(self.lexer)?;
|
||||||
|
|
||||||
|
self.record_index += 1;
|
||||||
|
function_compiler.record_index = self.record_index;
|
||||||
|
|
||||||
let identifier_info = if let Token::Identifier(text) = function_compiler.current_token {
|
let identifier_info = if let Token::Identifier(text) = function_compiler.current_token {
|
||||||
let position = function_compiler.current_position;
|
let position = function_compiler.current_position;
|
||||||
|
|
||||||
@ -1614,13 +1657,12 @@ impl<'src> Compiler<'src> {
|
|||||||
self.previous_position = function_compiler.previous_position;
|
self.previous_position = function_compiler.previous_position;
|
||||||
self.current_token = function_compiler.current_token;
|
self.current_token = function_compiler.current_token;
|
||||||
self.current_position = function_compiler.current_position;
|
self.current_position = function_compiler.current_position;
|
||||||
|
self.record_index = function_compiler.record_index;
|
||||||
|
|
||||||
self.lexer.skip_to(self.current_position.1);
|
self.lexer.skip_to(self.current_position.1);
|
||||||
|
|
||||||
let function_end = function_compiler.previous_position.1;
|
let function_end = function_compiler.previous_position.1;
|
||||||
let chunk = function_compiler.finish(None, value_parameters.clone());
|
let prototype = function_compiler.finish(None, value_parameters.clone());
|
||||||
let function = Value::Concrete(ConcreteValue::function(chunk));
|
|
||||||
let constant_index = self.push_or_get_constant(function);
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let function_type = FunctionType {
|
let function_type = FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
@ -1628,6 +1670,8 @@ impl<'src> Compiler<'src> {
|
|||||||
return_type,
|
return_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.prototypes.push(prototype);
|
||||||
|
|
||||||
if let Some((identifier, _)) = identifier_info {
|
if let Some((identifier, _)) = identifier_info {
|
||||||
self.declare_local(
|
self.declare_local(
|
||||||
identifier,
|
identifier,
|
||||||
@ -1636,23 +1680,11 @@ impl<'src> Compiler<'src> {
|
|||||||
false,
|
false,
|
||||||
self.current_scope,
|
self.current_scope,
|
||||||
);
|
);
|
||||||
|
|
||||||
let load_constant = Instruction::load_constant(destination, constant_index, false);
|
|
||||||
|
|
||||||
self.emit_instruction(
|
|
||||||
load_constant,
|
|
||||||
Type::function(function_type),
|
|
||||||
Span(function_start, function_end),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
let load_constant = Instruction::from(LoadConstant {
|
let load_function = Instruction::load_function(destination, self.record_index);
|
||||||
destination,
|
|
||||||
constant_index,
|
|
||||||
jump_next: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
load_constant,
|
load_function,
|
||||||
Type::function(function_type),
|
Type::function(function_type),
|
||||||
Span(function_start, function_end),
|
Span(function_start, function_end),
|
||||||
);
|
);
|
||||||
@ -1670,23 +1702,18 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !last_instruction.yields_value() {
|
if last_instruction.operation() != Operation::LOAD_FUNCTION {
|
||||||
return Err(CompileError::ExpectedExpression {
|
return Err(CompileError::ExpectedFunction {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
|
actual_type: last_instruction_type.clone(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument =
|
let prototype_index = last_instruction.b_field();
|
||||||
last_instruction
|
|
||||||
.as_argument()
|
|
||||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: self.previous_position,
|
|
||||||
})?;
|
|
||||||
let function_return_type = match last_instruction_type {
|
let function_return_type = match last_instruction_type {
|
||||||
Type::Function(function_type) => function_type.return_type.clone(),
|
Type::Function(function_type) => function_type.return_type.clone(),
|
||||||
Type::SelfChunk => self.return_type.clone().unwrap_or(Type::None),
|
Type::SelfFunction => self.return_type.clone().unwrap_or(Type::None),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedFunction {
|
return Err(CompileError::ExpectedFunction {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -1725,11 +1752,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let call = Instruction::from(Call {
|
let call = Instruction::call(destination, prototype_index, argument_count);
|
||||||
destination,
|
|
||||||
function: argument,
|
|
||||||
argument_count,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(call, function_return_type, Span(start, end));
|
self.emit_instruction(call, function_return_type, Span(start, end));
|
||||||
|
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
use crate::{Argument, Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
pub destination: u8,
|
pub destination: u8,
|
||||||
pub function: Argument,
|
pub prototype_index: u8,
|
||||||
pub argument_count: u8,
|
pub argument_count: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for Call {
|
impl From<&Instruction> for Call {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
let function = instruction.b_as_argument();
|
let prototype_index = instruction.b_field();
|
||||||
let argument_count = instruction.c_field();
|
let argument_count = instruction.c_field();
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
destination,
|
destination,
|
||||||
function,
|
prototype_index,
|
||||||
argument_count,
|
argument_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,9 +23,9 @@ impl From<&Instruction> for Call {
|
|||||||
impl From<Call> for Instruction {
|
impl From<Call> for Instruction {
|
||||||
fn from(call: Call) -> Self {
|
fn from(call: Call) -> Self {
|
||||||
let a = call.destination;
|
let a = call.destination;
|
||||||
let (b, b_is_constant) = call.function.as_index_and_constant_flag();
|
let b = call.prototype_index;
|
||||||
let c = call.argument_count;
|
let c = call.argument_count;
|
||||||
|
|
||||||
Instruction::new(Operation::CALL, a, b, c, b_is_constant, false, false)
|
Instruction::new(Operation::CALL, a, b, c, false, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionData;
|
||||||
|
|
||||||
pub struct Close {
|
pub struct Close {
|
||||||
pub from: u8,
|
pub from: u8,
|
||||||
pub to: u8,
|
pub to: u8,
|
||||||
@ -14,6 +16,15 @@ impl From<&Instruction> for Close {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InstructionData> for Close {
|
||||||
|
fn from(instruction: InstructionData) -> Self {
|
||||||
|
Close {
|
||||||
|
from: instruction.b,
|
||||||
|
to: instruction.c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Close> for Instruction {
|
impl From<Close> for Instruction {
|
||||||
fn from(close: Close) -> Self {
|
fn from(close: Close) -> Self {
|
||||||
let operation = Operation::CLOSE;
|
let operation = Operation::CLOSE;
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionData;
|
||||||
|
|
||||||
pub struct LoadBoolean {
|
pub struct LoadBoolean {
|
||||||
pub destination: u8,
|
pub destination: u8,
|
||||||
pub value: bool,
|
pub value: bool,
|
||||||
pub jump_next: bool,
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for LoadBoolean {
|
impl From<InstructionData> for LoadBoolean {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn from(instruction: InstructionData) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a;
|
||||||
let value = instruction.b_field() != 0;
|
let value = instruction.b != 0;
|
||||||
let jump_next = instruction.c_field() != 0;
|
let jump_next = instruction.c != 0;
|
||||||
|
|
||||||
LoadBoolean {
|
LoadBoolean {
|
||||||
destination,
|
destination,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
|
use super::InstructionData;
|
||||||
|
|
||||||
pub struct LoadConstant {
|
pub struct LoadConstant {
|
||||||
pub destination: u8,
|
pub destination: u8,
|
||||||
pub constant_index: u8,
|
pub constant_index: u8,
|
||||||
@ -20,6 +24,20 @@ impl From<&Instruction> for LoadConstant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InstructionData> for LoadConstant {
|
||||||
|
fn from(instruction: InstructionData) -> Self {
|
||||||
|
let destination = instruction.a;
|
||||||
|
let constant_index = instruction.b;
|
||||||
|
let jump_next = instruction.c != 0;
|
||||||
|
|
||||||
|
LoadConstant {
|
||||||
|
destination,
|
||||||
|
constant_index,
|
||||||
|
jump_next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LoadConstant> for Instruction {
|
impl From<LoadConstant> for Instruction {
|
||||||
fn from(load_constant: LoadConstant) -> Self {
|
fn from(load_constant: LoadConstant) -> Self {
|
||||||
let operation = Operation::LOAD_CONSTANT;
|
let operation = Operation::LOAD_CONSTANT;
|
||||||
@ -30,3 +48,21 @@ impl From<LoadConstant> for Instruction {
|
|||||||
Instruction::new(operation, a, b, c, false, false, false)
|
Instruction::new(operation, a, b, c, false, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for LoadConstant {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let LoadConstant {
|
||||||
|
destination,
|
||||||
|
constant_index,
|
||||||
|
jump_next,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
write!(f, "R{destination} = Constant {constant_index}")?;
|
||||||
|
|
||||||
|
if *jump_next {
|
||||||
|
write!(f, " JUMP +1")
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
42
dust-lang/src/instruction/load_function.rs
Normal file
42
dust-lang/src/instruction/load_function.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use super::{Instruction, Operation};
|
||||||
|
|
||||||
|
pub struct LoadFunction {
|
||||||
|
pub destination: u8,
|
||||||
|
pub prototype_index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Instruction> for LoadFunction {
|
||||||
|
fn from(instruction: &Instruction) -> Self {
|
||||||
|
let destination = instruction.a_field();
|
||||||
|
let prototype_index = instruction.b_field();
|
||||||
|
|
||||||
|
LoadFunction {
|
||||||
|
destination,
|
||||||
|
prototype_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LoadFunction> for Instruction {
|
||||||
|
fn from(load_function: LoadFunction) -> Self {
|
||||||
|
let operation = Operation::LOAD_FUNCTION;
|
||||||
|
|
||||||
|
Instruction::new(
|
||||||
|
operation,
|
||||||
|
load_function.destination,
|
||||||
|
load_function.prototype_index,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LoadFunction {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "R{} = P{}", self.destination, self.prototype_index)
|
||||||
|
}
|
||||||
|
}
|
@ -76,18 +76,18 @@
|
|||||||
//! // - `a = 2 + a`
|
//! // - `a = 2 + a`
|
||||||
//!
|
//!
|
||||||
//! let operation = mystery_instruction.operation();
|
//! let operation = mystery_instruction.operation();
|
||||||
//!
|
//! let is_add_assign = match operation {
|
||||||
//! match operation {
|
|
||||||
//! Operation::Add => {
|
//! Operation::Add => {
|
||||||
//! let Add { destination, left, right } = Add::from(&mystery_instruction);
|
//! let Add { destination, left, right } = Add::from(&mystery_instruction);
|
||||||
//! let is_add_assign =
|
|
||||||
//! left == Argument::Register(destination)
|
|
||||||
//! || right == Argument::Register(destination);
|
|
||||||
//!
|
//!
|
||||||
//! assert!(is_add_assign);
|
//! left == Argument::Register(destination)
|
||||||
|
//! || right == Argument::Register(destination);
|
||||||
|
//!
|
||||||
//! }
|
//! }
|
||||||
//! _ => {} // Handle other operations...
|
//! _ => false,
|
||||||
//! }
|
//! };
|
||||||
|
//!
|
||||||
|
//! assert!(is_add_assign);
|
||||||
//! ```
|
//! ```
|
||||||
mod add;
|
mod add;
|
||||||
mod call;
|
mod call;
|
||||||
@ -101,6 +101,7 @@ mod less;
|
|||||||
mod less_equal;
|
mod less_equal;
|
||||||
mod load_boolean;
|
mod load_boolean;
|
||||||
mod load_constant;
|
mod load_constant;
|
||||||
|
mod load_function;
|
||||||
mod load_list;
|
mod load_list;
|
||||||
mod load_self;
|
mod load_self;
|
||||||
mod modulo;
|
mod modulo;
|
||||||
@ -127,6 +128,7 @@ pub use less::Less;
|
|||||||
pub use less_equal::LessEqual;
|
pub use less_equal::LessEqual;
|
||||||
pub use load_boolean::LoadBoolean;
|
pub use load_boolean::LoadBoolean;
|
||||||
pub use load_constant::LoadConstant;
|
pub use load_constant::LoadConstant;
|
||||||
|
pub use load_function::LoadFunction;
|
||||||
pub use load_list::LoadList;
|
pub use load_list::LoadList;
|
||||||
pub use load_self::LoadSelf;
|
pub use load_self::LoadSelf;
|
||||||
pub use modulo::Modulo;
|
pub use modulo::Modulo;
|
||||||
@ -134,7 +136,7 @@ pub use multiply::Multiply;
|
|||||||
pub use negate::Negate;
|
pub use negate::Negate;
|
||||||
pub use not::Not;
|
pub use not::Not;
|
||||||
pub use operation::Operation;
|
pub use operation::Operation;
|
||||||
pub use r#move::Move;
|
pub use r#move::Point;
|
||||||
pub use r#return::Return;
|
pub use r#return::Return;
|
||||||
pub use set_local::SetLocal;
|
pub use set_local::SetLocal;
|
||||||
pub use subtract::Subtract;
|
pub use subtract::Subtract;
|
||||||
@ -239,8 +241,8 @@ impl Instruction {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#move(from: u8, to: u8) -> Instruction {
|
pub fn point(from: u8, to: u8) -> Instruction {
|
||||||
Instruction::from(Move { from, to })
|
Instruction::from(Point { from, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from: u8, to: u8) -> Instruction {
|
pub fn close(from: u8, to: u8) -> Instruction {
|
||||||
@ -263,6 +265,13 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_function(destination: u8, prototype_index: u8) -> Instruction {
|
||||||
|
Instruction::from(LoadFunction {
|
||||||
|
destination,
|
||||||
|
prototype_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_list(destination: u8, start_register: u8) -> Instruction {
|
pub fn load_list(destination: u8, start_register: u8) -> Instruction {
|
||||||
Instruction::from(LoadList {
|
Instruction::from(LoadList {
|
||||||
destination,
|
destination,
|
||||||
@ -376,10 +385,10 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(destination: u8, function: Argument, argument_count: u8) -> Instruction {
|
pub fn call(destination: u8, prototype_index: u8, argument_count: u8) -> Instruction {
|
||||||
Instruction::from(Call {
|
Instruction::from(Call {
|
||||||
destination,
|
destination,
|
||||||
function,
|
prototype_index,
|
||||||
argument_count,
|
argument_count,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -497,7 +506,7 @@ impl Instruction {
|
|||||||
|
|
||||||
function.returns_value()
|
function.returns_value()
|
||||||
}
|
}
|
||||||
Operation::MOVE
|
Operation::POINT
|
||||||
| Operation::CLOSE
|
| Operation::CLOSE
|
||||||
| Operation::SET_LOCAL
|
| Operation::SET_LOCAL
|
||||||
| Operation::TEST
|
| Operation::TEST
|
||||||
@ -509,14 +518,12 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassembly_info(&self) -> String {
|
pub fn disassembly_info(&self) -> String {
|
||||||
match self.operation() {
|
let (operation, data) = self.decode();
|
||||||
Operation::MOVE => {
|
|
||||||
let Move { from, to } = Move::from(self);
|
|
||||||
|
|
||||||
format!("R{to} = R{from}")
|
match operation {
|
||||||
}
|
Operation::POINT => Point::from(data).to_string(),
|
||||||
Operation::CLOSE => {
|
Operation::CLOSE => {
|
||||||
let Close { from, to } = Close::from(self);
|
let Close { from, to } = Close::from(data);
|
||||||
|
|
||||||
format!("R{from}..R{to}")
|
format!("R{from}..R{to}")
|
||||||
}
|
}
|
||||||
@ -525,7 +532,7 @@ impl Instruction {
|
|||||||
destination,
|
destination,
|
||||||
value,
|
value,
|
||||||
jump_next,
|
jump_next,
|
||||||
} = LoadBoolean::from(self);
|
} = LoadBoolean::from(data);
|
||||||
|
|
||||||
if jump_next {
|
if jump_next {
|
||||||
format!("R{destination} = {value} && JUMP +1")
|
format!("R{destination} = {value} && JUMP +1")
|
||||||
@ -546,6 +553,7 @@ impl Instruction {
|
|||||||
format!("R{destination} = C{constant_index}")
|
format!("R{destination} = C{constant_index}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Operation::LOAD_FUNCTION => LoadFunction::from(self).to_string(),
|
||||||
Operation::LOAD_LIST => {
|
Operation::LOAD_LIST => {
|
||||||
let LoadList {
|
let LoadList {
|
||||||
destination,
|
destination,
|
||||||
@ -689,17 +697,16 @@ impl Instruction {
|
|||||||
Operation::CALL => {
|
Operation::CALL => {
|
||||||
let Call {
|
let Call {
|
||||||
destination,
|
destination,
|
||||||
function,
|
prototype_index,
|
||||||
argument_count,
|
argument_count,
|
||||||
} = Call::from(self);
|
} = Call::from(self);
|
||||||
let arguments_start = destination.saturating_sub(argument_count);
|
let arguments_start = destination.saturating_sub(argument_count);
|
||||||
let arguments_end = arguments_start + argument_count;
|
|
||||||
|
|
||||||
match argument_count {
|
match argument_count {
|
||||||
0 => format!("R{destination} = {function}()"),
|
0 => format!("R{destination} = P{prototype_index}()"),
|
||||||
1 => format!("R{destination} = {function}(R{arguments_start})"),
|
1 => format!("R{destination} = P{prototype_index}(R{arguments_start})"),
|
||||||
_ => {
|
_ => {
|
||||||
format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})")
|
format!("R{destination} = P{prototype_index}(R{arguments_start}..R{destination})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,43 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
pub struct Move {
|
use super::InstructionData;
|
||||||
|
|
||||||
|
pub struct Point {
|
||||||
pub from: u8,
|
pub from: u8,
|
||||||
pub to: u8,
|
pub to: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for Move {
|
impl From<&Instruction> for Point {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
Move {
|
Point {
|
||||||
from: instruction.b_field(),
|
from: instruction.b_field(),
|
||||||
to: instruction.c_field(),
|
to: instruction.c_field(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Move> for Instruction {
|
impl From<InstructionData> for Point {
|
||||||
fn from(r#move: Move) -> Self {
|
fn from(instruction: InstructionData) -> Self {
|
||||||
let operation = Operation::MOVE;
|
Point {
|
||||||
|
from: instruction.b,
|
||||||
|
to: instruction.c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Point {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let Point { from, to } = self;
|
||||||
|
|
||||||
|
write!(f, "{from} -> {to}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Point> for Instruction {
|
||||||
|
fn from(r#move: Point) -> Self {
|
||||||
|
let operation = Operation::POINT;
|
||||||
let b = r#move.from;
|
let b = r#move.from;
|
||||||
let c = r#move.to;
|
let c = r#move.to;
|
||||||
|
|
||||||
|
@ -9,39 +9,41 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub struct Operation(pub u8);
|
pub struct Operation(pub u8);
|
||||||
|
|
||||||
impl Operation {
|
impl Operation {
|
||||||
pub const MOVE: Operation = Operation(0);
|
pub const POINT: Operation = Operation(0);
|
||||||
pub const CLOSE: Operation = Operation(1);
|
pub const CLOSE: Operation = Operation(1);
|
||||||
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
||||||
pub const LOAD_CONSTANT: Operation = Operation(3);
|
pub const LOAD_CONSTANT: Operation = Operation(3);
|
||||||
pub const LOAD_LIST: Operation = Operation(4);
|
pub const LOAD_FUNCTION: Operation = Operation(4);
|
||||||
pub const LOAD_SELF: Operation = Operation(5);
|
pub const LOAD_LIST: Operation = Operation(5);
|
||||||
pub const GET_LOCAL: Operation = Operation(6);
|
pub const LOAD_SELF: Operation = Operation(6);
|
||||||
pub const SET_LOCAL: Operation = Operation(7);
|
pub const GET_LOCAL: Operation = Operation(7);
|
||||||
pub const ADD: Operation = Operation(8);
|
pub const SET_LOCAL: Operation = Operation(8);
|
||||||
pub const SUBTRACT: Operation = Operation(9);
|
pub const ADD: Operation = Operation(9);
|
||||||
pub const MULTIPLY: Operation = Operation(10);
|
pub const SUBTRACT: Operation = Operation(10);
|
||||||
pub const DIVIDE: Operation = Operation(11);
|
pub const MULTIPLY: Operation = Operation(11);
|
||||||
pub const MODULO: Operation = Operation(12);
|
pub const DIVIDE: Operation = Operation(12);
|
||||||
pub const TEST: Operation = Operation(13);
|
pub const MODULO: Operation = Operation(13);
|
||||||
pub const TEST_SET: Operation = Operation(14);
|
pub const TEST: Operation = Operation(14);
|
||||||
pub const EQUAL: Operation = Operation(15);
|
pub const TEST_SET: Operation = Operation(15);
|
||||||
pub const LESS: Operation = Operation(16);
|
pub const EQUAL: Operation = Operation(16);
|
||||||
pub const LESS_EQUAL: Operation = Operation(17);
|
pub const LESS: Operation = Operation(17);
|
||||||
pub const NEGATE: Operation = Operation(18);
|
pub const LESS_EQUAL: Operation = Operation(18);
|
||||||
pub const NOT: Operation = Operation(19);
|
pub const NEGATE: Operation = Operation(19);
|
||||||
pub const CALL: Operation = Operation(20);
|
pub const NOT: Operation = Operation(20);
|
||||||
pub const CALL_NATIVE: Operation = Operation(21);
|
pub const CALL: Operation = Operation(21);
|
||||||
pub const JUMP: Operation = Operation(22);
|
pub const CALL_NATIVE: Operation = Operation(22);
|
||||||
pub const RETURN: Operation = Operation(23);
|
pub const JUMP: Operation = Operation(23);
|
||||||
|
pub const RETURN: Operation = Operation(24);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operation {
|
impl Operation {
|
||||||
pub fn name(self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match *self {
|
||||||
Self::MOVE => "MOVE",
|
Self::POINT => "MOVE",
|
||||||
Self::CLOSE => "CLOSE",
|
Self::CLOSE => "CLOSE",
|
||||||
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
||||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||||
|
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||||
Self::LOAD_LIST => "LOAD_LIST",
|
Self::LOAD_LIST => "LOAD_LIST",
|
||||||
Self::LOAD_SELF => "LOAD_SELF",
|
Self::LOAD_SELF => "LOAD_SELF",
|
||||||
Self::GET_LOCAL => "GET_LOCAL",
|
Self::GET_LOCAL => "GET_LOCAL",
|
||||||
|
@ -46,7 +46,7 @@ pub fn lex(source: &str) -> Result<Vec<(Token, Span)>, DustError> {
|
|||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Low-level tool for lexing a single token at a time.
|
/// Tool for lexing a single token at a time.
|
||||||
///
|
///
|
||||||
/// See the [`lex`] function for an example of how to create and use a Lexer.
|
/// See the [`lex`] function for an example of how to create and use a Lexer.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
@ -47,8 +47,10 @@ pub use crate::lexer::{lex, LexError, Lexer};
|
|||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
pub use crate::value::{AbstractValue, ConcreteValue, DustString, RangeValue, Value, ValueError};
|
pub use crate::value::{
|
||||||
pub use crate::vm::{run, Vm};
|
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
||||||
|
};
|
||||||
|
pub use crate::vm::{run, Pointer, Vm};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -2,16 +2,16 @@ use std::panic;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{DustString, NativeFunctionError, Value, Vm};
|
use crate::{vm::Record, DustString, NativeFunctionError, Value};
|
||||||
|
|
||||||
pub fn panic(
|
pub fn panic(
|
||||||
vm: &Vm,
|
record: &mut Record,
|
||||||
arguments: SmallVec<[&Value; 4]>,
|
arguments: SmallVec<[&Value; 4]>,
|
||||||
) -> Result<Option<Value>, NativeFunctionError> {
|
) -> Result<Option<Value>, NativeFunctionError> {
|
||||||
let mut message: Option<DustString> = None;
|
let mut message: Option<DustString> = None;
|
||||||
|
|
||||||
for value_ref in arguments {
|
for value_ref in arguments {
|
||||||
let string = value_ref.display(vm);
|
let string = value_ref.display(record);
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
Some(ref mut message) => message.push_str(&string),
|
Some(ref mut message) => message.push_str(&string),
|
||||||
|
@ -2,9 +2,12 @@ use std::io::{stdin, stdout, Write};
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{ConcreteValue, NativeFunctionError, Value, Vm};
|
use crate::{vm::Record, ConcreteValue, NativeFunctionError, Value};
|
||||||
|
|
||||||
pub fn read_line(vm: &Vm, _: SmallVec<[&Value; 4]>) -> Result<Option<Value>, NativeFunctionError> {
|
pub fn read_line(
|
||||||
|
record: &mut Record,
|
||||||
|
_: SmallVec<[&Value; 4]>,
|
||||||
|
) -> Result<Option<Value>, NativeFunctionError> {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
match stdin().read_line(&mut buffer) {
|
match stdin().read_line(&mut buffer) {
|
||||||
@ -17,25 +20,23 @@ pub fn read_line(vm: &Vm, _: SmallVec<[&Value; 4]>) -> Result<Option<Value>, Nat
|
|||||||
}
|
}
|
||||||
Err(error) => Err(NativeFunctionError::Io {
|
Err(error) => Err(NativeFunctionError::Io {
|
||||||
error: error.kind(),
|
error: error.kind(),
|
||||||
position: vm.current_position(),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(
|
pub fn write(
|
||||||
vm: &Vm,
|
record: &mut Record,
|
||||||
arguments: SmallVec<[&Value; 4]>,
|
arguments: SmallVec<[&Value; 4]>,
|
||||||
) -> Result<Option<Value>, NativeFunctionError> {
|
) -> Result<Option<Value>, NativeFunctionError> {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
let string = argument.display(vm);
|
let string = argument.display(record);
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(string.as_bytes())
|
.write_all(string.as_bytes())
|
||||||
.map_err(|io_error| NativeFunctionError::Io {
|
.map_err(|io_error| NativeFunctionError::Io {
|
||||||
error: io_error.kind(),
|
error: io_error.kind(),
|
||||||
position: vm.current_position(),
|
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,19 +44,18 @@ pub fn write(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(
|
pub fn write_line(
|
||||||
vm: &Vm,
|
record: &mut Record,
|
||||||
arguments: SmallVec<[&Value; 4]>,
|
arguments: SmallVec<[&Value; 4]>,
|
||||||
) -> Result<Option<Value>, NativeFunctionError> {
|
) -> Result<Option<Value>, NativeFunctionError> {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
let string = argument.display(vm);
|
let string = argument.display(record);
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(string.as_bytes())
|
.write_all(string.as_bytes())
|
||||||
.map_err(|io_error| NativeFunctionError::Io {
|
.map_err(|io_error| NativeFunctionError::Io {
|
||||||
error: io_error.kind(),
|
error: io_error.kind(),
|
||||||
position: vm.current_position(),
|
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +63,6 @@ pub fn write_line(
|
|||||||
.write(b"\n")
|
.write(b"\n")
|
||||||
.map_err(|io_error| NativeFunctionError::Io {
|
.map_err(|io_error| NativeFunctionError::Io {
|
||||||
error: io_error.kind(),
|
error: io_error.kind(),
|
||||||
position: vm.current_position(),
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -31,7 +31,7 @@ pub enum Type {
|
|||||||
Range {
|
Range {
|
||||||
r#type: Box<Type>,
|
r#type: Box<Type>,
|
||||||
},
|
},
|
||||||
SelfChunk,
|
SelfFunction,
|
||||||
String,
|
String,
|
||||||
Struct(StructType),
|
Struct(StructType),
|
||||||
Tuple {
|
Tuple {
|
||||||
@ -194,7 +194,7 @@ impl Display for Type {
|
|||||||
}
|
}
|
||||||
Type::None => write!(f, "none"),
|
Type::None => write!(f, "none"),
|
||||||
Type::Range { r#type } => write!(f, "{type} range"),
|
Type::Range { r#type } => write!(f, "{type} range"),
|
||||||
Type::SelfChunk => write!(f, "self"),
|
Type::SelfFunction => 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 } => {
|
||||||
@ -257,8 +257,8 @@ impl Ord for 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::SelfFunction, Type::SelfFunction) => Ordering::Equal,
|
||||||
(Type::SelfChunk, _) => Ordering::Greater,
|
(Type::SelfFunction, _) => Ordering::Greater,
|
||||||
(Type::String, Type::String) => Ordering::Equal,
|
(Type::String, Type::String) => Ordering::Equal,
|
||||||
(Type::String { .. }, _) => Ordering::Greater,
|
(Type::String { .. }, _) => Ordering::Greater,
|
||||||
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
||||||
|
45
dust-lang/src/value/abstract_list.rs
Normal file
45
dust-lang/src/value/abstract_list.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use crate::{vm::Record, Pointer, Type};
|
||||||
|
|
||||||
|
use super::DustString;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct AbstractList {
|
||||||
|
pub item_type: Type,
|
||||||
|
pub item_pointers: Vec<Pointer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbstractList {
|
||||||
|
pub fn display(&self, record: &Record) -> DustString {
|
||||||
|
let mut display = DustString::new();
|
||||||
|
|
||||||
|
display.push('[');
|
||||||
|
|
||||||
|
for (i, item) in self.item_pointers.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
display.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
let item_display = record.follow_pointer(*item).display(record);
|
||||||
|
|
||||||
|
display.push_str(&item_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.push(']');
|
||||||
|
|
||||||
|
display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AbstractList {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
for pointer in &self.item_pointers {
|
||||||
|
write!(f, "{}", pointer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{vm::Pointer, ConcreteValue, DustString, Type, Value, Vm};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, PartialOrd)]
|
|
||||||
pub enum AbstractValue {
|
|
||||||
FunctionSelf,
|
|
||||||
List {
|
|
||||||
item_type: Type,
|
|
||||||
item_pointers: Vec<Pointer>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractValue {
|
|
||||||
pub fn to_value(self) -> Value {
|
|
||||||
Value::Abstract(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_concrete_owned<'a>(&self, vm: &'a Vm<'a>) -> ConcreteValue {
|
|
||||||
match self {
|
|
||||||
AbstractValue::FunctionSelf => ConcreteValue::function(vm.chunk().clone()),
|
|
||||||
AbstractValue::List { item_pointers, .. } => {
|
|
||||||
let mut items: Vec<ConcreteValue> = Vec::with_capacity(item_pointers.len());
|
|
||||||
|
|
||||||
for pointer in item_pointers {
|
|
||||||
let item_option = vm.follow_pointer_allow_empty(*pointer);
|
|
||||||
let item = match item_option {
|
|
||||||
Some(value) => value.clone().into_concrete_owned(vm),
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcreteValue::List(items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self, vm: &Vm) -> DustString {
|
|
||||||
let mut display = DustString::new();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
AbstractValue::FunctionSelf => display.push_str("self"),
|
|
||||||
AbstractValue::List {
|
|
||||||
item_pointers: items,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
display.push('[');
|
|
||||||
|
|
||||||
for (i, item) in items.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
display.push_str(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let item_display = vm.follow_pointer(*item).display(vm);
|
|
||||||
|
|
||||||
display.push_str(&item_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.push(']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
AbstractValue::FunctionSelf => Type::SelfChunk,
|
|
||||||
AbstractValue::List { item_type, .. } => Type::List(Box::new(item_type.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for AbstractValue {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
log::trace!("Cloning abstract value {:?}", self);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
AbstractValue::FunctionSelf => AbstractValue::FunctionSelf,
|
|
||||||
AbstractValue::List {
|
|
||||||
item_type: r#type,
|
|
||||||
item_pointers: items,
|
|
||||||
} => AbstractValue::List {
|
|
||||||
item_type: r#type.clone(),
|
|
||||||
item_pointers: items.clone(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AbstractValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
AbstractValue::FunctionSelf => write!(f, "self"),
|
|
||||||
AbstractValue::List {
|
|
||||||
item_pointers: items,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (i, item) in items.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{}", item)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smartstring::{LazyCompact, SmartString};
|
use smartstring::{LazyCompact, SmartString};
|
||||||
|
|
||||||
use crate::{Chunk, Type, Value, ValueError};
|
use crate::{Type, Value, ValueError};
|
||||||
|
|
||||||
use super::RangeValue;
|
use super::RangeValue;
|
||||||
|
|
||||||
@ -15,7 +15,6 @@ pub enum ConcreteValue {
|
|||||||
Byte(u8),
|
Byte(u8),
|
||||||
Character(char),
|
Character(char),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Function(Box<Chunk>),
|
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
List(Vec<ConcreteValue>),
|
List(Vec<ConcreteValue>),
|
||||||
Range(RangeValue),
|
Range(RangeValue),
|
||||||
@ -27,10 +26,6 @@ impl ConcreteValue {
|
|||||||
Value::Concrete(self)
|
Value::Concrete(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(chunk: Chunk) -> Self {
|
|
||||||
ConcreteValue::Function(Box::new(chunk))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
||||||
ConcreteValue::List(into_list.into())
|
ConcreteValue::List(into_list.into())
|
||||||
}
|
}
|
||||||
@ -57,7 +52,6 @@ impl ConcreteValue {
|
|||||||
ConcreteValue::Byte(_) => Type::Byte,
|
ConcreteValue::Byte(_) => Type::Byte,
|
||||||
ConcreteValue::Character(_) => Type::Character,
|
ConcreteValue::Character(_) => Type::Character,
|
||||||
ConcreteValue::Float(_) => Type::Float,
|
ConcreteValue::Float(_) => Type::Float,
|
||||||
ConcreteValue::Function(chunk) => Type::function(chunk.r#type().clone()),
|
|
||||||
ConcreteValue::Integer(_) => Type::Integer,
|
ConcreteValue::Integer(_) => Type::Integer,
|
||||||
ConcreteValue::List(list) => {
|
ConcreteValue::List(list) => {
|
||||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
||||||
@ -70,28 +64,43 @@ impl ConcreteValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
pub fn add(&self, other: &Self) -> ConcreteValue {
|
||||||
use ConcreteValue::*;
|
use ConcreteValue::*;
|
||||||
|
|
||||||
let sum = match (self, other) {
|
match (self, other) {
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_add(*right)),
|
(Byte(left), Byte(right)) => {
|
||||||
(Character(left), Character(right)) => {
|
let sum = left.saturating_add(*right);
|
||||||
ConcreteValue::string(format!("{}{}", left, right))
|
|
||||||
}
|
|
||||||
(Character(left), String(right)) => ConcreteValue::string(format!("{}{}", left, right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(*left + *right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_add(*right)),
|
|
||||||
(String(left), String(right)) => ConcreteValue::string(format!("{}{}", left, right)),
|
|
||||||
(String(left), Character(right)) => ConcreteValue::string(format!("{}{}", left, right)),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotAdd(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(sum)
|
Byte(sum)
|
||||||
|
}
|
||||||
|
(Character(left), Character(right)) => {
|
||||||
|
let mut concatenated = DustString::new();
|
||||||
|
|
||||||
|
concatenated.push(*left);
|
||||||
|
concatenated.push(*right);
|
||||||
|
|
||||||
|
String(concatenated)
|
||||||
|
}
|
||||||
|
(Float(left), Float(right)) => {
|
||||||
|
let sum = left + right;
|
||||||
|
|
||||||
|
Float(sum)
|
||||||
|
}
|
||||||
|
(Integer(left), Integer(right)) => {
|
||||||
|
let sum = left.saturating_add(*right);
|
||||||
|
|
||||||
|
Integer(sum)
|
||||||
|
}
|
||||||
|
(String(left), Character(_)) => todo!(),
|
||||||
|
(String(left), String(right)) => todo!(),
|
||||||
|
_ => panic!(
|
||||||
|
"{}",
|
||||||
|
ValueError::CannotAdd(
|
||||||
|
Value::Concrete(self.clone()),
|
||||||
|
Value::Concrete(other.clone())
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
||||||
@ -168,18 +177,16 @@ impl ConcreteValue {
|
|||||||
Ok(product)
|
Ok(product)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negate(&self) -> Result<ConcreteValue, ValueError> {
|
pub fn negate(&self) -> ConcreteValue {
|
||||||
use ConcreteValue::*;
|
use ConcreteValue::*;
|
||||||
|
|
||||||
let negated = match self {
|
match self {
|
||||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
Boolean(value) => ConcreteValue::Boolean(!value),
|
||||||
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
||||||
Float(value) => ConcreteValue::Float(-value),
|
Float(value) => ConcreteValue::Float(-value),
|
||||||
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
||||||
_ => return Err(ValueError::CannotNegate(self.clone().to_value())),
|
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(negated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
||||||
@ -201,7 +208,6 @@ impl ConcreteValue {
|
|||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left == right),
|
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left == right),
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left == right),
|
(Character(left), Character(right)) => ConcreteValue::Boolean(left == right),
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left == right),
|
(Float(left), Float(right)) => ConcreteValue::Boolean(left == right),
|
||||||
(Function(left), Function(right)) => ConcreteValue::Boolean(left == right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left == right),
|
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left == right),
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left == right),
|
(List(left), List(right)) => ConcreteValue::Boolean(left == right),
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left == right),
|
(Range(left), Range(right)) => ConcreteValue::Boolean(left == right),
|
||||||
@ -226,7 +232,6 @@ impl ConcreteValue {
|
|||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
|
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
|
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
|
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
|
||||||
(Function(left), Function(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
|
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
|
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
|
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
|
||||||
@ -250,7 +255,6 @@ impl ConcreteValue {
|
|||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
|
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
|
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
|
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
(Function(left), Function(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
|
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
|
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
|
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
|
||||||
@ -276,7 +280,6 @@ impl Clone for ConcreteValue {
|
|||||||
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
|
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
|
||||||
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
||||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
||||||
ConcreteValue::Function(function) => ConcreteValue::Function(function.clone()),
|
|
||||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
||||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
||||||
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
||||||
@ -300,7 +303,6 @@ impl Display for ConcreteValue {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ConcreteValue::Function(chunk) => write!(f, "{}", chunk.r#type()),
|
|
||||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
||||||
ConcreteValue::List(list) => {
|
ConcreteValue::List(list) => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
|
26
dust-lang/src/value/function.rs
Normal file
26
dust-lang/src/value/function.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use crate::FunctionType;
|
||||||
|
|
||||||
|
use super::DustString;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct Function {
|
||||||
|
pub name: Option<DustString>,
|
||||||
|
pub r#type: FunctionType,
|
||||||
|
pub prototype_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Function {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let mut type_string = self.r#type.to_string();
|
||||||
|
|
||||||
|
if let Some(name) = &self.name {
|
||||||
|
debug_assert!(type_string.starts_with("fn "));
|
||||||
|
|
||||||
|
type_string.insert_str(3, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{type_string}")
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,34 @@
|
|||||||
//! Runtime values used by the VM.
|
//! Runtime values used by the VM.
|
||||||
mod abstract_value;
|
mod abstract_list;
|
||||||
mod concrete_value;
|
mod concrete_value;
|
||||||
|
mod function;
|
||||||
mod range_value;
|
mod range_value;
|
||||||
|
|
||||||
pub use abstract_value::AbstractValue;
|
pub use abstract_list::AbstractList;
|
||||||
pub use concrete_value::{ConcreteValue, DustString};
|
pub use concrete_value::{ConcreteValue, DustString};
|
||||||
|
pub use function::Function;
|
||||||
pub use range_value::RangeValue;
|
pub use range_value::RangeValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Type, Vm};
|
use crate::{vm::Record, Type, Vm};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
#[serde(skip)]
|
|
||||||
Abstract(AbstractValue),
|
|
||||||
Concrete(ConcreteValue),
|
Concrete(ConcreteValue),
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
AbstractList(AbstractList),
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
Function(Function),
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
SelfFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn into_concrete_owned<'a>(self, vm: &'a Vm<'a>) -> ConcreteValue {
|
|
||||||
match self {
|
|
||||||
Value::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm),
|
|
||||||
Value::Concrete(concrete_value) => concrete_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<&bool> {
|
pub fn as_boolean(&self) -> Option<&bool> {
|
||||||
if let Value::Concrete(ConcreteValue::Boolean(value)) = self {
|
if let Value::Concrete(ConcreteValue::Boolean(value)) = self {
|
||||||
Some(value)
|
Some(value)
|
||||||
@ -35,20 +37,34 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
pub fn as_function(&self) -> Option<&Function> {
|
||||||
match self {
|
if let Value::Function(function) = self {
|
||||||
Value::Abstract(abstract_value) => abstract_value.r#type(),
|
Some(function)
|
||||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn r#type(&self) -> Type {
|
||||||
match (self, other) {
|
match self {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.add(right).map(Value::Concrete),
|
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||||
_ => Err(ValueError::CannotAdd(self.to_owned(), other.to_owned())),
|
Value::AbstractList(AbstractList { item_type, .. }) => {
|
||||||
|
Type::List(Box::new(item_type.clone()))
|
||||||
|
}
|
||||||
|
Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())),
|
||||||
|
Value::SelfFunction => Type::SelfFunction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, other: &Value) -> Value {
|
||||||
|
let concrete = match (self, other) {
|
||||||
|
(Value::Concrete(left), Value::Concrete(right)) => left.add(right),
|
||||||
|
_ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::Concrete(concrete)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||||
@ -91,11 +107,13 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negate(&self) -> Result<Value, ValueError> {
|
pub fn negate(&self) -> Value {
|
||||||
match self {
|
let concrete = match self {
|
||||||
Value::Concrete(concrete_value) => concrete_value.negate().map(Value::Concrete),
|
Value::Concrete(concrete_value) => concrete_value.negate(),
|
||||||
_ => Err(ValueError::CannotNegate(self.to_owned())),
|
_ => panic!("{}", ValueError::CannotNegate(self.clone())),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Value::Concrete(concrete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(&self) -> Result<Value, ValueError> {
|
pub fn not(&self) -> Result<Value, ValueError> {
|
||||||
@ -132,10 +150,12 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display(&self, vm: &Vm) -> DustString {
|
pub fn display(&self, record: &Record) -> DustString {
|
||||||
match self {
|
match self {
|
||||||
Value::Abstract(abstract_value) => abstract_value.display(vm),
|
Value::AbstractList(list) => list.display(record),
|
||||||
Value::Concrete(concrete_value) => concrete_value.display(),
|
Value::Concrete(concrete_value) => concrete_value.display(),
|
||||||
|
Value::Function(function) => DustString::from(function.to_string()),
|
||||||
|
Value::SelfFunction => DustString::from("self"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,8 +163,10 @@ impl Value {
|
|||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::Abstract(abstract_value) => write!(f, "{}", abstract_value),
|
Value::Concrete(concrete_value) => write!(f, "{concrete_value}"),
|
||||||
Value::Concrete(concrete_value) => write!(f, "{}", concrete_value),
|
Value::AbstractList(list) => write!(f, "{list}"),
|
||||||
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
|
Value::SelfFunction => write!(f, "self"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
dust-lang/src/vm/call_stack.rs
Normal file
52
dust-lang/src/vm/call_stack.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
|
use super::FunctionCall;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct CallStack {
|
||||||
|
pub calls: Vec<FunctionCall>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallStack {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
CallStack {
|
||||||
|
calls: Vec::with_capacity(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
CallStack {
|
||||||
|
calls: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.calls.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, call: FunctionCall) {
|
||||||
|
self.calls.push(call);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<FunctionCall> {
|
||||||
|
self.calls.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for CallStack {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CallStack {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
writeln!(f, "-- DUST CALL STACK --")?;
|
||||||
|
|
||||||
|
for FunctionCall { function, .. } in &self.calls {
|
||||||
|
writeln!(f, "{function}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
25
dust-lang/src/vm/error.rs
Normal file
25
dust-lang/src/vm/error.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use crate::DustString;
|
||||||
|
|
||||||
|
use super::call_stack::CallStack;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum VmError {
|
||||||
|
CallStackUnderflow { thread_name: DustString },
|
||||||
|
InstructionIndexOutOfBounds { call_stack: CallStack, ip: usize },
|
||||||
|
MalformedInstruction { instruction: InstructionData },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for VmError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::CallStackUnderflow { thread_name } => {
|
||||||
|
write!(f, "Call stack underflow in thread {thread_name}")
|
||||||
|
}
|
||||||
|
Self::InstructionIndexOutOfBounds { call_stack, ip } => {
|
||||||
|
write!(f, "Instruction index {} out of bounds\n{call_stack}", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,216 +1,62 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
|
mod call_stack;
|
||||||
|
mod error;
|
||||||
|
mod record;
|
||||||
mod runner;
|
mod runner;
|
||||||
|
mod thread;
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
sync::mpsc,
|
||||||
|
thread::spawn,
|
||||||
|
};
|
||||||
|
|
||||||
use runner::Runner;
|
pub use error::VmError;
|
||||||
|
pub use record::Record;
|
||||||
|
use thread::Thread;
|
||||||
|
|
||||||
use crate::{compile, instruction::*, Chunk, DustError, Span, Value};
|
use crate::{compile, Chunk, DustError, Value};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
let vm = Vm::new(&chunk, None, None);
|
let vm = Vm::new(chunk);
|
||||||
|
|
||||||
Ok(vm.run())
|
Ok(vm.run())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dust virtual machine.
|
pub struct Vm {
|
||||||
///
|
threads: Vec<Thread>,
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Vm<'a> {
|
|
||||||
stack: Vec<Register>,
|
|
||||||
|
|
||||||
runners: Vec<Runner>,
|
|
||||||
chunk: &'a Chunk,
|
|
||||||
parent: Option<&'a Vm<'a>>,
|
|
||||||
|
|
||||||
ip: usize,
|
|
||||||
last_assigned_register: Option<u8>,
|
|
||||||
return_value: Option<Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Vm<'a> {
|
impl Vm {
|
||||||
pub fn new(chunk: &'a Chunk, parent: Option<&'a Vm<'a>>, runners: Option<Vec<Runner>>) -> Self {
|
pub fn new(chunk: Chunk) -> Self {
|
||||||
let stack = vec![Register::Empty; chunk.stack_size()];
|
let threads = vec![Thread::new(chunk)];
|
||||||
let runners = runners.unwrap_or_else(|| {
|
|
||||||
let mut runners = Vec::with_capacity(chunk.instructions().len());
|
|
||||||
|
|
||||||
for (instruction, _) in chunk.instructions() {
|
debug_assert_eq!(1, threads.capacity());
|
||||||
let runner = Runner::new(*instruction);
|
|
||||||
|
|
||||||
runners.push(runner);
|
Self { threads }
|
||||||
}
|
|
||||||
|
|
||||||
runners
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
chunk,
|
|
||||||
runners,
|
|
||||||
stack,
|
|
||||||
parent,
|
|
||||||
ip: 0,
|
|
||||||
last_assigned_register: None,
|
|
||||||
return_value: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk(&'a self) -> &'a Chunk {
|
|
||||||
self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_position(&self) -> Span {
|
|
||||||
let index = self.ip.saturating_sub(1);
|
|
||||||
let (_, position) = self.chunk.instructions()[index];
|
|
||||||
|
|
||||||
position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> Option<Value> {
|
pub fn run(mut self) -> Option<Value> {
|
||||||
self.execute_next_runner();
|
if self.threads.len() == 1 {
|
||||||
|
return self.threads[0].run();
|
||||||
self.return_value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_next_runner(&mut self) {
|
|
||||||
assert!(
|
|
||||||
self.ip < self.runners.len(),
|
|
||||||
"Runtime Error: IP out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
let runner = self.runners[self.ip];
|
|
||||||
|
|
||||||
runner.run(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
|
|
||||||
log::trace!("Follow pointer {pointer}");
|
|
||||||
|
|
||||||
match pointer {
|
|
||||||
Pointer::Stack(register_index) => self.open_register(register_index),
|
|
||||||
Pointer::Constant(constant_index) => self.get_constant(constant_index),
|
|
||||||
Pointer::ParentStack(register_index) => {
|
|
||||||
assert!(self.parent.is_some(), "Vm Error: Expected parent");
|
|
||||||
|
|
||||||
self.parent.unwrap().open_register(register_index)
|
|
||||||
}
|
|
||||||
Pointer::ParentConstant(constant_index) => {
|
|
||||||
assert!(self.parent.is_some(), "Vm Error: Expected parent");
|
|
||||||
|
|
||||||
self.parent.unwrap().get_constant(constant_index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn follow_pointer_allow_empty(&self, pointer: Pointer) -> Option<&Value> {
|
let (tx, rx) = mpsc::channel();
|
||||||
log::trace!("Follow pointer {pointer}");
|
|
||||||
|
|
||||||
match pointer {
|
for mut thread in self.threads {
|
||||||
Pointer::Stack(register_index) => self.open_register_allow_empty(register_index),
|
let tx = tx.clone();
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
let constant = self.get_constant(constant_index);
|
|
||||||
|
|
||||||
Some(constant)
|
spawn(move || {
|
||||||
}
|
let return_value = thread.run();
|
||||||
Pointer::ParentStack(register_index) => {
|
|
||||||
assert!(self.parent.is_some(), "Vm Error: Expected parent");
|
|
||||||
|
|
||||||
self.parent
|
if let Some(value) = return_value {
|
||||||
.unwrap()
|
tx.send(value).unwrap();
|
||||||
.open_register_allow_empty(register_index)
|
}
|
||||||
}
|
});
|
||||||
Pointer::ParentConstant(constant_index) => {
|
|
||||||
assert!(self.parent.is_some(), "Vm Error: Expected parent");
|
|
||||||
|
|
||||||
let constant = self.parent.unwrap().get_constant(constant_index);
|
|
||||||
|
|
||||||
Some(constant)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn open_register(&self, register_index: u8) -> &Value {
|
rx.into_iter().last()
|
||||||
log::trace!("Open register R{register_index}");
|
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
register_index < self.stack.len(),
|
|
||||||
"VM Error: Register index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
let register = &self.stack[register_index];
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => self.follow_pointer(*pointer),
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_register_allow_empty(&self, register_index: u8) -> Option<&Value> {
|
|
||||||
log::trace!("Open register R{register_index}");
|
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
register_index < self.stack.len(),
|
|
||||||
"VM Error: Register index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
let register = &self.stack[register_index];
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => Some(value),
|
|
||||||
Register::Pointer(pointer) => Some(self.follow_pointer(*pointer)),
|
|
||||||
Register::Empty => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get a value from an Argument
|
|
||||||
fn get_argument(&self, index: u8, is_constant: bool) -> &Value {
|
|
||||||
if is_constant {
|
|
||||||
self.get_constant(index)
|
|
||||||
} else {
|
|
||||||
self.open_register(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_register(&mut self, to_register: u8, register: Register) {
|
|
||||||
self.last_assigned_register = Some(to_register);
|
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
to_register < self.stack.len(),
|
|
||||||
"VM Error: Register index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.stack[to_register] = register;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_constant(&self, constant_index: u8) -> &Value {
|
|
||||||
let constant_index = constant_index as usize;
|
|
||||||
let constants = self.chunk.constants();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
constant_index < constants.len(),
|
|
||||||
"VM Error: Constant index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
&constants[constant_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_local_register(&self, local_index: u8) -> u8 {
|
|
||||||
let local_index = local_index as usize;
|
|
||||||
let locals = self.chunk.locals();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
local_index < locals.len(),
|
|
||||||
"VM Error: Local index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
locals[local_index].register_index
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +81,6 @@ impl Display for Register {
|
|||||||
pub enum Pointer {
|
pub enum Pointer {
|
||||||
Stack(u8),
|
Stack(u8),
|
||||||
Constant(u8),
|
Constant(u8),
|
||||||
ParentStack(u8),
|
|
||||||
ParentConstant(u8),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pointer {
|
impl Display for Pointer {
|
||||||
@ -244,8 +88,13 @@ impl Display for Pointer {
|
|||||||
match self {
|
match self {
|
||||||
Self::Stack(index) => write!(f, "R{}", index),
|
Self::Stack(index) => write!(f, "R{}", index),
|
||||||
Self::Constant(index) => write!(f, "C{}", index),
|
Self::Constant(index) => write!(f, "C{}", index),
|
||||||
Self::ParentStack(index) => write!(f, "PR{}", index),
|
|
||||||
Self::ParentConstant(index) => write!(f, "PC{}", index),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct FunctionCall {
|
||||||
|
prototype_index: usize,
|
||||||
|
record_index: usize,
|
||||||
|
return_register: u8,
|
||||||
|
}
|
||||||
|
167
dust-lang/src/vm/record.rs
Normal file
167
dust-lang/src/vm/record.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use std::env::consts::OS;
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::{Local, Span, Value};
|
||||||
|
|
||||||
|
use super::{runner::RunAction, Pointer, Register};
|
||||||
|
|
||||||
|
pub struct Record {
|
||||||
|
pub ip: usize,
|
||||||
|
pub actions: SmallVec<[RunAction; 32]>,
|
||||||
|
positions: SmallVec<[Span; 32]>,
|
||||||
|
|
||||||
|
stack: Vec<Register>,
|
||||||
|
constants: SmallVec<[Value; 16]>,
|
||||||
|
locals: SmallVec<[Local; 8]>,
|
||||||
|
|
||||||
|
last_assigned_register: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Record {
|
||||||
|
pub fn new(
|
||||||
|
stack: Vec<Register>,
|
||||||
|
constants: SmallVec<[Value; 16]>,
|
||||||
|
locals: SmallVec<[Local; 8]>,
|
||||||
|
actions: SmallVec<[RunAction; 32]>,
|
||||||
|
positions: SmallVec<[Span; 32]>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ip: 0,
|
||||||
|
actions,
|
||||||
|
positions,
|
||||||
|
stack,
|
||||||
|
constants,
|
||||||
|
locals,
|
||||||
|
last_assigned_register: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stack_size(&self) -> usize {
|
||||||
|
self.stack.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_position(&self) -> Span {
|
||||||
|
self.positions[self.ip]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_assigned_register(&self) -> Option<u8> {
|
||||||
|
self.last_assigned_register
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
|
||||||
|
log::trace!("Follow pointer {pointer}");
|
||||||
|
|
||||||
|
match pointer {
|
||||||
|
Pointer::Stack(register_index) => self.open_register(register_index),
|
||||||
|
Pointer::Constant(constant_index) => self.get_constant(constant_index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn follow_pointer_allow_empty(&self, pointer: Pointer) -> Option<&Value> {
|
||||||
|
log::trace!("Follow pointer {pointer}");
|
||||||
|
|
||||||
|
match pointer {
|
||||||
|
Pointer::Stack(register_index) => self.open_register_allow_empty(register_index),
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
let constant = self.get_constant(constant_index);
|
||||||
|
|
||||||
|
Some(constant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_register(&self, register_index: u8) -> &Register {
|
||||||
|
log::trace!("Get register R{register_index}");
|
||||||
|
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
register_index < self.stack.len(),
|
||||||
|
"VM Error: Register index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
&self.stack[register_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_register(&mut self, to_register: u8, register: Register) {
|
||||||
|
self.last_assigned_register = Some(to_register);
|
||||||
|
let to_register = to_register as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
to_register < self.stack.len(),
|
||||||
|
"VM Error: Register index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
self.stack[to_register] = register;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_register(&self, register_index: u8) -> &Value {
|
||||||
|
log::trace!("Open register R{register_index}");
|
||||||
|
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
register_index < self.stack.len(),
|
||||||
|
"VM Error: Register index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
let register = &self.stack[register_index];
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::Pointer(pointer) => self.follow_pointer(*pointer),
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_register_allow_empty(&self, register_index: u8) -> Option<&Value> {
|
||||||
|
log::trace!("Open register R{register_index}");
|
||||||
|
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
register_index < self.stack.len(),
|
||||||
|
"VM Error: Register index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
let register = &self.stack[register_index];
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => Some(value),
|
||||||
|
Register::Pointer(pointer) => Some(self.follow_pointer(*pointer)),
|
||||||
|
Register::Empty => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DRY helper to get a value from an Argument
|
||||||
|
pub fn get_argument(&self, index: u8, is_constant: bool) -> &Value {
|
||||||
|
if is_constant {
|
||||||
|
self.get_constant(index)
|
||||||
|
} else {
|
||||||
|
self.open_register(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constant(&self, constant_index: u8) -> &Value {
|
||||||
|
let constant_index = constant_index as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
constant_index < self.constants.len(),
|
||||||
|
"VM Error: Constant index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
&self.constants[constant_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
||||||
|
let local_index = local_index as usize;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
local_index < self.locals.len(),
|
||||||
|
"VM Error: Local index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
self.locals[local_index].register_index
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,37 @@
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{AbstractValue, ConcreteValue, NativeFunction, Type, Value};
|
use crate::{
|
||||||
|
instruction::{Close, LoadBoolean, LoadConstant, Point},
|
||||||
|
AbstractList, ConcreteValue, Instruction, InstructionData, NativeFunction, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Instruction, InstructionData, Pointer, Register, Vm};
|
use super::{thread::ThreadSignal, Pointer, Record, Register};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Runner {
|
pub struct RunAction {
|
||||||
logic: RunnerLogic,
|
pub logic: RunnerLogic,
|
||||||
data: InstructionData,
|
pub data: InstructionData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runner {
|
impl From<&Instruction> for RunAction {
|
||||||
pub fn new(instruction: Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
let (operation, data) = instruction.decode();
|
let (operation, data) = instruction.decode();
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||||
|
|
||||||
Self { logic, data }
|
RunAction { logic, data }
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_parts(logic: RunnerLogic, data: InstructionData) -> Self {
|
|
||||||
Self { logic, data }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(self, vm: &mut Vm) {
|
|
||||||
(self.logic)(vm, self.data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RunnerLogic = fn(&mut Vm, InstructionData);
|
impl From<Instruction> for RunAction {
|
||||||
|
fn from(instruction: Instruction) -> Self {
|
||||||
|
let (operation, data) = instruction.decode();
|
||||||
|
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||||
|
|
||||||
|
RunAction { logic, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RunnerLogic = fn(InstructionData, &mut Record) -> ThreadSignal;
|
||||||
|
|
||||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [
|
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [
|
||||||
r#move,
|
r#move,
|
||||||
@ -56,81 +60,79 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [
|
|||||||
r#return,
|
r#return,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn r#move(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn r#move(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { b, c, .. } = instruction_data;
|
let Point { from, to } = instruction_data.into();
|
||||||
let from_register_has_value = vm
|
let from_register = record.get_register(from);
|
||||||
.stack
|
let from_register_is_empty = matches!(from_register, Register::Empty);
|
||||||
.get(b as usize)
|
|
||||||
.is_some_and(|register| !matches!(register, Register::Empty));
|
|
||||||
let register = Register::Pointer(Pointer::Stack(b));
|
|
||||||
|
|
||||||
if from_register_has_value {
|
if !from_register_is_empty {
|
||||||
vm.set_register(c, register);
|
let register = Register::Pointer(Pointer::Stack(to));
|
||||||
|
|
||||||
|
record.set_register(from, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.ip += 1;
|
ThreadSignal::Continue
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn close(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { b, c, .. } = instruction_data;
|
let Close { from, to } = instruction_data.into();
|
||||||
|
|
||||||
assert!(b < c, "Runtime Error: Malformed instruction");
|
assert!(from < to, "Runtime Error: Malformed instruction");
|
||||||
|
|
||||||
for register_index in b..c {
|
for register_index in from..to {
|
||||||
assert!(
|
assert!(
|
||||||
(register_index as usize) < vm.stack.len(),
|
(register_index as usize) < record.stack_size(),
|
||||||
"Runtime Error: Register index out of bounds"
|
"Runtime Error: Register index out of bounds"
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.stack[register_index as usize] = Register::Empty;
|
record.set_register(register_index, Register::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.ip += 1;
|
ThreadSignal::Continue
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boolean(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn load_boolean(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, b, c, .. } = instruction_data;
|
let LoadBoolean {
|
||||||
let boolean = ConcreteValue::Boolean(b != 0).to_value();
|
destination,
|
||||||
|
value,
|
||||||
|
jump_next,
|
||||||
|
} = instruction_data.into();
|
||||||
|
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
||||||
let register = Register::Value(boolean);
|
let register = Register::Value(boolean);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(destination, register);
|
||||||
|
|
||||||
if c != 0 {
|
if jump_next {
|
||||||
vm.ip += 2;
|
record.ip += 1;
|
||||||
} else {
|
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
ThreadSignal::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn load_constant(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, b, c, .. } = instruction_data;
|
let LoadConstant {
|
||||||
let register = Register::Pointer(Pointer::Constant(b));
|
destination,
|
||||||
|
constant_index,
|
||||||
|
jump_next,
|
||||||
|
} = instruction_data.into();
|
||||||
|
let register = Register::Pointer(Pointer::Constant(constant_index));
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(destination, register);
|
||||||
|
|
||||||
if c != 0 {
|
if jump_next {
|
||||||
vm.ip += 2;
|
record.ip += 1;
|
||||||
} else {
|
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
ThreadSignal::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, b, .. } = instruction_data;
|
let InstructionData { a, b, .. } = instruction_data;
|
||||||
let mut item_pointers = Vec::with_capacity((a - b) as usize);
|
let mut item_pointers = Vec::with_capacity((a - b) as usize);
|
||||||
let stack = vm.stack.as_slice();
|
|
||||||
let mut item_type = Type::Any;
|
let mut item_type = Type::Any;
|
||||||
|
|
||||||
for register_index in b..a {
|
for register_index in b..a {
|
||||||
match &stack[register_index as usize] {
|
match record.get_register(register_index) {
|
||||||
Register::Empty => continue,
|
Register::Empty => continue,
|
||||||
Register::Value(value) => {
|
Register::Value(value) => {
|
||||||
if item_type == Type::Any {
|
if item_type == Type::Any {
|
||||||
@ -139,7 +141,7 @@ pub fn load_list(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
}
|
}
|
||||||
Register::Pointer(pointer) => {
|
Register::Pointer(pointer) => {
|
||||||
if item_type == Type::Any {
|
if item_type == Type::Any {
|
||||||
item_type = vm.follow_pointer(*pointer).r#type();
|
item_type = record.follow_pointer(*pointer).r#type();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,55 +151,39 @@ pub fn load_list(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
item_pointers.push(pointer);
|
item_pointers.push(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
let list_value = Value::Abstract(AbstractValue::List {
|
let list_value = Value::AbstractList(AbstractList {
|
||||||
item_type,
|
item_type,
|
||||||
item_pointers,
|
item_pointers,
|
||||||
});
|
});
|
||||||
let register = Register::Value(list_value);
|
let register = Register::Value(list_value);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_self(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, .. } = instruction_data;
|
let InstructionData { a, .. } = instruction_data;
|
||||||
let register = Register::Value(AbstractValue::FunctionSelf.to_value());
|
let register = Register::Value(Value::SelfFunction);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, b, .. } = instruction_data;
|
let InstructionData { a, b, .. } = instruction_data;
|
||||||
let local_register_index = vm.get_local_register(b);
|
let local_register_index = record.get_local_register(b);
|
||||||
let register = Register::Pointer(Pointer::Stack(local_register_index));
|
let register = Register::Pointer(Pointer::Stack(local_register_index));
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn set_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { b, c, .. } = instruction_data;
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
let local_register_index = vm.get_local_register(c);
|
let local_register_index = record.get_local_register(c);
|
||||||
let register = Register::Pointer(Pointer::Stack(b));
|
let register = Register::Pointer(Pointer::Stack(b));
|
||||||
|
|
||||||
vm.set_register(local_register_index, register);
|
record.set_register(local_register_index, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -206,8 +192,8 @@ pub fn add(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
c_is_constant,
|
c_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let sum = match (left, right) {
|
let sum = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -219,14 +205,10 @@ pub fn add(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(sum);
|
let register = Register::Value(sum);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -235,8 +217,8 @@ pub fn subtract(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
c_is_constant,
|
c_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let difference = match (left, right) {
|
let difference = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -248,14 +230,10 @@ pub fn subtract(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(difference);
|
let register = Register::Value(difference);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiply(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -264,8 +242,8 @@ pub fn multiply(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
c_is_constant,
|
c_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let product = match (left, right) {
|
let product = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -277,14 +255,10 @@ pub fn multiply(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(product);
|
let register = Register::Value(product);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divide(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -293,8 +267,8 @@ pub fn divide(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
c_is_constant,
|
c_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let quotient = match (left, right) {
|
let quotient = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -306,14 +280,10 @@ pub fn divide(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(quotient);
|
let register = Register::Value(quotient);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modulo(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -322,8 +292,8 @@ pub fn modulo(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
c_is_constant,
|
c_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let remainder = match (left, right) {
|
let remainder = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -335,41 +305,31 @@ pub fn modulo(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(remainder);
|
let register = Register::Value(remainder);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
b,
|
b,
|
||||||
b_is_constant,
|
b_is_constant,
|
||||||
c,
|
c,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let value = vm.get_argument(b, b_is_constant);
|
let value = record.get_argument(b, b_is_constant);
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!("VM Error: Expected boolean value for TEST operation",);
|
||||||
"VM Error: Expected boolean value for TEST operation at {}",
|
|
||||||
vm.current_position()
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
let test_value = c != 0;
|
let test_value = c != 0;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
vm.ip += 2;
|
record.ip += 2;
|
||||||
} else {
|
} else {
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_set(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -377,19 +337,16 @@ pub fn test_set(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
b_is_constant,
|
b_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let value = vm.get_argument(b, b_is_constant);
|
let value = record.get_argument(b, b_is_constant);
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!("VM Error: Expected boolean value for TEST_SET operation",);
|
||||||
"VM Error: Expected boolean value for TEST_SET operation at {}",
|
|
||||||
vm.current_position()
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
let test_value = c != 0;
|
let test_value = c != 0;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
vm.ip += 2;
|
record.ip += 2;
|
||||||
} else {
|
} else {
|
||||||
let pointer = if b_is_constant {
|
let pointer = if b_is_constant {
|
||||||
Pointer::Constant(b)
|
Pointer::Constant(b)
|
||||||
@ -398,15 +355,11 @@ pub fn test_set(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
};
|
};
|
||||||
let register = Register::Pointer(pointer);
|
let register = Register::Pointer(pointer);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn equal(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
b,
|
b,
|
||||||
c,
|
c,
|
||||||
@ -415,20 +368,17 @@ pub fn equal(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
d,
|
d,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let is_equal = left == right;
|
let is_equal = left == right;
|
||||||
|
|
||||||
if is_equal == d {
|
if is_equal == d {
|
||||||
vm.ip += 2;
|
record.ip += 2;
|
||||||
} else {
|
} else {
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn less(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
b,
|
b,
|
||||||
c,
|
c,
|
||||||
@ -437,20 +387,17 @@ pub fn less(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
d,
|
d,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let is_less = left < right;
|
let is_less = left < right;
|
||||||
|
|
||||||
if is_less == d {
|
if is_less == d {
|
||||||
vm.ip += 2;
|
record.ip += 2;
|
||||||
} else {
|
} else {
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_equal(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn less_equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
b,
|
b,
|
||||||
c,
|
c,
|
||||||
@ -459,107 +406,77 @@ pub fn less_equal(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
d,
|
d,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let left = vm.get_argument(b, b_is_constant);
|
let left = record.get_argument(b, b_is_constant);
|
||||||
let right = vm.get_argument(c, c_is_constant);
|
let right = record.get_argument(c, c_is_constant);
|
||||||
let is_less_or_equal = left <= right;
|
let is_less_or_equal = left <= right;
|
||||||
|
|
||||||
if is_less_or_equal == d {
|
if is_less_or_equal == d {
|
||||||
vm.ip += 2;
|
record.ip += 2;
|
||||||
} else {
|
} else {
|
||||||
vm.ip += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negate(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn negate(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
b_is_constant,
|
b_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let argument = vm.get_argument(b, b_is_constant);
|
let argument = record.get_argument(b, b_is_constant);
|
||||||
let negated = match argument {
|
let negated = argument.negate();
|
||||||
Value::Concrete(value) => match value {
|
let register = Register::Value(negated);
|
||||||
ConcreteValue::Float(float) => ConcreteValue::Float(-float),
|
|
||||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(-integer),
|
|
||||||
_ => panic!("Value Error: Cannot negate value"),
|
|
||||||
},
|
|
||||||
Value::Abstract(_) => panic!("VM Error: Cannot negate value"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(negated));
|
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
b_is_constant,
|
b_is_constant,
|
||||||
..
|
..
|
||||||
} = instruction_data;
|
} = instruction_data;
|
||||||
let argument = vm.get_argument(b, b_is_constant);
|
let argument = record.get_argument(b, b_is_constant);
|
||||||
let not = match argument {
|
let not = match argument {
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
||||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
||||||
};
|
};
|
||||||
let register = Register::Value(Value::Concrete(not));
|
let register = Register::Value(Value::Concrete(not));
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jump(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { b, c, .. } = instruction_data;
|
let InstructionData { b, c, .. } = instruction_data;
|
||||||
let offset = b as usize;
|
let offset = b as usize;
|
||||||
let is_positive = c != 0;
|
let is_positive = c != 0;
|
||||||
|
|
||||||
if is_positive {
|
if is_positive {
|
||||||
vm.ip += offset + 1
|
record.ip += offset + 1
|
||||||
} else {
|
} else {
|
||||||
vm.ip -= offset
|
record.ip -= offset
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(vm: &mut Vm<'_>, instruction_data: InstructionData) {
|
pub fn call(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData {
|
let InstructionData { a, b, c, .. } = instruction_data;
|
||||||
a,
|
let prototype = record.get_prototype(b);
|
||||||
b,
|
|
||||||
c,
|
|
||||||
b_is_constant,
|
|
||||||
..
|
|
||||||
} = instruction_data;
|
|
||||||
let function = vm.get_argument(b, b_is_constant);
|
|
||||||
let mut function_vm = if let Value::Concrete(ConcreteValue::Function(chunk)) = function {
|
|
||||||
Vm::new(chunk, Some(vm), None)
|
|
||||||
} else if let Value::Abstract(AbstractValue::FunctionSelf) = function {
|
|
||||||
Vm::new(vm.chunk, Some(vm), Some(vm.runners.clone()))
|
|
||||||
} else {
|
|
||||||
panic!("VM Error: Expected function")
|
|
||||||
};
|
|
||||||
let first_argument_index = a - c;
|
let first_argument_index = a - c;
|
||||||
let mut argument_index = 0;
|
let mut argument_index = 0;
|
||||||
|
|
||||||
for argument_register_index in first_argument_index..a {
|
for argument_register_index in first_argument_index..a {
|
||||||
let target_register_is_empty =
|
let target_register_is_empty = matches!(
|
||||||
matches!(vm.stack[argument_register_index as usize], Register::Empty);
|
record.stack[argument_register_index as usize],
|
||||||
|
Register::Empty
|
||||||
|
);
|
||||||
|
|
||||||
if target_register_is_empty {
|
if target_register_is_empty {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function_vm.set_register(
|
function_record.set_register(
|
||||||
argument_index as u8,
|
argument_index as u8,
|
||||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
||||||
);
|
);
|
||||||
@ -567,31 +484,27 @@ pub fn call(vm: &mut Vm<'_>, instruction_data: InstructionData) {
|
|||||||
argument_index += 1;
|
argument_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = function_vm.run();
|
let return_value = function_record.run();
|
||||||
|
|
||||||
if let Some(concrete_value) = return_value {
|
if let Some(concrete_value) = return_value {
|
||||||
let register = Register::Value(concrete_value);
|
let register = Register::Value(concrete_value);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_native(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn call_native(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let InstructionData { a, b, c, .. } = instruction_data;
|
let InstructionData { a, b, c, .. } = instruction_data;
|
||||||
let first_argument_index = (a - c) as usize;
|
let first_argument_index = (a - c) as usize;
|
||||||
let argument_range = first_argument_index..a as usize;
|
let argument_range = first_argument_index..a as usize;
|
||||||
let mut arguments: SmallVec<[&Value; 4]> = SmallVec::new();
|
let mut arguments: SmallVec<[&Value; 4]> = SmallVec::new();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
let register = &vm.stack[register_index];
|
let register = &record.stack[register_index];
|
||||||
let value = match register {
|
let value = match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
Register::Pointer(pointer) => {
|
Register::Pointer(pointer) => {
|
||||||
let value_option = vm.follow_pointer_allow_empty(*pointer);
|
let value_option = record.follow_pointer_allow_empty(*pointer);
|
||||||
|
|
||||||
match value_option {
|
match value_option {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
@ -605,30 +518,24 @@ pub fn call_native(vm: &mut Vm, instruction_data: InstructionData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let function = NativeFunction::from(b);
|
let function = NativeFunction::from(b);
|
||||||
let return_value = function.call(vm, arguments).unwrap();
|
let return_value = function.call(record.arguments).unwrap();
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
let register = Register::Value(value);
|
let register = Register::Value(value);
|
||||||
|
|
||||||
vm.set_register(a, register);
|
record.set_register(a, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.ip += 1;
|
|
||||||
|
|
||||||
vm.execute_next_runner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(vm: &mut Vm, instruction_data: InstructionData) {
|
pub fn r#return(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||||
let should_return_value = instruction_data.b != 0;
|
let should_return_value = instruction_data.b != 0;
|
||||||
|
|
||||||
if !should_return_value {
|
if !should_return_value {}
|
||||||
return vm.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(register_index) = &vm.last_assigned_register {
|
if let Some(register_index) = &record.last_assigned_register {
|
||||||
let return_value = vm.open_register(*register_index).clone();
|
let return_value = record.open_register(*register_index).clone();
|
||||||
|
|
||||||
vm.return_value = Some(return_value);
|
record.return_value = Some(return_value);
|
||||||
} else {
|
} else {
|
||||||
panic!("Stack underflow");
|
panic!("Stack underflow");
|
||||||
}
|
}
|
||||||
@ -642,7 +549,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
||||||
(Operation::MOVE, r#move),
|
(Operation::POINT, r#move),
|
||||||
(Operation::CLOSE, close),
|
(Operation::CLOSE, close),
|
||||||
(Operation::LOAD_BOOLEAN, load_boolean),
|
(Operation::LOAD_BOOLEAN, load_boolean),
|
||||||
(Operation::LOAD_CONSTANT, load_constant),
|
(Operation::LOAD_CONSTANT, load_constant),
|
||||||
|
93
dust-lang/src/vm/thread.rs
Normal file
93
dust-lang/src/vm/thread.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use crate::{Chunk, Value};
|
||||||
|
|
||||||
|
use super::{call_stack::CallStack, record::Record, runner::RunAction, FunctionCall, VmError};
|
||||||
|
|
||||||
|
fn create_records(chunk: Chunk, records: &mut Vec<Record>) {
|
||||||
|
let (_, _, instructions, positions, constants, locals, prototypes, stack_size) =
|
||||||
|
chunk.take_data();
|
||||||
|
let actions = instructions
|
||||||
|
.into_iter()
|
||||||
|
.map(|instruction| RunAction::from(instruction))
|
||||||
|
.collect();
|
||||||
|
let record = Record::new(
|
||||||
|
Vec::with_capacity(stack_size),
|
||||||
|
constants,
|
||||||
|
locals,
|
||||||
|
actions,
|
||||||
|
positions,
|
||||||
|
);
|
||||||
|
|
||||||
|
for chunk in prototypes {
|
||||||
|
create_records(chunk, records);
|
||||||
|
}
|
||||||
|
|
||||||
|
records.push(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Thread {
|
||||||
|
call_stack: CallStack,
|
||||||
|
records: Vec<Record>,
|
||||||
|
return_register: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
pub fn new(chunk: Chunk) -> Self {
|
||||||
|
let call_stack = CallStack::new();
|
||||||
|
let mut records = Vec::with_capacity(chunk.prototypes().len());
|
||||||
|
|
||||||
|
create_records(chunk, &mut records);
|
||||||
|
|
||||||
|
Thread {
|
||||||
|
call_stack,
|
||||||
|
records,
|
||||||
|
return_register: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Option<Value> {
|
||||||
|
assert!(!self.call_stack.is_empty());
|
||||||
|
|
||||||
|
let mut record = &mut self.records[0];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
assert!(
|
||||||
|
record.ip < record.actions.len(),
|
||||||
|
"{}",
|
||||||
|
VmError::InstructionIndexOutOfBounds {
|
||||||
|
call_stack: self.call_stack.clone(),
|
||||||
|
ip: record.ip,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let action = record.actions[record.ip];
|
||||||
|
let signal = (action.logic)(action.data, &mut record);
|
||||||
|
|
||||||
|
match signal {
|
||||||
|
ThreadSignal::Continue => {
|
||||||
|
record.ip += 1;
|
||||||
|
}
|
||||||
|
ThreadSignal::Call(FunctionCall {
|
||||||
|
record_index,
|
||||||
|
return_register,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
record = &mut self.records[record_index];
|
||||||
|
self.return_register = Some(return_register);
|
||||||
|
}
|
||||||
|
ThreadSignal::Return(value_option) => {
|
||||||
|
let outer_call = self.call_stack.pop();
|
||||||
|
|
||||||
|
if outer_call.is_none() {
|
||||||
|
return value_option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ThreadSignal {
|
||||||
|
Continue,
|
||||||
|
Call(FunctionCall),
|
||||||
|
Return(Option<Value>),
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user