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 super::call_stack::CallStack;
|
||||
use super::{stack::Stack, FunctionCall};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum VmError {
|
||||
CallStackUnderflow,
|
||||
ExpectedFunction { value: Value },
|
||||
InstructionIndexOutOfBounds { call_stack: CallStack, ip: usize },
|
||||
MalformedInstruction { instruction: InstructionData },
|
||||
ExpectedFunction {
|
||||
value: Value,
|
||||
},
|
||||
InstructionIndexOutOfBounds {
|
||||
call_stack: Stack<FunctionCall>,
|
||||
ip: usize,
|
||||
},
|
||||
MalformedInstruction {
|
||||
instruction: InstructionData,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for VmError {
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Virtual machine and errors
|
||||
mod call_stack;
|
||||
mod error;
|
||||
mod record;
|
||||
mod run_action;
|
||||
mod stack;
|
||||
mod thread;
|
||||
|
||||
use std::{
|
||||
@ -11,10 +11,10 @@ use std::{
|
||||
thread::spawn,
|
||||
};
|
||||
|
||||
pub use call_stack::{CallStack, FunctionCall};
|
||||
pub use error::VmError;
|
||||
pub use record::Record;
|
||||
pub use run_action::RunAction;
|
||||
pub use stack::{FunctionCall, Stack};
|
||||
pub use thread::{Thread, ThreadSignal};
|
||||
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,
|
||||
};
|
||||
|
||||
use super::{record::Record, CallStack, RunAction};
|
||||
use super::{record::Record, RunAction, Stack};
|
||||
|
||||
pub struct Thread {
|
||||
chunk: Chunk,
|
||||
@ -19,9 +19,8 @@ impl Thread {
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Option<Value> {
|
||||
let mut call_stack = CallStack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||
let mut records = Vec::with_capacity(self.chunk.prototypes.len() + 1);
|
||||
|
||||
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||
let mut records = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
||||
let main_call = FunctionCall {
|
||||
name: self.chunk.name.clone(),
|
||||
return_register: 0,
|
||||
@ -32,7 +31,7 @@ impl Thread {
|
||||
call_stack.push(main_call);
|
||||
records.push(main_record);
|
||||
|
||||
let mut active_record = &mut records[0];
|
||||
let mut active_record = records.last_mut_unchecked();
|
||||
|
||||
info!(
|
||||
"Starting thread with {}",
|
||||
@ -62,6 +61,16 @@ impl Thread {
|
||||
|
||||
match signal {
|
||||
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 {
|
||||
function_register,
|
||||
return_register,
|
||||
@ -93,22 +102,12 @@ impl Thread {
|
||||
call_stack.push(next_call);
|
||||
records.push(next_record);
|
||||
|
||||
active_record = records.last_mut().unwrap();
|
||||
active_record = records.last_mut_unchecked();
|
||||
|
||||
for (index, argument) in arguments.into_iter().enumerate() {
|
||||
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 {
|
||||
should_return_value,
|
||||
return_register,
|
||||
@ -124,8 +123,8 @@ impl Thread {
|
||||
None
|
||||
};
|
||||
|
||||
let current_call = call_stack.pop_or_panic();
|
||||
let _current_record = records.pop().unwrap();
|
||||
let current_call = call_stack.pop_unchecked();
|
||||
let _current_record = records.pop_unchecked();
|
||||
let destination = current_call.return_register;
|
||||
|
||||
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 {
|
||||
outer_record
|
||||
|
Loading…
x
Reference in New Issue
Block a user