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 = [
|
||||
"js",
|
||||
] } # 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 = [
|
||||
"serde",
|
||||
], default-features = false }
|
||||
|
@ -46,7 +46,7 @@ use std::{
|
||||
|
||||
use colored::{ColoredString, Colorize};
|
||||
|
||||
use crate::{value::ConcreteValue, Chunk, Local, Value};
|
||||
use crate::{Chunk, Local};
|
||||
|
||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
||||
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 24)];
|
||||
@ -93,7 +93,8 @@ pub struct Disassembler<'a, W> {
|
||||
// Options
|
||||
style: bool,
|
||||
indent: usize,
|
||||
output_width: usize,
|
||||
width: usize,
|
||||
show_type: bool,
|
||||
}
|
||||
|
||||
impl<'a, W: Write> Disassembler<'a, W> {
|
||||
@ -104,7 +105,8 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
source: None,
|
||||
style: false,
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
self.indent = indent;
|
||||
|
||||
@ -135,7 +149,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
fn line_length(&self) -> usize {
|
||||
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> {
|
||||
@ -159,10 +173,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
add_border: bool,
|
||||
) -> Result<(), io::Error> {
|
||||
let (line_content, overflow) = {
|
||||
if text.len() > self.output_width {
|
||||
if text.len() > self.width {
|
||||
let split_index = text
|
||||
.char_indices()
|
||||
.nth(self.output_width)
|
||||
.nth(self.width)
|
||||
.map(|(index, _)| index)
|
||||
.unwrap_or_else(|| text.len());
|
||||
|
||||
@ -226,15 +240,15 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -249,7 +263,8 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
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> {
|
||||
@ -260,21 +275,26 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
}
|
||||
|
||||
column_name_line.push('│');
|
||||
self.write_centered_with_border_bold("Instructions")?;
|
||||
self.write_centered_with_border(INSTRUCTION_BORDERS[0])?;
|
||||
self.write_centered_with_border(&column_name_line)?;
|
||||
self.write_centered_with_border(INSTRUCTION_BORDERS[1])?;
|
||||
self.write_center_border_bold("Instructions")?;
|
||||
self.write_center_border(INSTRUCTION_BORDERS[0])?;
|
||||
self.write_center_border(&column_name_line)?;
|
||||
self.write_center_border(INSTRUCTION_BORDERS[1])?;
|
||||
|
||||
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
||||
let position = position.to_string();
|
||||
for (index, instruction) in self.chunk.instructions().iter().enumerate() {
|
||||
let position = self
|
||||
.chunk
|
||||
.positions()
|
||||
.get(index)
|
||||
.map(|position| position.to_string())
|
||||
.unwrap_or("stripped".to_string());
|
||||
let operation = instruction.operation().to_string();
|
||||
let info = instruction.disassembly_info();
|
||||
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(())
|
||||
}
|
||||
@ -287,10 +307,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
}
|
||||
|
||||
column_name_line.push('│');
|
||||
self.write_centered_with_border_bold("Locals")?;
|
||||
self.write_centered_with_border(LOCAL_BORDERS[0])?;
|
||||
self.write_centered_with_border(&column_name_line)?;
|
||||
self.write_centered_with_border(LOCAL_BORDERS[1])?;
|
||||
self.write_center_border_bold("Locals")?;
|
||||
self.write_center_border(LOCAL_BORDERS[0])?;
|
||||
self.write_center_border(&column_name_line)?;
|
||||
self.write_center_border(LOCAL_BORDERS[1])?;
|
||||
|
||||
for (
|
||||
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}│"
|
||||
);
|
||||
|
||||
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(())
|
||||
}
|
||||
@ -330,17 +350,14 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
}
|
||||
|
||||
column_name_line.push('│');
|
||||
self.write_centered_with_border_bold("Constants")?;
|
||||
self.write_centered_with_border(CONSTANT_BORDERS[0])?;
|
||||
self.write_centered_with_border(&column_name_line)?;
|
||||
self.write_centered_with_border(CONSTANT_BORDERS[1])?;
|
||||
self.write_center_border_bold("Constants")?;
|
||||
self.write_center_border(CONSTANT_BORDERS[0])?;
|
||||
self.write_center_border(&column_name_line)?;
|
||||
self.write_center_border(CONSTANT_BORDERS[1])?;
|
||||
|
||||
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 value_display = if is_function {
|
||||
"Function displayed below".to_string()
|
||||
} else {
|
||||
let value_display = {
|
||||
let mut value_string = value.to_string();
|
||||
|
||||
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}│");
|
||||
|
||||
self.write_centered_with_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_center_border(&constant_display)?;
|
||||
}
|
||||
|
||||
self.write_centered_with_border(CONSTANT_BORDERS[2])?;
|
||||
self.write_center_border(CONSTANT_BORDERS[2])?;
|
||||
|
||||
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
|
||||
.chunk
|
||||
.name()
|
||||
@ -393,16 +416,20 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
.unwrap_or("Dust Chunk Disassembly".to_string())
|
||||
});
|
||||
|
||||
self.write_page_border(TOP_BORDER)?;
|
||||
self.write_char('\n')?;
|
||||
self.write_centered_with_border_bold(&name_display)?;
|
||||
self.write_center_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 {
|
||||
let lazily_formatted = source.split_whitespace().collect::<Vec<&str>>().join(" ");
|
||||
|
||||
self.write_centered_with_border("")?;
|
||||
self.write_centered_with_border(&lazily_formatted)?;
|
||||
self.write_centered_with_border("")?;
|
||||
self.write_center_border("")?;
|
||||
self.write_center_border(&lazily_formatted)?;
|
||||
self.write_center_border("")?;
|
||||
}
|
||||
|
||||
let info_line = format!(
|
||||
@ -413,21 +440,25 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
self.chunk.r#type.return_type
|
||||
);
|
||||
|
||||
self.write_centered_with_border_dim(&info_line)?;
|
||||
self.write_centered_with_border("")?;
|
||||
self.write_center_border_dim(&info_line)?;
|
||||
self.write_center_border("")?;
|
||||
|
||||
if !self.chunk.is_empty() {
|
||||
if !self.chunk.instructions.is_empty() {
|
||||
self.write_instruction_section()?;
|
||||
}
|
||||
|
||||
if !self.chunk.locals().is_empty() {
|
||||
if !self.chunk.locals.is_empty() {
|
||||
self.write_local_section()?;
|
||||
}
|
||||
|
||||
if !self.chunk.constants().is_empty() {
|
||||
if !self.chunk.constants.is_empty() {
|
||||
self.write_constant_section()?;
|
||||
}
|
||||
|
||||
if !self.chunk.prototypes.is_empty() {
|
||||
self.write_prototype_section()?;
|
||||
}
|
||||
|
||||
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
|
||||
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
||||
//! belong to a named function.
|
||||
//! A chunk is output by the compiler to represent all of the information needed to execute a Dust
|
||||
//! program. In addition to the program itself, each function in the source is compiled into its own
|
||||
//! 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 local;
|
||||
mod scope;
|
||||
@ -19,7 +29,7 @@ use smallvec::SmallVec;
|
||||
|
||||
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.
|
||||
#[derive(Clone, PartialOrd, Serialize, Deserialize)]
|
||||
@ -27,49 +37,84 @@ pub struct Chunk {
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
instructions: SmallVec<[Instruction; 32]>,
|
||||
positions: SmallVec<[Span; 32]>,
|
||||
constants: SmallVec<[Value; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
prototypes: Vec<Chunk>,
|
||||
|
||||
stack_size: usize,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
constants: SmallVec<[Value; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
instructions: impl Into<SmallVec<[Instruction; 32]>>,
|
||||
positions: impl Into<SmallVec<[Span; 32]>>,
|
||||
constants: impl Into<SmallVec<[Value; 16]>>,
|
||||
locals: impl Into<SmallVec<[Local; 8]>>,
|
||||
prototypes: impl Into<Vec<Chunk>>,
|
||||
stack_size: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
r#type,
|
||||
instructions,
|
||||
constants,
|
||||
locals,
|
||||
instructions: instructions.into(),
|
||||
positions: positions.into(),
|
||||
constants: constants.into(),
|
||||
locals: locals.into(),
|
||||
prototypes: prototypes.into(),
|
||||
stack_size,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn with_data(
|
||||
name: Option<DustString>,
|
||||
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]>>,
|
||||
locals: impl Into<SmallVec<[Local; 8]>>,
|
||||
prototypes: Vec<Chunk>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
r#type,
|
||||
instructions: instructions.into(),
|
||||
positions: positions.into(),
|
||||
constants: constants.into(),
|
||||
locals: locals.into(),
|
||||
prototypes,
|
||||
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> {
|
||||
self.name.as_ref()
|
||||
}
|
||||
@ -78,22 +123,26 @@ impl Chunk {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.instructions.is_empty()
|
||||
pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> {
|
||||
&self.instructions
|
||||
}
|
||||
|
||||
pub fn positions(&self) -> &SmallVec<[Span; 32]> {
|
||||
&self.positions
|
||||
}
|
||||
|
||||
pub fn constants(&self) -> &SmallVec<[Value; 16]> {
|
||||
&self.constants
|
||||
}
|
||||
|
||||
pub fn instructions(&self) -> &SmallVec<[(Instruction, Span); 32]> {
|
||||
&self.instructions
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &SmallVec<[Local; 8]> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn prototypes(&self) -> &Vec<Chunk> {
|
||||
&self.prototypes
|
||||
}
|
||||
|
||||
pub fn stack_size(&self) -> usize {
|
||||
self.stack_size
|
||||
}
|
||||
@ -141,8 +190,14 @@ impl Eq for Chunk {}
|
||||
|
||||
impl PartialEq for Chunk {
|
||||
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.locals == other.locals
|
||||
&& self.prototypes == other.prototypes
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
instruction::{
|
||||
Call, CallNative, Close, GetLocal, Jump, LoadConstant, LoadList, LoadSelf, Move, Negate,
|
||||
Not, Return, SetLocal, Test,
|
||||
CallNative, Close, GetLocal, Jump, LoadList, LoadSelf, Negate, Not, Point, Return,
|
||||
SetLocal, Test,
|
||||
},
|
||||
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
pub struct Compiler<'src> {
|
||||
self_name: Option<DustString>,
|
||||
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
|
||||
constants: SmallVec<[Value; 16]>,
|
||||
locals: SmallVec<[(Local, Type); 8]>,
|
||||
stack_size: usize,
|
||||
|
||||
/// Used to get tokens for the compiler.
|
||||
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_position: Span,
|
||||
previous_token: Token<'src>,
|
||||
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,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// The current scope of the compiler. This is used to test if a variable is in 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> {
|
||||
@ -90,6 +125,7 @@ impl<'src> Compiler<'src> {
|
||||
instructions: SmallVec::new(),
|
||||
constants: SmallVec::new(),
|
||||
locals: SmallVec::new(),
|
||||
prototypes: Vec::new(),
|
||||
stack_size: 0,
|
||||
lexer,
|
||||
current_token,
|
||||
@ -100,6 +136,7 @@ impl<'src> Compiler<'src> {
|
||||
minimum_register: 0,
|
||||
block_index: 0,
|
||||
current_scope: Scope::default(),
|
||||
record_index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@ -115,11 +152,11 @@ impl<'src> Compiler<'src> {
|
||||
value_parameters,
|
||||
return_type: self.return_type.unwrap_or(Type::None),
|
||||
};
|
||||
let instructions = self
|
||||
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
|
||||
.instructions
|
||||
.into_iter()
|
||||
.map(|(instruction, _, position)| (instruction, position))
|
||||
.collect::<SmallVec<[(Instruction, Span); 32]>>();
|
||||
.unzip();
|
||||
let locals = self
|
||||
.locals
|
||||
.into_iter()
|
||||
@ -130,8 +167,10 @@ impl<'src> Compiler<'src> {
|
||||
self.self_name,
|
||||
r#type,
|
||||
instructions,
|
||||
positions,
|
||||
self.constants,
|
||||
locals,
|
||||
self.prototypes,
|
||||
self.stack_size,
|
||||
)
|
||||
}
|
||||
@ -347,7 +386,7 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
if let Operation::LOAD_SELF = operation {
|
||||
return Ok(Type::SelfChunk);
|
||||
return Ok(Type::SelfFunction);
|
||||
}
|
||||
|
||||
if instruction.yields_value() {
|
||||
@ -989,7 +1028,7 @@ impl<'src> Compiler<'src> {
|
||||
let destination = self.next_register();
|
||||
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(());
|
||||
} else {
|
||||
@ -1264,7 +1303,7 @@ impl<'src> Compiler<'src> {
|
||||
optimize_test_with_loader_arguments(self);
|
||||
|
||||
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,
|
||||
to: if_last_register,
|
||||
});
|
||||
@ -1532,6 +1571,10 @@ impl<'src> Compiler<'src> {
|
||||
fn parse_function(&mut self) -> Result<(), CompileError> {
|
||||
let function_start = self.current_position.0;
|
||||
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 position = function_compiler.current_position;
|
||||
|
||||
@ -1614,13 +1657,12 @@ impl<'src> Compiler<'src> {
|
||||
self.previous_position = function_compiler.previous_position;
|
||||
self.current_token = function_compiler.current_token;
|
||||
self.current_position = function_compiler.current_position;
|
||||
self.record_index = function_compiler.record_index;
|
||||
|
||||
self.lexer.skip_to(self.current_position.1);
|
||||
|
||||
let function_end = function_compiler.previous_position.1;
|
||||
let chunk = function_compiler.finish(None, value_parameters.clone());
|
||||
let function = Value::Concrete(ConcreteValue::function(chunk));
|
||||
let constant_index = self.push_or_get_constant(function);
|
||||
let prototype = function_compiler.finish(None, value_parameters.clone());
|
||||
let destination = self.next_register();
|
||||
let function_type = FunctionType {
|
||||
type_parameters: None,
|
||||
@ -1628,6 +1670,8 @@ impl<'src> Compiler<'src> {
|
||||
return_type,
|
||||
};
|
||||
|
||||
self.prototypes.push(prototype);
|
||||
|
||||
if let Some((identifier, _)) = identifier_info {
|
||||
self.declare_local(
|
||||
identifier,
|
||||
@ -1636,23 +1680,11 @@ impl<'src> Compiler<'src> {
|
||||
false,
|
||||
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 {
|
||||
let load_constant = Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
let load_function = Instruction::load_function(destination, self.record_index);
|
||||
|
||||
self.emit_instruction(
|
||||
load_constant,
|
||||
load_function,
|
||||
Type::function(function_type),
|
||||
Span(function_start, function_end),
|
||||
);
|
||||
@ -1670,23 +1702,18 @@ impl<'src> Compiler<'src> {
|
||||
position: self.previous_position,
|
||||
})?;
|
||||
|
||||
if !last_instruction.yields_value() {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
if last_instruction.operation() != Operation::LOAD_FUNCTION {
|
||||
return Err(CompileError::ExpectedFunction {
|
||||
found: self.previous_token.to_owned(),
|
||||
actual_type: last_instruction_type.clone(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
|
||||
let argument =
|
||||
last_instruction
|
||||
.as_argument()
|
||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
})?;
|
||||
let prototype_index = last_instruction.b_field();
|
||||
let function_return_type = match last_instruction_type {
|
||||
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 {
|
||||
found: self.previous_token.to_owned(),
|
||||
@ -1725,11 +1752,7 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
let end = self.current_position.1;
|
||||
let destination = self.next_register();
|
||||
let call = Instruction::from(Call {
|
||||
destination,
|
||||
function: argument,
|
||||
argument_count,
|
||||
});
|
||||
let call = Instruction::call(destination, prototype_index, argument_count);
|
||||
|
||||
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 destination: u8,
|
||||
pub function: Argument,
|
||||
pub prototype_index: u8,
|
||||
pub argument_count: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Call {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let function = instruction.b_as_argument();
|
||||
let prototype_index = instruction.b_field();
|
||||
let argument_count = instruction.c_field();
|
||||
|
||||
Call {
|
||||
destination,
|
||||
function,
|
||||
prototype_index,
|
||||
argument_count,
|
||||
}
|
||||
}
|
||||
@ -23,9 +23,9 @@ impl From<&Instruction> for Call {
|
||||
impl From<Call> for Instruction {
|
||||
fn from(call: Call) -> Self {
|
||||
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;
|
||||
|
||||
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 super::InstructionData;
|
||||
|
||||
pub struct Close {
|
||||
pub from: 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 {
|
||||
fn from(close: Close) -> Self {
|
||||
let operation = Operation::CLOSE;
|
||||
|
@ -1,16 +1,18 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionData;
|
||||
|
||||
pub struct LoadBoolean {
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadBoolean {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_field();
|
||||
let value = instruction.b_field() != 0;
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
impl From<InstructionData> for LoadBoolean {
|
||||
fn from(instruction: InstructionData) -> Self {
|
||||
let destination = instruction.a;
|
||||
let value = instruction.b != 0;
|
||||
let jump_next = instruction.c != 0;
|
||||
|
||||
LoadBoolean {
|
||||
destination,
|
||||
|
@ -1,5 +1,9 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionData;
|
||||
|
||||
pub struct LoadConstant {
|
||||
pub destination: 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 {
|
||||
fn from(load_constant: LoadConstant) -> Self {
|
||||
let operation = Operation::LOAD_CONSTANT;
|
||||
@ -30,3 +48,21 @@ impl From<LoadConstant> for Instruction {
|
||||
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`
|
||||
//!
|
||||
//! let operation = mystery_instruction.operation();
|
||||
//!
|
||||
//! match operation {
|
||||
//! let is_add_assign = match operation {
|
||||
//! Operation::Add => {
|
||||
//! 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 call;
|
||||
@ -101,6 +101,7 @@ mod less;
|
||||
mod less_equal;
|
||||
mod load_boolean;
|
||||
mod load_constant;
|
||||
mod load_function;
|
||||
mod load_list;
|
||||
mod load_self;
|
||||
mod modulo;
|
||||
@ -127,6 +128,7 @@ pub use less::Less;
|
||||
pub use less_equal::LessEqual;
|
||||
pub use load_boolean::LoadBoolean;
|
||||
pub use load_constant::LoadConstant;
|
||||
pub use load_function::LoadFunction;
|
||||
pub use load_list::LoadList;
|
||||
pub use load_self::LoadSelf;
|
||||
pub use modulo::Modulo;
|
||||
@ -134,7 +136,7 @@ pub use multiply::Multiply;
|
||||
pub use negate::Negate;
|
||||
pub use not::Not;
|
||||
pub use operation::Operation;
|
||||
pub use r#move::Move;
|
||||
pub use r#move::Point;
|
||||
pub use r#return::Return;
|
||||
pub use set_local::SetLocal;
|
||||
pub use subtract::Subtract;
|
||||
@ -239,8 +241,8 @@ impl Instruction {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn r#move(from: u8, to: u8) -> Instruction {
|
||||
Instruction::from(Move { from, to })
|
||||
pub fn point(from: u8, to: u8) -> Instruction {
|
||||
Instruction::from(Point { from, to })
|
||||
}
|
||||
|
||||
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 {
|
||||
Instruction::from(LoadList {
|
||||
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 {
|
||||
destination,
|
||||
function,
|
||||
prototype_index,
|
||||
argument_count,
|
||||
})
|
||||
}
|
||||
@ -497,7 +506,7 @@ impl Instruction {
|
||||
|
||||
function.returns_value()
|
||||
}
|
||||
Operation::MOVE
|
||||
Operation::POINT
|
||||
| Operation::CLOSE
|
||||
| Operation::SET_LOCAL
|
||||
| Operation::TEST
|
||||
@ -509,14 +518,12 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn disassembly_info(&self) -> String {
|
||||
match self.operation() {
|
||||
Operation::MOVE => {
|
||||
let Move { from, to } = Move::from(self);
|
||||
let (operation, data) = self.decode();
|
||||
|
||||
format!("R{to} = R{from}")
|
||||
}
|
||||
match operation {
|
||||
Operation::POINT => Point::from(data).to_string(),
|
||||
Operation::CLOSE => {
|
||||
let Close { from, to } = Close::from(self);
|
||||
let Close { from, to } = Close::from(data);
|
||||
|
||||
format!("R{from}..R{to}")
|
||||
}
|
||||
@ -525,7 +532,7 @@ impl Instruction {
|
||||
destination,
|
||||
value,
|
||||
jump_next,
|
||||
} = LoadBoolean::from(self);
|
||||
} = LoadBoolean::from(data);
|
||||
|
||||
if jump_next {
|
||||
format!("R{destination} = {value} && JUMP +1")
|
||||
@ -546,6 +553,7 @@ impl Instruction {
|
||||
format!("R{destination} = C{constant_index}")
|
||||
}
|
||||
}
|
||||
Operation::LOAD_FUNCTION => LoadFunction::from(self).to_string(),
|
||||
Operation::LOAD_LIST => {
|
||||
let LoadList {
|
||||
destination,
|
||||
@ -689,17 +697,16 @@ impl Instruction {
|
||||
Operation::CALL => {
|
||||
let Call {
|
||||
destination,
|
||||
function,
|
||||
prototype_index,
|
||||
argument_count,
|
||||
} = Call::from(self);
|
||||
let arguments_start = destination.saturating_sub(argument_count);
|
||||
let arguments_end = arguments_start + argument_count;
|
||||
|
||||
match argument_count {
|
||||
0 => format!("R{destination} = {function}()"),
|
||||
1 => format!("R{destination} = {function}(R{arguments_start})"),
|
||||
0 => format!("R{destination} = P{prototype_index}()"),
|
||||
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};
|
||||
|
||||
pub struct Move {
|
||||
use super::InstructionData;
|
||||
|
||||
pub struct Point {
|
||||
pub from: u8,
|
||||
pub to: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Move {
|
||||
impl From<&Instruction> for Point {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Move {
|
||||
Point {
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Move> for Instruction {
|
||||
fn from(r#move: Move) -> Self {
|
||||
let operation = Operation::MOVE;
|
||||
impl From<InstructionData> for Point {
|
||||
fn from(instruction: InstructionData) -> Self {
|
||||
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 c = r#move.to;
|
||||
|
||||
|
@ -9,39 +9,41 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Operation(pub u8);
|
||||
|
||||
impl Operation {
|
||||
pub const MOVE: Operation = Operation(0);
|
||||
pub const POINT: Operation = Operation(0);
|
||||
pub const CLOSE: Operation = Operation(1);
|
||||
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
||||
pub const LOAD_CONSTANT: Operation = Operation(3);
|
||||
pub const LOAD_LIST: Operation = Operation(4);
|
||||
pub const LOAD_SELF: Operation = Operation(5);
|
||||
pub const GET_LOCAL: Operation = Operation(6);
|
||||
pub const SET_LOCAL: Operation = Operation(7);
|
||||
pub const ADD: Operation = Operation(8);
|
||||
pub const SUBTRACT: Operation = Operation(9);
|
||||
pub const MULTIPLY: Operation = Operation(10);
|
||||
pub const DIVIDE: Operation = Operation(11);
|
||||
pub const MODULO: Operation = Operation(12);
|
||||
pub const TEST: Operation = Operation(13);
|
||||
pub const TEST_SET: Operation = Operation(14);
|
||||
pub const EQUAL: Operation = Operation(15);
|
||||
pub const LESS: Operation = Operation(16);
|
||||
pub const LESS_EQUAL: Operation = Operation(17);
|
||||
pub const NEGATE: Operation = Operation(18);
|
||||
pub const NOT: Operation = Operation(19);
|
||||
pub const CALL: Operation = Operation(20);
|
||||
pub const CALL_NATIVE: Operation = Operation(21);
|
||||
pub const JUMP: Operation = Operation(22);
|
||||
pub const RETURN: Operation = Operation(23);
|
||||
pub const LOAD_FUNCTION: Operation = Operation(4);
|
||||
pub const LOAD_LIST: Operation = Operation(5);
|
||||
pub const LOAD_SELF: Operation = Operation(6);
|
||||
pub const GET_LOCAL: Operation = Operation(7);
|
||||
pub const SET_LOCAL: Operation = Operation(8);
|
||||
pub const ADD: Operation = Operation(9);
|
||||
pub const SUBTRACT: Operation = Operation(10);
|
||||
pub const MULTIPLY: Operation = Operation(11);
|
||||
pub const DIVIDE: Operation = Operation(12);
|
||||
pub const MODULO: Operation = Operation(13);
|
||||
pub const TEST: Operation = Operation(14);
|
||||
pub const TEST_SET: Operation = Operation(15);
|
||||
pub const EQUAL: Operation = Operation(16);
|
||||
pub const LESS: Operation = Operation(17);
|
||||
pub const LESS_EQUAL: Operation = Operation(18);
|
||||
pub const NEGATE: Operation = Operation(19);
|
||||
pub const NOT: Operation = Operation(20);
|
||||
pub const CALL: Operation = Operation(21);
|
||||
pub const CALL_NATIVE: Operation = Operation(22);
|
||||
pub const JUMP: Operation = Operation(23);
|
||||
pub const RETURN: Operation = Operation(24);
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::MOVE => "MOVE",
|
||||
pub fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::POINT => "MOVE",
|
||||
Self::CLOSE => "CLOSE",
|
||||
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||
Self::LOAD_LIST => "LOAD_LIST",
|
||||
Self::LOAD_SELF => "LOAD_SELF",
|
||||
Self::GET_LOCAL => "GET_LOCAL",
|
||||
|
@ -46,7 +46,7 @@ pub fn lex(source: &str) -> Result<Vec<(Token, Span)>, DustError> {
|
||||
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.
|
||||
#[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::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||
pub use crate::value::{AbstractValue, ConcreteValue, DustString, RangeValue, Value, ValueError};
|
||||
pub use crate::vm::{run, Vm};
|
||||
pub use crate::value::{
|
||||
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
||||
};
|
||||
pub use crate::vm::{run, Pointer, Vm};
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
|
@ -2,16 +2,16 @@ use std::panic;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{DustString, NativeFunctionError, Value, Vm};
|
||||
use crate::{vm::Record, DustString, NativeFunctionError, Value};
|
||||
|
||||
pub fn panic(
|
||||
vm: &Vm,
|
||||
record: &mut Record,
|
||||
arguments: SmallVec<[&Value; 4]>,
|
||||
) -> Result<Option<Value>, NativeFunctionError> {
|
||||
let mut message: Option<DustString> = None;
|
||||
|
||||
for value_ref in arguments {
|
||||
let string = value_ref.display(vm);
|
||||
let string = value_ref.display(record);
|
||||
|
||||
match message {
|
||||
Some(ref mut message) => message.push_str(&string),
|
||||
|
@ -2,9 +2,12 @@ use std::io::{stdin, stdout, Write};
|
||||
|
||||
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();
|
||||
|
||||
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 {
|
||||
error: error.kind(),
|
||||
position: vm.current_position(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
vm: &Vm,
|
||||
record: &mut Record,
|
||||
arguments: SmallVec<[&Value; 4]>,
|
||||
) -> Result<Option<Value>, NativeFunctionError> {
|
||||
let mut stdout = stdout();
|
||||
|
||||
for argument in arguments {
|
||||
let string = argument.display(vm);
|
||||
let string = argument.display(record);
|
||||
|
||||
stdout
|
||||
.write_all(string.as_bytes())
|
||||
.map_err(|io_error| NativeFunctionError::Io {
|
||||
error: io_error.kind(),
|
||||
position: vm.current_position(),
|
||||
})?;
|
||||
}
|
||||
|
||||
@ -43,19 +44,18 @@ pub fn write(
|
||||
}
|
||||
|
||||
pub fn write_line(
|
||||
vm: &Vm,
|
||||
record: &mut Record,
|
||||
arguments: SmallVec<[&Value; 4]>,
|
||||
) -> Result<Option<Value>, NativeFunctionError> {
|
||||
let mut stdout = stdout();
|
||||
|
||||
for argument in arguments {
|
||||
let string = argument.display(vm);
|
||||
let string = argument.display(record);
|
||||
|
||||
stdout
|
||||
.write_all(string.as_bytes())
|
||||
.map_err(|io_error| NativeFunctionError::Io {
|
||||
error: io_error.kind(),
|
||||
position: vm.current_position(),
|
||||
})?;
|
||||
}
|
||||
|
||||
@ -63,7 +63,6 @@ pub fn write_line(
|
||||
.write(b"\n")
|
||||
.map_err(|io_error| NativeFunctionError::Io {
|
||||
error: io_error.kind(),
|
||||
position: vm.current_position(),
|
||||
})?;
|
||||
|
||||
Ok(None)
|
||||
|
@ -31,7 +31,7 @@ pub enum Type {
|
||||
Range {
|
||||
r#type: Box<Type>,
|
||||
},
|
||||
SelfChunk,
|
||||
SelfFunction,
|
||||
String,
|
||||
Struct(StructType),
|
||||
Tuple {
|
||||
@ -194,7 +194,7 @@ impl Display for Type {
|
||||
}
|
||||
Type::None => write!(f, "none"),
|
||||
Type::Range { r#type } => write!(f, "{type} range"),
|
||||
Type::SelfChunk => write!(f, "self"),
|
||||
Type::SelfFunction => write!(f, "self"),
|
||||
Type::String => write!(f, "str"),
|
||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||
Type::Tuple { fields } => {
|
||||
@ -257,8 +257,8 @@ impl Ord for Type {
|
||||
left_type.cmp(right_type)
|
||||
}
|
||||
(Type::Range { .. }, _) => Ordering::Greater,
|
||||
(Type::SelfChunk, Type::SelfChunk) => Ordering::Equal,
|
||||
(Type::SelfChunk, _) => Ordering::Greater,
|
||||
(Type::SelfFunction, Type::SelfFunction) => Ordering::Equal,
|
||||
(Type::SelfFunction, _) => Ordering::Greater,
|
||||
(Type::String, Type::String) => Ordering::Equal,
|
||||
(Type::String { .. }, _) => Ordering::Greater,
|
||||
(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 smartstring::{LazyCompact, SmartString};
|
||||
|
||||
use crate::{Chunk, Type, Value, ValueError};
|
||||
use crate::{Type, Value, ValueError};
|
||||
|
||||
use super::RangeValue;
|
||||
|
||||
@ -15,7 +15,6 @@ pub enum ConcreteValue {
|
||||
Byte(u8),
|
||||
Character(char),
|
||||
Float(f64),
|
||||
Function(Box<Chunk>),
|
||||
Integer(i64),
|
||||
List(Vec<ConcreteValue>),
|
||||
Range(RangeValue),
|
||||
@ -27,10 +26,6 @@ impl ConcreteValue {
|
||||
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 {
|
||||
ConcreteValue::List(into_list.into())
|
||||
}
|
||||
@ -57,7 +52,6 @@ impl ConcreteValue {
|
||||
ConcreteValue::Byte(_) => Type::Byte,
|
||||
ConcreteValue::Character(_) => Type::Character,
|
||||
ConcreteValue::Float(_) => Type::Float,
|
||||
ConcreteValue::Function(chunk) => Type::function(chunk.r#type().clone()),
|
||||
ConcreteValue::Integer(_) => Type::Integer,
|
||||
ConcreteValue::List(list) => {
|
||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
||||
@ -70,28 +64,43 @@ impl ConcreteValue {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn add(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
||||
pub fn add(&self, other: &Self) -> ConcreteValue {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let sum = match (self, other) {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_add(*right)),
|
||||
(Character(left), Character(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(),
|
||||
))
|
||||
}
|
||||
};
|
||||
match (self, other) {
|
||||
(Byte(left), Byte(right)) => {
|
||||
let sum = left.saturating_add(*right);
|
||||
|
||||
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> {
|
||||
@ -168,18 +177,16 @@ impl ConcreteValue {
|
||||
Ok(product)
|
||||
}
|
||||
|
||||
pub fn negate(&self) -> Result<ConcreteValue, ValueError> {
|
||||
pub fn negate(&self) -> ConcreteValue {
|
||||
use ConcreteValue::*;
|
||||
|
||||
let negated = match self {
|
||||
match self {
|
||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
||||
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
||||
Float(value) => ConcreteValue::Float(-value),
|
||||
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
||||
_ => return Err(ValueError::CannotNegate(self.clone().to_value())),
|
||||
};
|
||||
|
||||
Ok(negated)
|
||||
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
||||
@ -201,7 +208,6 @@ impl ConcreteValue {
|
||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left == right),
|
||||
(Character(left), Character(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),
|
||||
(List(left), List(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),
|
||||
(Character(left), Character(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),
|
||||
(List(left), List(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),
|
||||
(Character(left), Character(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),
|
||||
(List(left), List(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::Character(character) => ConcreteValue::Character(*character),
|
||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
||||
ConcreteValue::Function(function) => ConcreteValue::Function(function.clone()),
|
||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
||||
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
||||
@ -300,7 +303,6 @@ impl Display for ConcreteValue {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
ConcreteValue::Function(chunk) => write!(f, "{}", chunk.r#type()),
|
||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
||||
ConcreteValue::List(list) => {
|
||||
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.
|
||||
mod abstract_value;
|
||||
mod abstract_list;
|
||||
mod concrete_value;
|
||||
mod function;
|
||||
mod range_value;
|
||||
|
||||
pub use abstract_value::AbstractValue;
|
||||
pub use abstract_list::AbstractList;
|
||||
pub use concrete_value::{ConcreteValue, DustString};
|
||||
pub use function::Function;
|
||||
pub use range_value::RangeValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
use crate::{Type, Vm};
|
||||
use crate::{vm::Record, Type, Vm};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Value {
|
||||
#[serde(skip)]
|
||||
Abstract(AbstractValue),
|
||||
Concrete(ConcreteValue),
|
||||
|
||||
#[serde(skip)]
|
||||
AbstractList(AbstractList),
|
||||
|
||||
#[serde(skip)]
|
||||
Function(Function),
|
||||
|
||||
#[serde(skip)]
|
||||
SelfFunction,
|
||||
}
|
||||
|
||||
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> {
|
||||
if let Value::Concrete(ConcreteValue::Boolean(value)) = self {
|
||||
Some(value)
|
||||
@ -35,20 +37,34 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
Value::Abstract(abstract_value) => abstract_value.r#type(),
|
||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||
pub fn as_function(&self) -> Option<&Function> {
|
||||
if let Value::Function(function) = self {
|
||||
Some(function)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => left.add(right).map(Value::Concrete),
|
||||
_ => Err(ValueError::CannotAdd(self.to_owned(), other.to_owned())),
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
||||
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> {
|
||||
match (self, other) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
||||
@ -91,11 +107,13 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn negate(&self) -> Result<Value, ValueError> {
|
||||
match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.negate().map(Value::Concrete),
|
||||
_ => Err(ValueError::CannotNegate(self.to_owned())),
|
||||
}
|
||||
pub fn negate(&self) -> Value {
|
||||
let concrete = match self {
|
||||
Value::Concrete(concrete_value) => concrete_value.negate(),
|
||||
_ => panic!("{}", ValueError::CannotNegate(self.clone())),
|
||||
};
|
||||
|
||||
Value::Concrete(concrete)
|
||||
}
|
||||
|
||||
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 {
|
||||
Value::Abstract(abstract_value) => abstract_value.display(vm),
|
||||
Value::AbstractList(list) => list.display(record),
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
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
|
||||
mod call_stack;
|
||||
mod error;
|
||||
mod record;
|
||||
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> {
|
||||
let chunk = compile(source)?;
|
||||
let vm = Vm::new(&chunk, None, None);
|
||||
let vm = Vm::new(chunk);
|
||||
|
||||
Ok(vm.run())
|
||||
}
|
||||
|
||||
/// Dust virtual machine.
|
||||
///
|
||||
/// 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>,
|
||||
pub struct Vm {
|
||||
threads: Vec<Thread>,
|
||||
}
|
||||
|
||||
impl<'a> Vm<'a> {
|
||||
pub fn new(chunk: &'a Chunk, parent: Option<&'a Vm<'a>>, runners: Option<Vec<Runner>>) -> Self {
|
||||
let stack = vec![Register::Empty; chunk.stack_size()];
|
||||
let runners = runners.unwrap_or_else(|| {
|
||||
let mut runners = Vec::with_capacity(chunk.instructions().len());
|
||||
impl Vm {
|
||||
pub fn new(chunk: Chunk) -> Self {
|
||||
let threads = vec![Thread::new(chunk)];
|
||||
|
||||
for (instruction, _) in chunk.instructions() {
|
||||
let runner = Runner::new(*instruction);
|
||||
debug_assert_eq!(1, threads.capacity());
|
||||
|
||||
runners.push(runner);
|
||||
}
|
||||
|
||||
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
|
||||
Self { threads }
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> Option<Value> {
|
||||
self.execute_next_runner();
|
||||
|
||||
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)
|
||||
}
|
||||
if self.threads.len() == 1 {
|
||||
return self.threads[0].run();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn follow_pointer_allow_empty(&self, pointer: Pointer) -> Option<&Value> {
|
||||
log::trace!("Follow pointer {pointer}");
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
match pointer {
|
||||
Pointer::Stack(register_index) => self.open_register_allow_empty(register_index),
|
||||
Pointer::Constant(constant_index) => {
|
||||
let constant = self.get_constant(constant_index);
|
||||
for mut thread in self.threads {
|
||||
let tx = tx.clone();
|
||||
|
||||
Some(constant)
|
||||
}
|
||||
Pointer::ParentStack(register_index) => {
|
||||
assert!(self.parent.is_some(), "Vm Error: Expected parent");
|
||||
spawn(move || {
|
||||
let return_value = thread.run();
|
||||
|
||||
self.parent
|
||||
.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)
|
||||
}
|
||||
if let Some(value) = return_value {
|
||||
tx.send(value).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
rx.into_iter().last()
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,8 +81,6 @@ impl Display for Register {
|
||||
pub enum Pointer {
|
||||
Stack(u8),
|
||||
Constant(u8),
|
||||
ParentStack(u8),
|
||||
ParentConstant(u8),
|
||||
}
|
||||
|
||||
impl Display for Pointer {
|
||||
@ -244,8 +88,13 @@ impl Display for Pointer {
|
||||
match self {
|
||||
Self::Stack(index) => write!(f, "R{}", 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 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)]
|
||||
pub struct Runner {
|
||||
logic: RunnerLogic,
|
||||
data: InstructionData,
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RunAction {
|
||||
pub logic: RunnerLogic,
|
||||
pub data: InstructionData,
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
pub fn new(instruction: Instruction) -> Self {
|
||||
impl From<&Instruction> for RunAction {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (operation, data) = instruction.decode();
|
||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
Self { 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);
|
||||
RunAction { logic, 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] = [
|
||||
r#move,
|
||||
@ -56,81 +60,79 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [
|
||||
r#return,
|
||||
];
|
||||
|
||||
pub fn r#move(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
let InstructionData { b, c, .. } = instruction_data;
|
||||
let from_register_has_value = vm
|
||||
.stack
|
||||
.get(b as usize)
|
||||
.is_some_and(|register| !matches!(register, Register::Empty));
|
||||
let register = Register::Pointer(Pointer::Stack(b));
|
||||
pub fn r#move(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let Point { from, to } = instruction_data.into();
|
||||
let from_register = record.get_register(from);
|
||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
||||
|
||||
if from_register_has_value {
|
||||
vm.set_register(c, register);
|
||||
if !from_register_is_empty {
|
||||
let register = Register::Pointer(Pointer::Stack(to));
|
||||
|
||||
record.set_register(from, register);
|
||||
}
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
ThreadSignal::Continue
|
||||
}
|
||||
|
||||
pub fn close(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
let InstructionData { b, c, .. } = instruction_data;
|
||||
pub fn close(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
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!(
|
||||
(register_index as usize) < vm.stack.len(),
|
||||
(register_index as usize) < record.stack_size(),
|
||||
"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;
|
||||
|
||||
vm.execute_next_runner();
|
||||
ThreadSignal::Continue
|
||||
}
|
||||
|
||||
pub fn load_boolean(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
let InstructionData { a, b, c, .. } = instruction_data;
|
||||
let boolean = ConcreteValue::Boolean(b != 0).to_value();
|
||||
pub fn load_boolean(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let LoadBoolean {
|
||||
destination,
|
||||
value,
|
||||
jump_next,
|
||||
} = instruction_data.into();
|
||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
||||
let register = Register::Value(boolean);
|
||||
|
||||
vm.set_register(a, register);
|
||||
record.set_register(destination, register);
|
||||
|
||||
if c != 0 {
|
||||
vm.ip += 2;
|
||||
} else {
|
||||
vm.ip += 1;
|
||||
if jump_next {
|
||||
record.ip += 1;
|
||||
}
|
||||
|
||||
vm.execute_next_runner();
|
||||
ThreadSignal::Continue
|
||||
}
|
||||
|
||||
pub fn load_constant(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
let InstructionData { a, b, c, .. } = instruction_data;
|
||||
let register = Register::Pointer(Pointer::Constant(b));
|
||||
pub fn load_constant(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let LoadConstant {
|
||||
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 {
|
||||
vm.ip += 2;
|
||||
} else {
|
||||
vm.ip += 1;
|
||||
if jump_next {
|
||||
record.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 mut item_pointers = Vec::with_capacity((a - b) as usize);
|
||||
let stack = vm.stack.as_slice();
|
||||
let mut item_type = Type::Any;
|
||||
|
||||
for register_index in b..a {
|
||||
match &stack[register_index as usize] {
|
||||
match record.get_register(register_index) {
|
||||
Register::Empty => continue,
|
||||
Register::Value(value) => {
|
||||
if item_type == Type::Any {
|
||||
@ -139,7 +141,7 @@ pub fn load_list(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
}
|
||||
Register::Pointer(pointer) => {
|
||||
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);
|
||||
}
|
||||
|
||||
let list_value = Value::Abstract(AbstractValue::List {
|
||||
let list_value = Value::AbstractList(AbstractList {
|
||||
item_type,
|
||||
item_pointers,
|
||||
});
|
||||
let register = Register::Value(list_value);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
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 register = Register::Value(AbstractValue::FunctionSelf.to_value());
|
||||
let register = Register::Value(Value::SelfFunction);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
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 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));
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
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 local_register_index = vm.get_local_register(c);
|
||||
let local_register_index = record.get_local_register(c);
|
||||
let register = Register::Pointer(Pointer::Stack(b));
|
||||
|
||||
vm.set_register(local_register_index, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(local_register_index, register);
|
||||
}
|
||||
|
||||
pub fn add(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
@ -206,8 +192,8 @@ pub fn add(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
c_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let sum = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, 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);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn subtract(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
@ -235,8 +217,8 @@ pub fn subtract(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
c_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let difference = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, 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);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn multiply(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
@ -264,8 +242,8 @@ pub fn multiply(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
c_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let product = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, 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);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn divide(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
@ -293,8 +267,8 @@ pub fn divide(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
c_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let quotient = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, 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);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn modulo(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
@ -322,8 +292,8 @@ pub fn modulo(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
c_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let remainder = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, 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);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn test(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
b,
|
||||
b_is_constant,
|
||||
c,
|
||||
..
|
||||
} = 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 {
|
||||
*boolean
|
||||
} else {
|
||||
panic!(
|
||||
"VM Error: Expected boolean value for TEST operation at {}",
|
||||
vm.current_position()
|
||||
);
|
||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
||||
};
|
||||
let test_value = c != 0;
|
||||
|
||||
if boolean == test_value {
|
||||
vm.ip += 2;
|
||||
record.ip += 2;
|
||||
} 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 {
|
||||
a,
|
||||
b,
|
||||
@ -377,19 +337,16 @@ pub fn test_set(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
b_is_constant,
|
||||
..
|
||||
} = 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 {
|
||||
*boolean
|
||||
} else {
|
||||
panic!(
|
||||
"VM Error: Expected boolean value for TEST_SET operation at {}",
|
||||
vm.current_position()
|
||||
);
|
||||
panic!("VM Error: Expected boolean value for TEST_SET operation",);
|
||||
};
|
||||
let test_value = c != 0;
|
||||
|
||||
if boolean == test_value {
|
||||
vm.ip += 2;
|
||||
record.ip += 2;
|
||||
} else {
|
||||
let pointer = if b_is_constant {
|
||||
Pointer::Constant(b)
|
||||
@ -398,15 +355,11 @@ pub fn test_set(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
};
|
||||
let register = Register::Pointer(pointer);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
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 {
|
||||
b,
|
||||
c,
|
||||
@ -415,20 +368,17 @@ pub fn equal(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
d,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let is_equal = left == right;
|
||||
|
||||
if is_equal == d {
|
||||
vm.ip += 2;
|
||||
record.ip += 2;
|
||||
} 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 {
|
||||
b,
|
||||
c,
|
||||
@ -437,20 +387,17 @@ pub fn less(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
d,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let is_less = left < right;
|
||||
|
||||
if is_less == d {
|
||||
vm.ip += 2;
|
||||
record.ip += 2;
|
||||
} 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 {
|
||||
b,
|
||||
c,
|
||||
@ -459,107 +406,77 @@ pub fn less_equal(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
d,
|
||||
..
|
||||
} = instruction_data;
|
||||
let left = vm.get_argument(b, b_is_constant);
|
||||
let right = vm.get_argument(c, c_is_constant);
|
||||
let left = record.get_argument(b, b_is_constant);
|
||||
let right = record.get_argument(c, c_is_constant);
|
||||
let is_less_or_equal = left <= right;
|
||||
|
||||
if is_less_or_equal == d {
|
||||
vm.ip += 2;
|
||||
record.ip += 2;
|
||||
} 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 {
|
||||
a,
|
||||
b,
|
||||
b_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let argument = vm.get_argument(b, b_is_constant);
|
||||
let negated = match argument {
|
||||
Value::Concrete(value) => match value {
|
||||
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));
|
||||
let argument = record.get_argument(b, b_is_constant);
|
||||
let negated = argument.negate();
|
||||
let register = Register::Value(negated);
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
pub fn not(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData {
|
||||
a,
|
||||
b,
|
||||
b_is_constant,
|
||||
..
|
||||
} = instruction_data;
|
||||
let argument = vm.get_argument(b, b_is_constant);
|
||||
let argument = record.get_argument(b, b_is_constant);
|
||||
let not = match argument {
|
||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
||||
};
|
||||
let register = Register::Value(Value::Concrete(not));
|
||||
|
||||
vm.set_register(a, register);
|
||||
|
||||
vm.ip += 1;
|
||||
|
||||
vm.execute_next_runner();
|
||||
record.set_register(a, register);
|
||||
}
|
||||
|
||||
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 offset = b as usize;
|
||||
let is_positive = c != 0;
|
||||
|
||||
if is_positive {
|
||||
vm.ip += offset + 1
|
||||
record.ip += offset + 1
|
||||
} else {
|
||||
vm.ip -= offset
|
||||
record.ip -= offset
|
||||
}
|
||||
|
||||
vm.execute_next_runner();
|
||||
}
|
||||
|
||||
pub fn call(vm: &mut Vm<'_>, instruction_data: InstructionData) {
|
||||
let InstructionData {
|
||||
a,
|
||||
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")
|
||||
};
|
||||
pub fn call(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
|
||||
let InstructionData { a, b, c, .. } = instruction_data;
|
||||
let prototype = record.get_prototype(b);
|
||||
|
||||
let first_argument_index = a - c;
|
||||
let mut argument_index = 0;
|
||||
|
||||
for argument_register_index in first_argument_index..a {
|
||||
let target_register_is_empty =
|
||||
matches!(vm.stack[argument_register_index as usize], Register::Empty);
|
||||
let target_register_is_empty = matches!(
|
||||
record.stack[argument_register_index as usize],
|
||||
Register::Empty
|
||||
);
|
||||
|
||||
if target_register_is_empty {
|
||||
continue;
|
||||
}
|
||||
|
||||
function_vm.set_register(
|
||||
function_record.set_register(
|
||||
argument_index as u8,
|
||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
||||
);
|
||||
@ -567,31 +484,27 @@ pub fn call(vm: &mut Vm<'_>, instruction_data: InstructionData) {
|
||||
argument_index += 1;
|
||||
}
|
||||
|
||||
let return_value = function_vm.run();
|
||||
let return_value = function_record.run();
|
||||
|
||||
if let Some(concrete_value) = return_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 first_argument_index = (a - c) as usize;
|
||||
let argument_range = first_argument_index..a as usize;
|
||||
let mut arguments: SmallVec<[&Value; 4]> = SmallVec::new();
|
||||
|
||||
for register_index in argument_range {
|
||||
let register = &vm.stack[register_index];
|
||||
let register = &record.stack[register_index];
|
||||
let value = match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Pointer(pointer) => {
|
||||
let value_option = vm.follow_pointer_allow_empty(*pointer);
|
||||
let value_option = record.follow_pointer_allow_empty(*pointer);
|
||||
|
||||
match value_option {
|
||||
Some(value) => value,
|
||||
@ -605,30 +518,24 @@ pub fn call_native(vm: &mut Vm, instruction_data: InstructionData) {
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
||||
if !should_return_value {
|
||||
return vm.ip += 1;
|
||||
}
|
||||
if !should_return_value {}
|
||||
|
||||
if let Some(register_index) = &vm.last_assigned_register {
|
||||
let return_value = vm.open_register(*register_index).clone();
|
||||
if let Some(register_index) = &record.last_assigned_register {
|
||||
let return_value = record.open_register(*register_index).clone();
|
||||
|
||||
vm.return_value = Some(return_value);
|
||||
record.return_value = Some(return_value);
|
||||
} else {
|
||||
panic!("Stack underflow");
|
||||
}
|
||||
@ -642,7 +549,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
||||
(Operation::MOVE, r#move),
|
||||
(Operation::POINT, r#move),
|
||||
(Operation::CLOSE, close),
|
||||
(Operation::LOAD_BOOLEAN, load_boolean),
|
||||
(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