Tweaking CachedString in vm.rs and constants map in compiler.rs for new Map and Set types. Does not build; in progress.

This commit is contained in:
paladin 2026-09-28 11:56:54 +01:00
parent db0ff1348c
commit f8deddcf2c
2 changed files with 310 additions and 223 deletions

View file

@ -5,12 +5,12 @@ use crate::parser::{
Statement, VarArg, Variable,
};
use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value};
use deimos::map::Map;
use std::cmp::PartialEq;
use std::ops::Deref;
use std::rc::Rc;
use deimos::map::{Equivalent, Hashed, Hashes, Map, TableEntry};
pub(crate) struct Traversal<'a,S> {
pub(crate) struct Traversal<'a, S> {
// A 'cursor' of where we are in assumed execution
machine: &'a mut Machine<S>,
contexts: Vec<Context>, // Stack of function prototypes being compiled
@ -20,40 +20,10 @@ pub(crate) struct Context {
// Everything known about a closure at a time
scopes: Vec<Scope>,
vararg: Option<Piece<Option<String>>>,
constants: Map<ConstantEntry>, // map some constant value to an index for not repeating constants
constants: Map<Value, Index>, // map some constant value to an index for not repeating constants
prototype: Prototype,
}
#[derive(Clone, Debug)]
struct ConstantEntry(Value, Index);
impl Equivalent<Self> for ConstantEntry {
fn matches(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Equivalent<Value> for ConstantEntry {
fn matches(&self, other: &Value) -> bool {
self.0 == *other
}
}
impl Hashes for ConstantEntry {
fn hashed(&self) -> Hashed {
self.0.hashed()
}
}
impl TableEntry for ConstantEntry {
fn new_vacant() -> Self {
ConstantEntry(Value::Nil, 0)
}
fn is_vacant(&self) -> bool {
matches!(self.0, Value::Nil)
}
}
#[derive(Clone, Debug)]
enum BlindJump {
Break(usize),
@ -70,7 +40,7 @@ pub(crate) struct Scope {
upvalue: bool,
}
impl<S> Traversal<'_,S> {
impl<S> Traversal<'_, S> {
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
where
T: Compile,
@ -147,14 +117,8 @@ impl<S> Traversal<'_,S> {
fn push_constant(&mut self, it: Value) -> Index {
// todo: constants need pieces too!!!
// don't use constants that already exist
if let Some(entry) = &mut self
.contexts
.last_mut()
.unwrap()
.constants
.get_table(it.clone())
{
entry.1
if let Some(entry) = self.contexts.last_mut().unwrap().constants.get(it.clone()) {
*entry
} else {
let index = {
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
@ -163,11 +127,7 @@ impl<S> Traversal<'_,S> {
}
.try_into()
.unwrap();
self.contexts
.last_mut()
.unwrap()
.constants
.set_table(ConstantEntry(it, index));
self.contexts.last_mut().unwrap().constants.set(it, index);
index
}
}
@ -684,7 +644,11 @@ fn jump_from(here: usize, there: usize) -> Result<Offset, LoadError> {
Ok((there as i64 - here as i64) as Offset)
}
fn resolve_break<S>(traversal: &mut Traversal<S>, begin: usize, end: usize) -> Result<(), LoadError> {
fn resolve_break<S>(
traversal: &mut Traversal<S>,
begin: usize,
end: usize,
) -> Result<(), LoadError> {
for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) {
// ?
match jump {
@ -1132,7 +1096,12 @@ impl Compile for Piece<&Block> {
traversal.compile(&return_expression.inner_ref())?;
}
if !return_expressions.is_empty() {
traversal.push_code(return_expressions.first().unwrap().swap(Code::Return(return_index)));
traversal.push_code(
return_expressions
.first()
.unwrap()
.swap(Code::Return(return_index)),
);
}
}
for _ in 0..depth {

440
src/vm.rs
View file

@ -2,7 +2,7 @@
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
use deimos::gc::{Allocates, AnyId, FreeList, Id, MaybeId, Traverse};
use deimos::map::{Equivalent, Hashed, Hashes, Map, TableEntry};
use deimos::map::{Equivalent, Hashed, Hashes, Map, Set};
use std::fmt::{Debug, Display, Formatter};
use std::ops::Deref;
use std::rc::Rc;
@ -77,9 +77,15 @@ impl std::fmt::Debug for Closure {
}
}
pub trait MarsAllocator: Allocates<Table> + Allocates<Closure> + Allocates<Upvalue> + Allocates<Closure> {}
pub trait MarsAllocator:
Allocates<Table> + Allocates<Closure> + Allocates<Upvalue> + Allocates<Closure>
{
}
impl<C> Traverse<C> for Upvalue where C: MarsAllocator {
impl<C> Traverse<C> for Upvalue
where
C: MarsAllocator,
{
fn traverse(id: &Id<Upvalue>, context: &mut C) {
if let Upvalue::Heap(object) = context.freelist().get(id).clone() {
object.traverse(context);
@ -98,17 +104,23 @@ pub enum Callable {
}
impl Callable {
fn traverse<C>(&self, context: &mut C) where C: MarsAllocator {
fn traverse<C>(&self, context: &mut C)
where
C: MarsAllocator,
{
match self {
Callable::Rust(_) => {}
Callable::Mars(closure) => {
Closure::traverse(closure,context);
Closure::traverse(closure, context);
}
}
}
}
impl<C> Traverse<C> for Closure where C: MarsAllocator {
impl<C> Traverse<C> for Closure
where
C: MarsAllocator,
{
fn traverse(id: &Id<Closure>, context: &mut C) {
for i in 0..context.freelist().get(id).upvalues.len() {
Upvalue::traverse(&context.freelist().get(id).upvalues[i].clone(), context);
@ -159,18 +171,23 @@ impl Display for RunError {
}
}
use crate::compiler::compile;
#[cfg(feature = "vector3")]
use glam::Vec3;
use crate::core::load;
use crate::table::Table;
#[cfg(feature = "vector3")]
use glam::Vec3;
// Any value in the language
const SHORT_STRING_LEN: usize = 40;
#[derive(Clone, Debug)]
pub(crate) struct CachedString(Rc<str>, pub(crate) Hashed);
#[derive(Clone, Debug)]
impl PartialEq for CachedString {
fn eq(&self, other: &Self) -> bool {
self.1 == other.1 && self.0 == other.0
}
}
/*#[derive(Clone, Debug)]
struct CachedStringEntry(Option<CachedString>);
impl Hashes for CachedStringEntry {
fn hashed(&self) -> Hashed {
@ -189,23 +206,11 @@ impl Equivalent<Self> for CachedStringEntry {
None => false,
}
}
}
}*/
impl TableEntry for CachedStringEntry {
fn new_vacant() -> Self {
CachedStringEntry(None)
}
fn is_vacant(&self) -> bool {
self.0.is_none()
}
}
impl Equivalent<&str> for CachedStringEntry {
impl Equivalent<&str> for CachedString {
fn matches(&self, string: &&str) -> bool {
match self.0 {
Some(ref entry) => entry.0.deref() == *string,
None => false,
}
self.0.as_ref().eq(*string)
}
}
@ -217,9 +222,9 @@ pub enum Value {
Number(f64),
Integer(u64), // 64 maybe we should use signed integers...
CachedString(CachedString),
String(Rc<str>,Hashed),
String(Rc<str>, Hashed),
Table(Id<Table>),
Object(AnyId,AnyId),
Object(AnyId, AnyId),
Function(Callable),
#[cfg(feature = "vector3")]
Vector(Vec3),
@ -234,15 +239,15 @@ impl Default for Value {
impl Debug for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Nil => write!(f,"nil"),
Value::Bool(boolean) => write!(f,"{}",boolean),
Value::Number(number) => write!(f,"{}",number),
Value::Integer(integer) => write!(f,"{}",integer),
Value::CachedString(string) => write!(f,"{}",string.0.deref()),
Value::Nil => write!(f, "nil"),
Value::Bool(boolean) => write!(f, "{}", boolean),
Value::Number(number) => write!(f, "{}", number),
Value::Integer(integer) => write!(f, "{}", integer),
Value::CachedString(string) => write!(f, "{}", string.0.deref()),
Value::String(string, _hash) => write!(f, "{}", string.deref()),
Value::Table(it) => write!(f,"{:?}",it),
Value::Object(it,meta) => write!(f,"{:?}:{:?}",it,meta),
Value::Function(it) => write!(f,"{:?}",it)
Value::Table(it) => write!(f, "{:?}", it),
Value::Object(it, meta) => write!(f, "{:?}:{:?}", it, meta),
Value::Function(it) => write!(f, "{:?}", it),
}
}
}
@ -262,10 +267,13 @@ impl Value {
_ => panic!(),
}
}
pub(crate) fn traverse<C>(&self, context: &mut C) where C: MarsAllocator {
pub(crate) fn traverse<C>(&self, context: &mut C)
where
C: MarsAllocator,
{
match self {
Value::Table(id) => {},
Value::Object(id,meta_id) => {}
Value::Table(id) => {}
Value::Object(id, meta_id) => {}
_ => {}
}
}
@ -324,8 +332,8 @@ impl PartialEq for Value {
Value::Table(other) => other == this,
_ => false,
},
Value::Object(this,..) => match other {
Value::Object(other,..) => other == this,
Value::Object(this, ..) => match other {
Value::Object(other, ..) => other == this,
_ => false,
},
Value::Function(this) => match other {
@ -347,24 +355,31 @@ pub struct Frame {
macro_rules! read_only {
() => {
|machine,id,needle| Err(RunError(format!("Field {} is read only",needle.pop())))
|machine, id, needle| Err(RunError(format!("Field {} is read only", needle.pop())))
};
}
pub struct MetaField<S> {
set: fn(&mut Machine<S>, id: AnyId, index: Value, value: Value, needle: Needle) -> Result<Value,RunError>,
get: fn(&mut Machine<S>, id: AnyId, index: Value, needle: Needle) -> Result<(),RunError>,
set: fn(
&mut Machine<S>,
id: AnyId,
index: Value,
value: Value,
needle: Needle,
) -> Result<Value, RunError>,
get: fn(&mut Machine<S>, id: AnyId, index: Value, needle: Needle) -> Result<(), RunError>,
}
pub struct Meta<S> {
pub string: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value,RunError>,
pub number: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value,RunError>,
pub fields: Map<Value,MetaField<S>>,
pub constants: Map<Value,fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value,RunError>>,
pub traverse: fn(id: AnyId) -> Result<(),RunError>,
pub string: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value, RunError>,
pub number: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value, RunError>,
pub fields: Map<Value, MetaField<S>>,
pub constants:
Map<Value, fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value, RunError>>,
pub traverse: fn(id: AnyId) -> Result<(), RunError>,
}
pub type ForeignFunction<S> = fn(&mut Machine<S>, needle: Needle) -> Result<(),RunError>;
pub type ForeignFunction<S> = fn(&mut Machine<S>, needle: Needle) -> Result<(), RunError>;
// Machine for executing functions
pub struct Machine<S> {
@ -375,8 +390,8 @@ pub struct Machine<S> {
upvalues: FreeList<Upvalue>,
natives: FreeList<ForeignFunction<S>>,
metas: FreeList<Meta<S>>,
cache: Map<CachedStringEntry>, // todo: better to use a generic version of Map
meta_strings: MetaStrings
cache: Set<CachedString>,
meta_strings: MetaStrings,
}
impl<S> MarsAllocator for Machine<S> {}
@ -391,7 +406,10 @@ impl<S> Allocates<ForeignFunction<S>> for Machine<S> {
}
}
impl<S> Traverse<Machine<S>> for ForeignFunction<S> where Machine<S>: MarsAllocator {
impl<S> Traverse<Machine<S>> for ForeignFunction<S>
where
Machine<S>: MarsAllocator,
{
fn traverse(id: &Id<ForeignFunction<S>>, _context: &mut Machine<S>) {}
fn collect(self) {
@ -504,7 +522,7 @@ pub(crate) type Stack = u16; // Stack index;
pub(crate) type Count = u16; // Number of arguments
pub(crate) type Offset = i16; // Jump to offset
const COMMENTS: [&'static str;1] = ["comment"];
const COMMENTS: [&'static str; 1] = ["comment"];
// Bytecode for execution in a procedure
#[derive(Debug, Clone, Copy)]
@ -563,7 +581,7 @@ impl MetaStrings {
}
impl<S> Machine<S> {
fn init_meta_strings(&mut self) -> Result<(),RunError> {
fn init_meta_strings(&mut self) -> Result<(), RunError> {
self.meta_strings = MetaStrings {
number: self.new_string(MetaStrings::TO_NUMBER)?,
string: self.new_string(MetaStrings::TO_STRING)?,
@ -578,14 +596,15 @@ impl<S> Machine<S> {
pub fn new_number(&mut self, number: f64) -> Value {
Value::Number(number)
}
pub fn new_table(&mut self) -> Result<Value,RunError> {
pub fn new_table(&mut self) -> Result<Value, RunError> {
Ok(Value::Table(self.tables.make(Table::new()).id))
}
// todo: maybe use a trait to make S provide Id<Meta<S>> through T, e.g. 'State<T>'
pub fn new_object<T>(&mut self, id: Id<T>, meta: Id<Meta<S>>) -> Value {
Value::Object(id.any(),meta.any())
Value::Object(id.any(), meta.any())
}
pub fn new_string(&mut self, string: &str) -> Result<Value, RunError> { // todo: why Result of RunError?
pub fn new_string(&mut self, string: &str) -> Result<Value, RunError> {
// todo: why Result of RunError?
if string.len() < SHORT_STRING_LEN {
let existing = self.cache.get_table(string);
match existing {
@ -601,27 +620,44 @@ impl<S> Machine<S> {
Ok(Value::String(Rc::from(string), string.hashed()))
}
}
fn meta_call(&mut self, dispatch: &mut Dispatch, value: Value, index: usize) -> Result<(),RunError> {
fn meta_call(
&mut self,
dispatch: &mut Dispatch,
value: Value,
index: usize,
) -> Result<(), RunError> {
match value {
Value::Function(callable) => {
self.call(dispatch,callable,index)?;
},
Value::Object(_,meta) => match self.tables.get(&meta.into()).get(self.meta_strings.call.clone())? {
Value::Function(callable) => self.call(dispatch,callable,index)?,
_ => return Err(RunError("Expected function for __call metatable entry".to_string()))
self.call(dispatch, callable, index)?;
}
Value::Object(_, meta) => match self
.tables
.get(&meta.into())
.get(self.meta_strings.call.clone())?
{
Value::Function(callable) => self.call(dispatch, callable, index)?,
_ => {
return Err(RunError(
"Expected function for __call metatable entry".to_string(),
));
}
},
Value::Table(table) => {
let mut table = self.tables.get(&table);
if let Some(meta) = table.meta.exists() {
match self.tables.get(&meta).get(self.meta_strings.call.clone())? {
Value::Function(callable) => self.call(dispatch,callable,index)?,
_ => return Err(RunError("Expected function for __call metatable entry".to_string()))
Value::Function(callable) => self.call(dispatch, callable, index)?,
_ => {
return Err(RunError(
"Expected function for __call metatable entry".to_string(),
));
}
}
} else {
return Err(RunError("Expected metatable to call table".to_string()))
return Err(RunError("Expected metatable to call table".to_string()));
};
}
_ => return Err(RunError("Cannot call primitive".to_string()))
_ => return Err(RunError("Cannot call primitive".to_string())),
}
Ok(())
}
@ -630,27 +666,35 @@ impl<S> Machine<S> {
match value {
integer @ Value::Integer(_) => Ok(integer.clone()),
number @ Value::Number(_) => Ok(number.clone()),
Value::Object(_,meta) => {
match self.tables.get(&meta.into()).get(self.meta_strings.number.clone())? {
Value::Object(_, meta) => {
match self
.tables
.get(&meta.into())
.get(self.meta_strings.number.clone())?
{
Value::Function(callable) => {
let mut needle = dispatch.needle();
needle.push(clone);
self.call(needle.dispatch,callable,1)?;
self.call(needle.dispatch, callable, 1)?;
Ok(needle.pop().unwrap_or(Value::Nil))
},
}
it => Ok(it),
}
},
}
Value::Table(table) => {
let table = self.tables.get(&table);
if let Some(meta) = table.meta.exists() {
match self.tables.get(&meta).get(self.meta_strings.number.clone())? {
match self
.tables
.get(&meta)
.get(self.meta_strings.number.clone())?
{
Value::Function(callable) => {
let mut needle = dispatch.needle();
needle.push(clone);
self.call(needle.dispatch,callable,1)?;
self.call(needle.dispatch, callable, 1)?;
Ok(needle.pop().unwrap_or(Value::Nil))
},
}
it => Ok(it),
}
} else {
@ -660,7 +704,11 @@ impl<S> Machine<S> {
_ => Ok(Value::Nil),
}
}
pub(crate) fn meta_to_string(&mut self, dispatch: &mut Dispatch, value: Value) -> Result<Value, RunError> {
pub(crate) fn meta_to_string(
&mut self,
dispatch: &mut Dispatch,
value: Value,
) -> Result<Value, RunError> {
let cloned = value.clone();
Ok(match value {
Value::Nil => self.new_string("nil".to_string().as_str())?,
@ -679,21 +727,31 @@ impl<S> Machine<S> {
self.new_string("{...}".to_string().as_str())?
}
}
Value::Object(_,meta) => {
match self.tables.get(&meta.into()).get(self.meta_strings.number.clone())? {
Value::Object(_, meta) => {
match self
.tables
.get(&meta.into())
.get(self.meta_strings.number.clone())?
{
Value::Function(callable) => {
let mut needle = dispatch.needle();
needle.push(cloned);
self.call(needle.dispatch,callable,1)?;
self.call(needle.dispatch, callable, 1)?;
needle.pop().unwrap_or(Value::Nil)
},
}
it => it,
}
},
}
Value::Function(callable) => self.new_string(format!("{:?}", callable).as_str())?,
})
}
fn meta_set(&mut self, dispatch: &mut Dispatch, subject: Value, index: Value, value: Value) -> Result<(), RunError> {
fn meta_set(
&mut self,
dispatch: &mut Dispatch,
subject: Value,
index: Value,
value: Value,
) -> Result<(), RunError> {
match subject {
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
Value::Bool(bool) => Err(RunError("Cannot set to boolean".to_string())),
@ -712,11 +770,18 @@ impl<S> Machine<S> {
table.set(index, value)
}
}
Value::Object(id,meta) => (self.metas.get(&meta.into()).set)(self, id, index, value, dispatch.needle()),
Value::Object(id, meta) => {
(self.metas.get(&meta.into()).set)(self, id, index, value, dispatch.needle())
}
Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())),
}
}
fn meta_get(&mut self, dispatch: &mut Dispatch, mut subject: Value, index: Value) -> Result<Value, RunError> {
fn meta_get(
&mut self,
dispatch: &mut Dispatch,
mut subject: Value,
index: Value,
) -> Result<Value, RunError> {
match subject {
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
Value::Bool(bool) => Err(RunError("Cannot index boolean".to_string())),
@ -735,46 +800,51 @@ impl<S> Machine<S> {
table.get(index.clone())
}
}
Value::Object(mut object,..) => Err(RunError("Object index unimplemented".to_string())),
Value::Object(mut object, ..) => {
Err(RunError("Object index unimplemented".to_string()))
}
Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())),
}
}
// todo: object-table equality
fn meta_equal(&mut self, lhs: &mut Value, rhs: &mut Value) -> Result<bool,RunError> { // with respect to metamethods
fn meta_equal(&mut self, lhs: &mut Value, rhs: &mut Value) -> Result<bool, RunError> {
// with respect to metamethods
Ok(match lhs {
Value::Nil => match rhs {
Value::Nil => true,
_ => false,
}
},
Value::Bool(this) => match rhs {
Value::Bool(other) => this == other,
_ => false,
}
},
Value::Number(this) => match rhs {
Value::Number(other) => this == other,
Value::Integer(other) => *this == *other as f64,
_ => false,
}
},
Value::Integer(this) => match rhs {
Value::Number(other) => *this as f64 == *other, // are these conversions ok?
Value::Integer(other) => this == other,
_ => false,
}
},
Value::String(string, hash) => match rhs {
Value::CachedString(other) =>
Value::CachedString(other) => {
if *hash == other.1 {
other.0.deref() == (*string).deref()
} else {
false
},
Value::String(other_string, other_hash) =>
}
}
Value::String(other_string, other_hash) => {
if hash == other_hash {
string == other_string
} else {
false
},
_ => false
}
}
_ => false,
},
Value::CachedString(string) => match rhs {
Value::CachedString(other) => other.0 == string.0, // pointer equality
Value::String(other_string, other_hash) => {
@ -784,20 +854,20 @@ impl<S> Machine<S> {
false
}
}
_ => false
}
_ => false,
},
Value::Table(this) => match rhs {
Value::Table(other) => other == this,
_ => false
}
Value::Object(this,meta) => match rhs {
Value::Object(other,meta) => other == this,
_ => false
}
_ => false,
},
Value::Object(this, meta) => match rhs {
Value::Object(other, meta) => other == this,
_ => false,
},
Value::Function(this) => match rhs {
Value::Function(other) => other == this,
_ => false
}
_ => false,
},
})
}
pub(crate) fn load(&mut self, source: &str) -> Result<Id<Closure>, MarsError> {
@ -815,12 +885,20 @@ impl<S> Machine<S> {
let global = self.global.clone();
let upvalues = vec![self.freelist().make(Upvalue::Heap(Value::Table(global))).id];
Ok(self.freelist().make(Closure {
Ok(self
.freelist()
.make(Closure {
prototype: Rc::new(prototype),
upvalues,
}).id)
})
.id)
}
fn call(&mut self, dispatch: &mut Dispatch, callable: Callable, n_args: usize) -> Result<(),RunError> {
fn call(
&mut self,
dispatch: &mut Dispatch,
callable: Callable,
n_args: usize,
) -> Result<(), RunError> {
self.dispatch_call(dispatch, callable, dispatch.stack.len() - n_args - 1)?;
self.dispatch(dispatch)?;
Ok(())
@ -828,19 +906,19 @@ impl<S> Machine<S> {
// Call a callable in a dispatch.
// For Rust functions, this calls it immediately.
// For Mars functions it will set up the dispatch to execute this function, so there must be a dispatch in progress already.
fn dispatch_call(&mut self, dispatch: &mut Dispatch, callable: Callable, index: usize) -> Result<(),RunError> {
fn dispatch_call(
&mut self,
dispatch: &mut Dispatch,
callable: Callable,
index: usize,
) -> Result<(), RunError> {
match callable {
Callable::Rust(function) => {
self.natives.get(&function.into())(self, Needle {
dispatch,
index,
})?;
self.natives.get(&function.into())(self, Needle { dispatch, index })?;
}
Callable::Mars(closure) => {
let nargs = self.freelist().get(&closure).prototype.args;
dispatch
.stack
.resize(index + nargs as usize, Register::NIL);
dispatch.stack.resize(index + nargs as usize, Register::NIL);
dispatch.frames.push(Frame {
// where to find the function, what about missing arguments?,
vararg: index, // start of frame points to the register after the function
@ -862,11 +940,17 @@ impl<S> Machine<S> {
}
};
loop {
let frame = dispatch.frames.last_mut().ok_or(RunError("No frames in needle!".to_string()))?.clone();
let frame = dispatch
.frames
.last_mut()
.ok_or(RunError("No frames in needle!".to_string()))?
.clone();
if dispatch.stack.len() > Stack::MAX as usize {
return Err(RunError("Soft stack limit of 65536 reached".to_string()));
}
let code = self.freelist().get(&frame.closure)
let code = self
.freelist()
.get(&frame.closure)
.prototype
.code
.get(frame.counter)
@ -891,7 +975,9 @@ impl<S> Machine<S> {
Code::Message(number) => {
eprintln!("{}", COMMENTS[number as usize])
}
Code::TableNew => dispatch.push(Value::Table(self.freelist().make(Table::new()).id)),
Code::TableNew => {
dispatch.push(Value::Table(self.freelist().make(Table::new()).id))
}
Code::TableSet => {
let table = dispatch.pop();
let index = dispatch.pop();
@ -919,16 +1005,24 @@ impl<S> Machine<S> {
}
}
Code::UpvalueGet(index) => {
match self.upvalues.get(&self.closures.get(&frame.closure).upvalues[index as usize])
match self
.upvalues
.get(&self.closures.get(&frame.closure).upvalues[index as usize])
{
Upvalue::Stack(stack) => dispatch.push(dispatch.stack[*stack].value.clone()),
Upvalue::Stack(stack) => {
dispatch.push(dispatch.stack[*stack].value.clone())
}
Upvalue::Heap(object) => dispatch.push(object.clone()),
}
}
Code::UpvalueSet(index) => {
match self.upvalues.get(&self.closures.get(&frame.closure).upvalues[index as usize])
match self
.upvalues
.get(&self.closures.get(&frame.closure).upvalues[index as usize])
{
Upvalue::Stack(register) => dispatch.stack[*register].value = dispatch.pop(),
Upvalue::Stack(register) => {
dispatch.stack[*register].value = dispatch.pop()
}
Upvalue::Heap(it) => *it = dispatch.pop(),
}
}
@ -940,19 +1034,16 @@ impl<S> Machine<S> {
}
Code::Constant(constant) => {
dispatch.push(
self.closures.get(&frame.closure).prototype.constants[constant as usize].clone(),
self.closures.get(&frame.closure).prototype.constants[constant as usize]
.clone(),
);
}
Code::Call(index) => {
match dispatch.pop() {
Code::Call(index) => match dispatch.pop() {
Value::Function(callable) => {
self.dispatch_call(dispatch, callable, frame.offset + index as usize)?;
}
_ => return Err(RunError("Could not call value".to_string())),
},
_ => {
return Err(RunError("Could not call value".to_string()))
}
}
}
Code::Discard(index) => {
dispatch
.stack
@ -990,9 +1081,9 @@ impl<S> Machine<S> {
}
dispatch.frames.pop();
if dispatch.frames.last_mut().is_some() {
continue
continue;
} else {
break
break;
}
}
Code::Binary(op) => {
@ -1007,8 +1098,10 @@ impl<S> Machine<S> {
for i in frame.offset + index as usize..dispatch.stack.len() {
if let Some(upvalue) = dispatch.stack[i].upvalue.exists() {
match self.upvalues.get(&upvalue) {
it@Upvalue::Stack(_) => *it = Upvalue::Heap(dispatch.stack[i].value.clone()),
_ => panic!()
it @ Upvalue::Stack(_) => {
*it = Upvalue::Heap(dispatch.stack[i].value.clone())
}
_ => panic!(),
}
}
}
@ -1016,15 +1109,21 @@ impl<S> Machine<S> {
.stack
.resize(frame.offset + index as usize, Register::NIL); // As with Code::Discard
}
Code::Closure(index) => { // Make a new closure from a prototype inside this closure and push to the stack
let prototype =
self.freelist().get(&frame.closure).prototype.prototypes[index as usize].clone(); // Get the prototype of this closure that the instruction is referring to
Code::Closure(index) => {
// Make a new closure from a prototype inside this closure and push to the stack
let prototype = self.freelist().get(&frame.closure).prototype.prototypes
[index as usize]
.clone(); // Get the prototype of this closure that the instruction is referring to
let mut upvalues = Vec::new(); // List of upvalues for this new closure
for reference in prototype.upvalues.iter() { // For every reference in it
upvalues.push(match reference { // Push it based on what the reference was
Reference::Closing(index) => { // It's actually alive inside this function already
for reference in prototype.upvalues.iter() {
// For every reference in it
upvalues.push(match reference {
// Push it based on what the reference was
Reference::Closing(index) => {
// It's actually alive inside this function already
let index = frame.offset + *index as usize; // Get its actual index
match &mut dispatch.stack[index].upvalue.exists() { // Does the upvalue in that register exist?
match &mut dispatch.stack[index].upvalue.exists() {
// Does the upvalue in that register exist?
Some(upvalue) => upvalue.clone(),
_ => {
let upvalue = self.freelist().make(Upvalue::Stack(index));
@ -1034,25 +1133,31 @@ impl<S> Machine<S> {
}
}
Reference::Upvalue(index) => {
self.freelist().get(&frame.closure).upvalues[*index as usize].clone()
self.freelist().get(&frame.closure).upvalues[*index as usize]
.clone()
}
});
}
dispatch.push(Value::Function(Callable::Mars(self.freelist().make(
Closure {
dispatch.push(Value::Function(Callable::Mars(
self.freelist()
.make(Closure {
prototype,
upvalues,
},
).id)));
})
.id,
)));
}
Code::RangeBegin => {
let len = dispatch.stack.len();
let init = len - 3;
let limit = len - 2;
let step = len - 1;
let init_value = self.meta_to_number(dispatch, dispatch.stack[init].value.clone())?;
let limit_value = self.meta_to_number(dispatch, dispatch.stack[limit].value.clone())?;
let step_value = self.meta_to_number(dispatch, dispatch.stack[step].value.clone())?;
let init_value =
self.meta_to_number(dispatch, dispatch.stack[init].value.clone())?;
let limit_value =
self.meta_to_number(dispatch, dispatch.stack[limit].value.clone())?;
let step_value =
self.meta_to_number(dispatch, dispatch.stack[step].value.clone())?;
if matches!(init_value, Value::Integer(_))
&& matches!(limit_value, Value::Integer(_))
&& matches!(step_value, Value::Integer(_))
@ -1133,7 +1238,7 @@ impl<S> Machine<S> {
let len = dispatch.stack.len();
let callable = match dispatch.stack[len - 2].value.clone() {
Value::Function(callable) => callable,
_ => return Err(RunError("Could not call value".to_string()))
_ => return Err(RunError("Could not call value".to_string())),
};
let state = dispatch.stack[len - 1].value.clone();
let var = dispatch.stack[len].value.clone();
@ -1143,7 +1248,9 @@ impl<S> Machine<S> {
self.dispatch_call(
dispatch,
callable,
frame.offset + Index::try_from(dispatch.stack.len() - frame_offset - 2).unwrap() as usize,
frame.offset
+ Index::try_from(dispatch.stack.len() - frame_offset - 2).unwrap()
as usize,
)?;
if matches!(
dispatch.stack.get(len + 1).unwrap_or(&Register::NIL).value,
@ -1171,18 +1278,29 @@ impl<S> Machine<S> {
}
pub fn set_global(&mut self, name: &str, value: Value) -> Result<(), RunError> {
let name = self.new_string(name)?;
self.tables.get(&self.global).set(name,value)
self.tables.get(&self.global).set(name, value)
}
pub fn set_global_object<T>(&mut self, name: &str, object: Id<T>, meta: Id<Meta<S>>) -> Result<(), RunError> {
let value = self.new_object(object,meta);
self.set_global(name,value)
pub fn set_global_object<T>(
&mut self,
name: &str,
object: Id<T>,
meta: Id<Meta<S>>,
) -> Result<(), RunError> {
let value = self.new_object(object, meta);
self.set_global(name, value)
}
pub fn set_global_function(&mut self, name: &str, function: ForeignFunction<S>) -> Result<(),RunError> {
pub fn set_global_function(
&mut self,
name: &str,
function: ForeignFunction<S>,
) -> Result<(), RunError> {
let fun = self.new_foreign(function)?;
self.set_global(name,fun)
self.set_global(name, fun)
}
pub fn new_foreign(&mut self, function: ForeignFunction<S>) -> Result<Value,RunError> {
Ok(Value::Function(Callable::Rust(self.natives.make(function).id.any())))
pub fn new_foreign(&mut self, function: ForeignFunction<S>) -> Result<Value, RunError> {
Ok(Value::Function(Callable::Rust(
self.natives.make(function).id.any(),
)))
}
pub fn new(state: impl FnOnce(&mut FreeList<Meta<S>>) -> S) -> Result<Machine<S>, RunError> {
let mut tables = FreeList::<Table>::new();
@ -1197,7 +1315,7 @@ impl<S> Machine<S> {
upvalues: FreeList::new(),
natives: FreeList::new(),
meta_strings: MetaStrings::new(),
cache: Map::new(),
cache: Set::new(),
};
machine.init_meta_strings()?;
load(&mut machine)?;