Add generic "Stack" type for call stacks and record stacks
This commit is contained in:
parent
e9bd9b37b0
commit
1f88d77476
@ -1,113 +0,0 @@
|
|||||||
use std::fmt::{self, Debug, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::DustString;
|
|
||||||
|
|
||||||
use super::VmError;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub struct CallStack {
|
|
||||||
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 len(&self) -> usize {
|
|
||||||
self.calls.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, call: FunctionCall) {
|
|
||||||
self.calls.push(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Option<FunctionCall> {
|
|
||||||
self.calls.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last(&self) -> Option<&FunctionCall> {
|
|
||||||
self.calls.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_mut(&mut self) -> Option<&mut FunctionCall> {
|
|
||||||
self.calls.last_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_or_panic(&mut self) -> FunctionCall {
|
|
||||||
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
|
||||||
|
|
||||||
self.calls.pop().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_or_panic(&self) -> &FunctionCall {
|
|
||||||
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
|
||||||
|
|
||||||
self.calls.last().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_mut_or_panic(&mut self) -> &mut FunctionCall {
|
|
||||||
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
|
||||||
|
|
||||||
self.calls.last_mut().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CallStack {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 function_call in self.calls.iter().rev() {
|
|
||||||
writeln!(f, "{function_call}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(f, "--")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct FunctionCall {
|
|
||||||
pub name: Option<DustString>,
|
|
||||||
pub return_register: u8,
|
|
||||||
pub ip: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
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})")
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,14 +2,21 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{InstructionData, Value};
|
use crate::{InstructionData, Value};
|
||||||
|
|
||||||
use super::call_stack::CallStack;
|
use super::{stack::Stack, FunctionCall};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
CallStackUnderflow,
|
CallStackUnderflow,
|
||||||
ExpectedFunction { value: Value },
|
ExpectedFunction {
|
||||||
InstructionIndexOutOfBounds { call_stack: CallStack, ip: usize },
|
value: Value,
|
||||||
MalformedInstruction { instruction: InstructionData },
|
},
|
||||||
|
InstructionIndexOutOfBounds {
|
||||||
|
call_stack: Stack<FunctionCall>,
|
||||||
|
ip: usize,
|
||||||
|
},
|
||||||
|
MalformedInstruction {
|
||||||
|
instruction: InstructionData,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for VmError {
|
impl Display for VmError {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
mod call_stack;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod record;
|
mod record;
|
||||||
mod run_action;
|
mod run_action;
|
||||||
|
mod stack;
|
||||||
mod thread;
|
mod thread;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -11,10 +11,10 @@ use std::{
|
|||||||
thread::spawn,
|
thread::spawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use call_stack::{CallStack, FunctionCall};
|
|
||||||
pub use error::VmError;
|
pub use error::VmError;
|
||||||
pub use record::Record;
|
pub use record::Record;
|
||||||
pub use run_action::RunAction;
|
pub use run_action::RunAction;
|
||||||
|
pub use stack::{FunctionCall, Stack};
|
||||||
pub use thread::{Thread, ThreadSignal};
|
pub use thread::{Thread, ThreadSignal};
|
||||||
use tracing::{span, Level};
|
use tracing::{span, Level};
|
||||||
|
|
||||||
|
142
dust-lang/src/vm/stack.rs
Normal file
142
dust-lang/src/vm/stack.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
ops::{Index, IndexMut, Range},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::DustString;
|
||||||
|
|
||||||
|
use super::VmError;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Stack<T> {
|
||||||
|
items: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Stack<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Stack {
|
||||||
|
items: Vec::with_capacity(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
Stack {
|
||||||
|
items: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.items.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.items.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: T) {
|
||||||
|
self.items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
|
self.items.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last(&self) -> Option<&T> {
|
||||||
|
self.items.last()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_mut(&mut self) -> Option<&mut T> {
|
||||||
|
self.items.last_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_unchecked(&mut self) -> T {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
||||||
|
|
||||||
|
self.items.pop().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.items.pop().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_unchecked(&self) -> &T {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
||||||
|
|
||||||
|
self.items.last().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.items.last().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_mut_unchecked(&mut self) -> &mut T {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
|
||||||
|
|
||||||
|
self.items.last_mut().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.items.last_mut().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Stack<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<Range<usize>> for Stack<T> {
|
||||||
|
type Output = [T];
|
||||||
|
|
||||||
|
fn index(&self, index: Range<usize>) -> &Self::Output {
|
||||||
|
&self.items[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IndexMut<Range<usize>> for Stack<T> {
|
||||||
|
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
|
||||||
|
&mut self.items[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Stack<FunctionCall> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Stack<FunctionCall> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
writeln!(f, "-- DUST CALL STACK --")?;
|
||||||
|
|
||||||
|
for function_call in self.items.iter().rev() {
|
||||||
|
writeln!(f, "{function_call}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f, "--")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct FunctionCall {
|
||||||
|
pub name: Option<DustString>,
|
||||||
|
pub return_register: u8,
|
||||||
|
pub ip: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
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})")
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
Chunk, DustString, Value,
|
Chunk, DustString, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{record::Record, CallStack, RunAction};
|
use super::{record::Record, RunAction, Stack};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
@ -19,9 +19,8 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Option<Value> {
|
pub fn run(&mut self) -> Option<Value> {
|
||||||
let mut call_stack = CallStack::with_capacity(self.chunk.prototypes.len() + 1);
|
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||||
let mut records = Vec::with_capacity(self.chunk.prototypes.len() + 1);
|
let mut records = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||||
|
|
||||||
let main_call = FunctionCall {
|
let main_call = FunctionCall {
|
||||||
name: self.chunk.name.clone(),
|
name: self.chunk.name.clone(),
|
||||||
return_register: 0,
|
return_register: 0,
|
||||||
@ -32,7 +31,7 @@ impl Thread {
|
|||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
records.push(main_record);
|
records.push(main_record);
|
||||||
|
|
||||||
let mut active_record = &mut records[0];
|
let mut active_record = records.last_mut_unchecked();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Starting thread with {}",
|
"Starting thread with {}",
|
||||||
@ -62,6 +61,16 @@ impl Thread {
|
|||||||
|
|
||||||
match signal {
|
match signal {
|
||||||
ThreadSignal::Continue => {}
|
ThreadSignal::Continue => {}
|
||||||
|
ThreadSignal::LoadFunction {
|
||||||
|
destination,
|
||||||
|
prototype_index,
|
||||||
|
} => {
|
||||||
|
let function_record_index = prototype_index as usize;
|
||||||
|
let function = self.chunk.prototypes[function_record_index].as_function();
|
||||||
|
let register = Register::Value(Value::Function(function));
|
||||||
|
|
||||||
|
active_record.set_register(destination, register);
|
||||||
|
}
|
||||||
ThreadSignal::Call {
|
ThreadSignal::Call {
|
||||||
function_register,
|
function_register,
|
||||||
return_register,
|
return_register,
|
||||||
@ -93,22 +102,12 @@ impl Thread {
|
|||||||
call_stack.push(next_call);
|
call_stack.push(next_call);
|
||||||
records.push(next_record);
|
records.push(next_record);
|
||||||
|
|
||||||
active_record = records.last_mut().unwrap();
|
active_record = records.last_mut_unchecked();
|
||||||
|
|
||||||
for (index, argument) in arguments.into_iter().enumerate() {
|
for (index, argument) in arguments.into_iter().enumerate() {
|
||||||
active_record.set_register(index as u8, Register::Value(argument));
|
active_record.set_register(index as u8, Register::Value(argument));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadSignal::LoadFunction {
|
|
||||||
destination,
|
|
||||||
prototype_index,
|
|
||||||
} => {
|
|
||||||
let function_record_index = prototype_index as usize;
|
|
||||||
let function = self.chunk.prototypes[function_record_index].as_function();
|
|
||||||
let register = Register::Value(Value::Function(function));
|
|
||||||
|
|
||||||
active_record.set_register(destination, register);
|
|
||||||
}
|
|
||||||
ThreadSignal::Return {
|
ThreadSignal::Return {
|
||||||
should_return_value,
|
should_return_value,
|
||||||
return_register,
|
return_register,
|
||||||
@ -124,8 +123,8 @@ impl Thread {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_call = call_stack.pop_or_panic();
|
let current_call = call_stack.pop_unchecked();
|
||||||
let _current_record = records.pop().unwrap();
|
let _current_record = records.pop_unchecked();
|
||||||
let destination = current_call.return_register;
|
let destination = current_call.return_register;
|
||||||
|
|
||||||
if call_stack.is_empty() {
|
if call_stack.is_empty() {
|
||||||
@ -136,7 +135,7 @@ impl Thread {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let outer_record = records.last_mut().unwrap();
|
let outer_record = records.last_mut_unchecked();
|
||||||
|
|
||||||
if should_return_value {
|
if should_return_value {
|
||||||
outer_record
|
outer_record
|
||||||
|
Loading…
x
Reference in New Issue
Block a user