feat: initial parallel impl

This commit is contained in:
2025-06-08 17:27:43 +08:00
parent 3797544fc2
commit 7293cb9f75
18 changed files with 529 additions and 934 deletions

View File

@@ -7,74 +7,94 @@ use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use crate::engine::Engine;
use crate::error::{Error, Result};
use crate::ty::public::Symbol;
use super::super::public as p;
use super::Value;
#[repr(transparent)]
#[derive(Constructor, Clone, PartialEq)]
pub struct AttrSet<'gc> {
data: HashMap<EcoString, Value<'gc>>,
pub struct AttrSet {
data: HashMap<EcoString, Value>,
}
impl<'gc> From<HashMap<EcoString, Value<'gc>>> for AttrSet<'gc> {
fn from(data: HashMap<EcoString, Value<'gc>>) -> Self {
impl From<HashMap<EcoString, Value>> for AttrSet {
fn from(data: HashMap<EcoString, Value>) -> Self {
Self { data }
}
}
impl<'gc> Deref for AttrSet<'gc> {
type Target = HashMap<EcoString, Value<'gc>>;
impl Deref for AttrSet {
type Target = HashMap<EcoString, Value>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'gc> AttrSet<'gc> {
impl AttrSet {
pub fn with_capacity(cap: usize) -> Self {
AttrSet {
data: HashMap::with_capacity(cap),
}
}
pub fn push_attr_force(&mut self, sym: EcoString, val: Value<'gc>) {
pub fn push_attr_force(&mut self, sym: EcoString, val: Value) {
self.data.insert(sym, val);
}
pub fn push_attr(&mut self, sym: EcoString, val: Value<'gc>) {
pub fn push_attr(&mut self, sym: EcoString, val: Value) {
if self.data.get(&sym).is_some() {
todo!()
}
self.data.insert(sym, val);
}
pub fn select(&self, sym: &EcoString) -> Option<Value<'gc>> {
self.data.get(sym).cloned()
pub fn select(&self, mut path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<Value> {
// .ok_or_else(|| Error::EvalError())),
let mut data = &self.data;
let last = path.nth_back(0).unwrap();
for item in path {
let item = item?;
let Some(Value::AttrSet(attrs)) = data.get(&item) else {
return Err(Error::EvalError(format!("{} not found", Symbol::from(item))))
};
data = attrs.as_inner();
}
let last = last?;
data.get(&last).cloned().ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last))))
}
pub fn has_attr(&self, sym: &EcoString) -> bool {
self.data.get(sym).is_some()
pub fn has_attr(&self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<bool> {
let mut data = &self.data;
for item in path {
let Some(Value::AttrSet(attrs)) = data.get(&item?) else {
return Ok(false)
};
data = attrs.as_inner();
}
Ok(true)
}
pub fn update(&mut self, other: &AttrSet<'gc>) {
pub fn update(&mut self, other: &AttrSet) {
for (k, v) in other.data.iter() {
self.push_attr_force(k.clone(), v.clone())
}
}
pub fn as_inner(&self) -> &HashMap<EcoString, Value<'gc>> {
pub fn as_inner(&self) -> &HashMap<EcoString, Value> {
&self.data
}
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<EcoString, Value<'gc>>> {
pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<EcoString, Value>> {
unsafe { std::mem::transmute(self) }
}
pub fn from_inner(data: HashMap<EcoString, Value<'gc>>) -> Self {
pub fn from_inner(data: HashMap<EcoString, Value>) -> Self {
Self { data }
}
pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool {
pub fn eq_impl(&self, other: &AttrSet) -> bool {
self.data.iter().len() == other.data.iter().len()
&& std::iter::zip(
self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)),
@@ -83,7 +103,7 @@ impl<'gc> AttrSet<'gc> {
.all(|((_, v1), (_, v2))| v1.eq_impl(v2))
}
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
p::Value::AttrSet(p::AttrSet::new(
self.data
.iter()

View File

@@ -5,11 +5,11 @@ use crate::env::VmEnv;
use crate::ir;
pub struct Func<'gc> {
pub func: &'gc ir::Func,
pub env: Rc<VmEnv<'gc>>,
pub env: Rc<VmEnv>,
}
impl<'gc> Func<'gc> {
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv<'gc>>) -> Self {
pub fn new(func: &'gc ir::Func, env: Rc<VmEnv>) -> Self {
Self {
func,
env,

View File

@@ -1,5 +1,7 @@
use std::ops::Deref;
use std::rc::Weak;
use ecow::EcoVec;
use hashbrown::HashSet;
use crate::env::VmEnv;
@@ -9,50 +11,64 @@ use crate::engine::Engine;
use super::Value;
#[derive(Clone, PartialEq)]
pub struct List<'gc> {
data: Vec<Value<'gc>>,
pub struct List {
data: EcoVec<Value>,
}
impl<'gc> Default for List<'gc> {
impl Default for List {
fn default() -> Self {
Self::new()
}
}
impl<'gc> List<'gc> {
impl<'gc, T: Into<EcoVec<Value>>> From<T> for List {
fn from(value: T) -> Self {
Self { data: value.into() }
}
}
impl Deref for List {
type Target = [Value];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl List {
pub fn new() -> Self {
List { data: Vec::new() }
List { data: EcoVec::new() }
}
pub fn with_capacity(cap: usize) -> Self {
List {
data: Vec::with_capacity(cap),
data: EcoVec::with_capacity(cap),
}
}
pub fn push(&mut self, elem: Value<'gc>) {
pub fn push(&mut self, elem: Value) {
self.data.push(elem);
}
pub fn concat(&mut self, other: &List<'gc>) {
pub fn concat(&mut self, other: &List) {
for elem in other.data.iter() {
self.data.push(elem.clone());
}
}
pub fn capture(&mut self, env: &Weak<VmEnv<'gc>>) {
pub fn capture(&mut self, env: &Weak<VmEnv>) {
self.data.iter().for_each(|v| {
if let Value::Thunk(ref thunk) = v.clone() {
thunk.capture_env_weak(env.clone());
todo!()
// thunk.capture_env_weak(env.clone());
}
})
}
pub fn into_inner(self) -> Vec<Value<'gc>> {
pub fn into_inner(self) -> EcoVec<Value> {
self.data
}
pub fn to_public(&self, engine: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
p::Value::List(p::List::new(
self.data
.iter()

View File

@@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::{Rc, Weak};
use std::sync::RwLock;
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
@@ -21,39 +22,84 @@ mod primop;
mod string;
pub use attrset::*;
pub use func::*;
pub use list::List;
pub use primop::*;
#[derive(Clone)]
pub enum EnvRef {
Strong(Rc<VmEnv>),
Weak(Weak<VmEnv>)
}
#[derive(Clone)]
pub struct ThunkRef {
pub idx: usize,
pub env: Option<EnvRef>
}
pub enum Thunk {
Expr(ThunkRef),
Suspended,
Value(Value)
}
impl Thunk {
pub fn new(idx: usize) -> Self {
Thunk::Expr(ThunkRef::new(idx))
}
pub fn force(&mut self, engine: &Engine) -> Result<Value> {
todo!()
}
}
impl ThunkRef {
pub fn new(idx: usize) -> Self {
ThunkRef { idx, env: None }
}
pub fn capture(&mut self, env: EnvRef) {
let _ = self.env.insert(env);
}
}
impl EnvRef {
pub fn upgrade(&mut self) {
if let EnvRef::Weak(weak) = &*self {
*self = EnvRef::Strong(weak.upgrade().unwrap())
}
}
}
#[derive(IsVariant, Unwrap, Clone)]
pub enum Value<'gc> {
pub enum Value {
Int(i64),
Float(f64),
Bool(bool),
String(EcoString),
Null,
Thunk(Thunk<'gc>),
AttrSet(Rc<AttrSet<'gc>>),
List(Rc<List<'gc>>),
Thunk(Rc<RwLock<Thunk>>),
AttrSet(Rc<AttrSet>),
List(List),
Catchable(EcoString),
PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
Func(Rc<Func<'gc>>),
PartialPrimOp(Rc<PartialPrimOp>),
Func(Rc<RwLock<ThunkRef>>),
}
impl Hash for Value<'_> {
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Value::*;
std::mem::discriminant(self).hash(state);
match self {
AttrSet(x) => Rc::as_ptr(x).hash(state),
List(x) => Rc::as_ptr(x).hash(state),
List(x) => x.as_ptr().hash(state),
_ => 0.hash(state),
}
}
}
impl<'gc> Value<'gc> {
impl Value {
fn eq_impl(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
@@ -71,37 +117,37 @@ impl<'gc> Value<'gc> {
}
}
impl<'gc> PartialEq for Value<'gc> {
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()),
_ => false,
}
}
}
impl Eq for Value<'_> {}
impl Eq for Value {}
#[derive(IsVariant, Unwrap, Clone)]
pub enum ValueAsRef<'v, 'gc> {
pub enum ValueAsRef<'v> {
Int(i64),
Float(f64),
Bool(bool),
String(&'v str),
String(&'v EcoString),
Null,
Thunk(&'v Thunk<'gc>),
AttrSet(&'v AttrSet<'gc>),
List(&'v List<'gc>),
Thunk(&'v RwLock<Thunk>),
AttrSet(&'v AttrSet),
List(&'v List),
Catchable(&'v str),
PrimOp(&'v PrimOp),
PartialPrimOp(&'v PartialPrimOp<'gc>),
Func(&'v Func<'gc>),
PartialPrimOp(&'v PartialPrimOp),
Func(&'v RwLock<ThunkRef>),
}
impl<'gc, 'v> Value<'gc> {
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> {
impl Value {
pub fn as_ref(&self) -> ValueAsRef {
use Value::*;
use ValueAsRef as R;
match self {
@@ -120,7 +166,7 @@ impl<'gc, 'v> Value<'gc> {
}
}
}
impl<'gc> Value<'gc> {
impl Value {
pub fn ok(self) -> Result<Self> {
Ok(self)
}
@@ -151,15 +197,15 @@ impl<'gc> Value<'gc> {
}
}
pub fn call(&mut self, arg: Self, vm: &Engine) -> Result<()> {
pub fn call(&mut self, arg: Self, engine: &Engine) -> Result<()> {
use Value::*;
if matches!(arg, Value::Catchable(_)) {
*self = arg;
return Ok(());
}
*self = match self {
PrimOp(func) => func.call(arg, vm),
PartialPrimOp(func) => func.call(arg, vm),
PrimOp(func) => func.call(arg, engine),
PartialPrimOp(func) => func.call(arg, engine),
Catchable(_) => return Ok(()),
_ => todo!(),
}?;
@@ -293,7 +339,7 @@ impl<'gc> Value<'gc> {
pub fn push(&mut self, elem: Self) -> &mut Self {
if let Value::List(list) = self {
Rc::make_mut(list).push(elem);
list.push(elem);
} else if let Value::Catchable(_) = self {
} else if let Value::Catchable(_) = elem {
*self = elem;
@@ -310,7 +356,7 @@ impl<'gc> Value<'gc> {
}
match (self, other) {
(Value::List(a), Value::List(b)) => {
Rc::make_mut(a).concat(&b);
a.concat(&b);
}
(Value::Catchable(_), _) => (),
_ => todo!(),
@@ -343,11 +389,10 @@ impl<'gc> Value<'gc> {
}
}
pub fn select(&mut self, sym: &EcoString) -> Result<&mut Self> {
pub fn select(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>) -> Result<&mut Self> {
let val = match self {
Value::AttrSet(attrs) => attrs
.select(sym)
.ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(sym.as_str())))),
.select(path),
Value::Catchable(_) => return Ok(self),
_ => Err(Error::EvalError(format!(
"cannot select from {:?}",
@@ -358,9 +403,9 @@ impl<'gc> Value<'gc> {
Ok(self)
}
pub fn select_with_default(&mut self, sym: &EcoString, default: Self) -> Result<&mut Self> {
pub fn select_with_default(&mut self, path: impl DoubleEndedIterator<Item = Result<EcoString>>, default: Self) -> Result<&mut Self> {
let val = match self {
Value::AttrSet(attrs) => attrs.select(sym).unwrap_or(default),
Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default),
Value::Catchable(_) => return Ok(self),
_ => {
return Err(Error::EvalError(format!(
@@ -373,15 +418,15 @@ impl<'gc> Value<'gc> {
Ok(self)
}
pub fn has_attr(&mut self, sym: &EcoString) -> &mut Self {
pub fn has_attr(&mut self, path: impl IntoIterator<Item = Result<EcoString>>) -> Result<()> {
if let Value::AttrSet(attrs) = self {
let val = Value::Bool(attrs.has_attr(sym));
let val = Value::Bool(attrs.has_attr(path)?);
*self = val;
} else if let Value::Catchable(_) = self {
} else {
*self = Value::Bool(false);
}
self
Ok(())
}
pub fn coerce_to_string(&mut self) -> &mut Self {
@@ -393,7 +438,18 @@ impl<'gc> Value<'gc> {
self
}
pub fn to_public(&self, vm: &'gc Engine, seen: &mut HashSet<Value<'gc>>) -> p::Value {
pub fn force(&mut self, engine: &Engine) -> Result<&mut Self> {
if let Value::Thunk(thunk) = &*self {
let val = {
let mut thunk = thunk.write().unwrap();
thunk.force(engine)
}?;
*self = val;
}
Ok(self)
}
pub fn to_public(&self, engine: &Engine, seen: &mut HashSet<Value>) -> p::Value {
use self::Value::*;
use p::Value;
if seen.contains(self) {
@@ -402,11 +458,11 @@ impl<'gc> Value<'gc> {
match self {
AttrSet(attrs) => {
seen.insert(self.clone());
attrs.to_public(vm, seen)
attrs.to_public(engine, seen)
}
List(list) => {
seen.insert(self.clone());
list.to_public(vm, seen)
list.to_public(engine, seen)
}
Catchable(catchable) => Value::Catchable(catchable.clone().into()),
Int(x) => Value::Const(Const::Int(*x)),
@@ -422,90 +478,3 @@ impl<'gc> Value<'gc> {
}
}
#[repr(transparent)]
#[derive(Clone)]
pub struct Thunk<'gc> {
pub thunk: Rc<RefCell<_Thunk<'gc>>>,
}
// TODO: impl
type OpCodes = ();
#[derive(IsVariant, Unwrap)]
pub enum _Thunk<'gc> {
Code(&'gc OpCodes, Option<Env<'gc>>),
Suspended,
Value(Value<'gc>),
}
#[derive(Unwrap)]
pub enum Env<'gc> {
Strong(Rc<VmEnv<'gc>>),
Weak(Weak<VmEnv<'gc>>),
}
impl Env<'_> {
fn upgrade(self) -> Self {
if let Self::Weak(weak) = self {
Env::Strong(weak.upgrade().unwrap())
} else {
self
}
}
}
impl<'gc> Thunk<'gc> {
pub fn new(opcodes: &'gc OpCodes) -> Self {
Thunk {
thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(),
}
}
pub fn capture_env(&self, env: Rc<VmEnv<'gc>>) {
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
let _ = envcell.insert(Env::Strong(env));
}
}
pub fn capture_env_weak(&self, env: Weak<VmEnv<'gc>>) {
if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
let _ = envcell.insert(Env::Weak(env));
}
}
pub fn upgrade(&self) {
replace_with_or_abort(&mut *self.thunk.borrow_mut(), |this| {
if let _Thunk::Code(opcodes, envcell) = this {
_Thunk::Code(opcodes, envcell.map(Env::upgrade))
} else {
this
}
});
}
pub fn suspend(&self) -> Result<(&'gc OpCodes, Rc<VmEnv<'gc>>)> {
use _Thunk::*;
match std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Suspended) {
Code(opcodes, env) => Ok((opcodes, env.unwrap().upgrade().unwrap_strong())),
_ => Err(Error::EvalError("infinite recursion encountered".into())),
}
}
pub fn insert_value(&self, value: Value<'gc>) {
*self.thunk.borrow_mut() = _Thunk::Value(value);
}
pub fn get_value(&self) -> Option<&Value<'gc>> {
if let _Thunk::Value(val) = unsafe { &*self.thunk.as_ptr() } {
Some(val)
} else {
None
}
}
}
impl PartialEq for Thunk<'_> {
fn eq(&self, _: &Self) -> bool {
false
}
}

View File

@@ -11,7 +11,7 @@ use super::Value;
pub struct PrimOp {
pub name: &'static str,
arity: usize,
func: for<'gc> fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
func: fn(Vec<Value>, &Engine) -> Result<Value>,
}
impl PartialEq for PrimOp {
@@ -21,7 +21,7 @@ impl PartialEq for PrimOp {
}
impl PrimOp {
pub fn call<'gc>(&self, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> {
pub fn call(&self, arg: Value, ctx: &Engine) -> Result<Value> {
let mut args = Vec::with_capacity(self.arity);
args.push(arg);
if self.arity > 1 {
@@ -39,21 +39,21 @@ impl PrimOp {
}
#[derive(Clone)]
pub struct PartialPrimOp<'gc> {
pub struct PartialPrimOp {
pub name: &'static str,
arity: usize,
args: Vec<Value<'gc>>,
func: fn(Vec<Value<'gc>>, &Engine) -> Result<Value<'gc>>,
args: Vec<Value>,
func: fn(Vec<Value>, &Engine) -> Result<Value>,
}
impl PartialEq for PartialPrimOp<'_> {
impl PartialEq for PartialPrimOp {
fn eq(&self, _: &Self) -> bool {
false
}
}
impl<'gc> PartialPrimOp<'gc> {
pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, ctx: &Engine) -> Result<Value<'gc>> {
impl PartialPrimOp {
pub fn call(self: &mut Rc<Self>, arg: Value, ctx: &Engine) -> Result<Value> {
let func = self.func;
let Some(ret) = ({
let self_mut = Rc::make_mut(self);