summaryrefslogtreecommitdiff
path: root/src/eval/func.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-10-17 19:26:24 +0200
committerLaurenz <laurmaedje@gmail.com>2022-10-17 20:04:22 +0200
commite21822665591dc19766275da1e185215a6b945ef (patch)
tree7788e211c3c33c8b5a8ad7d5eb7574e33631eb16 /src/eval/func.rs
parent4fd031a256b2ecfe524859d5599fafb386395572 (diff)
Merge some modules
Diffstat (limited to 'src/eval/func.rs')
-rw-r--r--src/eval/func.rs248
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
- }
-}