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:
parent
db0ff1348c
commit
f8deddcf2c
2 changed files with 310 additions and 223 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
466
src/vm.rs
466
src/vm.rs
|
|
@ -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)]
|
||||
|
|
@ -523,12 +541,12 @@ pub(crate) enum Code {
|
|||
Discard(Stack), // consume: R[I...] (used with above to cull results or pad nil)
|
||||
VarArg(Count), // push: VA[1 -> count] OR VA[all]
|
||||
Jump(Offset), // PC += offset
|
||||
Close(Stack), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues
|
||||
Skip, // consume: condition; if condition then PC++ (and naturally ++ again)
|
||||
Return(Stack), // consume: R[I...] (move arguments back to start of frame and finish)
|
||||
Binary(BinaryOp), // consume: fst, snd; push: result TODO: just use direct instructions?
|
||||
Unary(UnaryOp), // consume: value; push: result
|
||||
Closure(Index), // push: closure constructed from prototypes[I]
|
||||
Close(Stack), // Raise R[I...].upvalue from Upvalue::Stack to Upvalue::Heap, 'closing' the register, same effect as Discard for None upvalues
|
||||
Skip, // consume: condition; if condition then PC++ (and naturally ++ again)
|
||||
Return(Stack), // consume: R[I...] (move arguments back to start of frame and finish)
|
||||
Binary(BinaryOp), // consume: fst, snd; push: result TODO: just use direct instructions?
|
||||
Unary(UnaryOp), // consume: value; push: result
|
||||
Closure(Index), // push: closure constructed from prototypes[I]
|
||||
IteratorLoop(Offset),
|
||||
RangeBegin,
|
||||
RangeLoop(Offset),
|
||||
|
|
@ -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 {
|
||||
prototype: Rc::new(prototype),
|
||||
upvalues,
|
||||
}).id)
|
||||
Ok(self
|
||||
.freelist()
|
||||
.make(Closure {
|
||||
prototype: Rc::new(prototype),
|
||||
upvalues,
|
||||
})
|
||||
.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() {
|
||||
Value::Function(callable) => {
|
||||
self.dispatch_call(dispatch, callable, frame.offset + index as usize)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(RunError("Could not call value".to_string()))
|
||||
}
|
||||
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())),
|
||||
},
|
||||
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 {
|
||||
prototype,
|
||||
upvalues,
|
||||
},
|
||||
).id)));
|
||||
dispatch.push(Value::Function(Callable::Mars(
|
||||
self.freelist()
|
||||
.make(Closure {
|
||||
prototype,
|
||||
upvalues,
|
||||
})
|
||||
.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)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue