Let eval take code instead of markup

This commit is contained in:
Laurenz 2023-02-13 17:44:14 +01:00
parent 5233b1c50a
commit 17e9805b34
4 changed files with 62 additions and 33 deletions

View File

@ -1,9 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use comemo::Track;
use typst::model;
use typst::syntax::Source;
/// # Type /// # Type
/// Determine a value's type. /// Determine a value's type.
/// ///
@ -92,32 +88,29 @@ pub fn assert(args: &mut Args) -> SourceResult<Value> {
} }
/// # Evaluate /// # 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
/// ```example /// ```example
/// #let markup = "= Heading\n _Emphasis_" /// #eval("1 + 2") \
/// #eval(markup) /// #eval("[*Strong text*]") \
/// #eval("(1, 2, 3)").len()
/// ``` /// ```
/// ///
/// ## Parameters /// ## Parameters
/// - source: `String` (positional, required) /// - 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 /// ## Category
/// foundations /// foundations
#[func] #[func]
pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> { pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?; let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
let source = Source::synthesized(text, span); typst::model::eval_code_str(vm.world(), &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()))
} }

View File

@ -16,7 +16,9 @@ use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint, bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
}; };
use crate::syntax::ast::AstNode; 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::util::PathExt;
use crate::World; use crate::World;
@ -63,6 +65,40 @@ pub fn eval(
Ok(Module::new(name).with_scope(vm.scopes.top).with_content(result?)) 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. /// A virtual machine.
/// ///
/// Holds the state needed to [evaluate](eval) Typst sources. A new /// Holds the state needed to [evaluate](eval) Typst sources. A new

View File

@ -175,7 +175,7 @@ impl SyntaxNode {
} }
/// Set a synthetic span for the node and all its descendants. /// 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 { match &mut self.0 {
Repr::Leaf(leaf) => leaf.span = span, Repr::Leaf(leaf) => leaf.span = span,
Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span), Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span),

View File

@ -27,14 +27,14 @@
#test(type(10 / 3), "float") #test(type(10 / 3), "float")
--- ---
#eval("_Hello" + " World!_") #eval("[_Hello" + " World!_]")
--- ---
// Error: 7-13 expected identifier // Error: 7-12 expected identifier
#eval("#let") #eval("let")
--- ---
#show raw: it => text("IBM Plex Sans", eval(it.text)) #show raw: it => text("IBM Plex Sans", eval("[" + it.text + "]"))
Interacting Interacting
``` ```
@ -43,28 +43,28 @@ Blue #move(dy: -0.15em)[🌊]
``` ```
--- ---
// Error: 7-18 cannot continue outside of loop // Error: 7-17 cannot continue outside of loop
#eval("#continue") #eval("continue")
--- ---
// Error: 7-33 cannot access file system from here // Error: 7-32 cannot access file system from here
#eval("#include \"../coma.typ\"") #eval("include \"../coma.typ\"")
--- ---
// Error: 7-31 cannot access file system from here // Error: 7-30 cannot access file system from here
#eval("#image(\"/tiger.jpg\")") #eval("image(\"/tiger.jpg\")")
--- ---
// Error: 23-30 cannot access file system from here // Error: 23-30 cannot access file system from here
#show raw: it => eval(it.text) #show raw: it => eval(it.text)
``` ```
#image("/tiger.jpg") image("/tiger.jpg")
``` ```
--- ---
// Error: 23-30 cannot access file system from here // Error: 23-42 cannot access file system from here
#show raw: it => eval(it.text) #show raw: it => eval("[" + it.text + "]")
``` ```
#show emph: _ => image("/giraffe.jpg") #show emph: _ => image("/giraffe.jpg")
@ -72,5 +72,5 @@ _No relative giraffe!_
``` ```
--- ---
// Error: 7-15 expected comma // Error: 7-12 expected semicolon or line break
#eval("#(1 2)") #eval("1 2")