Make values sync

This commit is contained in:
Laurenz 2022-02-17 14:09:26 +01:00
parent 91e45458e3
commit 17e3353483
3 changed files with 54 additions and 65 deletions

View File

@ -29,7 +29,6 @@ pub use styles::*;
pub use template::*; pub use template::*;
pub use value::*; pub use value::*;
use std::cell::RefMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::mem; use std::mem;
@ -46,7 +45,7 @@ use crate::loading::Loader;
use crate::source::{SourceId, SourceStore}; use crate::source::{SourceId, SourceStore};
use crate::syntax::ast::*; use crate::syntax::ast::*;
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
use crate::util::{EcoString, RefMutExt}; use crate::util::EcoString;
use crate::Context; use crate::Context;
/// An evaluated module, ready for importing or conversion to a root layout /// An evaluated module, ready for importing or conversion to a root layout
@ -347,7 +346,7 @@ impl Eval for Ident {
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> { fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
match ctx.scopes.get(self) { match ctx.scopes.get(self) {
Some(slot) => Ok(slot.borrow().clone()), Some(slot) => Ok(slot.read().unwrap().clone()),
None => bail!(self.span(), "unknown variable"), None => bail!(self.span(), "unknown variable"),
} }
} }
@ -450,10 +449,11 @@ impl Eval for BinaryExpr {
impl BinaryExpr { impl BinaryExpr {
/// Apply a basic binary operation. /// Apply a basic binary operation.
fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> TypResult<Value> fn apply(
where &self,
F: FnOnce(Value, Value) -> StrResult<Value>, ctx: &mut EvalContext,
{ op: fn(Value, Value) -> StrResult<Value>,
) -> TypResult<Value> {
let lhs = self.lhs().eval(ctx)?; let lhs = self.lhs().eval(ctx)?;
// Short-circuit boolean operations. // Short-circuit boolean operations.
@ -468,14 +468,20 @@ impl BinaryExpr {
} }
/// Apply an assignment operation. /// Apply an assignment operation.
fn assign<F>(&self, ctx: &mut EvalContext, op: F) -> TypResult<Value> fn assign(
where &self,
F: FnOnce(Value, Value) -> StrResult<Value>, ctx: &mut EvalContext,
{ op: fn(Value, Value) -> StrResult<Value>,
) -> TypResult<Value> {
let rhs = self.rhs().eval(ctx)?; let rhs = self.rhs().eval(ctx)?;
let mut target = self.lhs().access(ctx)?; self.lhs().access(
ctx,
Box::new(|target| {
let lhs = mem::take(&mut *target); let lhs = mem::take(&mut *target);
*target = op(lhs, rhs).at(self.span())?; *target = op(lhs, rhs).at(self.span())?;
Ok(())
}),
)?;
Ok(Value::None) Ok(Value::None)
} }
} }
@ -766,13 +772,13 @@ impl Eval for ImportExpr {
match self.imports() { match self.imports() {
Imports::Wildcard => { Imports::Wildcard => {
for (var, slot) in module.scope.iter() { for (var, slot) in module.scope.iter() {
ctx.scopes.def_mut(var, slot.borrow().clone()); ctx.scopes.def_mut(var, slot.read().unwrap().clone());
} }
} }
Imports::Items(idents) => { Imports::Items(idents) => {
for ident in idents { for ident in idents {
if let Some(slot) = module.scope.get(&ident) { if let Some(slot) = module.scope.get(&ident) {
ctx.scopes.def_mut(ident.take(), slot.borrow().clone()); ctx.scopes.def_mut(ident.take(), slot.read().unwrap().clone());
} else { } else {
bail!(ident.span(), "unresolved import"); bail!(ident.span(), "unresolved import");
} }
@ -825,24 +831,27 @@ impl Eval for ReturnExpr {
/// This only works if the expression is a valid lvalue. /// This only works if the expression is a valid lvalue.
pub trait Access { pub trait Access {
/// Try to access the value. /// Try to access the value.
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>>; fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()>;
} }
/// Process an accessed value.
type Handler<'a> = Box<dyn FnOnce(&mut Value) -> TypResult<()> + 'a>;
impl Access for Expr { impl Access for Expr {
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> {
match self { match self {
Expr::Ident(ident) => ident.access(ctx), Expr::Ident(ident) => ident.access(ctx, f),
Expr::Call(call) => call.access(ctx), Expr::Call(call) => call.access(ctx, f),
_ => bail!(self.span(), "cannot access this expression mutably"), _ => bail!(self.span(), "cannot access this expression mutably"),
} }
} }
} }
impl Access for Ident { impl Access for Ident {
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> {
match ctx.scopes.get(self) { match ctx.scopes.get(self) {
Some(slot) => match slot.try_borrow_mut() { Some(slot) => match slot.try_write() {
Ok(guard) => Ok(guard), Ok(mut guard) => f(&mut guard),
Err(_) => bail!(self.span(), "cannot mutate a constant"), Err(_) => bail!(self.span(), "cannot mutate a constant"),
}, },
None => bail!(self.span(), "unknown variable"), None => bail!(self.span(), "unknown variable"),
@ -851,18 +860,21 @@ impl Access for Ident {
} }
impl Access for CallExpr { impl Access for CallExpr {
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> { fn access(&self, ctx: &mut EvalContext, f: Handler) -> TypResult<()> {
let args = self.args().eval(ctx)?; let args = self.args().eval(ctx)?;
let guard = self.callee().access(ctx)?; self.callee().access(
ctx,
RefMut::try_map(guard, |value| match value { Box::new(|value| match value {
Value::Array(array) => array.get_mut(args.into_index()?).at(self.span()), Value::Array(array) => {
Value::Dict(dict) => Ok(dict.get_mut(args.into_key()?)), f(array.get_mut(args.into_index()?).at(self.span())?)
}
Value::Dict(dict) => f(dict.get_mut(args.into_key()?)),
v => bail!( v => bail!(
self.callee().span(), self.callee().span(),
"expected collection, found {}", "expected collection, found {}",
v.type_name(), v.type_name(),
), ),
}) }),
)
} }
} }

View File

@ -1,16 +1,15 @@
use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::iter; use std::iter;
use std::sync::Arc; use std::sync::{Arc, RwLock};
use super::{Args, Class, Construct, EvalContext, Func, Set, Value}; use super::{Args, Class, Construct, EvalContext, Func, Set, Value};
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::util::EcoString; use crate::util::EcoString;
/// A slot where a variable is stored. /// A slot where a variable is stored.
pub type Slot = Arc<RefCell<Value>>; pub type Slot = Arc<RwLock<Value>>;
/// A stack of scopes. /// A stack of scopes.
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
@ -80,18 +79,17 @@ impl Scope {
/// Define a constant variable with a value. /// Define a constant variable with a value.
pub fn def_const(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) { pub fn def_const(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) {
let cell = RefCell::new(value.into()); let cell = RwLock::new(value.into());
// Make it impossible to write to this value again. // Make it impossible to write to this value again.
// FIXME: Use Ref::leak once stable. std::mem::forget(cell.read());
std::mem::forget(cell.borrow());
self.values.insert(var.into(), Arc::new(cell)); self.values.insert(var.into(), Arc::new(cell));
} }
/// Define a mutable variable with a value. /// Define a mutable variable with a value.
pub fn def_mut(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) { pub fn def_mut(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) {
self.values.insert(var.into(), Arc::new(RefCell::new(value.into()))); self.values.insert(var.into(), Arc::new(RwLock::new(value.into())));
} }
/// Define a variable with a slot. /// Define a variable with a slot.
@ -132,7 +130,7 @@ impl Hash for Scope {
self.values.len().hash(state); self.values.len().hash(state);
for (name, value) in self.values.iter() { for (name, value) in self.values.iter() {
name.hash(state); name.hash(state);
value.borrow().hash(state); value.read().unwrap().hash(state);
} }
} }
} }
@ -141,7 +139,7 @@ impl Debug for Scope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Scope ")?; f.write_str("Scope ")?;
f.debug_map() f.debug_map()
.entries(self.values.iter().map(|(k, v)| (k, v.borrow()))) .entries(self.values.iter().map(|(k, v)| (k, v.read().unwrap())))
.finish() .finish()
} }
} }

View File

@ -9,7 +9,6 @@ pub use eco_string::EcoString;
pub use mac_roman::decode_mac_roman; pub use mac_roman::decode_mac_roman;
pub use prehashed::Prehashed; pub use prehashed::Prehashed;
use std::cell::RefMut;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::Range; use std::ops::Range;
@ -223,23 +222,3 @@ impl PathExt for Path {
out out
} }
} }
/// Additional methods for [`RefMut`].
pub trait RefMutExt<'a, T> {
fn try_map<U, F, E>(orig: Self, f: F) -> Result<RefMut<'a, U>, E>
where
F: FnOnce(&mut T) -> Result<&mut U, E>;
}
impl<'a, T> RefMutExt<'a, T> for RefMut<'a, T> {
fn try_map<U, F, E>(mut orig: Self, f: F) -> Result<RefMut<'a, U>, E>
where
F: FnOnce(&mut T) -> Result<&mut U, E>,
{
// Taken from here:
// https://github.com/rust-lang/rust/issues/27746#issuecomment-172899746
f(&mut orig)
.map(|new| new as *mut U)
.map(|raw| RefMut::map(orig, |_| unsafe { &mut *raw }))
}
}