diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-10-17 19:26:24 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-10-17 20:04:22 +0200 |
| commit | e21822665591dc19766275da1e185215a6b945ef (patch) | |
| tree | 7788e211c3c33c8b5a8ad7d5eb7574e33631eb16 /src/eval/func.rs | |
| parent | 4fd031a256b2ecfe524859d5599fafb386395572 (diff) | |
Merge some modules
Diffstat (limited to 'src/eval/func.rs')
| -rw-r--r-- | src/eval/func.rs | 248 |
1 files changed, 0 insertions, 248 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs deleted file mode 100644 index c307b237..00000000 --- a/src/eval/func.rs +++ /dev/null @@ -1,248 +0,0 @@ -use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::sync::Arc; - -use comemo::{Track, Tracked}; - -use super::{Args, Eval, Flow, Route, Scope, Scopes, Value, Vm}; -use crate::diag::{SourceResult, StrResult}; -use crate::model::{Content, NodeId, StyleMap}; -use crate::source::SourceId; -use crate::syntax::ast::Expr; -use crate::util::EcoString; -use crate::World; - -/// An evaluatable function. -#[derive(Clone, Hash)] -pub struct Func(Arc<Repr>); - -/// The different kinds of function representations. -#[derive(Hash)] -enum Repr { - /// A native rust function. - Native(Native), - /// A user-defined closure. - Closure(Closure), - /// A nested function with pre-applied arguments. - With(Func, Args), -} - -impl Func { - /// Create a new function from a native rust function. - pub fn from_fn( - name: &'static str, - func: fn(&mut Vm, &mut Args) -> SourceResult<Value>, - ) -> Self { - Self(Arc::new(Repr::Native(Native { - name, - func, - set: None, - node: None, - }))) - } - - /// Create a new function from a native rust node. - pub fn from_node<T: Node>(name: &'static str) -> Self { - Self(Arc::new(Repr::Native(Native { - name, - func: |ctx, args| { - let styles = T::set(args, true)?; - let content = T::construct(ctx, args)?; - Ok(Value::Content(content.styled_with_map(styles.scoped()))) - }, - set: Some(|args| T::set(args, false)), - node: T::SHOWABLE.then(|| NodeId::of::<T>()), - }))) - } - - /// Create a new function from a closure. - pub fn from_closure(closure: Closure) -> Self { - Self(Arc::new(Repr::Closure(closure))) - } - - /// Apply the given arguments to the function. - pub fn with(self, args: Args) -> Self { - Self(Arc::new(Repr::With(self, args))) - } - - /// The name of the function. - pub fn name(&self) -> Option<&str> { - match self.0.as_ref() { - Repr::Native(native) => Some(native.name), - Repr::Closure(closure) => closure.name.as_deref(), - Repr::With(func, _) => func.name(), - } - } - - /// The number of positional arguments this function takes, if known. - pub fn argc(&self) -> Option<usize> { - match self.0.as_ref() { - Repr::Closure(closure) => Some( - closure.params.iter().filter(|(_, default)| default.is_none()).count(), - ), - Repr::With(wrapped, applied) => Some(wrapped.argc()?.saturating_sub( - applied.items.iter().filter(|arg| arg.name.is_none()).count(), - )), - _ => None, - } - } - - /// Call the function with the given arguments. - pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> { - let value = match self.0.as_ref() { - Repr::Native(native) => (native.func)(vm, &mut args)?, - Repr::Closure(closure) => closure.call(vm, &mut args)?, - Repr::With(wrapped, applied) => { - args.items.splice(.. 0, applied.items.iter().cloned()); - return wrapped.call(vm, args); - } - }; - args.finish()?; - Ok(value) - } - - /// Call the function without an existing virtual machine. - pub fn call_detached( - &self, - world: Tracked<dyn World>, - args: Args, - ) -> SourceResult<Value> { - let route = Route::default(); - let mut vm = Vm::new(world, route.track(), None, Scopes::new(None)); - self.call(&mut vm, args) - } - - /// Execute the function's set rule and return the resulting style map. - pub fn set(&self, mut args: Args) -> SourceResult<StyleMap> { - let styles = match self.0.as_ref() { - Repr::Native(Native { set: Some(set), .. }) => set(&mut args)?, - _ => StyleMap::new(), - }; - args.finish()?; - Ok(styles) - } - - /// The id of the node to customize with this function's show rule. - pub fn node(&self) -> StrResult<NodeId> { - match self.0.as_ref() { - Repr::Native(Native { node: Some(id), .. }) => Ok(*id), - _ => Err("this function cannot be customized with show")?, - } - } -} - -impl Debug for Func { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self.name() { - Some(name) => f.write_str(name), - None => f.write_str("(..) => {..}"), - } - } -} - -impl PartialEq for Func { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) - } -} - -/// A function defined by a native rust function or node. -struct Native { - /// The name of the function. - pub name: &'static str, - /// The function pointer. - pub func: fn(&mut Vm, &mut Args) -> SourceResult<Value>, - /// The set rule. - pub set: Option<fn(&mut Args) -> SourceResult<StyleMap>>, - /// The id of the node to customize with this function's show rule. - pub node: Option<NodeId>, -} - -impl Hash for Native { - fn hash<H: Hasher>(&self, state: &mut H) { - self.name.hash(state); - (self.func as usize).hash(state); - self.set.map(|set| set as usize).hash(state); - self.node.hash(state); - } -} - -/// A constructable, stylable content node. -pub trait Node: 'static { - /// Whether this node can be customized through a show rule. - const SHOWABLE: bool; - - /// Construct a node from the arguments. - /// - /// This is passed only the arguments that remain after execution of the - /// node's set rule. - fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>; - - /// Parse relevant arguments into style properties for this node. - /// - /// When `constructor` is true, [`construct`](Self::construct) will run - /// after this invocation of `set` with the remaining arguments. - fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>; -} - -/// A user-defined closure. -#[derive(Hash)] -pub struct Closure { - /// The source file where the closure was defined. - pub location: Option<SourceId>, - /// The name of the closure. - pub name: Option<EcoString>, - /// Captured values from outer scopes. - pub captured: Scope, - /// The parameter names and default values. Parameters with default value - /// are named parameters. - pub params: Vec<(EcoString, Option<Value>)>, - /// The name of an argument sink where remaining arguments are placed. - pub sink: Option<EcoString>, - /// The expression the closure should evaluate to. - pub body: Expr, -} - -impl Closure { - /// Call the function in the context with the arguments. - pub fn call(&self, vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { - // Don't leak the scopes from the call site. Instead, we use the scope - // of captured variables we collected earlier. - let mut scopes = Scopes::new(None); - scopes.top = self.captured.clone(); - - // Parse the arguments according to the parameter list. - for (param, default) in &self.params { - scopes.top.define(param.clone(), match default { - None => args.expect::<Value>(param)?, - Some(default) => { - args.named::<Value>(param)?.unwrap_or_else(|| default.clone()) - } - }); - } - - // Put the remaining arguments into the sink. - if let Some(sink) = &self.sink { - scopes.top.define(sink.clone(), args.take()); - } - - // Determine the route inside the closure. - let detached = vm.location.is_none(); - let fresh = Route::new(self.location); - let route = if detached { fresh.track() } else { vm.route }; - - // Evaluate the body. - let mut sub = Vm::new(vm.world, route, self.location, scopes); - let result = self.body.eval(&mut sub); - - // Handle control flow. - match sub.flow { - Some(Flow::Return(_, Some(explicit))) => return Ok(explicit), - Some(Flow::Return(_, None)) => {} - Some(flow) => bail!(flow.forbidden()), - None => {} - } - - result - } -} |
