summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-16 19:13:39 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-16 20:22:48 +0200
commit242b01549a472d4eeca1404b8f63427e23224253 (patch)
tree7d44ef801ce857469912a75233a1e7a1603e405e /src/library
parenta741bd6b83d1e374c8218b5439e26522499cc4ae (diff)
Safe `eval` function
Diffstat (limited to 'src/library')
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/utility/mod.rs31
2 files changed, 32 insertions, 0 deletions
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 6a30badf..ac0cbb92 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -73,6 +73,7 @@ pub fn new() -> Scope {
// Utility.
std.def_fn("type", utility::type_);
std.def_fn("assert", utility::assert);
+ std.def_fn("eval", utility::eval);
std.def_fn("int", utility::int);
std.def_fn("float", utility::float);
std.def_fn("abs", utility::abs);
diff --git a/src/library/utility/mod.rs b/src/library/utility/mod.rs
index 13220242..355315e4 100644
--- a/src/library/utility/mod.rs
+++ b/src/library/utility/mod.rs
@@ -8,7 +8,11 @@ pub use color::*;
pub use math::*;
pub use string::*;
+use std::mem;
+
+use crate::eval::{Eval, Scopes};
use crate::library::prelude::*;
+use crate::source::SourceFile;
/// The name of a value's type.
pub fn type_(_: &mut Context, args: &mut Args) -> TypResult<Value> {
@@ -23,3 +27,30 @@ pub fn assert(_: &mut Context, args: &mut Args) -> TypResult<Value> {
}
Ok(Value::None)
}
+
+/// Evaluate a string as Typst markup.
+pub fn eval(ctx: &mut Context, args: &mut Args) -> TypResult<Value> {
+ let Spanned { v: src, span } = args.expect::<Spanned<String>>("source")?;
+
+ // Parse the source and set a synthetic span for all nodes.
+ let mut source = SourceFile::detached(src);
+ source.synthesize(span);
+ let ast = source.ast()?;
+
+ // Save the old context, then detach it.
+ let prev_flow = ctx.flow.take();
+ let prev_route = mem::take(&mut ctx.route);
+
+ // Evaluate the source.
+ let std = ctx.std.clone();
+ let mut scp = Scopes::new(Some(&std));
+ let result = ast.eval(ctx, &mut scp);
+
+ // Restore the old context and handle control flow.
+ ctx.route = prev_route;
+ if let Some(flow) = mem::replace(&mut ctx.flow, prev_flow) {
+ return Err(flow.forbidden());
+ }
+
+ Ok(Value::Content(result?))
+}