Methods for page and location on queried content

This commit is contained in:
Laurenz 2023-03-17 18:43:20 +01:00
parent af7fe4d760
commit 59903270dc
5 changed files with 101 additions and 43 deletions

View File

@ -13,7 +13,7 @@ use super::{
Vm, Vm,
}; };
use crate::diag::{bail, SourceResult, StrResult}; use crate::diag::{bail, SourceResult, StrResult};
use crate::model::{NodeId, Selector, StyleMap, Vt}; use crate::model::{Introspector, NodeId, Selector, StabilityProvider, StyleMap, Vt};
use crate::syntax::ast::{self, AstNode, Expr}; use crate::syntax::ast::{self, AstNode, Expr};
use crate::syntax::{SourceId, Span, SyntaxNode}; use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::util::hash128; use crate::util::hash128;
@ -102,9 +102,11 @@ impl Func {
Closure::call( Closure::call(
self, self,
vm.world, vm.world(),
route, route,
TrackedMut::reborrow_mut(&mut vm.tracer), TrackedMut::reborrow_mut(&mut vm.vt.tracer),
TrackedMut::reborrow_mut(&mut vm.vt.provider),
vm.vt.introspector,
vm.depth + 1, vm.depth + 1,
args, args,
) )
@ -125,13 +127,7 @@ impl Func {
let route = Route::default(); let route = Route::default();
let id = SourceId::detached(); let id = SourceId::detached();
let scopes = Scopes::new(None); let scopes = Scopes::new(None);
let mut vm = Vm::new( let mut vm = Vm::new(vt.reborrow_mut(), route.track(), id, scopes);
vt.world,
route.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
id,
scopes,
);
let args = Args::new(self.span(), args); let args = Args::new(self.span(), args);
self.call_vm(&mut vm, args) self.call_vm(&mut vm, args)
} }
@ -318,6 +314,8 @@ impl Closure {
world: Tracked<dyn World>, world: Tracked<dyn World>,
route: Tracked<Route>, route: Tracked<Route>,
tracer: TrackedMut<Tracer>, tracer: TrackedMut<Tracer>,
provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>,
depth: usize, depth: usize,
mut args: Args, mut args: Args,
) -> SourceResult<Value> { ) -> SourceResult<Value> {
@ -358,7 +356,8 @@ impl Closure {
args.finish()?; args.finish()?;
// Evaluate the body. // Evaluate the body.
let mut sub = Vm::new(world, route, tracer, closure.location, scopes); let vt = Vt { world, tracer, provider, introspector };
let mut sub = Vm::new(vt, route, closure.location, scopes);
sub.depth = depth; sub.depth = depth;
// Handle control flow. // Handle control flow.

View File

@ -73,6 +73,8 @@ pub fn call(
"func" => Value::Func(content.id().into()), "func" => Value::Func(content.id().into()),
"has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)), "has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)),
"at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(), "at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(),
"page" => content.page(&vm.vt).at(span)?.into(),
"location" => content.location(&vm.vt).at(span)?.into(),
_ => return missing(), _ => return missing(),
}, },
@ -249,7 +251,13 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
("starts-with", true), ("starts-with", true),
("trim", true), ("trim", true),
], ],
"content" => &[("func", false), ("has", true), ("at", true)], "content" => &[
("func", false),
("has", true),
("at", true),
("page", false),
("location", false),
],
"array" => &[ "array" => &[
("all", true), ("all", true),
("any", true), ("any", true),

View File

@ -47,7 +47,10 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{ use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint, bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
}; };
use crate::model::Introspector;
use crate::model::StabilityProvider;
use crate::model::Unlabellable; use crate::model::Unlabellable;
use crate::model::Vt;
use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform}; use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform};
use crate::syntax::ast::AstNode; use crate::syntax::ast::AstNode;
use crate::syntax::{ use crate::syntax::{
@ -81,7 +84,15 @@ pub fn eval(
// Evaluate the module. // Evaluate the module.
let route = unsafe { Route::insert(route, id) }; let route = unsafe { Route::insert(route, id) };
let scopes = Scopes::new(Some(library)); let scopes = Scopes::new(Some(library));
let mut vm = Vm::new(world, route.track(), tracer, id, scopes); let mut provider = StabilityProvider::new();
let introspector = Introspector::new(&[]);
let vt = Vt {
world,
tracer,
provider: provider.track_mut(),
introspector: introspector.track(),
};
let mut vm = Vm::new(vt, route.track(), id, scopes);
let root = match source.root().cast::<ast::Markup>() { let root = match source.root().cast::<ast::Markup>() {
Some(markup) if vm.traced.is_some() => markup, Some(markup) if vm.traced.is_some() => markup,
_ => source.ast()?, _ => source.ast()?,
@ -121,7 +132,15 @@ pub fn eval_code_str(
let scopes = Scopes::new(Some(library)); let scopes = Scopes::new(Some(library));
let route = Route::default(); let route = Route::default();
let mut tracer = Tracer::default(); let mut tracer = Tracer::default();
let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes); let mut provider = StabilityProvider::new();
let introspector = Introspector::new(&[]);
let vt = Vt {
world,
tracer: tracer.track_mut(),
provider: provider.track_mut(),
introspector: introspector.track(),
};
let mut vm = Vm::new(vt, route.track(), id, scopes);
let code = root.cast::<ast::Code>().unwrap(); let code = root.cast::<ast::Code>().unwrap();
let result = code.eval(&mut vm); let result = code.eval(&mut vm);
@ -138,14 +157,12 @@ pub fn eval_code_str(
/// Holds the state needed to [evaluate](eval) Typst sources. A new /// Holds the state needed to [evaluate](eval) Typst sources. A new
/// virtual machine is created for each module evaluation and function call. /// virtual machine is created for each module evaluation and function call.
pub struct Vm<'a> { pub struct Vm<'a> {
/// The compilation environment. /// The underlying virtual typesetter.
world: Tracked<'a, dyn World>, vt: Vt<'a>,
/// The language items. /// The language items.
items: LangItems, items: LangItems,
/// The route of source ids the VM took to reach its current location. /// The route of source ids the VM took to reach its current location.
route: Tracked<'a, Route>, route: Tracked<'a, Route>,
/// The tracer for inspection of the values an expression produces.
tracer: TrackedMut<'a, Tracer>,
/// The current location. /// The current location.
location: SourceId, location: SourceId,
/// A control flow event that is currently happening. /// A control flow event that is currently happening.
@ -161,18 +178,17 @@ pub struct Vm<'a> {
impl<'a> Vm<'a> { impl<'a> Vm<'a> {
/// Create a new virtual machine. /// Create a new virtual machine.
fn new( fn new(
world: Tracked<'a, dyn World>, vt: Vt<'a>,
route: Tracked<'a, Route>, route: Tracked<'a, Route>,
tracer: TrackedMut<'a, Tracer>,
location: SourceId, location: SourceId,
scopes: Scopes<'a>, scopes: Scopes<'a>,
) -> Self { ) -> Self {
let traced = tracer.span(location); let traced = vt.tracer.span(location);
let items = vt.world.library().items.clone();
Self { Self {
world, vt,
items: world.library().items.clone(), items,
route, route,
tracer,
location, location,
flow: None, flow: None,
scopes, scopes,
@ -183,14 +199,14 @@ impl<'a> Vm<'a> {
/// Access the underlying world. /// Access the underlying world.
pub fn world(&self) -> Tracked<'a, dyn World> { pub fn world(&self) -> Tracked<'a, dyn World> {
self.world self.vt.world
} }
/// Define a variable in the current scope. /// Define a variable in the current scope.
pub fn define(&mut self, var: ast::Ident, value: impl Into<Value>) { pub fn define(&mut self, var: ast::Ident, value: impl Into<Value>) {
let value = value.into(); let value = value.into();
if self.traced == Some(var.span()) { if self.traced == Some(var.span()) {
self.tracer.trace(value.clone()); self.vt.tracer.trace(value.clone());
} }
self.scopes.top.define(var.take(), value); self.scopes.top.define(var.take(), value);
} }
@ -200,10 +216,10 @@ impl<'a> Vm<'a> {
pub fn locate(&self, path: &str) -> StrResult<PathBuf> { pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
if !self.location.is_detached() { if !self.location.is_detached() {
if let Some(path) = path.strip_prefix('/') { if let Some(path) = path.strip_prefix('/') {
return Ok(self.world.root().join(path).normalize()); return Ok(self.world().root().join(path).normalize());
} }
if let Some(dir) = self.world.source(self.location).path().parent() { if let Some(dir) = self.world().source(self.location).path().parent() {
return Ok(dir.join(path).normalize()); return Ok(dir.join(path).normalize());
} }
} }
@ -450,7 +466,7 @@ impl Eval for ast::Expr {
.spanned(span); .spanned(span);
if vm.traced == Some(span) { if vm.traced == Some(span) {
vm.tracer.trace(v.clone()); vm.vt.tracer.trace(v.clone());
} }
Ok(v) Ok(v)
@ -1004,16 +1020,22 @@ impl Eval for ast::FuncCall {
let args = args.eval(vm)?; let args = args.eval(vm)?;
let target = target.access(vm)?; let target = target.access(vm)?;
if !matches!(target, Value::Symbol(_) | Value::Module(_)) { if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
return methods::call_mut(target, &field, args, span) return methods::call_mut(target, &field, args, span).trace(
.trace(vm.world, point, span); vm.world(),
point,
span,
);
} }
(target.field(&field).at(field_span)?, args) (target.field(&field).at(field_span)?, args)
} else { } else {
let target = target.eval(vm)?; let target = target.eval(vm)?;
let args = args.eval(vm)?; let args = args.eval(vm)?;
if !matches!(target, Value::Symbol(_) | Value::Module(_)) { if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
return methods::call(vm, target, &field, args, span) return methods::call(vm, target, &field, args, span).trace(
.trace(vm.world, point, span); vm.world(),
point,
span,
);
} }
(target.field(&field).at(field_span)?, args) (target.field(&field).at(field_span)?, args)
} }
@ -1052,7 +1074,7 @@ impl Eval for ast::FuncCall {
let callee = callee.cast::<Func>().at(callee_span)?; let callee = callee.cast::<Func>().at(callee_span)?;
let point = || Tracepoint::Call(callee.name().map(Into::into)); let point = || Tracepoint::Call(callee.name().map(Into::into));
callee.call_vm(vm, args).trace(vm.world, point, span) callee.call_vm(vm, args).trace(vm.world(), point, span)
} }
} }
@ -1431,8 +1453,9 @@ fn import(vm: &mut Vm, source: Value, span: Span) -> SourceResult<Module> {
}; };
// Load the source file. // Load the source file.
let world = vm.world();
let full = vm.locate(&path).at(span)?; let full = vm.locate(&path).at(span)?;
let id = vm.world.resolve(&full).at(span)?; let id = world.resolve(&full).at(span)?;
// Prevent cyclic importing. // Prevent cyclic importing.
if vm.route.contains(id) { if vm.route.contains(id) {
@ -1440,10 +1463,10 @@ fn import(vm: &mut Vm, source: Value, span: Span) -> SourceResult<Module> {
} }
// Evaluate the file. // Evaluate the file.
let source = vm.world.source(id); let source = world.source(id);
let point = || Tracepoint::Import; let point = || Tracepoint::Import;
eval(vm.world, vm.route, TrackedMut::reborrow_mut(&mut vm.tracer), source) eval(world, vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), source)
.trace(vm.world, point, span) .trace(world, point, span)
} }
impl Eval for ast::LoopBreak { impl Eval for ast::LoopBreak {
@ -1506,7 +1529,7 @@ impl Access for ast::Ident {
let span = self.span(); let span = self.span();
let value = vm.scopes.get_mut(self).at(span)?; let value = vm.scopes.get_mut(self).at(span)?;
if vm.traced == Some(span) { if vm.traced == Some(span) {
vm.tracer.trace(value.clone()); vm.vt.tracer.trace(value.clone());
} }
Ok(value) Ok(value)
} }

View File

@ -2,6 +2,7 @@ use std::any::TypeId;
use std::fmt::{self, Debug, Formatter, Write}; use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::iter::{self, Sum}; use std::iter::{self, Sum};
use std::num::NonZeroUsize;
use std::ops::{Add, AddAssign, Deref}; use std::ops::{Add, AddAssign, Deref};
use ecow::{eco_format, EcoString, EcoVec}; use ecow::{eco_format, EcoString, EcoVec};
@ -9,10 +10,10 @@ use once_cell::sync::Lazy;
use super::{ use super::{
node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap, node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap,
Synthesize, Synthesize, Vt,
}; };
use crate::diag::{SourceResult, StrResult}; use crate::diag::{SourceResult, StrResult};
use crate::doc::Meta; use crate::doc::{Location, Meta};
use crate::eval::{ use crate::eval::{
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm, cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
}; };
@ -185,6 +186,22 @@ impl Content {
self.field(field).ok_or_else(|| missing_field(field)) self.field(field).ok_or_else(|| missing_field(field))
} }
/// Determine the page of this content.
pub fn page(&self, vt: &Vt) -> StrResult<NonZeroUsize> {
match self.stable_id() {
Some(id) => Ok(vt.introspector.page(id)),
None => Err("this method can only be called on queried content".into()),
}
}
/// Determine the location of this content.
pub fn location(&self, vt: &Vt) -> StrResult<Location> {
match self.stable_id() {
Some(id) => Ok(vt.introspector.location(id)),
None => Err("this method can only be called on queried content".into()),
}
}
/// The content's label. /// The content's label.
pub fn label(&self) -> Option<&Label> { pub fn label(&self) -> Option<&Label> {
match self.field("label")? { match self.field("label")? {

View File

@ -53,8 +53,7 @@ pub fn typeset(
/// A virtual typesetter. /// A virtual typesetter.
/// ///
/// Holds the state needed to [typeset] content. This is the equivalent to the /// Holds the state needed to [typeset] content.
/// [Vm](crate::eval::Vm) for typesetting.
pub struct Vt<'a> { pub struct Vt<'a> {
/// The compilation environment. /// The compilation environment.
pub world: Tracked<'a, dyn World>, pub world: Tracked<'a, dyn World>,
@ -66,6 +65,18 @@ pub struct Vt<'a> {
pub introspector: Tracked<'a, Introspector>, pub introspector: Tracked<'a, Introspector>,
} }
impl Vt<'_> {
/// Mutably reborrow with a shorter lifetime.
pub fn reborrow_mut(&mut self) -> Vt<'_> {
Vt {
world: self.world,
tracer: TrackedMut::reborrow_mut(&mut self.tracer),
provider: TrackedMut::reborrow_mut(&mut self.provider),
introspector: self.introspector,
}
}
}
/// Provides stable identities to nodes. /// Provides stable identities to nodes.
#[derive(Clone)] #[derive(Clone)]
pub struct StabilityProvider { pub struct StabilityProvider {