Small tweaks

This commit is contained in:
paladin 2026-10-05 07:34:03 +01:00
parent bfc418a55e
commit 93d6f6a886
4 changed files with 175 additions and 119 deletions

53
Cargo.lock generated
View file

@ -5,6 +5,10 @@ version = 4
[[package]]
name = "deimos"
version = "0.1.0"
dependencies = [
"macros",
"map",
]
[[package]]
name = "glam"
@ -12,6 +16,20 @@ version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6"
[[package]]
name = "macros"
version = "0.0.0"
dependencies = [
"map",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "map"
version = "0.1.0"
[[package]]
name = "mars"
version = "0.1.0"
@ -19,3 +37,38 @@ dependencies = [
"deimos",
"glam",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"

View file

@ -24,7 +24,6 @@ mod tests {
let closure = match machine.load(include_str!("tests/script.lua")) {
Err(error) => panic!("{}", error),
Ok(closure) => closure,
_ => panic!(),
};
println!("{:?}", machine.freelist().get(&closure).prototype);
match machine.enter(closure) {

View file

@ -1,6 +1,6 @@
use crate::vm::{Callable, MarsAllocator, RunError, Value};
use deimos::gc::{Allocates, Id, MaybeId, Traverse};
use deimos::map::{DisplacementSet, Entry, Equivalent, Hashed, Hashes, Map, TableEntry};
use deimos::gc::{Id, MaybeId, Traverse};
use deimos::map::{DisplacementSet, Equivalent, Hashed, Hashes, TableEntry};
#[derive(Debug, Clone)]
struct KeyValue {
@ -47,8 +47,8 @@ impl Hashes for Value {
0
}
}
Value::CachedString(cached) => cached.1,
Value::String(_string, hash) => *hash,
Value::CachedString(hashed) => hashed.1,
Value::String(hashed) => hashed.1,
Value::Function(callable) => match callable {
Callable::Rust(native) => native.hashed(),
Callable::Mars(closure) => closure.hashed(),

232
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::{DisplacementSet, Equivalent, Hashed, Hashes, Map, TableEntry};
use deimos::map::{DisplacementSet, Equivalent, Hashed, Hashes, MapEntry, TableEntry};
use std::fmt::{Debug, Display, Formatter};
use std::ops::Deref;
use std::rc::Rc;
@ -157,7 +157,7 @@ impl std::fmt::Debug for Callable {
}
#[derive(Debug)]
pub struct RunError(pub(crate) String);
pub struct RunError(pub String);
impl RunError {
pub fn new(string: &str) -> RunError {
@ -165,36 +165,50 @@ impl RunError {
}
}
impl From<String> for RunError {
fn from(value: String) -> Self {
RunError(value)
}
}
impl Display for RunError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for RunError {}
use crate::compiler::compile;
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);
pub struct HashedString(Rc<str>, pub(crate) Hashed);
impl Hashes for CachedString {
impl Hashes for HashedString {
fn hashed(&self) -> Hashed {
self.1
}
}
impl PartialEq for CachedString {
impl PartialEq for HashedString {
fn eq(&self, other: &Self) -> bool {
self.1 == other.1 && self.0 == other.0
}
}
impl HashedString {
pub fn as_str(&self) -> &str {
self.0.deref()
}
}
#[derive(Clone, Debug)]
struct CachedStringEntry(Option<CachedString>);
struct CachedStringEntry(Option<HashedString>);
impl Hashes for CachedStringEntry {
fn hashed(&self) -> Hashed {
self.0.as_ref().unwrap().0.deref().hashed()
@ -227,15 +241,14 @@ impl Equivalent<&str> for CachedStringEntry {
}
}
const PHRASE_LEN: usize = 16;
#[derive(Clone)]
pub enum Value {
Nil, // Empty value
Bool(bool),
Number(f64),
Integer(u64), // 64 maybe we should use signed integers...
CachedString(CachedString),
String(Rc<str>, Hashed),
CachedString(HashedString),
String(HashedString),
Table(Id<Table>),
Object(AnyId, AnyId),
Function(Callable),
@ -257,7 +270,7 @@ impl Debug for Value {
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::String(string) => write!(f, "{}", string.0.deref()),
Value::Table(it) => write!(f, "{:?}", it),
Value::Object(it, meta) => write!(f, "{:?}:{:?}", it, meta),
Value::Function(it) => write!(f, "{:?}", it),
@ -275,11 +288,18 @@ impl Value {
}
pub(crate) fn unwrap_str(&mut self) -> Rc<str> {
match self {
Value::CachedString(cached) => cached.0.clone(),
Value::String(string, _hash) => string.clone(),
Value::CachedString(hashed) => hashed.0.clone(),
Value::String(hashed) => hashed.0.clone(),
_ => panic!(),
}
}
pub fn try_string(self) -> Option<HashedString> {
match self {
Value::CachedString(hashed) => Some(hashed),
Value::String(hashed) => Some(hashed),
_ => None,
}
}
pub(crate) fn traverse<C>(&self, context: &mut C)
where
C: MarsAllocator,
@ -313,17 +333,17 @@ impl PartialEq for Value {
Value::Integer(other) => this == other,
_ => false,
},
Value::String(string, hash) => match other {
Value::String(string) => match other {
Value::CachedString(other) => {
if *hash == other.1 {
other.0.deref() == string.deref()
if string.1 == other.1 {
other.0.deref() == string.0.deref()
} else {
false
}
}
Value::String(other_string, other_hash) => {
if hash == other_hash {
string == other_string
Value::String(other) => {
if string.1 == other.1 {
string.0.deref() == other.0.deref()
} else {
false
}
@ -332,9 +352,9 @@ impl PartialEq for Value {
},
Value::CachedString(string) => match other {
Value::CachedString(other) => other.0 == string.0, // pointer equality
Value::String(other_string, other_hash) => {
if string.1 == *other_hash {
string.0.deref() == other_string.deref()
Value::String(other) => {
if string.1 == other.1 {
string.0.deref() == other.0.deref()
} else {
false
}
@ -366,43 +386,86 @@ pub struct Frame {
closure: Id<Closure>, // The function that was called on this frame
}
macro_rules! read_only {
() => {
|machine, id, needle| Err(RunError(format!("Field {} is read only", needle.pop())))
};
impl<V> Equivalent<HashedString> for MapEntry<&str,V> { // For the constant maps
fn matches(&self, other: &HashedString) -> bool {
self.0.as_ref().unwrap().0 == other.as_str()
}
}
type MetaFieldSet<S> = fn(
&mut Machine<S>,
id: AnyId,
index: Value,
value: Value,
needle: Needle,
) -> Result<Value, RunError>;
type MetaFieldGet<S> = fn(&mut Machine<S>, id: AnyId, index: Value, needle: Needle) -> Result<(), RunError>;
pub struct MetaField<S> {
set: MetaFieldSet<S>,
get: MetaFieldGet<S>,
#[macro_export]
macro_rules! getter {
(@$state:ident,$index:literal=>$f:expr,$($x:literal=>$y:expr),*) => {
let temp: MetaOutFn<$state> = $f;
$crate::getter!(@$($x=>$y)*)
};
(@$state:ident,$index:literal=>$f:expr) => {
let temp: MetaOutFn<$state> = $f;
};
(@) => {
};
($ident:tt,$state:ident,{$($map:tt)*}) => {
|machine,id,index,needle| {
use $crate::deimos::map::Entry;
use $crate::deimos::map::MapEntry;
$crate::getter!(@$state,$($map)*);
static MAP: &[
$crate::deimos::map::Entry<
$crate::deimos::map::MapEntry<&'static str,$crate::MetaOutFn<$state>>
>] = &$crate::deimos::macros::map!{$($map)*};
if let Some(string) = index.clone().try_string() {
if let Some(func) = $crate::deimos::map::get(MAP,string) {
(func.0.unwrap().1)(machine,id,needle)
} else {
Err(RunError::from(format!("No field {:?} for {}",index,stringify!($ident))))
}
} else {
Err(RunError::from(format!("No field {:?} for {}",index,stringify!($ident))))
}
}
}
}
#[macro_export]
macro_rules! setter {
($ident:tt,$state:tt,$($map:tt)*) => {
|machine,id,index,value,needle| {
static MAP: &[Entry<MapEntry<&'static str,$crate::MetaInFn<$state>>>] = &$($map)*;
if let Some(string) = index.clone().try_string() {
if let Some(func) = $crate::deimos::map::get(MAP,string) {
(func.0.unwrap().1)(machine,id,value,needle)
} else {
Err(RunError::from(format!("No field {:?} for {}",index,stringify!($ident))))
}
} else {
Err(RunError::from(format!("No field {:?} for {}",index,stringify!($ident))))
}
}
}
}
pub type MetaGetFn<S> = fn(&mut Machine<S>, AnyId, Value, Needle) -> Result<Value, RunError>;
pub type MetaSetFn<S> = fn(&mut Machine<S>, AnyId, Value, Value, Needle) -> Result<(), RunError>;
pub type MetaInFn<S> = fn(&mut Machine<S>, AnyId, Value, Needle) -> Result<(), RunError>;
pub type MetaOutFn<S> = fn(&mut Machine<S>, AnyId, Needle) -> Result<Value, RunError>;
pub type MetaTraverseFn<S> = fn(&mut Machine<S>, AnyId) -> 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 get: fn(&mut Machine<S>, id: AnyId, index: Value, needle: Needle) -> Result<Value, RunError>,
pub set: fn(&mut Machine<S>, id: AnyId, index: Value, value: Value, needle: Needle) -> Result<(), RunError>,
pub traverse: fn(&mut Machine<S>, id: AnyId) -> Result<(), RunError>,
pub fields: Map<Value, MetaField<S>>,
pub constants: Map<Value, Value>,
pub string: MetaOutFn<S>,
pub number: MetaOutFn<S>,
pub get: MetaGetFn<S>,
pub set: MetaSetFn<S>,
pub traverse: MetaTraverseFn<S>,
}
impl<S> Default for Meta<S> {
fn default() -> Self {
Meta {
string: |machine, id, needle| Err(RunError::new("Type does not implement to string.")),
number: |machine, id, needle| Err(RunError::new("Type does not implement to number.")),
get: |machine, id, index, needle|,
set: |machine, id, index, value, needle| (),
traverse: |machine, id| (),
string: |_machine, _id, _needle| Err(RunError::new("Type does not implement to string")),
number: |_machine, _id, _needle| Err(RunError::new("Type does not implement to number")),
get: |_machine, _id, _index, _needle| Err(RunError::new("Type does not implement get")),
set: |_machine, _id, _index, _value, _needle| Err(RunError::new("Type does not implement set")),
traverse: |_machine, _id| Err(RunError::new("Type did not subscribe to traversal")),
}
}
}
@ -411,7 +474,7 @@ pub type ForeignFunction<S> = fn(&mut Machine<S>, needle: Needle) -> Result<(),
// Machine for executing functions
pub struct Machine<S> {
state: S,
pub state: S,
global: Id<Table>,
tables: FreeList<Table>,
closures: FreeList<Closure>,
@ -637,13 +700,13 @@ impl<S> Machine<S> {
match self.cache.get(string) {
Some(ref entry) => Ok(Value::CachedString(entry.0.as_ref().unwrap().clone())),
None => {
let cached = CachedString(Rc::from(string), string.hashed());
let cached = HashedString(Rc::from(string), string.hashed());
self.cache.set(CachedStringEntry(Some(cached.clone())));
Ok(Value::CachedString(cached))
}
}
} else {
Ok(Value::String(Rc::from(string), string.hashed()))
Ok(Value::String(HashedString(Rc::from(string), string.hashed())))
}
}
fn meta_call(
@ -831,68 +894,9 @@ impl<S> Machine<S> {
// todo: object-table equality
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) => {
if *hash == other.1 {
other.0.deref() == (*string).deref()
} else {
false
}
}
Value::String(other_string, other_hash) => {
if hash == other_hash {
string == other_string
} else {
false
}
}
_ => false,
},
Value::CachedString(string) => match rhs {
Value::CachedString(other) => other.0 == string.0, // pointer equality
Value::String(other_string, other_hash) => {
if string.1 == *other_hash {
string.0.deref() == (*other_string).deref()
} else {
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,
},
Value::Function(this) => match rhs {
Value::Function(other) => other == this,
_ => false,
},
})
Ok(false)
}
pub(crate) fn load(&mut self, source: &str) -> Result<Id<Closure>, MarsError> {
pub fn load(&mut self, source: &str) -> Result<Id<Closure>, MarsError> {
let chunk = match parse(source) {
Ok(parsed) => parsed,
Err(error) => return Err(MarsError(error.msg(source))),
@ -1286,7 +1290,7 @@ impl<S> Machine<S> {
}
Ok(())
}
pub(crate) fn enter(&mut self, closure: Id<Closure>) -> Result<(), RunError> {
pub fn enter(&mut self, closure: Id<Closure>) -> Result<(), RunError> {
let mut needle = Dispatch {
frames: vec![Frame {
vararg: 0,