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,
|
Statement, VarArg, Variable,
|
||||||
};
|
};
|
||||||
use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value};
|
use crate::vm::{Code, Index, Machine, Offset, Prototype, Reference, Value};
|
||||||
|
use deimos::map::Map;
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
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
|
// A 'cursor' of where we are in assumed execution
|
||||||
machine: &'a mut Machine<S>,
|
machine: &'a mut Machine<S>,
|
||||||
contexts: Vec<Context>, // Stack of function prototypes being compiled
|
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
|
// Everything known about a closure at a time
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
vararg: Option<Piece<Option<String>>>,
|
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,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
enum BlindJump {
|
enum BlindJump {
|
||||||
Break(usize),
|
Break(usize),
|
||||||
|
|
@ -70,7 +40,7 @@ pub(crate) struct Scope {
|
||||||
upvalue: bool,
|
upvalue: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Traversal<'_,S> {
|
impl<S> Traversal<'_, S> {
|
||||||
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
fn compile<T>(&mut self, it: &T) -> Result<(), LoadError>
|
||||||
where
|
where
|
||||||
T: Compile,
|
T: Compile,
|
||||||
|
|
@ -147,14 +117,8 @@ impl<S> Traversal<'_,S> {
|
||||||
fn push_constant(&mut self, it: Value) -> Index {
|
fn push_constant(&mut self, it: Value) -> Index {
|
||||||
// todo: constants need pieces too!!!
|
// todo: constants need pieces too!!!
|
||||||
// don't use constants that already exist
|
// don't use constants that already exist
|
||||||
if let Some(entry) = &mut self
|
if let Some(entry) = self.contexts.last_mut().unwrap().constants.get(it.clone()) {
|
||||||
.contexts
|
*entry
|
||||||
.last_mut()
|
|
||||||
.unwrap()
|
|
||||||
.constants
|
|
||||||
.get_table(it.clone())
|
|
||||||
{
|
|
||||||
entry.1
|
|
||||||
} else {
|
} else {
|
||||||
let index = {
|
let index = {
|
||||||
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
let constants = &mut self.contexts.last_mut().unwrap().prototype.constants;
|
||||||
|
|
@ -163,11 +127,7 @@ impl<S> Traversal<'_,S> {
|
||||||
}
|
}
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.contexts
|
self.contexts.last_mut().unwrap().constants.set(it, index);
|
||||||
.last_mut()
|
|
||||||
.unwrap()
|
|
||||||
.constants
|
|
||||||
.set_table(ConstantEntry(it, index));
|
|
||||||
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)
|
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()) {
|
for jump in std::mem::replace(&mut traversal.last_scope().jumps, Vec::new()) {
|
||||||
// ?
|
// ?
|
||||||
match jump {
|
match jump {
|
||||||
|
|
@ -1132,7 +1096,12 @@ impl Compile for Piece<&Block> {
|
||||||
traversal.compile(&return_expression.inner_ref())?;
|
traversal.compile(&return_expression.inner_ref())?;
|
||||||
}
|
}
|
||||||
if !return_expressions.is_empty() {
|
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 {
|
for _ in 0..depth {
|
||||||
|
|
|
||||||
466
src/vm.rs
466
src/vm.rs
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
use crate::parser::{BinaryOp, Piece, UnaryOp, parse};
|
||||||
use deimos::gc::{Allocates, AnyId, FreeList, Id, MaybeId, Traverse};
|
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::fmt::{Debug, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
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) {
|
fn traverse(id: &Id<Upvalue>, context: &mut C) {
|
||||||
if let Upvalue::Heap(object) = context.freelist().get(id).clone() {
|
if let Upvalue::Heap(object) = context.freelist().get(id).clone() {
|
||||||
object.traverse(context);
|
object.traverse(context);
|
||||||
|
|
@ -98,17 +104,23 @@ pub enum Callable {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
match self {
|
||||||
Callable::Rust(_) => {}
|
Callable::Rust(_) => {}
|
||||||
Callable::Mars(closure) => {
|
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) {
|
fn traverse(id: &Id<Closure>, context: &mut C) {
|
||||||
for i in 0..context.freelist().get(id).upvalues.len() {
|
for i in 0..context.freelist().get(id).upvalues.len() {
|
||||||
Upvalue::traverse(&context.freelist().get(id).upvalues[i].clone(), context);
|
Upvalue::traverse(&context.freelist().get(id).upvalues[i].clone(), context);
|
||||||
|
|
@ -159,18 +171,23 @@ impl Display for RunError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
use crate::compiler::compile;
|
use crate::compiler::compile;
|
||||||
#[cfg(feature = "vector3")]
|
|
||||||
use glam::Vec3;
|
|
||||||
use crate::core::load;
|
use crate::core::load;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
|
#[cfg(feature = "vector3")]
|
||||||
|
use glam::Vec3;
|
||||||
|
|
||||||
// Any value in the language
|
// Any value in the language
|
||||||
const SHORT_STRING_LEN: usize = 40;
|
const SHORT_STRING_LEN: usize = 40;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct CachedString(Rc<str>, pub(crate) Hashed);
|
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>);
|
struct CachedStringEntry(Option<CachedString>);
|
||||||
impl Hashes for CachedStringEntry {
|
impl Hashes for CachedStringEntry {
|
||||||
fn hashed(&self) -> Hashed {
|
fn hashed(&self) -> Hashed {
|
||||||
|
|
@ -189,23 +206,11 @@ impl Equivalent<Self> for CachedStringEntry {
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
impl TableEntry for CachedStringEntry {
|
impl Equivalent<&str> for CachedString {
|
||||||
fn new_vacant() -> Self {
|
|
||||||
CachedStringEntry(None)
|
|
||||||
}
|
|
||||||
fn is_vacant(&self) -> bool {
|
|
||||||
self.0.is_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Equivalent<&str> for CachedStringEntry {
|
|
||||||
fn matches(&self, string: &&str) -> bool {
|
fn matches(&self, string: &&str) -> bool {
|
||||||
match self.0 {
|
self.0.as_ref().eq(*string)
|
||||||
Some(ref entry) => entry.0.deref() == *string,
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,9 +222,9 @@ pub enum Value {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Integer(u64), // 64 maybe we should use signed integers...
|
Integer(u64), // 64 maybe we should use signed integers...
|
||||||
CachedString(CachedString),
|
CachedString(CachedString),
|
||||||
String(Rc<str>,Hashed),
|
String(Rc<str>, Hashed),
|
||||||
Table(Id<Table>),
|
Table(Id<Table>),
|
||||||
Object(AnyId,AnyId),
|
Object(AnyId, AnyId),
|
||||||
Function(Callable),
|
Function(Callable),
|
||||||
#[cfg(feature = "vector3")]
|
#[cfg(feature = "vector3")]
|
||||||
Vector(Vec3),
|
Vector(Vec3),
|
||||||
|
|
@ -234,15 +239,15 @@ impl Default for Value {
|
||||||
impl Debug for Value {
|
impl Debug for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::Nil => write!(f,"nil"),
|
Value::Nil => write!(f, "nil"),
|
||||||
Value::Bool(boolean) => write!(f,"{}",boolean),
|
Value::Bool(boolean) => write!(f, "{}", boolean),
|
||||||
Value::Number(number) => write!(f,"{}",number),
|
Value::Number(number) => write!(f, "{}", number),
|
||||||
Value::Integer(integer) => write!(f,"{}",integer),
|
Value::Integer(integer) => write!(f, "{}", integer),
|
||||||
Value::CachedString(string) => write!(f,"{}",string.0.deref()),
|
Value::CachedString(string) => write!(f, "{}", string.0.deref()),
|
||||||
Value::String(string, _hash) => write!(f, "{}", string.deref()),
|
Value::String(string, _hash) => write!(f, "{}", string.deref()),
|
||||||
Value::Table(it) => write!(f,"{:?}",it),
|
Value::Table(it) => write!(f, "{:?}", it),
|
||||||
Value::Object(it,meta) => write!(f,"{:?}:{:?}",it,meta),
|
Value::Object(it, meta) => write!(f, "{:?}:{:?}", it, meta),
|
||||||
Value::Function(it) => write!(f,"{:?}",it)
|
Value::Function(it) => write!(f, "{:?}", it),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -262,10 +267,13 @@ impl Value {
|
||||||
_ => panic!(),
|
_ => 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 {
|
match self {
|
||||||
Value::Table(id) => {},
|
Value::Table(id) => {}
|
||||||
Value::Object(id,meta_id) => {}
|
Value::Object(id, meta_id) => {}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -324,8 +332,8 @@ impl PartialEq for Value {
|
||||||
Value::Table(other) => other == this,
|
Value::Table(other) => other == this,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Value::Object(this,..) => match other {
|
Value::Object(this, ..) => match other {
|
||||||
Value::Object(other,..) => other == this,
|
Value::Object(other, ..) => other == this,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Value::Function(this) => match other {
|
Value::Function(this) => match other {
|
||||||
|
|
@ -347,24 +355,31 @@ pub struct Frame {
|
||||||
|
|
||||||
macro_rules! read_only {
|
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> {
|
pub struct MetaField<S> {
|
||||||
set: fn(&mut Machine<S>, id: AnyId, index: Value, value: Value, needle: Needle) -> Result<Value,RunError>,
|
set: fn(
|
||||||
get: fn(&mut Machine<S>, id: AnyId, index: Value, needle: Needle) -> Result<(),RunError>,
|
&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 struct Meta<S> {
|
||||||
pub string: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value,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 number: fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value, RunError>,
|
||||||
pub fields: Map<Value,MetaField<S>>,
|
pub fields: Map<Value, MetaField<S>>,
|
||||||
pub constants: Map<Value,fn(&mut Machine<S>, id: AnyId, needle: Needle) -> Result<Value,RunError>>,
|
pub constants:
|
||||||
pub traverse: fn(id: AnyId) -> Result<(),RunError>,
|
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
|
// Machine for executing functions
|
||||||
pub struct Machine<S> {
|
pub struct Machine<S> {
|
||||||
|
|
@ -375,8 +390,8 @@ pub struct Machine<S> {
|
||||||
upvalues: FreeList<Upvalue>,
|
upvalues: FreeList<Upvalue>,
|
||||||
natives: FreeList<ForeignFunction<S>>,
|
natives: FreeList<ForeignFunction<S>>,
|
||||||
metas: FreeList<Meta<S>>,
|
metas: FreeList<Meta<S>>,
|
||||||
cache: Map<CachedStringEntry>, // todo: better to use a generic version of Map
|
cache: Set<CachedString>,
|
||||||
meta_strings: MetaStrings
|
meta_strings: MetaStrings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> MarsAllocator for Machine<S> {}
|
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 traverse(id: &Id<ForeignFunction<S>>, _context: &mut Machine<S>) {}
|
||||||
|
|
||||||
fn collect(self) {
|
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 Count = u16; // Number of arguments
|
||||||
pub(crate) type Offset = i16; // Jump to offset
|
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
|
// Bytecode for execution in a procedure
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[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)
|
Discard(Stack), // consume: R[I...] (used with above to cull results or pad nil)
|
||||||
VarArg(Count), // push: VA[1 -> count] OR VA[all]
|
VarArg(Count), // push: VA[1 -> count] OR VA[all]
|
||||||
Jump(Offset), // PC += offset
|
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
|
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)
|
Skip, // consume: condition; if condition then PC++ (and naturally ++ again)
|
||||||
Return(Stack), // consume: R[I...] (move arguments back to start of frame and finish)
|
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?
|
Binary(BinaryOp), // consume: fst, snd; push: result TODO: just use direct instructions?
|
||||||
Unary(UnaryOp), // consume: value; push: result
|
Unary(UnaryOp), // consume: value; push: result
|
||||||
Closure(Index), // push: closure constructed from prototypes[I]
|
Closure(Index), // push: closure constructed from prototypes[I]
|
||||||
IteratorLoop(Offset),
|
IteratorLoop(Offset),
|
||||||
RangeBegin,
|
RangeBegin,
|
||||||
RangeLoop(Offset),
|
RangeLoop(Offset),
|
||||||
|
|
@ -563,7 +581,7 @@ impl MetaStrings {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Machine<S> {
|
impl<S> Machine<S> {
|
||||||
fn init_meta_strings(&mut self) -> Result<(),RunError> {
|
fn init_meta_strings(&mut self) -> Result<(), RunError> {
|
||||||
self.meta_strings = MetaStrings {
|
self.meta_strings = MetaStrings {
|
||||||
number: self.new_string(MetaStrings::TO_NUMBER)?,
|
number: self.new_string(MetaStrings::TO_NUMBER)?,
|
||||||
string: self.new_string(MetaStrings::TO_STRING)?,
|
string: self.new_string(MetaStrings::TO_STRING)?,
|
||||||
|
|
@ -578,14 +596,15 @@ impl<S> Machine<S> {
|
||||||
pub fn new_number(&mut self, number: f64) -> Value {
|
pub fn new_number(&mut self, number: f64) -> Value {
|
||||||
Value::Number(number)
|
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))
|
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>'
|
// 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 {
|
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 {
|
if string.len() < SHORT_STRING_LEN {
|
||||||
let existing = self.cache.get_table(string);
|
let existing = self.cache.get_table(string);
|
||||||
match existing {
|
match existing {
|
||||||
|
|
@ -601,27 +620,44 @@ impl<S> Machine<S> {
|
||||||
Ok(Value::String(Rc::from(string), string.hashed()))
|
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 {
|
match value {
|
||||||
Value::Function(callable) => {
|
Value::Function(callable) => {
|
||||||
self.call(dispatch,callable,index)?;
|
self.call(dispatch, callable, index)?;
|
||||||
},
|
}
|
||||||
Value::Object(_,meta) => match self.tables.get(&meta.into()).get(self.meta_strings.call.clone())? {
|
Value::Object(_, meta) => match self
|
||||||
Value::Function(callable) => self.call(dispatch,callable,index)?,
|
.tables
|
||||||
_ => return Err(RunError("Expected function for __call metatable entry".to_string()))
|
.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) => {
|
Value::Table(table) => {
|
||||||
let mut table = self.tables.get(&table);
|
let mut table = self.tables.get(&table);
|
||||||
if let Some(meta) = table.meta.exists() {
|
if let Some(meta) = table.meta.exists() {
|
||||||
match self.tables.get(&meta).get(self.meta_strings.call.clone())? {
|
match self.tables.get(&meta).get(self.meta_strings.call.clone())? {
|
||||||
Value::Function(callable) => self.call(dispatch,callable,index)?,
|
Value::Function(callable) => self.call(dispatch, callable, index)?,
|
||||||
_ => return Err(RunError("Expected function for __call metatable entry".to_string()))
|
_ => {
|
||||||
|
return Err(RunError(
|
||||||
|
"Expected function for __call metatable entry".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -630,27 +666,35 @@ impl<S> Machine<S> {
|
||||||
match value {
|
match value {
|
||||||
integer @ Value::Integer(_) => Ok(integer.clone()),
|
integer @ Value::Integer(_) => Ok(integer.clone()),
|
||||||
number @ Value::Number(_) => Ok(number.clone()),
|
number @ Value::Number(_) => Ok(number.clone()),
|
||||||
Value::Object(_,meta) => {
|
Value::Object(_, meta) => {
|
||||||
match self.tables.get(&meta.into()).get(self.meta_strings.number.clone())? {
|
match self
|
||||||
|
.tables
|
||||||
|
.get(&meta.into())
|
||||||
|
.get(self.meta_strings.number.clone())?
|
||||||
|
{
|
||||||
Value::Function(callable) => {
|
Value::Function(callable) => {
|
||||||
let mut needle = dispatch.needle();
|
let mut needle = dispatch.needle();
|
||||||
needle.push(clone);
|
needle.push(clone);
|
||||||
self.call(needle.dispatch,callable,1)?;
|
self.call(needle.dispatch, callable, 1)?;
|
||||||
Ok(needle.pop().unwrap_or(Value::Nil))
|
Ok(needle.pop().unwrap_or(Value::Nil))
|
||||||
},
|
}
|
||||||
it => Ok(it),
|
it => Ok(it),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Value::Table(table) => {
|
Value::Table(table) => {
|
||||||
let table = self.tables.get(&table);
|
let table = self.tables.get(&table);
|
||||||
if let Some(meta) = table.meta.exists() {
|
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) => {
|
Value::Function(callable) => {
|
||||||
let mut needle = dispatch.needle();
|
let mut needle = dispatch.needle();
|
||||||
needle.push(clone);
|
needle.push(clone);
|
||||||
self.call(needle.dispatch,callable,1)?;
|
self.call(needle.dispatch, callable, 1)?;
|
||||||
Ok(needle.pop().unwrap_or(Value::Nil))
|
Ok(needle.pop().unwrap_or(Value::Nil))
|
||||||
},
|
}
|
||||||
it => Ok(it),
|
it => Ok(it),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -660,7 +704,11 @@ impl<S> Machine<S> {
|
||||||
_ => Ok(Value::Nil),
|
_ => 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();
|
let cloned = value.clone();
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Value::Nil => self.new_string("nil".to_string().as_str())?,
|
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())?
|
self.new_string("{...}".to_string().as_str())?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Object(_,meta) => {
|
Value::Object(_, meta) => {
|
||||||
match self.tables.get(&meta.into()).get(self.meta_strings.number.clone())? {
|
match self
|
||||||
|
.tables
|
||||||
|
.get(&meta.into())
|
||||||
|
.get(self.meta_strings.number.clone())?
|
||||||
|
{
|
||||||
Value::Function(callable) => {
|
Value::Function(callable) => {
|
||||||
let mut needle = dispatch.needle();
|
let mut needle = dispatch.needle();
|
||||||
needle.push(cloned);
|
needle.push(cloned);
|
||||||
self.call(needle.dispatch,callable,1)?;
|
self.call(needle.dispatch, callable, 1)?;
|
||||||
needle.pop().unwrap_or(Value::Nil)
|
needle.pop().unwrap_or(Value::Nil)
|
||||||
},
|
}
|
||||||
it => it,
|
it => it,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Value::Function(callable) => self.new_string(format!("{:?}", callable).as_str())?,
|
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 {
|
match subject {
|
||||||
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
|
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
|
||||||
Value::Bool(bool) => Err(RunError("Cannot set to boolean".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)
|
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())),
|
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 {
|
match subject {
|
||||||
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
|
Value::Nil => Err(RunError("Cannot set to nil".to_string())),
|
||||||
Value::Bool(bool) => Err(RunError("Cannot index boolean".to_string())),
|
Value::Bool(bool) => Err(RunError("Cannot index boolean".to_string())),
|
||||||
|
|
@ -735,46 +800,51 @@ impl<S> Machine<S> {
|
||||||
table.get(index.clone())
|
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())),
|
Value::Function(_callable) => Err(RunError("Function index unimplemented".to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// todo: object-table equality
|
// 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 {
|
Ok(match lhs {
|
||||||
Value::Nil => match rhs {
|
Value::Nil => match rhs {
|
||||||
Value::Nil => true,
|
Value::Nil => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Bool(this) => match rhs {
|
Value::Bool(this) => match rhs {
|
||||||
Value::Bool(other) => this == other,
|
Value::Bool(other) => this == other,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Number(this) => match rhs {
|
Value::Number(this) => match rhs {
|
||||||
Value::Number(other) => this == other,
|
Value::Number(other) => this == other,
|
||||||
Value::Integer(other) => *this == *other as f64,
|
Value::Integer(other) => *this == *other as f64,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Integer(this) => match rhs {
|
Value::Integer(this) => match rhs {
|
||||||
Value::Number(other) => *this as f64 == *other, // are these conversions ok?
|
Value::Number(other) => *this as f64 == *other, // are these conversions ok?
|
||||||
Value::Integer(other) => this == other,
|
Value::Integer(other) => this == other,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::String(string, hash) => match rhs {
|
Value::String(string, hash) => match rhs {
|
||||||
Value::CachedString(other) =>
|
Value::CachedString(other) => {
|
||||||
if *hash == other.1 {
|
if *hash == other.1 {
|
||||||
other.0.deref() == (*string).deref()
|
other.0.deref() == (*string).deref()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
Value::String(other_string, other_hash) =>
|
}
|
||||||
|
Value::String(other_string, other_hash) => {
|
||||||
if hash == other_hash {
|
if hash == other_hash {
|
||||||
string == other_string
|
string == other_string
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
_ => false
|
}
|
||||||
}
|
_ => false,
|
||||||
|
},
|
||||||
Value::CachedString(string) => match rhs {
|
Value::CachedString(string) => match rhs {
|
||||||
Value::CachedString(other) => other.0 == string.0, // pointer equality
|
Value::CachedString(other) => other.0 == string.0, // pointer equality
|
||||||
Value::String(other_string, other_hash) => {
|
Value::String(other_string, other_hash) => {
|
||||||
|
|
@ -784,20 +854,20 @@ impl<S> Machine<S> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Table(this) => match rhs {
|
Value::Table(this) => match rhs {
|
||||||
Value::Table(other) => other == this,
|
Value::Table(other) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Object(this,meta) => match rhs {
|
Value::Object(this, meta) => match rhs {
|
||||||
Value::Object(other,meta) => other == this,
|
Value::Object(other, meta) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
Value::Function(this) => match rhs {
|
Value::Function(this) => match rhs {
|
||||||
Value::Function(other) => other == this,
|
Value::Function(other) => other == this,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn load(&mut self, source: &str) -> Result<Id<Closure>, MarsError> {
|
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 global = self.global.clone();
|
||||||
let upvalues = vec![self.freelist().make(Upvalue::Heap(Value::Table(global))).id];
|
let upvalues = vec![self.freelist().make(Upvalue::Heap(Value::Table(global))).id];
|
||||||
Ok(self.freelist().make(Closure {
|
Ok(self
|
||||||
prototype: Rc::new(prototype),
|
.freelist()
|
||||||
upvalues,
|
.make(Closure {
|
||||||
}).id)
|
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_call(dispatch, callable, dispatch.stack.len() - n_args - 1)?;
|
||||||
self.dispatch(dispatch)?;
|
self.dispatch(dispatch)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -828,19 +906,19 @@ impl<S> Machine<S> {
|
||||||
// Call a callable in a dispatch.
|
// Call a callable in a dispatch.
|
||||||
// For Rust functions, this calls it immediately.
|
// 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.
|
// 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 {
|
match callable {
|
||||||
Callable::Rust(function) => {
|
Callable::Rust(function) => {
|
||||||
self.natives.get(&function.into())(self, Needle {
|
self.natives.get(&function.into())(self, Needle { dispatch, index })?;
|
||||||
dispatch,
|
|
||||||
index,
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
Callable::Mars(closure) => {
|
Callable::Mars(closure) => {
|
||||||
let nargs = self.freelist().get(&closure).prototype.args;
|
let nargs = self.freelist().get(&closure).prototype.args;
|
||||||
dispatch
|
dispatch.stack.resize(index + nargs as usize, Register::NIL);
|
||||||
.stack
|
|
||||||
.resize(index + nargs as usize, Register::NIL);
|
|
||||||
dispatch.frames.push(Frame {
|
dispatch.frames.push(Frame {
|
||||||
// where to find the function, what about missing arguments?,
|
// where to find the function, what about missing arguments?,
|
||||||
vararg: index, // start of frame points to the register after the function
|
vararg: index, // start of frame points to the register after the function
|
||||||
|
|
@ -862,11 +940,17 @@ impl<S> Machine<S> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
loop {
|
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 {
|
if dispatch.stack.len() > Stack::MAX as usize {
|
||||||
return Err(RunError("Soft stack limit of 65536 reached".to_string()));
|
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
|
.prototype
|
||||||
.code
|
.code
|
||||||
.get(frame.counter)
|
.get(frame.counter)
|
||||||
|
|
@ -891,7 +975,9 @@ impl<S> Machine<S> {
|
||||||
Code::Message(number) => {
|
Code::Message(number) => {
|
||||||
eprintln!("{}", COMMENTS[number as usize])
|
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 => {
|
Code::TableSet => {
|
||||||
let table = dispatch.pop();
|
let table = dispatch.pop();
|
||||||
let index = dispatch.pop();
|
let index = dispatch.pop();
|
||||||
|
|
@ -919,16 +1005,24 @@ impl<S> Machine<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Code::UpvalueGet(index) => {
|
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()),
|
Upvalue::Heap(object) => dispatch.push(object.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Code::UpvalueSet(index) => {
|
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(),
|
Upvalue::Heap(it) => *it = dispatch.pop(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -940,19 +1034,16 @@ impl<S> Machine<S> {
|
||||||
}
|
}
|
||||||
Code::Constant(constant) => {
|
Code::Constant(constant) => {
|
||||||
dispatch.push(
|
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) => {
|
Code::Call(index) => match dispatch.pop() {
|
||||||
match dispatch.pop() {
|
Value::Function(callable) => {
|
||||||
Value::Function(callable) => {
|
self.dispatch_call(dispatch, callable, frame.offset + index as usize)?;
|
||||||
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) => {
|
Code::Discard(index) => {
|
||||||
dispatch
|
dispatch
|
||||||
.stack
|
.stack
|
||||||
|
|
@ -990,9 +1081,9 @@ impl<S> Machine<S> {
|
||||||
}
|
}
|
||||||
dispatch.frames.pop();
|
dispatch.frames.pop();
|
||||||
if dispatch.frames.last_mut().is_some() {
|
if dispatch.frames.last_mut().is_some() {
|
||||||
continue
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Code::Binary(op) => {
|
Code::Binary(op) => {
|
||||||
|
|
@ -1007,8 +1098,10 @@ impl<S> Machine<S> {
|
||||||
for i in frame.offset + index as usize..dispatch.stack.len() {
|
for i in frame.offset + index as usize..dispatch.stack.len() {
|
||||||
if let Some(upvalue) = dispatch.stack[i].upvalue.exists() {
|
if let Some(upvalue) = dispatch.stack[i].upvalue.exists() {
|
||||||
match self.upvalues.get(&upvalue) {
|
match self.upvalues.get(&upvalue) {
|
||||||
it@Upvalue::Stack(_) => *it = Upvalue::Heap(dispatch.stack[i].value.clone()),
|
it @ Upvalue::Stack(_) => {
|
||||||
_ => panic!()
|
*it = Upvalue::Heap(dispatch.stack[i].value.clone())
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1016,15 +1109,21 @@ impl<S> Machine<S> {
|
||||||
.stack
|
.stack
|
||||||
.resize(frame.offset + index as usize, Register::NIL); // As with Code::Discard
|
.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
|
Code::Closure(index) => {
|
||||||
let prototype =
|
// Make a new closure from a prototype inside this closure and push to the stack
|
||||||
self.freelist().get(&frame.closure).prototype.prototypes[index as usize].clone(); // Get the prototype of this closure that the instruction is referring to
|
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
|
let mut upvalues = Vec::new(); // List of upvalues for this new closure
|
||||||
for reference in prototype.upvalues.iter() { // For every reference in it
|
for reference in prototype.upvalues.iter() {
|
||||||
upvalues.push(match reference { // Push it based on what the reference was
|
// For every reference in it
|
||||||
Reference::Closing(index) => { // It's actually alive inside this function already
|
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
|
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(),
|
Some(upvalue) => upvalue.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
let upvalue = self.freelist().make(Upvalue::Stack(index));
|
let upvalue = self.freelist().make(Upvalue::Stack(index));
|
||||||
|
|
@ -1034,25 +1133,31 @@ impl<S> Machine<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference::Upvalue(index) => {
|
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(
|
dispatch.push(Value::Function(Callable::Mars(
|
||||||
Closure {
|
self.freelist()
|
||||||
prototype,
|
.make(Closure {
|
||||||
upvalues,
|
prototype,
|
||||||
},
|
upvalues,
|
||||||
).id)));
|
})
|
||||||
|
.id,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Code::RangeBegin => {
|
Code::RangeBegin => {
|
||||||
let len = dispatch.stack.len();
|
let len = dispatch.stack.len();
|
||||||
let init = len - 3;
|
let init = len - 3;
|
||||||
let limit = len - 2;
|
let limit = len - 2;
|
||||||
let step = len - 1;
|
let step = len - 1;
|
||||||
let init_value = self.meta_to_number(dispatch, dispatch.stack[init].value.clone())?;
|
let init_value =
|
||||||
let limit_value = self.meta_to_number(dispatch, dispatch.stack[limit].value.clone())?;
|
self.meta_to_number(dispatch, dispatch.stack[init].value.clone())?;
|
||||||
let step_value = self.meta_to_number(dispatch, dispatch.stack[step].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(_))
|
if matches!(init_value, Value::Integer(_))
|
||||||
&& matches!(limit_value, Value::Integer(_))
|
&& matches!(limit_value, Value::Integer(_))
|
||||||
&& matches!(step_value, Value::Integer(_))
|
&& matches!(step_value, Value::Integer(_))
|
||||||
|
|
@ -1133,7 +1238,7 @@ impl<S> Machine<S> {
|
||||||
let len = dispatch.stack.len();
|
let len = dispatch.stack.len();
|
||||||
let callable = match dispatch.stack[len - 2].value.clone() {
|
let callable = match dispatch.stack[len - 2].value.clone() {
|
||||||
Value::Function(callable) => callable,
|
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 state = dispatch.stack[len - 1].value.clone();
|
||||||
let var = dispatch.stack[len].value.clone();
|
let var = dispatch.stack[len].value.clone();
|
||||||
|
|
@ -1143,7 +1248,9 @@ impl<S> Machine<S> {
|
||||||
self.dispatch_call(
|
self.dispatch_call(
|
||||||
dispatch,
|
dispatch,
|
||||||
callable,
|
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!(
|
if matches!(
|
||||||
dispatch.stack.get(len + 1).unwrap_or(&Register::NIL).value,
|
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> {
|
pub fn set_global(&mut self, name: &str, value: Value) -> Result<(), RunError> {
|
||||||
let name = self.new_string(name)?;
|
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> {
|
pub fn set_global_object<T>(
|
||||||
let value = self.new_object(object,meta);
|
&mut self,
|
||||||
self.set_global(name,value)
|
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)?;
|
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> {
|
pub fn new_foreign(&mut self, function: ForeignFunction<S>) -> Result<Value, RunError> {
|
||||||
Ok(Value::Function(Callable::Rust(self.natives.make(function).id.any())))
|
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> {
|
pub fn new(state: impl FnOnce(&mut FreeList<Meta<S>>) -> S) -> Result<Machine<S>, RunError> {
|
||||||
let mut tables = FreeList::<Table>::new();
|
let mut tables = FreeList::<Table>::new();
|
||||||
|
|
@ -1197,7 +1315,7 @@ impl<S> Machine<S> {
|
||||||
upvalues: FreeList::new(),
|
upvalues: FreeList::new(),
|
||||||
natives: FreeList::new(),
|
natives: FreeList::new(),
|
||||||
meta_strings: MetaStrings::new(),
|
meta_strings: MetaStrings::new(),
|
||||||
cache: Map::new(),
|
cache: Set::new(),
|
||||||
};
|
};
|
||||||
machine.init_meta_strings()?;
|
machine.init_meta_strings()?;
|
||||||
load(&mut machine)?;
|
load(&mut machine)?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue