Consolidate Record type into the ThreadData type
This commit is contained in:
parent
9d370aea2a
commit
d9a0f6866b
@ -3,18 +3,20 @@ use std::{ops::Range, panic};
|
|||||||
use crate::vm::ThreadData;
|
use crate::vm::ThreadData;
|
||||||
|
|
||||||
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool {
|
pub fn panic(data: &mut ThreadData, _: Option<u8>, argument_range: Range<u8>) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
let position = data.current_position();
|
||||||
let position = record.current_position();
|
|
||||||
let mut message = format!("Dust panic at {position}!");
|
let mut message = format!("Dust panic at {position}!");
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
let value = record.open_register_unchecked(register_index);
|
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
|
let value = match value_option {
|
||||||
|
Some(value) => value,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let string = value.display(data);
|
||||||
|
|
||||||
if let Some(string) = value.as_string() {
|
message.push_str(&string);
|
||||||
message.push_str(string);
|
|
||||||
message.push('\n');
|
message.push('\n');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
panic!("{}", message)
|
panic!("{}", message)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ pub fn read_line(
|
|||||||
destination: Option<u8>,
|
destination: Option<u8>,
|
||||||
_argument_range: Range<u8>,
|
_argument_range: Range<u8>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let destination = destination.unwrap();
|
let destination = destination.unwrap();
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
@ -22,27 +21,26 @@ pub fn read_line(
|
|||||||
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
|
pub fn write(data: &mut ThreadData, _destination: Option<u8>, argument_range: Range<u8>) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
|
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
||||||
let string = value.display(record);
|
let string = value.display(data);
|
||||||
let _ = stdout.write(string.as_bytes());
|
let _ = stdout.write(string.as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -52,19 +50,18 @@ pub fn write_line(
|
|||||||
_destination: Option<u8>,
|
_destination: Option<u8>,
|
||||||
argument_range: Range<u8>,
|
argument_range: Range<u8>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
|
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
||||||
let string = value.display(record);
|
let string = value.display(data);
|
||||||
let _ = stdout.write(string.as_bytes());
|
let _ = stdout.write(string.as_bytes());
|
||||||
let _ = stdout.write(b"\n");
|
let _ = stdout.write(b"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,14 @@ pub fn to_string(
|
|||||||
destination: Option<u8>,
|
destination: Option<u8>,
|
||||||
argument_range: Range<u8>,
|
argument_range: Range<u8>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
let argument_value = data.open_register_unchecked(argument_range.start);
|
||||||
let argument_value = record.open_register_unchecked(argument_range.start);
|
let argument_string = argument_value.display(data);
|
||||||
let argument_string = argument_value.display(record);
|
|
||||||
let destination = destination.unwrap();
|
let destination = destination.unwrap();
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::Record, Pointer, Type};
|
use crate::{vm::ThreadData, Pointer, Type};
|
||||||
|
|
||||||
use super::DustString;
|
use super::DustString;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ pub struct AbstractList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractList {
|
impl AbstractList {
|
||||||
pub fn display(&self, record: &Record) -> DustString {
|
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||||
let mut display = DustString::new();
|
let mut display = DustString::new();
|
||||||
|
|
||||||
display.push('[');
|
display.push('[');
|
||||||
@ -21,7 +21,7 @@ impl AbstractList {
|
|||||||
display.push_str(", ");
|
display.push_str(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_display = record.follow_pointer(*item).display(record);
|
let item_display = data.follow_pointer_unchecked(*item).display(data);
|
||||||
|
|
||||||
display.push_str(&item_display);
|
display.push_str(&item_display);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::Record, Type};
|
use crate::{vm::ThreadData, Type};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -179,9 +179,9 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display(&self, record: &Record) -> DustString {
|
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||||
match self {
|
match self {
|
||||||
Value::AbstractList(list) => list.display(record),
|
Value::AbstractList(list) => list.display(data),
|
||||||
Value::Concrete(concrete_value) => concrete_value.display(),
|
Value::Concrete(concrete_value) => concrete_value.display(),
|
||||||
Value::Function(function) => DustString::from(function.to_string()),
|
Value::Function(function) => DustString::from(function.to_string()),
|
||||||
}
|
}
|
||||||
|
39
dust-lang/src/vm/function_call.rs
Normal file
39
dust-lang/src/vm/function_call.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
|
use crate::{Chunk, DustString};
|
||||||
|
|
||||||
|
use super::Register;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionCall<'a> {
|
||||||
|
pub chunk: &'a Chunk,
|
||||||
|
pub ip: usize,
|
||||||
|
pub return_register: u8,
|
||||||
|
pub registers: Vec<Register>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FunctionCall<'a> {
|
||||||
|
pub fn new(chunk: &'a Chunk, return_register: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
chunk,
|
||||||
|
ip: 0,
|
||||||
|
return_register,
|
||||||
|
registers: vec![Register::Empty; chunk.register_count],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FunctionCall<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"FunctionCall: {} | IP: {} | Registers: {}",
|
||||||
|
self.chunk
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&DustString::from("anonymous")),
|
||||||
|
self.ip,
|
||||||
|
self.registers.len()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
mod record;
|
mod function_call;
|
||||||
mod run_action;
|
mod run_action;
|
||||||
mod stack;
|
mod stack;
|
||||||
mod thread;
|
mod thread;
|
||||||
@ -10,10 +10,10 @@ use std::{
|
|||||||
thread::spawn,
|
thread::spawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use record::Record;
|
pub use function_call::FunctionCall;
|
||||||
pub(crate) use run_action::get_next_action;
|
pub(crate) use run_action::get_next_action;
|
||||||
pub use run_action::RunAction;
|
pub use run_action::RunAction;
|
||||||
pub use stack::{FunctionCall, Stack};
|
pub use stack::Stack;
|
||||||
pub use thread::{Thread, ThreadData};
|
pub use thread::{Thread, ThreadData};
|
||||||
|
|
||||||
use tracing::{span, Level};
|
use tracing::{span, Level};
|
||||||
@ -85,15 +85,19 @@ impl Display for Register {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Pointer {
|
pub enum Pointer {
|
||||||
Stack(u8),
|
Register(u8),
|
||||||
Constant(u8),
|
Constant(u8),
|
||||||
|
Stack(usize, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pointer {
|
impl Display for Pointer {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Stack(index) => write!(f, "PR{}", index),
|
Self::Register(index) => write!(f, "PR{}", index),
|
||||||
Self::Constant(index) => write!(f, "PC{}", index),
|
Self::Constant(index) => write!(f, "PC{}", index),
|
||||||
|
Self::Stack(call_index, register_index) => {
|
||||||
|
write!(f, "PS{}R{}", call_index, register_index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,181 +0,0 @@
|
|||||||
use std::mem::replace;
|
|
||||||
|
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use crate::{Argument, Chunk, DustString, Function, Span, Value};
|
|
||||||
|
|
||||||
use super::{Pointer, Register};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Record<'a> {
|
|
||||||
pub ip: usize,
|
|
||||||
pub chunk: &'a Chunk,
|
|
||||||
registers: Vec<Register>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Record<'a> {
|
|
||||||
pub fn new(chunk: &'a Chunk) -> Self {
|
|
||||||
Self {
|
|
||||||
ip: 0,
|
|
||||||
registers: vec![Register::Empty; chunk.register_count],
|
|
||||||
chunk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> Option<&DustString> {
|
|
||||||
self.chunk.name.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stack_size(&self) -> usize {
|
|
||||||
self.registers.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_position(&self) -> Span {
|
|
||||||
self.chunk.positions[self.ip]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_function(&self) -> Function {
|
|
||||||
self.chunk.as_function()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value {
|
|
||||||
trace!("Follow pointer {pointer}");
|
|
||||||
|
|
||||||
match pointer {
|
|
||||||
Pointer::Stack(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
|
|
||||||
trace!("Get register R{register_index}");
|
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
&self.registers[register_index]
|
|
||||||
} else {
|
|
||||||
unsafe { self.registers.get_unchecked(register_index) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_register(&mut self, to_register: u8, register: Register) {
|
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
self.registers[to_register] = register;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_register_unchecked(&self, register_index: u8) -> &Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
let register = if cfg!(debug_assertions) {
|
|
||||||
&self.registers[register_index]
|
|
||||||
} else {
|
|
||||||
unsafe { self.registers.get_unchecked(register_index) }
|
|
||||||
};
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => {
|
|
||||||
trace!("Register R{register_index} opened to value {value}");
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} opened to pointer {pointer}");
|
|
||||||
|
|
||||||
self.follow_pointer(*pointer)
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
|
||||||
trace!("Open register R{register_index}");
|
|
||||||
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => {
|
|
||||||
trace!("Register R{register_index} openned to value {value}");
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
trace!("Open register R{register_index} openned to pointer {pointer}");
|
|
||||||
|
|
||||||
Some(self.follow_pointer(*pointer))
|
|
||||||
}
|
|
||||||
Register::Empty => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
register_index < self.registers.len(),
|
|
||||||
"VM Error: Register index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
let old_register = replace(&mut self.registers[register_index], Register::Empty);
|
|
||||||
|
|
||||||
match old_register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Stack(register_index) => {
|
|
||||||
self.empty_register_or_clone_constant_unchecked(register_index)
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(constant_index).clone()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value {
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value.clone(),
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Stack(register_index) => {
|
|
||||||
self.open_register_unchecked(*register_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(*constant_index).clone()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get a value from an Argument
|
|
||||||
pub fn get_argument_unchecked(&self, argument: Argument) -> &Value {
|
|
||||||
match argument {
|
|
||||||
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
Argument::Register(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value {
|
|
||||||
let constant_index = constant_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
&self.chunk.constants[constant_index]
|
|
||||||
} else {
|
|
||||||
unsafe { self.chunk.constants.get_unchecked(constant_index) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
|
||||||
let local_index = local_index as usize;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
local_index < self.chunk.locals.len(),
|
|
||||||
"VM Error: Local index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.chunk.locals[local_index].register_index
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,10 +7,10 @@ use crate::{
|
|||||||
Return, SetLocal, Subtract, Test, TestSet,
|
Return, SetLocal, Subtract, Test, TestSet,
|
||||||
},
|
},
|
||||||
vm::FunctionCall,
|
vm::FunctionCall,
|
||||||
AbstractList, Argument, ConcreteValue, DustString, Instruction, Type, Value,
|
AbstractList, Argument, ConcreteValue, Instruction, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{thread::ThreadData, Pointer, Record, Register};
|
use super::{thread::ThreadData, Pointer, Register};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RunAction {
|
pub struct RunAction {
|
||||||
@ -57,48 +57,46 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|||||||
r#return,
|
r#return,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn get_next_action(record: &mut Record) -> RunAction {
|
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
|
||||||
let instruction = record.chunk.instructions[record.ip];
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
let instruction = current_call.chunk.instructions[current_call.ip];
|
||||||
let operation = instruction.operation();
|
let operation = instruction.operation();
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||||
|
|
||||||
record.ip += 1;
|
current_call.ip += 1;
|
||||||
|
|
||||||
RunAction { logic, instruction }
|
RunAction { logic, instruction }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Point { from, to } = instruction.into();
|
let Point { from, to } = instruction.into();
|
||||||
let from_register = record.get_register_unchecked(from);
|
let from_register = data.get_register_unchecked(from);
|
||||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
let from_register_is_empty = matches!(from_register, Register::Empty);
|
||||||
|
|
||||||
if !from_register_is_empty {
|
if !from_register_is_empty {
|
||||||
let register = Register::Pointer(Pointer::Stack(to));
|
let register = Register::Pointer(Pointer::Register(to));
|
||||||
|
|
||||||
record.set_register(from, register);
|
data.set_register(from, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Close { from, to } = instruction.into();
|
let Close { from, to } = instruction.into();
|
||||||
|
|
||||||
for register_index in from..to {
|
for register_index in from..to {
|
||||||
record.set_register(register_index, Register::Empty);
|
data.set_register(register_index, Register::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LoadBoolean {
|
let LoadBoolean {
|
||||||
destination,
|
destination,
|
||||||
value,
|
value,
|
||||||
@ -107,19 +105,20 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
||||||
let register = Register::Value(boolean);
|
let register = Register::Value(boolean);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
if jump_next {
|
if jump_next {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LoadConstant {
|
let LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
constant_index,
|
constant_index,
|
||||||
@ -129,19 +128,20 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
|
|
||||||
trace!("Load constant {constant_index} into R{destination}");
|
trace!("Load constant {constant_index} into R{destination}");
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
if jump_next {
|
if jump_next {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LoadList {
|
let LoadList {
|
||||||
destination,
|
destination,
|
||||||
start_register,
|
start_register,
|
||||||
@ -150,7 +150,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
let mut item_type = Type::Any;
|
let mut item_type = Type::Any;
|
||||||
|
|
||||||
for register_index in start_register..destination {
|
for register_index in start_register..destination {
|
||||||
match record.get_register_unchecked(register_index) {
|
match data.get_register_unchecked(register_index) {
|
||||||
Register::Empty => continue,
|
Register::Empty => continue,
|
||||||
Register::Value(value) => {
|
Register::Value(value) => {
|
||||||
if item_type == Type::Any {
|
if item_type == Type::Any {
|
||||||
@ -159,12 +159,12 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
}
|
}
|
||||||
Register::Pointer(pointer) => {
|
Register::Pointer(pointer) => {
|
||||||
if item_type == Type::Any {
|
if item_type == Type::Any {
|
||||||
item_type = record.follow_pointer(*pointer).r#type();
|
item_type = data.follow_pointer_unchecked(*pointer).r#type();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointer = Pointer::Stack(register_index);
|
let pointer = Pointer::Register(register_index);
|
||||||
|
|
||||||
item_pointers.push(pointer);
|
item_pointers.push(pointer);
|
||||||
}
|
}
|
||||||
@ -175,122 +175,119 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
});
|
});
|
||||||
let register = Register::Value(list_value);
|
let register = Register::Value(list_value);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LoadFunction {
|
let LoadFunction {
|
||||||
destination,
|
destination,
|
||||||
prototype_index,
|
prototype_index,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let prototype_index = prototype_index as usize;
|
let prototype_index = prototype_index as usize;
|
||||||
let function = record.chunk.prototypes[prototype_index].as_function();
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
let prototype = ¤t_call.chunk.prototypes[prototype_index];
|
||||||
|
let function = prototype.as_function();
|
||||||
let register = Register::Value(Value::Function(function));
|
let register = Register::Value(Value::Function(function));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LoadSelf { destination } = instruction.into();
|
let LoadSelf { destination } = instruction.into();
|
||||||
let function = record.as_function();
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
let prototype = ¤t_call.chunk;
|
||||||
|
let function = prototype.as_function();
|
||||||
let register = Register::Value(Value::Function(function));
|
let register = Register::Value(Value::Function(function));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let GetLocal {
|
let GetLocal {
|
||||||
destination,
|
destination,
|
||||||
local_index,
|
local_index,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let local_register_index = record.get_local_register(local_index);
|
let local_register_index = data.get_local_register(local_index);
|
||||||
let register = Register::Pointer(Pointer::Stack(local_register_index));
|
let register = Register::Pointer(Pointer::Register(local_register_index));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let SetLocal {
|
let SetLocal {
|
||||||
register_index,
|
register_index,
|
||||||
local_index,
|
local_index,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let local_register_index = record.get_local_register(local_index);
|
let local_register_index = data.get_local_register(local_index);
|
||||||
let register = Register::Pointer(Pointer::Stack(register_index));
|
let register = Register::Pointer(Pointer::Register(register_index));
|
||||||
|
|
||||||
record.set_register(local_register_index, register);
|
data.set_register(local_register_index, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Add {
|
let Add {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let sum = left.add(right);
|
let sum = left.add(right);
|
||||||
let register = Register::Value(sum);
|
let register = Register::Value(sum);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Subtract {
|
let Subtract {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let difference = left.subtract(right);
|
let difference = left.subtract(right);
|
||||||
let register = Register::Value(difference);
|
let register = Register::Value(difference);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Multiply {
|
let Multiply {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let product = match (left, right) {
|
let product = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -302,22 +299,21 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(product);
|
let register = Register::Value(product);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Divide {
|
let Divide {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let quotient = match (left, right) {
|
let quotient = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -329,22 +325,21 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(quotient);
|
let register = Register::Value(quotient);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Modulo {
|
let Modulo {
|
||||||
destination,
|
destination,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let remainder = match (left, right) {
|
let remainder = match (left, right) {
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
@ -356,20 +351,19 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
};
|
};
|
||||||
let register = Register::Value(remainder);
|
let register = Register::Value(remainder);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Test {
|
let Test {
|
||||||
operand_register,
|
operand_register,
|
||||||
test_value,
|
test_value,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let value = record.open_register_unchecked(operand_register);
|
let value = data.open_register_unchecked(operand_register);
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
@ -377,22 +371,23 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let TestSet {
|
let TestSet {
|
||||||
destination,
|
destination,
|
||||||
argument,
|
argument,
|
||||||
test_value,
|
test_value,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let value = record.get_argument_unchecked(argument);
|
let value = data.get_argument_unchecked(argument);
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
@ -403,179 +398,161 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
let pointer = match argument {
|
let pointer = match argument {
|
||||||
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
|
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
|
||||||
Argument::Register(register_index) => Pointer::Stack(register_index),
|
Argument::Register(register_index) => Pointer::Register(register_index),
|
||||||
};
|
};
|
||||||
let register = Register::Pointer(pointer);
|
let register = Register::Pointer(pointer);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Equal { value, left, right } = instruction.into();
|
let Equal { value, left, right } = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let is_equal = left.equals(right);
|
let is_equal = left.equals(right);
|
||||||
|
|
||||||
if is_equal == value {
|
if is_equal == value {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Less { value, left, right } = instruction.into();
|
let Less { value, left, right } = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let is_less = left < right;
|
let is_less = left < right;
|
||||||
|
|
||||||
if is_less == value {
|
if is_less == value {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let LessEqual { value, left, right } = instruction.into();
|
let LessEqual { value, left, right } = instruction.into();
|
||||||
let left = record.get_argument_unchecked(left);
|
let left = data.get_argument_unchecked(left);
|
||||||
let right = record.get_argument_unchecked(right);
|
let right = data.get_argument_unchecked(right);
|
||||||
let is_less_or_equal = left <= right;
|
let is_less_or_equal = left <= right;
|
||||||
|
|
||||||
if is_less_or_equal == value {
|
if is_less_or_equal == value {
|
||||||
record.ip += 1;
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
|
current_call.ip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Negate {
|
let Negate {
|
||||||
destination,
|
destination,
|
||||||
argument,
|
argument,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let argument = record.get_argument_unchecked(argument);
|
let argument = data.get_argument_unchecked(argument);
|
||||||
let negated = argument.negate();
|
let negated = argument.negate();
|
||||||
let register = Register::Value(negated);
|
let register = Register::Value(negated);
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Not {
|
let Not {
|
||||||
destination,
|
destination,
|
||||||
argument,
|
argument,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let argument = record.get_argument_unchecked(argument);
|
let argument = data.get_argument_unchecked(argument);
|
||||||
let not = match argument {
|
let not = match argument {
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
||||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
||||||
};
|
};
|
||||||
let register = Register::Value(Value::Concrete(not));
|
let register = Register::Value(Value::Concrete(not));
|
||||||
|
|
||||||
record.set_register(destination, register);
|
data.set_register(destination, register);
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let record = &mut data.call_stack.last_mut_unchecked().record;
|
|
||||||
let Jump {
|
let Jump {
|
||||||
offset,
|
offset,
|
||||||
is_positive,
|
is_positive,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let offset = offset as usize;
|
let offset = offset as usize;
|
||||||
|
let current_call = data.call_stack.last_mut_unchecked();
|
||||||
|
|
||||||
if is_positive {
|
if is_positive {
|
||||||
record.ip += offset;
|
current_call.ip += offset;
|
||||||
} else {
|
} else {
|
||||||
record.ip -= offset + 1;
|
current_call.ip -= offset + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
|
||||||
let current_call = data.call_stack.pop_unchecked();
|
|
||||||
let Call {
|
let Call {
|
||||||
destination: return_register,
|
destination: return_register,
|
||||||
function_register,
|
function_register,
|
||||||
argument_count,
|
argument_count,
|
||||||
is_recursive,
|
is_recursive,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let function = current_call
|
let current_call = data.call_stack.last_unchecked();
|
||||||
.record
|
let first_argument_register = return_register - argument_count;
|
||||||
|
let prototype = if is_recursive {
|
||||||
|
current_call.chunk
|
||||||
|
} else {
|
||||||
|
let function = data
|
||||||
.open_register_unchecked(function_register)
|
.open_register_unchecked(function_register)
|
||||||
.as_function()
|
.as_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let first_argument_register = return_register - argument_count;
|
|
||||||
let prototype = if is_recursive {
|
¤t_call.chunk.prototypes[function.prototype_index as usize]
|
||||||
current_call.record.chunk
|
|
||||||
} else {
|
|
||||||
¤t_call.record.chunk.prototypes[function.prototype_index as usize]
|
|
||||||
};
|
|
||||||
let mut next_call = FunctionCall {
|
|
||||||
name: prototype.name.clone(),
|
|
||||||
return_register,
|
|
||||||
ip: current_call.ip,
|
|
||||||
record: Record::new(prototype),
|
|
||||||
};
|
};
|
||||||
|
let mut next_call = FunctionCall::new(prototype, return_register);
|
||||||
let mut argument_index = 0;
|
let mut argument_index = 0;
|
||||||
|
|
||||||
for register_index in first_argument_register..return_register {
|
for register_index in first_argument_register..return_register {
|
||||||
let value_option = current_call
|
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
.record
|
|
||||||
.open_register_allow_empty_unchecked(register_index);
|
|
||||||
let argument = if let Some(value) = value_option {
|
let argument = if let Some(value) = value_option {
|
||||||
value.clone()
|
value.clone()
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(
|
next_call.registers[argument_index] = Register::Value(argument);
|
||||||
"Passing argument \"{argument}\" to {}",
|
|
||||||
function
|
|
||||||
.name
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
|
||||||
);
|
|
||||||
|
|
||||||
next_call
|
|
||||||
.record
|
|
||||||
.set_register(argument_index, Register::Value(argument));
|
|
||||||
|
|
||||||
argument_index += 1;
|
argument_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(&mut next_call.record);
|
|
||||||
|
|
||||||
data.call_stack.push(current_call);
|
|
||||||
data.call_stack.push(next_call);
|
data.call_stack.push(next_call);
|
||||||
|
|
||||||
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,30 +575,24 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|||||||
should_return_value,
|
should_return_value,
|
||||||
return_register,
|
return_register,
|
||||||
} = instruction.into();
|
} = instruction.into();
|
||||||
let mut current_call = if data.call_stack.len() == 1 {
|
let (destination, return_value) = if data.call_stack.len() == 1 {
|
||||||
if should_return_value {
|
if should_return_value {
|
||||||
data.return_value_index = Some(return_register);
|
data.return_value_index = Some(return_register);
|
||||||
};
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
data.call_stack.pop_unchecked()
|
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
|
||||||
|
let destination = data.call_stack.pop_unchecked().return_register;
|
||||||
|
|
||||||
|
(destination, return_value)
|
||||||
};
|
};
|
||||||
|
|
||||||
let outer_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
if should_return_value {
|
if should_return_value {
|
||||||
let return_value = current_call
|
data.set_register(destination, Register::Value(return_value));
|
||||||
.record
|
|
||||||
.empty_register_or_clone_constant_unchecked(return_register);
|
|
||||||
let destination = current_call.return_register;
|
|
||||||
|
|
||||||
outer_call
|
|
||||||
.record
|
|
||||||
.set_register(destination, Register::Value(return_value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(&mut outer_call.record);
|
data.next_action = get_next_action(data);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ use std::{
|
|||||||
ops::{Index, IndexMut, Range},
|
ops::{Index, IndexMut, Range},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::DustString;
|
use super::FunctionCall;
|
||||||
|
|
||||||
use super::Record;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Stack<T> {
|
pub struct Stack<T> {
|
||||||
@ -43,6 +41,16 @@ impl<T> Stack<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
assert!(index < self.len(), "Stack underflow");
|
||||||
|
|
||||||
|
&mut self.items[index]
|
||||||
|
} else {
|
||||||
|
unsafe { self.items.get_unchecked_mut(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, item: T) {
|
pub fn push(&mut self, item: T) {
|
||||||
self.items.push(item);
|
self.items.push(item);
|
||||||
}
|
}
|
||||||
@ -118,36 +126,12 @@ impl<T: Debug> Debug for Stack<T> {
|
|||||||
|
|
||||||
impl Display for Stack<FunctionCall<'_>> {
|
impl Display for Stack<FunctionCall<'_>> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
writeln!(f, "-- DUST CALL STACK --")?;
|
writeln!(f, "----- DUST CALL STACK -----")?;
|
||||||
|
|
||||||
for function_call in self.items.iter().rev() {
|
for (index, function_call) in self.items.iter().enumerate().rev() {
|
||||||
writeln!(f, "{function_call}")?;
|
writeln!(f, "{index:02} | {function_call}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(f, "--")
|
write!(f, "---------------------------")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FunctionCall<'a> {
|
|
||||||
pub name: Option<DustString>,
|
|
||||||
pub return_register: u8,
|
|
||||||
pub ip: usize,
|
|
||||||
pub record: Record<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FunctionCall<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let FunctionCall {
|
|
||||||
name,
|
|
||||||
return_register,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
let name = name
|
|
||||||
.as_ref()
|
|
||||||
.map(|name| name.as_str())
|
|
||||||
.unwrap_or("anonymous");
|
|
||||||
|
|
||||||
write!(f, "{name} (Return register: {return_register})")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
use std::mem::replace;
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{vm::FunctionCall, Chunk, DustString, Value};
|
use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value};
|
||||||
|
|
||||||
use super::{record::Record, RunAction, Stack};
|
use super::{Pointer, Register, RunAction, Stack};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
@ -23,12 +25,7 @@ impl Thread {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||||
let main_call = FunctionCall {
|
let main_call = FunctionCall::new(&self.chunk, 0);
|
||||||
name: self.chunk.name.clone(),
|
|
||||||
return_register: 0, // Never used, the main function's return is the thread's return
|
|
||||||
ip: 0,
|
|
||||||
record: Record::new(&self.chunk),
|
|
||||||
};
|
|
||||||
|
|
||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
|
|
||||||
@ -49,11 +46,8 @@ impl Thread {
|
|||||||
|
|
||||||
if should_end {
|
if should_end {
|
||||||
let return_value = if let Some(register_index) = thread_data.return_value_index {
|
let return_value = if let Some(register_index) = thread_data.return_value_index {
|
||||||
let value = thread_data
|
let value =
|
||||||
.call_stack
|
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
||||||
.last_mut_unchecked()
|
|
||||||
.record
|
|
||||||
.empty_register_or_clone_constant_unchecked(register_index);
|
|
||||||
|
|
||||||
Some(value)
|
Some(value)
|
||||||
} else {
|
} else {
|
||||||
@ -72,3 +66,207 @@ pub struct ThreadData<'a> {
|
|||||||
pub next_action: RunAction,
|
pub next_action: RunAction,
|
||||||
pub return_value_index: Option<u8>,
|
pub return_value_index: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ThreadData<'_> {
|
||||||
|
pub fn current_position(&self) -> Span {
|
||||||
|
let current_call = self.call_stack.last_unchecked();
|
||||||
|
|
||||||
|
current_call.chunk.positions[current_call.ip]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
||||||
|
trace!("Follow pointer {pointer}");
|
||||||
|
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
||||||
|
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
||||||
|
Pointer::Stack(stack_index, register_index) => unsafe {
|
||||||
|
let register = self
|
||||||
|
.call_stack
|
||||||
|
.get_unchecked(stack_index)
|
||||||
|
.registers
|
||||||
|
.get_unchecked(register_index as usize);
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_register_unchecked(&self, register_index: u8) -> &Register {
|
||||||
|
trace!("Get register R{register_index}");
|
||||||
|
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
&self.call_stack.last_unchecked().registers[register_index]
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.call_stack
|
||||||
|
.last_unchecked()
|
||||||
|
.registers
|
||||||
|
.get_unchecked(register_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_register(&mut self, to_register: u8, register: Register) {
|
||||||
|
let to_register = to_register as usize;
|
||||||
|
|
||||||
|
self.call_stack.last_mut_unchecked().registers[to_register] = register;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_register_unchecked(&self, register_index: u8) -> &Value {
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
|
||||||
|
let register = if cfg!(debug_assertions) {
|
||||||
|
&self.call_stack.last_unchecked().registers[register_index]
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.call_stack
|
||||||
|
.last_unchecked()
|
||||||
|
.registers
|
||||||
|
.get_unchecked(register_index)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => {
|
||||||
|
trace!("Register R{register_index} opened to value {value}");
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
Register::Pointer(pointer) => {
|
||||||
|
trace!("Open register R{register_index} opened to pointer {pointer}");
|
||||||
|
|
||||||
|
self.follow_pointer_unchecked(*pointer)
|
||||||
|
}
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
||||||
|
trace!("Open register R{register_index}");
|
||||||
|
|
||||||
|
let register = self.get_register_unchecked(register_index);
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => {
|
||||||
|
trace!("Register R{register_index} openned to value {value}");
|
||||||
|
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
Register::Pointer(pointer) => {
|
||||||
|
trace!("Open register R{register_index} openned to pointer {pointer}");
|
||||||
|
|
||||||
|
Some(self.follow_pointer_unchecked(*pointer))
|
||||||
|
}
|
||||||
|
Register::Empty => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value {
|
||||||
|
let register_index = register_index as usize;
|
||||||
|
let old_register = replace(
|
||||||
|
&mut self.call_stack.last_mut_unchecked().registers[register_index],
|
||||||
|
Register::Empty,
|
||||||
|
);
|
||||||
|
|
||||||
|
match old_register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::Pointer(pointer) => match pointer {
|
||||||
|
Pointer::Register(register_index) => {
|
||||||
|
self.empty_register_or_clone_constant_unchecked(register_index)
|
||||||
|
}
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
self.get_constant_unchecked(constant_index).clone()
|
||||||
|
}
|
||||||
|
Pointer::Stack(stack_index, register_index) => {
|
||||||
|
let call = self.call_stack.get_unchecked_mut(stack_index);
|
||||||
|
|
||||||
|
let old_register = replace(
|
||||||
|
&mut call.registers[register_index as usize],
|
||||||
|
Register::Empty,
|
||||||
|
);
|
||||||
|
|
||||||
|
match old_register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::Pointer(pointer) => {
|
||||||
|
self.follow_pointer_unchecked(pointer).clone()
|
||||||
|
}
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value {
|
||||||
|
let register = self.get_register_unchecked(register_index);
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => value.clone(),
|
||||||
|
Register::Pointer(pointer) => match pointer {
|
||||||
|
Pointer::Register(register_index) => {
|
||||||
|
self.open_register_unchecked(*register_index).clone()
|
||||||
|
}
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
self.get_constant_unchecked(*constant_index).clone()
|
||||||
|
}
|
||||||
|
Pointer::Stack(stack_index, register_index) => {
|
||||||
|
let call = self.call_stack.get_unchecked(*stack_index);
|
||||||
|
let register = &call.registers[*register_index as usize];
|
||||||
|
|
||||||
|
match register {
|
||||||
|
Register::Value(value) => value.clone(),
|
||||||
|
Register::Pointer(pointer) => {
|
||||||
|
self.follow_pointer_unchecked(*pointer).clone()
|
||||||
|
}
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DRY helper to get a value from an Argument
|
||||||
|
pub fn get_argument_unchecked(&self, argument: Argument) -> &Value {
|
||||||
|
match argument {
|
||||||
|
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
||||||
|
Argument::Register(register_index) => self.open_register_unchecked(register_index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value {
|
||||||
|
let constant_index = constant_index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
&self.call_stack.last().unwrap().chunk.constants[constant_index]
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.call_stack
|
||||||
|
.last_unchecked()
|
||||||
|
.chunk
|
||||||
|
.constants
|
||||||
|
.get_unchecked(constant_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
||||||
|
let local_index = local_index as usize;
|
||||||
|
let chunk = self.call_stack.last_unchecked().chunk;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
local_index < chunk.locals.len(),
|
||||||
|
"VM Error: Local index out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
chunk.locals[local_index].register_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user