mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Make values sync
This commit is contained in:
parent
91e45458e3
commit
17e3353483
@ -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(
|
||||||
let lhs = mem::take(&mut *target);
|
ctx,
|
||||||
*target = op(lhs, rhs).at(self.span())?;
|
Box::new(|target| {
|
||||||
|
let lhs = mem::take(&mut *target);
|
||||||
|
*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())?)
|
||||||
v => bail!(
|
}
|
||||||
self.callee().span(),
|
Value::Dict(dict) => f(dict.get_mut(args.into_key()?)),
|
||||||
"expected collection, found {}",
|
v => bail!(
|
||||||
v.type_name(),
|
self.callee().span(),
|
||||||
),
|
"expected collection, found {}",
|
||||||
})
|
v.type_name(),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user