diff --git a/cli/src/main.rs b/cli/src/main.rs index 65b21e228..b7cac1eea 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -229,12 +229,11 @@ fn compile(command: CompileCommand) -> StrResult<()> { /// Compile a single time. fn compile_once(world: &mut SystemWorld, command: &CompileCommand) -> StrResult<()> { status(command, Status::Compiling).unwrap(); + world.reset(); + world.main = world.resolve(&command.input).map_err(|err| err.to_string())?; - let main = world.resolve(&command.input).map_err(|err| err.to_string())?; - let source = world.source(main); - - match typst::compile(world, source) { + match typst::compile(world) { // Export the PDF. Ok(document) => { let buffer = typst::export::pdf(&document); @@ -372,6 +371,7 @@ struct SystemWorld { hashes: RefCell>>, paths: RefCell>, sources: FrozenVec>, + main: SourceId, } /// Holds details about the location of a font and lazily the font itself. @@ -401,6 +401,7 @@ impl SystemWorld { hashes: RefCell::default(), paths: RefCell::default(), sources: FrozenVec::new(), + main: SourceId::detached(), } } } @@ -414,6 +415,25 @@ impl World for SystemWorld { &self.library } + fn main(&self) -> &Source { + self.source(self.main) + } + + fn resolve(&self, path: &Path) -> FileResult { + self.slot(path)? + .source + .get_or_init(|| { + let buf = read(path)?; + let text = String::from_utf8(buf)?; + Ok(self.insert(path, text)) + }) + .clone() + } + + fn source(&self, id: SourceId) -> &Source { + &self.sources[id.into_u16() as usize] + } + fn book(&self) -> &Prehashed { &self.book } @@ -434,21 +454,6 @@ impl World for SystemWorld { .get_or_init(|| read(path).map(Buffer::from)) .clone() } - - fn resolve(&self, path: &Path) -> FileResult { - self.slot(path)? - .source - .get_or_init(|| { - let buf = read(path)?; - let text = String::from_utf8(buf)?; - Ok(self.insert(path, text)) - }) - .clone() - } - - fn source(&self, id: SourceId) -> &Source { - &self.sources[id.into_u16() as usize] - } } impl SystemWorld { diff --git a/docs/src/html.rs b/docs/src/html.rs index fc9d7d9e3..206dee004 100644 --- a/docs/src/html.rs +++ b/docs/src/html.rs @@ -307,7 +307,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html { let source = Source::new(SourceId::from_u16(0), Path::new("main.typ"), compile); let world = DocWorld(source); - let mut frames = match typst::compile(&world, &world.0) { + let mut frames = match typst::compile(&world) { Ok(doc) => doc.pages, Err(err) => { let msg = &err[0].message; @@ -335,6 +335,19 @@ impl World for DocWorld { &LIBRARY } + fn main(&self) -> &Source { + &self.0 + } + + fn resolve(&self, _: &Path) -> FileResult { + unimplemented!() + } + + fn source(&self, id: SourceId) -> &Source { + assert_eq!(id.into_u16(), 0, "invalid source id"); + &self.0 + } + fn book(&self) -> &Prehashed { &FONTS.0 } @@ -350,13 +363,4 @@ impl World for DocWorld { .contents() .into()) } - - fn resolve(&self, _: &Path) -> FileResult { - unimplemented!() - } - - fn source(&self, id: SourceId) -> &Source { - assert_eq!(id.into_u16(), 0, "invalid source id"); - &self.0 - } } diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs index 1c08cd5f7..05b42bd82 100644 --- a/library/src/layout/enum.rs +++ b/library/src/layout/enum.rs @@ -180,7 +180,7 @@ impl Layout for EnumNode { let resolved = if full { parents.push(number); - let content = numbering.apply(vt.world, &parents)?.display(); + let content = numbering.apply_vt(vt, &parents)?.display(); parents.pop(); content } else { @@ -188,7 +188,7 @@ impl Layout for EnumNode { Numbering::Pattern(pattern) => { TextNode::packed(pattern.apply_kth(parents.len(), number)) } - other => other.apply(vt.world, &[number])?.display(), + other => other.apply_vt(vt, &[number])?.display(), } }; diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs index c954ab674..fe78131d5 100644 --- a/library/src/layout/list.rs +++ b/library/src/layout/list.rs @@ -128,7 +128,7 @@ impl Layout for ListNode { }; let depth = self.depth(styles); - let marker = self.marker(styles).resolve(vt.world, depth)?; + let marker = self.marker(styles).resolve(vt, depth)?; let mut cells = vec![]; for item in self.children() { @@ -181,17 +181,14 @@ pub enum ListMarker { impl ListMarker { /// Resolve the marker for the given depth. - fn resolve(&self, world: Tracked, depth: usize) -> SourceResult { + fn resolve(&self, vt: &mut Vt, depth: usize) -> SourceResult { Ok(match self { Self::Content(list) => list .get(depth) .or(list.last()) .cloned() .unwrap_or_else(|| TextNode::packed('•')), - Self::Func(func) => { - let args = Args::new(func.span(), [Value::Int(depth as i64)]); - func.call_detached(world, args)?.display() - } + Self::Func(func) => func.call_vt(vt, [Value::Int(depth as i64)])?.display(), }) } } diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index fc0279eb9..b29da700e 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -47,6 +47,7 @@ use std::mem; use typed_arena::Arena; use typst::diag::SourceResult; +use typst::eval::Tracer; use typst::model::{applicable, realize, SequenceNode, StyleVecBuilder, StyledNode}; use crate::math::{FormulaNode, LayoutMath}; @@ -68,11 +69,12 @@ impl LayoutRoot for Content { fn cached( node: &Content, world: Tracked, + tracer: TrackedMut, provider: TrackedMut, introspector: Tracked, styles: StyleChain, ) -> SourceResult { - let mut vt = Vt { world, provider, introspector }; + let mut vt = Vt { world, tracer, provider, introspector }; let scratch = Scratch::default(); let (realized, styles) = realize_root(&mut vt, &scratch, node, styles)?; realized @@ -84,6 +86,7 @@ impl LayoutRoot for Content { cached( self, vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), TrackedMut::reborrow_mut(&mut vt.provider), vt.introspector, styles, @@ -129,12 +132,13 @@ impl Layout for Content { fn cached( node: &Content, world: Tracked, + tracer: TrackedMut, provider: TrackedMut, introspector: Tracked, styles: StyleChain, regions: Regions, ) -> SourceResult { - let mut vt = Vt { world, provider, introspector }; + let mut vt = Vt { world, tracer, provider, introspector }; let scratch = Scratch::default(); let (realized, styles) = realize_block(&mut vt, &scratch, node, styles)?; realized @@ -146,6 +150,7 @@ impl Layout for Content { cached( self, vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), TrackedMut::reborrow_mut(&mut vt.provider), vt.introspector, styles, diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 962e8a168..eeb74a557 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -403,13 +403,10 @@ pub enum Marginal { impl Marginal { /// Resolve the marginal based on the page number. - pub fn resolve(&self, vt: &Vt, page: usize) -> SourceResult { + pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult { Ok(match self { Self::Content(content) => content.clone(), - Self::Func(func) => { - let args = Args::new(func.span(), [Value::Int(page as i64)]); - func.call_detached(vt.world, args)?.display() - } + Self::Func(func) => func.call_vt(vt, [Value::Int(page as i64)])?.display(), }) } } diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index 1906dd7c7..8657d7fb5 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -1,3 +1,4 @@ +use typst::eval::Tracer; use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_script::{Script, UnicodeScript}; use xi_unicode::LineBreakIterator; @@ -138,6 +139,7 @@ impl ParNode { fn cached( par: &ParNode, world: Tracked, + tracer: TrackedMut, provider: TrackedMut, introspector: Tracked, styles: StyleChain, @@ -145,7 +147,7 @@ impl ParNode { region: Size, expand: bool, ) -> SourceResult { - let mut vt = Vt { world, provider, introspector }; + let mut vt = Vt { world, tracer, provider, introspector }; let children = par.children(); // Collect all text into one string for BiDi analysis. @@ -166,6 +168,7 @@ impl ParNode { cached( self, vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), TrackedMut::reborrow_mut(&mut vt.provider), vt.introspector, styles, diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs index 024bb0d66..d4b6e7d7f 100644 --- a/library/src/layout/table.rs +++ b/library/src/layout/table.rs @@ -227,14 +227,13 @@ pub enum Celled { impl Celled { /// Resolve the value based on the cell position. - pub fn resolve(&self, vt: &Vt, x: usize, y: usize) -> SourceResult { + pub fn resolve(&self, vt: &mut Vt, x: usize, y: usize) -> SourceResult { Ok(match self { Self::Value(value) => value.clone(), - Self::Func(func) => { - let args = - Args::new(func.span(), [Value::Int(x as i64), Value::Int(y as i64)]); - func.call_detached(vt.world, args)?.cast().at(func.span())? - } + Self::Func(func) => func + .call_vt(vt, [Value::Int(x as i64), Value::Int(y as i64)])? + .cast() + .at(func.span())?, }) } } diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs index b8f719ac8..b61e89133 100644 --- a/library/src/meta/counter.rs +++ b/library/src/meta/counter.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use ecow::{eco_vec, EcoVec}; use smallvec::{smallvec, SmallVec}; +use typst::eval::Tracer; use super::{Numbering, NumberingPattern}; use crate::layout::PageNode; @@ -125,10 +126,16 @@ impl Show for CounterNode { return counter.resolve(vt, id, &numbering); } - let sequence = counter.sequence(vt.world, vt.introspector)?; + let sequence = counter.sequence( + vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), + TrackedMut::reborrow_mut(&mut vt.provider), + vt.introspector, + )?; + Ok(match (sequence.single(id), sequence.single(None)) { (Some(current), Some(total)) => { - numbering.apply(vt.world, &[current, total])?.display() + numbering.apply_vt(vt, &[current, total])?.display() } _ => Content::empty(), }) @@ -213,7 +220,7 @@ impl Counter { /// id. pub fn resolve( &self, - vt: &Vt, + vt: &mut Vt, stop: Option, numbering: &Numbering, ) -> SourceResult { @@ -221,9 +228,15 @@ impl Counter { return Ok(Content::empty()); } - let sequence = self.sequence(vt.world, vt.introspector)?; + let sequence = self.sequence( + vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), + TrackedMut::reborrow_mut(&mut vt.provider), + vt.introspector, + )?; + Ok(match sequence.at(stop) { - Some(state) => numbering.apply(vt.world, &state.0)?.display(), + Some(state) => numbering.apply_vt(vt, &state.0)?.display(), None => Content::empty(), }) } @@ -236,8 +249,11 @@ impl Counter { fn sequence( &self, world: Tracked, + tracer: TrackedMut, + provider: TrackedMut, introspector: Tracked, ) -> SourceResult { + let mut vt = Vt { world, tracer, provider, introspector }; let mut search = Selector::Node( NodeId::of::(), Some(dict! { "counter" => self.clone() }), @@ -277,7 +293,7 @@ impl Counter { None => Some(CounterUpdate::Step(NonZeroUsize::ONE)), }, } { - state.update(world, update)?; + state.update(&mut vt, update)?; } stops.push((id, state.clone())); @@ -335,17 +351,15 @@ pub struct CounterState(pub SmallVec<[NonZeroUsize; 3]>); impl CounterState { /// Advance the counter and return the numbers for the given heading. - pub fn update( - &mut self, - world: Tracked, - update: CounterUpdate, - ) -> SourceResult<()> { + pub fn update(&mut self, vt: &mut Vt, update: CounterUpdate) -> SourceResult<()> { match update { CounterUpdate::Set(state) => *self = state, CounterUpdate::Step(level) => self.step(level), CounterUpdate::Func(func) => { - let args = Args::new(func.span(), self.0.iter().copied().map(Into::into)); - *self = func.call_detached(world, args)?.cast().at(func.span())? + *self = func + .call_vt(vt, self.0.iter().copied().map(Into::into))? + .cast() + .at(func.span())? } } Ok(()) diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs index 6febc408b..6facb8338 100644 --- a/library/src/meta/numbering.rs +++ b/library/src/meta/numbering.rs @@ -64,7 +64,7 @@ pub fn numbering( #[variadic] numbers: Vec, ) -> Value { - numbering.apply(vm.world(), &numbers)? + numbering.apply_vm(vm, &numbers)? } /// How to number a sequence of things. @@ -78,11 +78,7 @@ pub enum Numbering { impl Numbering { /// Apply the pattern to the given numbers. - pub fn apply( - &self, - world: Tracked, - numbers: &[NonZeroUsize], - ) -> SourceResult { + pub fn apply_vm(&self, vm: &mut Vm, numbers: &[NonZeroUsize]) -> SourceResult { Ok(match self { Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Func(func) => { @@ -90,7 +86,17 @@ impl Numbering { func.span(), numbers.iter().map(|n| Value::Int(n.get() as i64)), ); - func.call_detached(world, args)? + func.call_vm(vm, args)? + } + }) + } + + /// Apply the pattern to the given numbers. + pub fn apply_vt(&self, vt: &mut Vt, numbers: &[NonZeroUsize]) -> SourceResult { + Ok(match self { + Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), + Self::Func(func) => { + func.call_vt(vt, numbers.iter().map(|n| Value::Int(n.get() as i64)))? } }) } diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs index 976110360..7cd26ebaa 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -91,10 +91,12 @@ impl Show for OutlineNode { let depth = self.depth(styles); let mut ancestors: Vec<&HeadingNode> = vec![]; - for node in vt.introspector.query(Selector::Node( + let nodes = vt.introspector.query(Selector::Node( NodeId::of::(), Some(dict! { "outlined" => true }), - )) { + )); + + for node in &nodes { let heading = node.to::().unwrap(); let stable_id = heading.0.stable_id().unwrap(); if !heading.outlined(StyleChain::default()) { diff --git a/library/src/meta/query.rs b/library/src/meta/query.rs index c91f0d1ae..bab8ed7c0 100644 --- a/library/src/meta/query.rs +++ b/library/src/meta/query.rs @@ -59,11 +59,6 @@ impl Show for QueryNode { let target = self.target(); let (before, after) = vt.introspector.query_split(target, id); let func = self.format(); - let args = Args::new(func.span(), [encode(before), encode(after)]); - Ok(func.call_detached(vt.world, args)?.display()) + Ok(func.call_vt(vt, [before.into(), after.into()])?.display()) } } - -fn encode(list: Vec<&Content>) -> Value { - Value::Array(list.into_iter().cloned().map(Value::Content).collect()) -} diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index f05692dd3..0603ee4ec 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -80,7 +80,7 @@ impl Show for RefNode { return self.to_citation(styles).show(vt, styles); } - let &[node] = matches.as_slice() else { + let [node] = matches.as_slice() else { bail!(self.span(), if matches.is_empty() { "label does not exist in the document" } else { @@ -102,8 +102,7 @@ impl Show for RefNode { Smart::Custom(None) => Content::empty(), Smart::Custom(Some(Supplement::Content(content))) => content.clone(), Smart::Custom(Some(Supplement::Func(func))) => { - let args = Args::new(func.span(), [node.clone().into()]); - func.call_detached(vt.world, args)?.display() + func.call_vt(vt, [node.clone().into()])?.display() } }; diff --git a/library/src/meta/state.rs b/library/src/meta/state.rs index 8b0a0aa64..6b521301f 100644 --- a/library/src/meta/state.rs +++ b/library/src/meta/state.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use ecow::EcoVec; +use typst::eval::Tracer; use crate::prelude::*; @@ -118,7 +119,7 @@ impl State { /// Display the state at the postition of the given stable id. fn resolve( &self, - vt: &Vt, + vt: &mut Vt, stop: Option, func: Option, ) -> SourceResult { @@ -126,12 +127,17 @@ impl State { return Ok(Content::empty()); } - let sequence = self.sequence(vt.world, vt.introspector)?; + let sequence = self.sequence( + vt.world, + TrackedMut::reborrow_mut(&mut vt.tracer), + TrackedMut::reborrow_mut(&mut vt.provider), + vt.introspector, + )?; + Ok(match sequence.at(stop) { Some(value) => { if let Some(func) = func { - let args = Args::new(func.span(), [value]); - func.call_detached(vt.world, args)?.display() + func.call_vt(vt, [value])?.display() } else { value.display() } @@ -148,8 +154,11 @@ impl State { fn sequence( &self, world: Tracked, + tracer: TrackedMut, + provider: TrackedMut, introspector: Tracked, ) -> SourceResult { + let mut vt = Vt { world, tracer, provider, introspector }; let search = Selector::Node( NodeId::of::(), Some(dict! { "state" => self.clone() }), @@ -165,10 +174,7 @@ impl State { if let StateAction::Update(update) = node.action() { match update { StateUpdate::Set(value) => state = value, - StateUpdate::Func(func) => { - let args = Args::new(func.span(), [state]); - state = func.call_detached(world, args)?; - } + StateUpdate::Func(func) => state = func.call_vt(&mut vt, [state])?, } } diff --git a/src/eval/array.rs b/src/eval/array.rs index 979f15d4c..e42fd28da 100644 --- a/src/eval/array.rs +++ b/src/eval/array.rs @@ -144,7 +144,7 @@ impl Array { } for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); - if func.call(vm, args)?.cast::().at(func.span())? { + if func.call_vm(vm, args)?.cast::().at(func.span())? { return Ok(Some(item.clone())); } } @@ -158,7 +158,7 @@ impl Array { } for (i, item) in self.iter().enumerate() { let args = Args::new(func.span(), [item.clone()]); - if func.call(vm, args)?.cast::().at(func.span())? { + if func.call_vm(vm, args)?.cast::().at(func.span())? { return Ok(Some(i as i64)); } } @@ -175,7 +175,7 @@ impl Array { let mut kept = EcoVec::new(); for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); - if func.call(vm, args)?.cast::().at(func.span())? { + if func.call_vm(vm, args)?.cast::().at(func.span())? { kept.push(item.clone()) } } @@ -196,7 +196,7 @@ impl Array { args.push(func.span(), Value::Int(i as i64)); } args.push(func.span(), item.clone()); - func.call(vm, args) + func.call_vm(vm, args) }) .collect() } @@ -209,7 +209,7 @@ impl Array { let mut acc = init; for item in self.iter() { let args = Args::new(func.span(), [acc, item.clone()]); - acc = func.call(vm, args)?; + acc = func.call_vm(vm, args)?; } Ok(acc) } @@ -221,7 +221,7 @@ impl Array { } for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); - if func.call(vm, args)?.cast::().at(func.span())? { + if func.call_vm(vm, args)?.cast::().at(func.span())? { return Ok(true); } } @@ -236,7 +236,7 @@ impl Array { } for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); - if !func.call(vm, args)?.cast::().at(func.span())? { + if !func.call_vm(vm, args)?.cast::().at(func.span())? { return Ok(false); } } diff --git a/src/eval/func.rs b/src/eval/func.rs index a5fa6fa1c..c3ec42334 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -13,7 +13,7 @@ use super::{ Vm, }; use crate::diag::{bail, SourceResult, StrResult}; -use crate::model::{NodeId, Selector, StyleMap}; +use crate::model::{NodeId, Selector, StyleMap, Vt}; use crate::syntax::ast::{self, AstNode, Expr}; use crate::syntax::{SourceId, Span, SyntaxNode}; use crate::util::hash128; @@ -82,7 +82,7 @@ impl Func { } /// Call the function with the given arguments. - pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult { + pub fn call_vm(&self, vm: &mut Vm, mut args: Args) -> SourceResult { match &**self.0 { Repr::Native(native) => { let value = (native.func)(vm, &mut args)?; @@ -111,23 +111,29 @@ impl Func { } Repr::With(wrapped, applied) => { args.items = applied.items.iter().cloned().chain(args.items).collect(); - return wrapped.call(vm, args); + return wrapped.call_vm(vm, args); } } } - /// Call the function without an existing virtual machine. - pub fn call_detached( + /// Call the function with a Vt. + pub fn call_vt( &self, - world: Tracked, - args: Args, + vt: &mut Vt, + args: impl IntoIterator, ) -> SourceResult { let route = Route::default(); let id = SourceId::detached(); let scopes = Scopes::new(None); - let mut tracer = Tracer::default(); - let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes, 0); - self.call(&mut vm, args) + let mut vm = Vm::new( + vt.world, + route.track(), + TrackedMut::reborrow_mut(&mut vt.tracer), + id, + scopes, + ); + let args = Args::new(self.span(), args); + self.call_vm(&mut vm, args) } /// Apply the given arguments to the function. @@ -208,7 +214,7 @@ impl From for Func { /// A native Rust function. pub struct NativeFunc { /// The function's implementation. - pub func: fn(&Vm, &mut Args) -> SourceResult, + pub func: fn(&mut Vm, &mut Args) -> SourceResult, /// Details about the function. pub info: Lazy, } @@ -352,10 +358,11 @@ impl Closure { args.finish()?; // Evaluate the body. - let mut sub = Vm::new(world, route, tracer, closure.location, scopes, depth); - let result = closure.body.eval(&mut sub); + let mut sub = Vm::new(world, route, tracer, closure.location, scopes); + sub.depth = depth; // Handle control flow. + let result = closure.body.eval(&mut sub); match sub.flow { Some(Flow::Return(_, Some(explicit))) => return Ok(explicit), Some(Flow::Return(_, None)) => {} diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 127c930fb..1c002c494 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -81,7 +81,7 @@ pub fn eval( // Evaluate the module. let route = unsafe { Route::insert(route, id) }; let scopes = Scopes::new(Some(library)); - let mut vm = Vm::new(world, route.track(), tracer, id, scopes, 0); + let mut vm = Vm::new(world, route.track(), tracer, id, scopes); let root = match source.root().cast::() { Some(markup) if vm.traced.is_some() => markup, _ => source.ast()?, @@ -121,7 +121,7 @@ pub fn eval_code_str( 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 mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes); let code = root.cast::().unwrap(); let result = code.eval(&mut vm); @@ -139,34 +139,33 @@ pub fn eval_code_str( /// virtual machine is created for each module evaluation and function call. pub struct Vm<'a> { /// The compilation environment. - pub(super) world: Tracked<'a, dyn World>, + world: Tracked<'a, dyn World>, /// The language items. - pub(super) items: LangItems, + items: LangItems, /// The route of source ids the VM took to reach its current location. - pub(super) route: Tracked<'a, Route>, + route: Tracked<'a, Route>, /// The tracer for inspection of the values an expression produces. - pub(super) tracer: TrackedMut<'a, Tracer>, + tracer: TrackedMut<'a, Tracer>, /// The current location. - pub(super) location: SourceId, + location: SourceId, /// A control flow event that is currently happening. - pub(super) flow: Option, + flow: Option, /// The stack of scopes. - pub(super) scopes: Scopes<'a>, + scopes: Scopes<'a>, /// The current call depth. - pub(super) depth: usize, + depth: usize, /// A span that is currently traced. - pub(super) traced: Option, + traced: Option, } impl<'a> Vm<'a> { /// Create a new virtual machine. - pub(super) fn new( + fn new( world: Tracked<'a, dyn World>, route: Tracked<'a, Route>, tracer: TrackedMut<'a, Tracer>, location: SourceId, scopes: Scopes<'a>, - depth: usize, ) -> Self { let traced = tracer.span(location); Self { @@ -177,7 +176,7 @@ impl<'a> Vm<'a> { location, flow: None, scopes, - depth, + depth: 0, traced, } } @@ -358,7 +357,7 @@ fn eval_markup( } let tail = eval_markup(vm, exprs)?; - seq.push(tail.styled_with_recipe(vm.world, recipe)?) + seq.push(tail.styled_with_recipe(vm, recipe)?) } expr => match expr.eval(vm)? { Value::Label(label) => { @@ -791,7 +790,7 @@ fn eval_code( } let tail = eval_code(vm, exprs)?.display(); - Value::Content(tail.styled_with_recipe(vm.world, recipe)?) + Value::Content(tail.styled_with_recipe(vm, recipe)?) } _ => expr.eval(vm)?, }; @@ -1053,7 +1052,7 @@ impl Eval for ast::FuncCall { let callee = callee.cast::().at(callee_span)?; let point = || Tracepoint::Call(callee.name().map(Into::into)); - callee.call(vm, args).trace(vm.world, point, span) + callee.call_vm(vm, args).trace(vm.world, point, span) } } diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs index ccb89a9ca..68b82b059 100644 --- a/src/ide/analyze.rs +++ b/src/ide/analyze.rs @@ -36,11 +36,23 @@ pub fn analyze_expr(world: &(dyn World + 'static), node: &LinkedNode) -> Vec SourceResult { +pub fn compile(world: &(dyn World + 'static)) -> SourceResult { // Evaluate the source file into a module. let route = Route::default(); let mut tracer = Tracer::default(); - let module = eval::eval(world.track(), route.track(), tracer.track_mut(), source)?; + let module = + eval::eval(world.track(), route.track(), tracer.track_mut(), world.main())?; // Typeset the module's contents. - model::typeset(world.track(), &module.content()) + model::typeset(world.track(), tracer.track_mut(), &module.content()) } /// The environment in which typesetting occurs. @@ -86,6 +87,15 @@ pub trait World { /// The standard library. fn library(&self) -> &Prehashed; + /// The main source file. + fn main(&self) -> &Source; + + /// Try to resolve the unique id of a source file. + fn resolve(&self, path: &Path) -> FileResult; + + /// Access a source file by id. + fn source(&self, id: SourceId) -> &Source; + /// Metadata about all known fonts. fn book(&self) -> &Prehashed; @@ -94,10 +104,4 @@ pub trait World { /// Try to access a file at a path. fn file(&self, path: &Path) -> FileResult; - - /// Try to resolve the unique id of a source file. - fn resolve(&self, path: &Path) -> FileResult; - - /// Access a source file by id. - fn source(&self, id: SourceId) -> &Source; } diff --git a/src/model/content.rs b/src/model/content.rs index bd24829dc..5317236e6 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -4,7 +4,6 @@ use std::hash::{Hash, Hasher}; use std::iter::{self, Sum}; use std::ops::{Add, AddAssign, Deref}; -use comemo::Tracked; use ecow::{eco_format, EcoString, EcoVec}; use once_cell::sync::Lazy; @@ -19,7 +18,6 @@ use crate::eval::{ }; use crate::syntax::Span; use crate::util::pretty_array_like; -use crate::World; /// Composable representation of styled content. #[derive(Clone, Hash)] @@ -219,13 +217,9 @@ impl Content { } /// Style this content with a recipe, eagerly applying it if possible. - pub fn styled_with_recipe( - self, - world: Tracked, - recipe: Recipe, - ) -> SourceResult { + pub fn styled_with_recipe(self, vm: &mut Vm, recipe: Recipe) -> SourceResult { if recipe.selector.is_none() { - recipe.apply(world, self) + recipe.apply_vm(vm, self) } else { Ok(self.styled(Style::Recipe(recipe))) } diff --git a/src/model/realize.rs b/src/model/realize.rs index 70c756448..634a31fd9 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -97,7 +97,7 @@ pub fn realize( /// Try to apply a recipe to the target. fn try_apply( - vt: &Vt, + vt: &mut Vt, target: &Content, recipe: &Recipe, guard: Guard, @@ -108,7 +108,7 @@ fn try_apply( return Ok(None); } - recipe.apply(vt.world, target.clone().guarded(guard)).map(Some) + recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some) } Some(Selector::Label(label)) => { @@ -116,7 +116,7 @@ fn try_apply( return Ok(None); } - recipe.apply(vt.world, target.clone().guarded(guard)).map(Some) + recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some) } Some(Selector::Regex(regex)) => { @@ -140,7 +140,7 @@ fn try_apply( } let piece = make(m.as_str().into()).guarded(guard); - let transformed = recipe.apply(vt.world, piece)?; + let transformed = recipe.apply_vt(vt, piece)?; result.push(transformed); cursor = m.end(); } diff --git a/src/model/styles.rs b/src/model/styles.rs index 359f1461e..8cccb5f67 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -1,15 +1,13 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::iter; -use comemo::Tracked; use ecow::{eco_format, EcoString, EcoVec}; -use super::{Content, Label, Node, NodeId}; +use super::{Content, Label, Node, NodeId, Vt}; use crate::diag::{SourceResult, Trace, Tracepoint}; -use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value}; +use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value, Vm}; use crate::syntax::Span; use crate::util::pretty_array_like; -use crate::World; /// A map of style properties. #[derive(Default, Clone, Hash)] @@ -197,20 +195,32 @@ impl Recipe { } /// Apply the recipe to the given content. - pub fn apply( - &self, - world: Tracked, - content: Content, - ) -> SourceResult { + pub fn apply_vm(&self, vm: &mut Vm, content: Content) -> SourceResult { match &self.transform { Transform::Content(content) => Ok(content.clone()), Transform::Func(func) => { let args = Args::new(self.span, [Value::Content(content.clone())]); - let mut result = func.call_detached(world, args); + let mut result = func.call_vm(vm, args); // For selector-less show rules, a tracepoint makes no sense. if self.selector.is_some() { let point = || Tracepoint::Show(content.id().name.into()); - result = result.trace(world, point, content.span()); + result = result.trace(vm.world(), point, content.span()); + } + Ok(result?.display()) + } + Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())), + } + } + + /// Apply the recipe to the given content. + pub fn apply_vt(&self, vt: &mut Vt, content: Content) -> SourceResult { + match &self.transform { + Transform::Content(content) => Ok(content.clone()), + Transform::Func(func) => { + let mut result = func.call_vt(vt, [Value::Content(content.clone())]); + if self.selector.is_some() { + let point = || Tracepoint::Show(content.id().name.into()); + result = result.trace(vt.world, point, content.span()); } Ok(result?.display()) } diff --git a/src/model/typeset.rs b/src/model/typeset.rs index e9cb3d2c6..683f2846a 100644 --- a/src/model/typeset.rs +++ b/src/model/typeset.rs @@ -6,13 +6,18 @@ use comemo::{Constraint, Track, Tracked, TrackedMut}; use super::{Content, Selector, StyleChain}; use crate::diag::SourceResult; use crate::doc::{Document, Element, Frame, Location, Meta}; +use crate::eval::Tracer; use crate::geom::{Point, Transform}; use crate::util::NonZeroExt; use crate::World; /// Typeset content into a fully layouted document. #[comemo::memoize] -pub fn typeset(world: Tracked, content: &Content) -> SourceResult { +pub fn typeset( + world: Tracked, + mut tracer: TrackedMut, + content: &Content, +) -> SourceResult { let library = world.library(); let styles = StyleChain::new(&library.styles); @@ -27,6 +32,7 @@ pub fn typeset(world: Tracked, content: &Content) -> SourceResult, content: &Content) -> SourceResult { /// The compilation environment. pub world: Tracked<'a, dyn World>, + /// The tracer for inspection of the values an expression produces. + pub tracer: TrackedMut<'a, Tracer>, /// Provides stable identities to nodes. pub provider: TrackedMut<'a, StabilityProvider>, /// Provides access to information about the document. @@ -162,8 +170,8 @@ impl Introspector { } /// Query for all metadata matches for the given selector. - pub fn query(&self, selector: Selector) -> Vec<&Content> { - self.all().filter(|node| selector.matches(node)).collect() + pub fn query(&self, selector: Selector) -> Vec { + self.all().filter(|node| selector.matches(node)).cloned().collect() } /// Query for all metadata matches before the given id. @@ -171,14 +179,15 @@ impl Introspector { &self, selector: Selector, id: StableId, - ) -> (Vec<&Content>, Vec<&Content>) { + ) -> (Vec, Vec) { let mut iter = self.all(); let before = iter .by_ref() .take_while(|node| node.stable_id() != Some(id)) .filter(|node| selector.matches(node)) + .cloned() .collect(); - let after = iter.filter(|node| selector.matches(node)).collect(); + let after = iter.filter(|node| selector.matches(node)).cloned().collect(); (before, after) } diff --git a/tests/src/benches.rs b/tests/src/benches.rs index 21fdbc7ad..b1e77692e 100644 --- a/tests/src/benches.rs +++ b/tests/src/benches.rs @@ -80,17 +80,17 @@ fn bench_typeset(iai: &mut Iai) { ) .unwrap(); let content = module.content(); - iai.run(|| typst::model::typeset(world.track(), &content)); + iai.run(|| typst::model::typeset(world.track(), tracer.track_mut(), &content)); } fn bench_compile(iai: &mut Iai) { let world = BenchWorld::new(); - iai.run(|| typst::compile(&world, &world.source)); + iai.run(|| typst::compile(&world)); } fn bench_render(iai: &mut Iai) { let world = BenchWorld::new(); - let document = typst::compile(&world, &world.source).unwrap(); + let document = typst::compile(&world).unwrap(); iai.run(|| typst::export::render(&document.pages[0], 1.0, Color::WHITE)) } @@ -124,6 +124,18 @@ impl World for BenchWorld { &self.library } + fn main(&self) -> &Source { + &self.source + } + + fn resolve(&self, path: &Path) -> FileResult { + Err(FileError::NotFound(path.into())) + } + + fn source(&self, _: SourceId) -> &Source { + &self.source + } + fn book(&self) -> &Prehashed { &self.book } @@ -135,12 +147,4 @@ impl World for BenchWorld { fn file(&self, path: &Path) -> FileResult { Err(FileError::NotFound(path.into())) } - - fn resolve(&self, path: &Path) -> FileResult { - Err(FileError::NotFound(path.into())) - } - - fn source(&self, _: SourceId) -> &Source { - unimplemented!() - } } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 1fb40204d..9e6d94a3a 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -207,6 +207,7 @@ struct TestWorld { fonts: Vec, paths: RefCell>, sources: FrozenVec>, + main: SourceId, } #[derive(Default)] @@ -236,6 +237,7 @@ impl TestWorld { fonts, paths: RefCell::default(), sources: FrozenVec::new(), + main: SourceId::detached(), } } } @@ -249,19 +251,8 @@ impl World for TestWorld { &self.library } - fn book(&self) -> &Prehashed { - &self.book - } - - fn font(&self, id: usize) -> Option { - Some(self.fonts[id].clone()) - } - - fn file(&self, path: &Path) -> FileResult { - self.slot(path) - .buffer - .get_or_init(|| read(path).map(Buffer::from)) - .clone() + fn main(&self) -> &Source { + self.source(self.main) } fn resolve(&self, path: &Path) -> FileResult { @@ -278,20 +269,38 @@ impl World for TestWorld { fn source(&self, id: SourceId) -> &Source { &self.sources[id.into_u16() as usize] } + + fn book(&self) -> &Prehashed { + &self.book + } + + fn font(&self, id: usize) -> Option { + Some(self.fonts[id].clone()) + } + + fn file(&self, path: &Path) -> FileResult { + self.slot(path) + .buffer + .get_or_init(|| read(path).map(Buffer::from)) + .clone() + } } impl TestWorld { fn set(&mut self, path: &Path, text: String) -> SourceId { let slot = self.slot(path); - if let Some(&Ok(id)) = slot.source.get() { + let id = if let Some(&Ok(id)) = slot.source.get() { drop(slot); self.sources.as_mut()[id.into_u16() as usize].replace(text); id } else { let id = self.insert(path, text); slot.source.set(Ok(id)).unwrap(); + drop(slot); id - } + }; + self.main = id; + id } fn slot(&self, path: &Path) -> RefMut { @@ -448,7 +457,7 @@ fn test_part( println!("Model:\n{:#?}\n", module.content()); } - let (mut frames, errors) = match typst::compile(world, source) { + let (mut frames, errors) = match typst::compile(world) { Ok(document) => (document.pages, vec![]), Err(errors) => (vec![], *errors), };