summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-02-13 17:44:14 +0100
committerLaurenz <laurmaedje@gmail.com>2023-02-13 17:45:08 +0100
commit17e9805b34562781d514ba6b0df31155c8d39824 (patch)
treec5c4761f59d6ffa57755660f51e88621ddcff689
parent5233b1c50a4a671fdc38cf0d40868f8c075d9ed7 (diff)
Let `eval` take code instead of markup
-rw-r--r--library/src/compute/foundations.rs25
-rw-r--r--src/model/eval.rs38
-rw-r--r--src/syntax/node.rs2
-rw-r--r--tests/typ/compute/foundations.typ30
4 files changed, 62 insertions, 33 deletions
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index 33a90ae4..82270dd3 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -1,9 +1,5 @@
use crate::prelude::*;
-use comemo::Track;
-use typst::model;
-use typst::syntax::Source;
-
/// # Type
/// Determine a value's type.
///
@@ -92,32 +88,29 @@ pub fn assert(args: &mut Args) -> SourceResult<Value> {
}
/// # Evaluate
-/// Evaluate a string as Typst markup.
+/// Evaluate a string as Typst code.
///
-/// You shouldn't typically need this function, but it is there if you do.
+/// This function should only be used as a last resort.
///
/// ## Example
/// ```example
-/// #let markup = "= Heading\n _Emphasis_"
-/// #eval(markup)
+/// #eval("1 + 2") \
+/// #eval("[*Strong text*]") \
+/// #eval("(1, 2, 3)").len()
/// ```
///
/// ## Parameters
/// - source: `String` (positional, required)
-/// A string of Typst markup to evaluate.
+/// A string of Typst code to evaluate.
///
-/// The markup and code in the string cannot interact with the file system.
+/// The code in the string cannot interact with the file system.
///
-/// - returns: content
+/// - returns: any
///
/// ## Category
/// foundations
#[func]
pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
- let source = Source::synthesized(text, span);
- let route = model::Route::default();
- let mut tracer = model::Tracer::default();
- let module = model::eval(vm.world(), route.track(), tracer.track_mut(), &source)?;
- Ok(Value::Content(module.content()))
+ typst::model::eval_code_str(vm.world(), &text, span)
}
diff --git a/src/model/eval.rs b/src/model/eval.rs
index 1fbf4125..6e118f8a 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -16,7 +16,9 @@ use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
use crate::syntax::ast::AstNode;
-use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode};
+use crate::syntax::{
+ ast, parse_code, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode,
+};
use crate::util::PathExt;
use crate::World;
@@ -63,6 +65,40 @@ pub fn eval(
Ok(Module::new(name).with_scope(vm.scopes.top).with_content(result?))
}
+/// Evaluate a string as code and return the resulting value.
+///
+/// Everything in the output is associated with the given `span`.
+#[comemo::memoize]
+pub fn eval_code_str(
+ world: Tracked<dyn World>,
+ text: &str,
+ span: Span,
+) -> SourceResult<Value> {
+ let mut root = parse_code(text);
+ root.synthesize(span);
+
+ let errors = root.errors();
+ if !errors.is_empty() {
+ return Err(Box::new(errors));
+ }
+
+ let id = SourceId::detached();
+ let library = world.library();
+ let scopes = Scopes::new(Some(library));
+ let route = Route::default();
+ let mut tracer = Tracer::default();
+ let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes, 0);
+ let code = root.cast::<ast::Code>().unwrap();
+ let result = code.eval(&mut vm);
+
+ // Handle control flow.
+ if let Some(flow) = vm.flow {
+ bail!(flow.forbidden());
+ }
+
+ result
+}
+
/// A virtual machine.
///
/// Holds the state needed to [evaluate](eval) Typst sources. A new
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
index 1fdb0a83..e153b0bf 100644
--- a/src/syntax/node.rs
+++ b/src/syntax/node.rs
@@ -175,7 +175,7 @@ impl SyntaxNode {
}
/// Set a synthetic span for the node and all its descendants.
- pub(super) fn synthesize(&mut self, span: Span) {
+ pub(crate) fn synthesize(&mut self, span: Span) {
match &mut self.0 {
Repr::Leaf(leaf) => leaf.span = span,
Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span),
diff --git a/tests/typ/compute/foundations.typ b/tests/typ/compute/foundations.typ
index 602bc22d..83cda65f 100644
--- a/tests/typ/compute/foundations.typ
+++ b/tests/typ/compute/foundations.typ
@@ -27,14 +27,14 @@
#test(type(10 / 3), "float")
---
-#eval("_Hello" + " World!_")
+#eval("[_Hello" + " World!_]")
---
-// Error: 7-13 expected identifier
-#eval("#let")
+// Error: 7-12 expected identifier
+#eval("let")
---
-#show raw: it => text("IBM Plex Sans", eval(it.text))
+#show raw: it => text("IBM Plex Sans", eval("[" + it.text + "]"))
Interacting
```
@@ -43,28 +43,28 @@ Blue #move(dy: -0.15em)[🌊]
```
---
-// Error: 7-18 cannot continue outside of loop
-#eval("#continue")
+// Error: 7-17 cannot continue outside of loop
+#eval("continue")
---
-// Error: 7-33 cannot access file system from here
-#eval("#include \"../coma.typ\"")
+// Error: 7-32 cannot access file system from here
+#eval("include \"../coma.typ\"")
---
-// Error: 7-31 cannot access file system from here
-#eval("#image(\"/tiger.jpg\")")
+// Error: 7-30 cannot access file system from here
+#eval("image(\"/tiger.jpg\")")
---
// Error: 23-30 cannot access file system from here
#show raw: it => eval(it.text)
```
-#image("/tiger.jpg")
+image("/tiger.jpg")
```
---
-// Error: 23-30 cannot access file system from here
-#show raw: it => eval(it.text)
+// Error: 23-42 cannot access file system from here
+#show raw: it => eval("[" + it.text + "]")
```
#show emph: _ => image("/giraffe.jpg")
@@ -72,5 +72,5 @@ _No relative giraffe!_
```
---
-// Error: 7-15 expected comma
-#eval("#(1 2)")
+// Error: 7-12 expected semicolon or line break
+#eval("1 2")