Move route from context to VM

This commit is contained in:
Laurenz 2022-05-25 13:50:33 +02:00
parent 6935cf8dfe
commit c010cbc17d
45 changed files with 266 additions and 280 deletions

View File

@ -4,10 +4,10 @@ use std::sync::Arc;
use iai::{black_box, main, Iai}; use iai::{black_box, main, Iai};
use unscanny::Scanner; use unscanny::Scanner;
use typst::eval::evaluate;
use typst::loading::MemLoader; use typst::loading::MemLoader;
use typst::parse::{parse, TokenMode, Tokens}; use typst::parse::{TokenMode, Tokens};
use typst::source::SourceId; use typst::source::SourceId;
use typst::syntax::highlight_node;
use typst::{Config, Context}; use typst::{Config, Context};
const SRC: &str = include_str!("bench.typ"); const SRC: &str = include_str!("bench.typ");
@ -61,7 +61,7 @@ fn bench_tokenize(iai: &mut Iai) {
} }
fn bench_parse(iai: &mut Iai) { fn bench_parse(iai: &mut Iai) {
iai.run(|| parse(SRC)); iai.run(|| typst::parse::parse(SRC));
} }
fn bench_edit(iai: &mut Iai) { fn bench_edit(iai: &mut Iai) {
@ -73,7 +73,7 @@ fn bench_highlight(iai: &mut Iai) {
let (ctx, id) = context(); let (ctx, id) = context();
let source = ctx.sources.get(id); let source = ctx.sources.get(id);
iai.run(|| { iai.run(|| {
highlight_node( typst::syntax::highlight_node(
source.red().as_ref(), source.red().as_ref(),
0 .. source.len_bytes(), 0 .. source.len_bytes(),
&mut |_, _| {}, &mut |_, _| {},
@ -83,17 +83,17 @@ fn bench_highlight(iai: &mut Iai) {
fn bench_eval(iai: &mut Iai) { fn bench_eval(iai: &mut Iai) {
let (mut ctx, id) = context(); let (mut ctx, id) = context();
iai.run(|| ctx.evaluate(id).unwrap()); iai.run(|| typst::eval::evaluate(&mut ctx, id, vec![]).unwrap());
} }
fn bench_layout(iai: &mut Iai) { fn bench_layout(iai: &mut Iai) {
let (mut ctx, id) = context(); let (mut ctx, id) = context();
let module = ctx.evaluate(id).unwrap(); let module = evaluate(&mut ctx, id, vec![]).unwrap();
iai.run(|| module.content.layout(&mut ctx)); iai.run(|| typst::model::layout(&mut ctx, &module.content));
} }
fn bench_render(iai: &mut Iai) { fn bench_render(iai: &mut Iai) {
let (mut ctx, id) = context(); let (mut ctx, id) = context();
let frames = ctx.typeset(id).unwrap(); let frames = typst::typeset(&mut ctx, id).unwrap();
iai.run(|| typst::export::render(&mut ctx, &frames[0], 1.0)) iai.run(|| typst::export::render(&mut ctx, &frames[0], 1.0))
} }

View File

@ -26,13 +26,8 @@ pub struct Arg {
} }
impl Args { impl Args {
/// Create empty arguments from a span.
pub fn new(span: Span) -> Self {
Self { span, items: vec![] }
}
/// Create positional arguments from a span and values. /// Create positional arguments from a span and values.
pub fn from_values(span: Span, values: impl IntoIterator<Item = Value>) -> Self { pub fn new(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
let items = values let items = values
.into_iter() .into_iter()
.map(|value| Arg { .map(|value| Arg {

View File

@ -3,11 +3,10 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::sync::Arc; use std::sync::Arc;
use super::{ops, Args, Func, Value}; use super::{ops, Args, Func, Machine, Value};
use crate::diag::{At, StrResult, TypResult}; use crate::diag::{At, StrResult, TypResult};
use crate::syntax::Spanned; use crate::syntax::Spanned;
use crate::util::ArcExt; use crate::util::ArcExt;
use crate::Context;
/// Create a new [`Array`] from values. /// Create a new [`Array`] from values.
#[allow(unused_macros)] #[allow(unused_macros)]
@ -120,21 +119,21 @@ impl Array {
} }
/// Transform each item in the array with a function. /// Transform each item in the array with a function.
pub fn map(&self, ctx: &mut Context, f: Spanned<Func>) -> TypResult<Self> { pub fn map(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
Ok(self Ok(self
.iter() .iter()
.cloned() .cloned()
.map(|item| f.v.call(ctx, Args::from_values(f.span, [item]))) .map(|item| f.v.call(vm, Args::new(f.span, [item])))
.collect::<TypResult<_>>()?) .collect::<TypResult<_>>()?)
} }
/// Return a new array with only those elements for which the function /// Return a new array with only those elements for which the function
/// return true. /// return true.
pub fn filter(&self, ctx: &mut Context, f: Spanned<Func>) -> TypResult<Self> { pub fn filter(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
let mut kept = vec![]; let mut kept = vec![];
for item in self.iter() { for item in self.iter() {
if f.v if f.v
.call(ctx, Args::from_values(f.span, [item.clone()]))? .call(vm, Args::new(f.span, [item.clone()]))?
.cast::<bool>() .cast::<bool>()
.at(f.span)? .at(f.span)?
{ {

View File

@ -3,12 +3,11 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::sync::Arc; use std::sync::Arc;
use super::{Args, Array, Func, Value}; use super::{Args, Array, Func, Machine, Value};
use crate::diag::{StrResult, TypResult}; use crate::diag::{StrResult, TypResult};
use crate::parse::is_ident; use crate::parse::is_ident;
use crate::syntax::Spanned; use crate::syntax::Spanned;
use crate::util::{ArcExt, EcoString}; use crate::util::{ArcExt, EcoString};
use crate::Context;
/// Create a new [`Dict`] from key-value pairs. /// Create a new [`Dict`] from key-value pairs.
#[allow(unused_macros)] #[allow(unused_macros)]
@ -97,14 +96,12 @@ impl Dict {
} }
/// Transform each pair in the array with a function. /// Transform each pair in the array with a function.
pub fn map(&self, ctx: &mut Context, f: Spanned<Func>) -> TypResult<Array> { pub fn map(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Array> {
Ok(self Ok(self
.iter() .iter()
.map(|(key, value)| { .map(|(key, value)| {
f.v.call( let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]);
ctx, f.v.call(vm, args)
Args::from_values(f.span, [Value::Str(key.clone()), value.clone()]),
)
}) })
.collect::<TypResult<_>>()?) .collect::<TypResult<_>>()?)
} }

View File

@ -29,7 +29,7 @@ impl Func {
/// Create a new function from a native rust function. /// Create a new function from a native rust function.
pub fn from_fn( pub fn from_fn(
name: &'static str, name: &'static str,
func: fn(&mut Context, &mut Args) -> TypResult<Value>, func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
) -> Self { ) -> Self {
Self(Arc::new(Repr::Native(Native { Self(Arc::new(Repr::Native(Native {
name, name,
@ -86,19 +86,25 @@ impl Func {
} }
/// Call the function with the given arguments. /// Call the function with the given arguments.
pub fn call(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> { pub fn call(&self, vm: &mut Machine, mut args: Args) -> TypResult<Value> {
let value = match self.0.as_ref() { let value = match self.0.as_ref() {
Repr::Native(native) => (native.func)(ctx, &mut args)?, Repr::Native(native) => (native.func)(vm, &mut args)?,
Repr::Closure(closure) => closure.call(ctx, &mut args)?, Repr::Closure(closure) => closure.call(vm, &mut args)?,
Repr::With(wrapped, applied) => { Repr::With(wrapped, applied) => {
args.items.splice(.. 0, applied.items.iter().cloned()); args.items.splice(.. 0, applied.items.iter().cloned());
return wrapped.call(ctx, args); return wrapped.call(vm, args);
} }
}; };
args.finish()?; args.finish()?;
Ok(value) Ok(value)
} }
/// Call the function without an existing virtual machine.
pub fn call_detached(&self, ctx: &mut Context, args: Args) -> TypResult<Value> {
let mut vm = Machine::new(ctx, vec![], Scopes::new(None));
self.call(&mut vm, args)
}
/// Execute the function's set rule. /// Execute the function's set rule.
pub fn set(&self, mut args: Args) -> TypResult<StyleMap> { pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
let styles = match self.0.as_ref() { let styles = match self.0.as_ref() {
@ -138,7 +144,7 @@ struct Native {
/// The name of the function. /// The name of the function.
pub name: &'static str, pub name: &'static str,
/// The function pointer. /// The function pointer.
pub func: fn(&mut Context, &mut Args) -> TypResult<Value>, pub func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
/// The set rule. /// The set rule.
pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>, pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>,
/// The id of the node to customize with this function's show rule. /// The id of the node to customize with this function's show rule.
@ -163,7 +169,7 @@ pub trait Node: 'static {
/// ///
/// This is passed only the arguments that remain after execution of the /// This is passed only the arguments that remain after execution of the
/// node's set rule. /// node's set rule.
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>; fn construct(vm: &mut Machine, args: &mut Args) -> TypResult<Content>;
/// Parse the arguments into style properties for this node. /// Parse the arguments into style properties for this node.
/// ///
@ -192,7 +198,7 @@ pub struct Closure {
impl Closure { impl Closure {
/// Call the function in the context with the arguments. /// Call the function in the context with the arguments.
pub fn call(&self, ctx: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn call(&self, vm: &mut Machine, args: &mut Args) -> TypResult<Value> {
// Don't leak the scopes from the call site. Instead, we use the // Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier. // scope of captured variables we collected earlier.
let mut scopes = Scopes::new(None); let mut scopes = Scopes::new(None);
@ -213,24 +219,20 @@ impl Closure {
scopes.top.def_mut(sink, args.take()); scopes.top.def_mut(sink, args.take());
} }
// Set the new route if we are detached. // Determine the route inside the closure.
let detached = ctx.route.is_empty(); let detached = vm.route.is_empty();
if detached { let route = if detached {
ctx.route = self.location.into_iter().collect(); self.location.into_iter().collect()
} } else {
vm.route.clone()
};
// Evaluate the body. // Evaluate the body.
let mut vm = Machine::new(ctx, scopes); let mut sub = Machine::new(vm.ctx, route, scopes);
let result = self.body.eval(&mut vm); let result = self.body.eval(&mut sub);
let flow = vm.flow;
// Restore the old route.
if detached {
ctx.route.clear();
}
// Handle control flow. // Handle control flow.
match flow { match sub.flow {
Some(Flow::Return(_, Some(explicit))) => return Ok(explicit), Some(Flow::Return(_, Some(explicit))) => return Ok(explicit),
Some(Flow::Return(_, None)) => {} Some(Flow::Return(_, None)) => {}
Some(flow) => return Err(flow.forbidden())?, Some(flow) => return Err(flow.forbidden())?,

View File

@ -1,12 +1,18 @@
use std::path::PathBuf;
use super::{Scopes, Value}; use super::{Scopes, Value};
use crate::diag::TypError; use crate::diag::{StrResult, TypError};
use crate::source::SourceId;
use crate::syntax::Span; use crate::syntax::Span;
use crate::util::PathExt;
use crate::Context; use crate::Context;
/// A virtual machine. /// A virtual machine.
pub struct Machine<'a> { pub struct Machine<'a> {
/// The core context. /// The core context.
pub ctx: &'a mut Context, pub ctx: &'a mut Context,
/// The route of source ids at which the machine is located.
pub route: Vec<SourceId>,
/// The stack of scopes. /// The stack of scopes.
pub scopes: Scopes<'a>, pub scopes: Scopes<'a>,
/// A control flow event that is currently happening. /// A control flow event that is currently happening.
@ -15,8 +21,24 @@ pub struct Machine<'a> {
impl<'a> Machine<'a> { impl<'a> Machine<'a> {
/// Create a new virtual machine. /// Create a new virtual machine.
pub fn new(ctx: &'a mut Context, scopes: Scopes<'a>) -> Self { pub fn new(ctx: &'a mut Context, route: Vec<SourceId>, scopes: Scopes<'a>) -> Self {
Self { ctx, scopes, flow: None } Self { ctx, route, scopes, flow: None }
}
/// Resolve a user-entered path to be relative to the compilation
/// environment's root.
pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
if let Some(&id) = self.route.last() {
if let Some(path) = path.strip_prefix('/') {
return Ok(self.ctx.config.root.join(path).normalize());
}
if let Some(dir) = self.ctx.sources.get(id).path().parent() {
return Ok(dir.join(path).normalize());
}
}
return Err("cannot access file system from here".into());
} }
} }

View File

@ -1,14 +1,13 @@
//! Methods on values. //! Methods on values.
use super::{Args, Regex, StrExt, Value}; use super::{Args, Machine, Regex, StrExt, Value};
use crate::diag::{At, TypResult}; use crate::diag::{At, TypResult};
use crate::syntax::Span; use crate::syntax::Span;
use crate::util::EcoString; use crate::util::EcoString;
use crate::Context;
/// Call a method on a value. /// Call a method on a value.
pub fn call( pub fn call(
ctx: &mut Context, vm: &mut Machine,
value: Value, value: Value,
method: &str, method: &str,
mut args: Args, mut args: Args,
@ -35,8 +34,8 @@ pub fn call(
} }
Value::Array(array.slice(start, end).at(span)?) Value::Array(array.slice(start, end).at(span)?)
} }
"map" => Value::Array(array.map(ctx, args.expect("function")?)?), "map" => Value::Array(array.map(vm, args.expect("function")?)?),
"filter" => Value::Array(array.filter(ctx, args.expect("function")?)?), "filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
"flatten" => Value::Array(array.flatten()), "flatten" => Value::Array(array.flatten()),
"find" => array.find(args.expect("value")?).map_or(Value::None, Value::Int), "find" => array.find(args.expect("value")?).map_or(Value::None, Value::Int),
"join" => { "join" => {
@ -52,7 +51,7 @@ pub fn call(
"len" => Value::Int(dict.len()), "len" => Value::Int(dict.len()),
"keys" => Value::Array(dict.keys()), "keys" => Value::Array(dict.keys()),
"values" => Value::Array(dict.values()), "values" => Value::Array(dict.values()),
"pairs" => Value::Array(dict.map(ctx, args.expect("function")?)?), "pairs" => Value::Array(dict.map(vm, args.expect("function")?)?),
_ => missing()?, _ => missing()?,
}, },

View File

@ -43,13 +43,63 @@ use crate::syntax::{Span, Spanned};
use crate::util::EcoString; use crate::util::EcoString;
use crate::Context; use crate::Context;
/// Evaluate an expression. /// Evaluate a source file and return the resulting module.
pub trait Eval { ///
/// The output of evaluating the expression. /// Returns either a module containing a scope with top-level bindings and
type Output; /// layoutable contents or diagnostics in the form of a vector of error
/// messages with file and span information.
pub fn evaluate(
ctx: &mut Context,
id: SourceId,
mut route: Vec<SourceId>,
) -> TypResult<Module> {
// Prevent cyclic evaluation.
if route.contains(&id) {
let path = ctx.sources.get(id).path().display();
panic!("Tried to cyclicly evaluate {}", path);
}
/// Evaluate the expression to the output value. // Check whether the module was already evaluated.
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output>; if let Some(module) = ctx.modules.get(&id) {
if module.valid(&ctx.sources) {
return Ok(module.clone());
} else {
ctx.modules.remove(&id);
}
}
route.push(id);
// Parse the file.
let source = ctx.sources.get(id);
let ast = source.ast()?;
// Save the old dependencies.
let prev_deps = std::mem::replace(&mut ctx.deps, vec![(id, source.rev())]);
// Evaluate the module.
let std = ctx.config.std.clone();
let scopes = Scopes::new(Some(&std));
let mut vm = Machine::new(ctx, route, scopes);
let result = ast.eval(&mut vm);
let scope = vm.scopes.top;
let flow = vm.flow;
// Restore the and dependencies.
let deps = std::mem::replace(&mut ctx.deps, prev_deps);
// Handle control flow.
if let Some(flow) = flow {
return Err(flow.forbidden());
}
// Assemble the module.
let module = Module { scope, content: result?, deps };
// Save the evaluated module.
ctx.modules.insert(id, module.clone());
Ok(module)
} }
/// An evaluated module, ready for importing or layouting. /// An evaluated module, ready for importing or layouting.
@ -70,6 +120,15 @@ impl Module {
} }
} }
/// Evaluate an expression.
pub trait Eval {
/// The output of evaluating the expression.
type Output;
/// Evaluate the expression to the output value.
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output>;
}
impl Eval for Markup { impl Eval for Markup {
type Output = Content; type Output = Content;
@ -553,7 +612,7 @@ impl Eval for FuncCall {
Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(), Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(),
Value::Func(func) => { Value::Func(func) => {
let point = || Tracepoint::Call(func.name().map(ToString::to_string)); let point = || Tracepoint::Call(func.name().map(ToString::to_string));
func.call(vm.ctx, args).trace(point, self.span())? func.call(vm, args).trace(point, self.span())?
} }
v => bail!( v => bail!(
@ -581,7 +640,7 @@ impl Eval for MethodCall {
} else { } else {
let value = self.receiver().eval(vm)?; let value = self.receiver().eval(vm)?;
let args = self.args().eval(vm)?; let args = self.args().eval(vm)?;
methods::call(vm.ctx, value, &method, args, span).trace(point, span)? methods::call(vm, value, &method, args, span).trace(point, span)?
}) })
} }
} }
@ -672,7 +731,7 @@ impl Eval for ClosureExpr {
// Define the actual function. // Define the actual function.
Ok(Value::Func(Func::from_closure(Closure { Ok(Value::Func(Func::from_closure(Closure {
location: vm.ctx.route.last().copied(), location: vm.route.last().copied(),
name, name,
captured, captured,
params, params,
@ -731,7 +790,7 @@ impl Eval for ShowExpr {
let body = self.body(); let body = self.body();
let span = body.span(); let span = body.span();
let func = Func::from_closure(Closure { let func = Func::from_closure(Closure {
location: vm.ctx.route.last().copied(), location: vm.route.last().copied(),
name: None, name: None,
captured, captured,
params, params,
@ -875,7 +934,7 @@ impl Eval for ImportExpr {
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> { fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
let span = self.path().span(); let span = self.path().span();
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?; let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
let module = import(vm.ctx, &path, span)?; let module = import(vm, &path, span)?;
match self.imports() { match self.imports() {
Imports::Wildcard => { Imports::Wildcard => {
@ -904,16 +963,16 @@ impl Eval for IncludeExpr {
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> { fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
let span = self.path().span(); let span = self.path().span();
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?; let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
let module = import(vm.ctx, &path, span)?; let module = import(vm, &path, span)?;
Ok(module.content.clone()) Ok(module.content.clone())
} }
} }
/// Process an import of a module relative to the current location. /// Process an import of a module relative to the current location.
fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> { fn import(vm: &mut Machine, path: &str, span: Span) -> TypResult<Module> {
// Load the source file. // Load the source file.
let full = ctx.locate(&path).at(span)?; let full = vm.locate(&path).at(span)?;
let id = ctx.sources.load(&full).map_err(|err| match err.kind() { let id = vm.ctx.sources.load(&full).map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => { std::io::ErrorKind::NotFound => {
error!(span, "file not found (searched at {})", full.display()) error!(span, "file not found (searched at {})", full.display())
} }
@ -921,13 +980,14 @@ fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> {
})?; })?;
// Prevent cyclic importing. // Prevent cyclic importing.
if ctx.route.contains(&id) { if vm.route.contains(&id) {
bail!(span, "cyclic import"); bail!(span, "cyclic import");
} }
// Evaluate the file. // Evaluate the file.
let module = ctx.evaluate(id).trace(|| Tracepoint::Import, span)?; let route = vm.route.clone();
ctx.deps.extend(module.deps.iter().cloned()); let module = evaluate(vm.ctx, id, route).trace(|| Tracepoint::Import, span)?;
vm.ctx.deps.extend(module.deps.iter().cloned());
Ok(module) Ok(module)
} }

View File

@ -6,10 +6,9 @@ use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use super::{Args, Func, Node, Value}; use super::{Args, Func, Machine, Node, Value};
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::util::EcoString; use crate::util::EcoString;
use crate::Context;
/// A slot where a variable is stored. /// A slot where a variable is stored.
pub type Slot = Arc<RwLock<Value>>; pub type Slot = Arc<RwLock<Value>>;
@ -89,7 +88,7 @@ impl Scope {
pub fn def_fn( pub fn def_fn(
&mut self, &mut self,
name: &'static str, name: &'static str,
func: fn(&mut Context, &mut Args) -> TypResult<Value>, func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
) { ) {
self.def_const(name, Func::from_fn(name, func)); self.def_const(name, Func::from_fn(name, func));
} }

View File

@ -23,7 +23,7 @@ use crate::Context;
/// In addition to the frame, you need to pass in the context used during /// In addition to the frame, you need to pass in the context used during
/// compilation so that fonts and images can be rendered and rendering artifacts /// compilation so that fonts and images can be rendered and rendering artifacts
/// can be cached. /// can be cached.
pub fn render(ctx: &mut Context, frame: &Frame, pixel_per_pt: f32) -> sk::Pixmap { pub fn render(ctx: &Context, frame: &Frame, pixel_per_pt: f32) -> sk::Pixmap {
let pxw = (pixel_per_pt * frame.size.x.to_f32()).round().max(1.0) as u32; let pxw = (pixel_per_pt * frame.size.x.to_f32()).round().max(1.0) as u32;
let pxh = (pixel_per_pt * frame.size.y.to_f32()).round().max(1.0) as u32; let pxh = (pixel_per_pt * frame.size.y.to_f32()).round().max(1.0) as u32;
@ -41,7 +41,7 @@ fn render_frame(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
mask: Option<&sk::ClipMask>, mask: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
frame: &Frame, frame: &Frame,
) { ) {
for (pos, element) in &frame.elements { for (pos, element) in &frame.elements {
@ -72,7 +72,7 @@ fn render_group(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
mask: Option<&sk::ClipMask>, mask: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
group: &Group, group: &Group,
) { ) {
let ts = ts.pre_concat(group.transform.into()); let ts = ts.pre_concat(group.transform.into());
@ -114,7 +114,7 @@ fn render_text(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
mask: Option<&sk::ClipMask>, mask: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
text: &Text, text: &Text,
) { ) {
let mut x = 0.0; let mut x = 0.0;
@ -136,7 +136,7 @@ fn render_svg_glyph(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
_: Option<&sk::ClipMask>, _: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
text: &Text, text: &Text,
id: GlyphId, id: GlyphId,
) -> Option<()> { ) -> Option<()> {
@ -187,7 +187,7 @@ fn render_bitmap_glyph(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
mask: Option<&sk::ClipMask>, mask: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
text: &Text, text: &Text,
id: GlyphId, id: GlyphId,
) -> Option<()> { ) -> Option<()> {
@ -213,7 +213,7 @@ fn render_outline_glyph(
canvas: &mut sk::Pixmap, canvas: &mut sk::Pixmap,
ts: sk::Transform, ts: sk::Transform,
mask: Option<&sk::ClipMask>, mask: Option<&sk::ClipMask>,
ctx: &mut Context, ctx: &Context,
text: &Text, text: &Text,
id: GlyphId, id: GlyphId,
) -> Option<()> { ) -> Option<()> {

View File

@ -21,10 +21,10 @@
//! [parsed]: parse::parse //! [parsed]: parse::parse
//! [green tree]: syntax::GreenNode //! [green tree]: syntax::GreenNode
//! [AST]: syntax::ast //! [AST]: syntax::ast
//! [evaluate]: eval::Eval //! [evaluate]: eval::evaluate
//! [module]: eval::Module //! [module]: eval::Module
//! [content]: model::Content //! [content]: model::Content
//! [layouted]: model::Content::layout //! [layouted]: model::layout
//! [PDF]: export::pdf //! [PDF]: export::pdf
#![allow(clippy::len_without_is_empty)] #![allow(clippy::len_without_is_empty)]
@ -52,19 +52,27 @@ pub mod source;
pub mod syntax; pub mod syntax;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use crate::diag::{StrResult, TypResult}; use crate::diag::TypResult;
use crate::eval::{Eval, Machine, Module, Scope, Scopes}; use crate::eval::{Module, Scope};
use crate::font::FontStore; use crate::font::FontStore;
use crate::frame::Frame; use crate::frame::Frame;
use crate::image::ImageStore; use crate::image::ImageStore;
use crate::loading::Loader; use crate::loading::Loader;
use crate::model::StyleMap; use crate::model::StyleMap;
use crate::source::{SourceId, SourceStore}; use crate::source::{SourceId, SourceStore};
use crate::util::PathExt;
/// Typeset a source file into a collection of layouted frames.
///
/// Returns either a vector of frames representing individual pages or
/// diagnostics in the form of a vector of error message with file and span
/// information.
pub fn typeset(ctx: &mut Context, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
let module = eval::evaluate(ctx, id, vec![])?;
model::layout(ctx, &module.content)
}
/// The core context which holds the configuration and stores. /// The core context which holds the configuration and stores.
pub struct Context { pub struct Context {
@ -78,8 +86,6 @@ pub struct Context {
pub config: Config, pub config: Config,
/// Cached modules. /// Cached modules.
modules: HashMap<SourceId, Module>, modules: HashMap<SourceId, Module>,
/// The stack of imported files that led to evaluation of the current file.
route: Vec<SourceId>,
/// The dependencies of the current evaluation process. /// The dependencies of the current evaluation process.
deps: Vec<(SourceId, usize)>, deps: Vec<(SourceId, usize)>,
} }
@ -93,90 +99,9 @@ impl Context {
images: ImageStore::new(loader), images: ImageStore::new(loader),
config, config,
modules: HashMap::new(), modules: HashMap::new(),
route: vec![],
deps: vec![], deps: vec![],
} }
} }
/// Evaluate a source file and return the resulting module.
///
/// Returns either a module containing a scope with top-level bindings and
/// layoutable contents or diagnostics in the form of a vector of error
/// messages with file and span information.
pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> {
// Prevent cyclic evaluation.
if self.route.contains(&id) {
let path = self.sources.get(id).path().display();
panic!("Tried to cyclicly evaluate {}", path);
}
// Check whether the module was already evaluated.
if let Some(module) = self.modules.get(&id) {
if module.valid(&self.sources) {
return Ok(module.clone());
} else {
self.modules.remove(&id);
}
}
// Parse the file.
let source = self.sources.get(id);
let ast = source.ast()?;
// Save the old dependencies and update the route.
let prev_deps = mem::replace(&mut self.deps, vec![(id, source.rev())]);
self.route.push(id);
// Evaluate the module.
let std = self.config.std.clone();
let scopes = Scopes::new(Some(&std));
let mut vm = Machine::new(self, scopes);
let result = ast.eval(&mut vm);
let scope = vm.scopes.top;
let flow = vm.flow;
// Restore the old route and dependencies.
self.route.pop().unwrap();
let deps = mem::replace(&mut self.deps, prev_deps);
// Handle control flow.
if let Some(flow) = flow {
return Err(flow.forbidden());
}
// Assemble the module.
let module = Module { scope, content: result?, deps };
// Save the evaluated module.
self.modules.insert(id, module.clone());
Ok(module)
}
/// Typeset a source file into a collection of layouted frames.
///
/// Returns either a vector of frames representing individual pages or
/// diagnostics in the form of a vector of error message with file and span
/// information.
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
self.evaluate(id)?.content.layout(self)
}
/// Resolve a user-entered path to be relative to the compilation
/// environment's root.
fn locate(&self, path: &str) -> StrResult<PathBuf> {
if let Some(&id) = self.route.last() {
if let Some(path) = path.strip_prefix('/') {
return Ok(self.config.root.join(path).normalize());
}
if let Some(dir) = self.sources.get(id).path().parent() {
return Ok(dir.join(path).normalize());
}
}
return Err("cannot access file system from here".into());
}
} }
/// Compilation configuration. /// Compilation configuration.

View File

@ -6,7 +6,7 @@ pub struct HideNode(pub LayoutNode);
#[node] #[node]
impl HideNode { impl HideNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::inline(Self(args.expect("body")?))) Ok(Content::inline(Self(args.expect("body")?)))
} }
} }

View File

@ -11,12 +11,12 @@ impl ImageNode {
/// How the image should adjust itself to a given area. /// How the image should adjust itself to a given area.
pub const FIT: ImageFit = ImageFit::Cover; pub const FIT: ImageFit = ImageFit::Cover;
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(vm: &mut Machine, args: &mut Args) -> TypResult<Content> {
let Spanned { v: path, span } = let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to image file")?; args.expect::<Spanned<EcoString>>("path to image file")?;
let full = ctx.locate(&path).at(span)?; let full = vm.locate(&path).at(span)?;
let id = ctx.images.load(&full).map_err(|err| match err.kind() { let id = vm.ctx.images.load(&full).map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => { std::io::ErrorKind::NotFound => {
error!(span, "file not found (searched at {})", full.display()) error!(span, "file not found (searched at {})", full.display())
} }

View File

@ -15,7 +15,7 @@ impl LineNode {
#[property(resolve, fold)] #[property(resolve, fold)]
pub const STROKE: RawStroke = RawStroke::default(); pub const STROKE: RawStroke = RawStroke::default();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let origin = args.named("origin")?.unwrap_or_default(); let origin = args.named("origin")?.unwrap_or_default();
let delta = match args.named::<Spec<Relative<RawLength>>>("to")? { let delta = match args.named::<Spec<Relative<RawLength>>>("to")? {

View File

@ -37,7 +37,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
#[property(skip, resolve, fold)] #[property(skip, resolve, fold)]
pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero()); pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let size = match S { let size = match S {
SQUARE => args.named::<RawLength>("size")?.map(Relative::from), SQUARE => args.named::<RawLength>("size")?.map(Relative::from),
CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)), CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)),

View File

@ -12,7 +12,7 @@ pub struct MoveNode {
#[node] #[node]
impl MoveNode { impl MoveNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let dx = args.named("dx")?.unwrap_or_default(); let dx = args.named("dx")?.unwrap_or_default();
let dy = args.named("dy")?.unwrap_or_default(); let dy = args.named("dy")?.unwrap_or_default();
Ok(Content::inline(Self { Ok(Content::inline(Self {
@ -62,7 +62,7 @@ impl<const T: TransformKind> TransformNode<T> {
#[property(resolve)] #[property(resolve)]
pub const ORIGIN: Spec<Option<RawAlign>> = Spec::default(); pub const ORIGIN: Spec<Option<RawAlign>> = Spec::default();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let transform = match T { let transform = match T {
ROTATE => { ROTATE => {
let angle = args.named_or_find("angle")?.unwrap_or_default(); let angle = args.named_or_find("angle")?.unwrap_or_default();

View File

@ -12,7 +12,7 @@ pub struct AlignNode {
#[node] #[node]
impl AlignNode { impl AlignNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let aligns: Spec<Option<RawAlign>> = args.find()?.unwrap_or_default(); let aligns: Spec<Option<RawAlign>> = args.find()?.unwrap_or_default();
let body: Content = args.expect("body")?; let body: Content = args.expect("body")?;
Ok(match (body, aligns) { Ok(match (body, aligns) {

View File

@ -17,7 +17,7 @@ impl ColumnsNode {
#[property(resolve)] #[property(resolve)]
pub const GUTTER: Relative<RawLength> = Ratio::new(0.04).into(); pub const GUTTER: Relative<RawLength> = Ratio::new(0.04).into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::block(Self { Ok(Content::block(Self {
columns: args.expect("column count")?, columns: args.expect("column count")?,
child: args.expect("body")?, child: args.expect("body")?,
@ -106,7 +106,7 @@ pub struct ColbreakNode;
#[node] #[node]
impl ColbreakNode { impl ColbreakNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Content::Colbreak { weak }) Ok(Content::Colbreak { weak })
} }

View File

@ -5,7 +5,7 @@ pub struct BoxNode;
#[node] #[node]
impl BoxNode { impl BoxNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let width = args.named("width")?; let width = args.named("width")?;
let height = args.named("height")?; let height = args.named("height")?;
let body: LayoutNode = args.eat()?.unwrap_or_default(); let body: LayoutNode = args.eat()?.unwrap_or_default();
@ -18,7 +18,7 @@ pub struct BlockNode;
#[node] #[node]
impl BlockNode { impl BlockNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::Block(args.eat()?.unwrap_or_default())) Ok(Content::Block(args.eat()?.unwrap_or_default()))
} }
} }

View File

@ -13,7 +13,7 @@ pub struct GridNode {
#[node] #[node]
impl GridNode { impl GridNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let columns = args.named("columns")?.unwrap_or_default(); let columns = args.named("columns")?.unwrap_or_default();
let rows = args.named("rows")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default();
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default(); let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();

View File

@ -11,7 +11,7 @@ pub struct PadNode {
#[node] #[node]
impl PadNode { impl PadNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let all = args.named("rest")?.or(args.find()?); let all = args.named("rest")?.or(args.find()?);
let x = args.named("x")?; let x = args.named("x")?;
let y = args.named("y")?; let y = args.named("y")?;

View File

@ -35,7 +35,7 @@ impl PageNode {
#[property(referenced)] #[property(referenced)]
pub const FOOTER: Marginal = Marginal::None; pub const FOOTER: Marginal = Marginal::None;
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::Page(Self(args.expect("body")?))) Ok(Content::Page(Self(args.expect("body")?)))
} }
@ -109,7 +109,7 @@ impl PageNode {
let w = size.x - padding.left - padding.right; let w = size.x - padding.left - padding.right;
let area = Size::new(w, h); let area = Size::new(w, h);
let pod = Regions::one(area, area, area.map(Length::is_finite)); let pod = Regions::one(area, area, area.map(Length::is_finite));
let sub = Layout::layout(&content, ctx, &pod, styles)?.remove(0); let sub = content.layout(ctx, &pod, styles)?.remove(0);
Arc::make_mut(frame).push_frame(pos, sub); Arc::make_mut(frame).push_frame(pos, sub);
} }
} }
@ -134,7 +134,7 @@ pub struct PagebreakNode;
#[node] #[node]
impl PagebreakNode { impl PagebreakNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Content::Pagebreak { weak }) Ok(Content::Pagebreak { weak })
} }
@ -158,8 +158,8 @@ impl Marginal {
Self::None => None, Self::None => None,
Self::Content(content) => Some(content.clone()), Self::Content(content) => Some(content.clone()),
Self::Func(func, span) => { Self::Func(func, span) => {
let args = Args::from_values(*span, [Value::Int(page as i64)]); let args = Args::new(*span, [Value::Int(page as i64)]);
Some(func.call(ctx, args)?.display()) Some(func.call_detached(ctx, args)?.display())
} }
}) })
} }

View File

@ -7,7 +7,7 @@ pub struct PlaceNode(pub LayoutNode);
#[node] #[node]
impl PlaceNode { impl PlaceNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let aligns = args.find()?.unwrap_or(Spec::with_x(Some(RawAlign::Start))); let aligns = args.find()?.unwrap_or(Spec::with_x(Some(RawAlign::Start)));
let dx = args.named("dx")?.unwrap_or_default(); let dx = args.named("dx")?.unwrap_or_default();
let dy = args.named("dy")?.unwrap_or_default(); let dy = args.named("dy")?.unwrap_or_default();

View File

@ -8,7 +8,7 @@ pub struct HNode;
#[node] #[node]
impl HNode { impl HNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let amount = args.expect("spacing")?; let amount = args.expect("spacing")?;
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Content::Horizontal { amount, weak }) Ok(Content::Horizontal { amount, weak })
@ -20,7 +20,7 @@ pub struct VNode;
#[node] #[node]
impl VNode { impl VNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let amount = args.expect("spacing")?; let amount = args.expect("spacing")?;
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Content::Vertical { amount, weak, generated: false }) Ok(Content::Vertical { amount, weak, generated: false })

View File

@ -15,7 +15,7 @@ pub struct StackNode {
#[node] #[node]
impl StackNode { impl StackNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::block(Self { Ok(Content::block(Self {
dir: args.named("dir")?.unwrap_or(Dir::TTB), dir: args.named("dir")?.unwrap_or(Dir::TTB),
spacing: args.named("spacing")?, spacing: args.named("spacing")?,

View File

@ -28,7 +28,7 @@ impl MathNode {
#[property(resolve, shorthand(around))] #[property(resolve, shorthand(around))]
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into()); pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self { Ok(Content::show(Self {
formula: args.expect("formula")?, formula: args.expect("formula")?,
display: args.named("display")?.unwrap_or(false), display: args.named("display")?.unwrap_or(false),

View File

@ -9,8 +9,8 @@ pub use typst_macros::node;
pub use crate::diag::{with_alternative, At, Error, StrResult, TypError, TypResult}; pub use crate::diag::{with_alternative, At, Error, StrResult, TypError, TypResult};
pub use crate::eval::{ pub use crate::eval::{
Arg, Args, Array, Cast, Dict, Func, Node, RawAlign, RawLength, RawStroke, Scope, Arg, Args, Array, Cast, Dict, Func, Machine, Node, RawAlign, RawLength, RawStroke,
Smart, Value, Scope, Smart, Value,
}; };
pub use crate::frame::*; pub use crate::frame::*;
pub use crate::geom::*; pub use crate::geom::*;

View File

@ -55,7 +55,7 @@ impl HeadingNode {
pub const BELOW: Leveled<Option<BlockSpacing>> = pub const BELOW: Leveled<Option<BlockSpacing>> =
Leveled::Value(Some(Ratio::new(0.55).into())); Leveled::Value(Some(Ratio::new(0.55).into()));
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self { Ok(Content::show(Self {
body: args.expect("body")?, body: args.expect("body")?,
level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()), level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
@ -142,8 +142,8 @@ impl<T: Cast + Clone> Leveled<T> {
Self::Value(value) => value.clone(), Self::Value(value) => value.clone(),
Self::Mapping(mapping) => mapping(level), Self::Mapping(mapping) => mapping(level),
Self::Func(func, span) => { Self::Func(func, span) => {
let args = Args::from_values(*span, [Value::Int(level.get() as i64)]); let args = Args::new(*span, [Value::Int(level.get() as i64)]);
func.call(ctx, args)?.cast().at(*span)? func.call_detached(ctx, args)?.cast().at(*span)?
} }
}) })
} }

View File

@ -56,7 +56,7 @@ impl<const L: ListKind> ListNode<L> {
#[property(resolve)] #[property(resolve)]
pub const SPACING: BlockSpacing = Ratio::one().into(); pub const SPACING: BlockSpacing = Ratio::one().into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self { Ok(Content::show(Self {
start: args.named("start")?.unwrap_or(1), start: args.named("start")?.unwrap_or(1),
tight: args.named("tight")?.unwrap_or(true), tight: args.named("tight")?.unwrap_or(true),
@ -216,8 +216,8 @@ impl Label {
} }
Self::Content(content) => content.clone(), Self::Content(content) => content.clone(),
Self::Func(func, span) => { Self::Func(func, span) => {
let args = Args::from_values(*span, [Value::Int(number as i64)]); let args = Args::new(*span, [Value::Int(number as i64)]);
func.call(ctx, args)?.display() func.call_detached(ctx, args)?.display()
} }
}) })
} }

View File

@ -30,7 +30,7 @@ impl TableNode {
#[property(resolve, shorthand(around))] #[property(resolve, shorthand(around))]
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into()); pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let columns = args.named("columns")?.unwrap_or_default(); let columns = args.named("columns")?.unwrap_or_default();
let rows = args.named("rows")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default();
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default(); let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
@ -128,11 +128,8 @@ impl<T: Cast + Clone> Celled<T> {
Ok(match self { Ok(match self {
Self::Value(value) => value.clone(), Self::Value(value) => value.clone(),
Self::Func(func, span) => { Self::Func(func, span) => {
let args = Args::from_values(*span, [ let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
Value::Int(x as i64), func.call_detached(ctx, args)?.cast().at(*span)?
Value::Int(y as i64),
]);
func.call(ctx, args)?.cast().at(*span)?
} }
}) })
} }

View File

@ -35,7 +35,7 @@ impl<const L: DecoLine> DecoNode<L> {
/// with the glyphs. Does not apply to strikethrough. /// with the glyphs. Does not apply to strikethrough.
pub const EVADE: bool = true; pub const EVADE: bool = true;
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self(args.expect("body")?))) Ok(Content::show(Self(args.expect("body")?)))
} }
} }

View File

@ -19,7 +19,7 @@ impl LinkNode {
/// Whether to underline link. /// Whether to underline link.
pub const UNDERLINE: bool = true; pub const UNDERLINE: bool = true;
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self { Ok(Content::show(Self {
url: args.expect::<EcoString>("url")?, url: args.expect::<EcoString>("url")?,
body: args.eat()?, body: args.eat()?,

View File

@ -127,7 +127,7 @@ impl TextNode {
#[property(skip, fold)] #[property(skip, fold)]
pub const DECO: Decoration = vec![]; pub const DECO: Decoration = vec![];
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
// The text constructor is special: It doesn't create a text node. // The text constructor is special: It doesn't create a text node.
// Instead, it leaves the passed argument structurally unchanged, but // Instead, it leaves the passed argument structurally unchanged, but
// styles all text in it. // styles all text in it.
@ -443,12 +443,12 @@ impl Fold for Vec<(Tag, u32)> {
} }
/// Convert text to lowercase. /// Convert text to lowercase.
pub fn lower(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn lower(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
case(Case::Lower, args) case(Case::Lower, args)
} }
/// Convert text to uppercase. /// Convert text to uppercase.
pub fn upper(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn upper(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
case(Case::Upper, args) case(Case::Upper, args)
} }
@ -482,7 +482,7 @@ impl Case {
} }
/// Display text in small capitals. /// Display text in small capitals.
pub fn smallcaps(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn smallcaps(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let body: Content = args.expect("content")?; let body: Content = args.expect("content")?;
Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true))) Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
} }
@ -514,7 +514,7 @@ pub struct StrongNode(pub Content);
#[node(showable)] #[node(showable)]
impl StrongNode { impl StrongNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self(args.expect("body")?))) Ok(Content::show(Self(args.expect("body")?)))
} }
} }
@ -539,7 +539,7 @@ pub struct EmphNode(pub Content);
#[node(showable)] #[node(showable)]
impl EmphNode { impl EmphNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self(args.expect("body")?))) Ok(Content::show(Self(args.expect("body")?)))
} }
} }

View File

@ -51,7 +51,7 @@ impl ParNode {
#[property(resolve)] #[property(resolve)]
pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto; pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto;
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
// The paragraph constructor is special: It doesn't create a paragraph // The paragraph constructor is special: It doesn't create a paragraph
// node. Instead, it just ensures that the passed content lives is in a // node. Instead, it just ensures that the passed content lives is in a
// separate paragraph and styles it. // separate paragraph and styles it.
@ -172,7 +172,7 @@ pub struct ParbreakNode;
#[node] #[node]
impl ParbreakNode { impl ParbreakNode {
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, _: &mut Args) -> TypResult<Content> {
Ok(Content::Parbreak) Ok(Content::Parbreak)
} }
} }
@ -182,7 +182,7 @@ pub struct LinebreakNode;
#[node] #[node]
impl LinebreakNode { impl LinebreakNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
let justified = args.named("justified")?.unwrap_or(false); let justified = args.named("justified")?.unwrap_or(false);
Ok(Content::Linebreak { justified }) Ok(Content::Linebreak { justified })
} }

View File

@ -35,7 +35,7 @@ impl RawNode {
#[property(resolve, shorthand(around))] #[property(resolve, shorthand(around))]
pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into()); pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self { Ok(Content::show(Self {
text: args.expect("text")?, text: args.expect("text")?,
block: args.named("block")?.unwrap_or(false), block: args.named("block")?.unwrap_or(false),

View File

@ -6,7 +6,7 @@ pub struct RepeatNode(pub LayoutNode);
#[node] #[node]
impl RepeatNode { impl RepeatNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::inline(Self(args.expect("body")?))) Ok(Content::inline(Self(args.expect("body")?)))
} }
} }

View File

@ -3,7 +3,7 @@ use std::str::FromStr;
use crate::library::prelude::*; use crate::library::prelude::*;
/// Create an RGB(A) color. /// Create an RGB(A) color.
pub fn rgb(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn rgb(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
Ok(Value::from( Ok(Value::from(
if let Some(string) = args.find::<Spanned<EcoString>>()? { if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) { match RgbaColor::from_str(&string.v) {
@ -37,7 +37,7 @@ pub fn rgb(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// Create a CMYK color. /// Create a CMYK color.
pub fn cmyk(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn cmyk(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
struct Component(u8); struct Component(u8);
castable! { castable! {

View File

@ -3,7 +3,7 @@ use std::cmp::Ordering;
use crate::library::prelude::*; use crate::library::prelude::*;
/// Convert a value to a integer. /// Convert a value to a integer.
pub fn int(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn int(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect("value")?; let Spanned { v, span } = args.expect("value")?;
Ok(Value::Int(match v { Ok(Value::Int(match v {
Value::Bool(v) => v as i64, Value::Bool(v) => v as i64,
@ -18,7 +18,7 @@ pub fn int(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// Convert a value to a float. /// Convert a value to a float.
pub fn float(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn float(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect("value")?; let Spanned { v, span } = args.expect("value")?;
Ok(Value::Float(match v { Ok(Value::Float(match v {
Value::Int(v) => v as f64, Value::Int(v) => v as f64,
@ -32,7 +32,7 @@ pub fn float(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// The absolute value of a numeric value. /// The absolute value of a numeric value.
pub fn abs(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn abs(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect("numeric value")?; let Spanned { v, span } = args.expect("numeric value")?;
Ok(match v { Ok(match v {
Value::Int(v) => Value::Int(v.abs()), Value::Int(v) => Value::Int(v.abs()),
@ -48,12 +48,12 @@ pub fn abs(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// The minimum of a sequence of values. /// The minimum of a sequence of values.
pub fn min(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn min(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
minmax(args, Ordering::Less) minmax(args, Ordering::Less)
} }
/// The maximum of a sequence of values. /// The maximum of a sequence of values.
pub fn max(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn max(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
minmax(args, Ordering::Greater) minmax(args, Ordering::Greater)
} }
@ -79,17 +79,17 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
} }
/// Whether an integer is even. /// Whether an integer is even.
pub fn even(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn even(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0)) Ok(Value::Bool(args.expect::<i64>("integer")? % 2 == 0))
} }
/// Whether an integer is odd. /// Whether an integer is odd.
pub fn odd(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn odd(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0)) Ok(Value::Bool(args.expect::<i64>("integer")? % 2 != 0))
} }
/// The modulo of two numbers. /// The modulo of two numbers.
pub fn mod_(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn mod_(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?; let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?; let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
@ -119,7 +119,7 @@ pub fn mod_(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// Create a sequence of numbers. /// Create a sequence of numbers.
pub fn range(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn range(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let first = args.expect::<i64>("end")?; let first = args.expect::<i64>("end")?;
let (start, end) = match args.eat::<i64>()? { let (start, end) = match args.eat::<i64>()? {
Some(second) => (first, second), Some(second) => (first, second),

View File

@ -8,19 +8,17 @@ pub use color::*;
pub use math::*; pub use math::*;
pub use string::*; pub use string::*;
use std::mem;
use crate::eval::{Eval, Machine, Scopes}; use crate::eval::{Eval, Machine, Scopes};
use crate::library::prelude::*; use crate::library::prelude::*;
use crate::source::SourceFile; use crate::source::SourceFile;
/// The name of a value's type. /// The name of a value's type.
pub fn type_(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn type_(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
Ok(args.expect::<Value>("value")?.type_name().into()) Ok(args.expect::<Value>("value")?.type_name().into())
} }
/// Ensure that a condition is fulfilled. /// Ensure that a condition is fulfilled.
pub fn assert(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn assert(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?; let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
if !v { if !v {
bail!(span, "assertion failed"); bail!(span, "assertion failed");
@ -29,28 +27,21 @@ pub fn assert(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// Evaluate a string as Typst markup. /// Evaluate a string as Typst markup.
pub fn eval(ctx: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn eval(vm: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v: src, span } = args.expect::<Spanned<String>>("source")?; let Spanned { v: src, span } = args.expect::<Spanned<String>>("source")?;
// Parse the source and set a synthetic span for all nodes. // Parse the source and set a synthetic span for all nodes.
let source = SourceFile::synthesized(src, span); let source = SourceFile::synthesized(src, span);
let ast = source.ast()?; let ast = source.ast()?;
// Save the old route, then detach it.
let prev_route = mem::take(&mut ctx.route);
// Evaluate the source. // Evaluate the source.
let std = ctx.config.std.clone(); let std = vm.ctx.config.std.clone();
let scopes = Scopes::new(Some(&std)); let scopes = Scopes::new(Some(&std));
let mut vm = Machine::new(ctx, scopes); let mut sub = Machine::new(vm.ctx, vec![], scopes);
let result = ast.eval(&mut vm); let result = ast.eval(&mut sub);
let flow = vm.flow;
// Restore the old route.
ctx.route = prev_route;
// Handle control flow. // Handle control flow.
if let Some(flow) = flow { if let Some(flow) = sub.flow {
return Err(flow.forbidden()); return Err(flow.forbidden());
} }

View File

@ -4,12 +4,12 @@ use crate::eval::Regex;
use crate::library::prelude::*; use crate::library::prelude::*;
/// The string representation of a value. /// The string representation of a value.
pub fn repr(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn repr(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
Ok(args.expect::<Value>("value")?.repr().into()) Ok(args.expect::<Value>("value")?.repr().into())
} }
/// Cconvert a value to a string. /// Cconvert a value to a string.
pub fn str(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn str(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect("value")?; let Spanned { v, span } = args.expect("value")?;
Ok(Value::Str(match v { Ok(Value::Str(match v {
Value::Int(v) => format_eco!("{}", v), Value::Int(v) => format_eco!("{}", v),
@ -20,29 +20,29 @@ pub fn str(_: &mut Context, args: &mut Args) -> TypResult<Value> {
} }
/// Create blind text. /// Create blind text.
pub fn lorem(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn lorem(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let words: usize = args.expect("number of words")?; let words: usize = args.expect("number of words")?;
Ok(Value::Str(lipsum_from_seed(words, 97).into())) Ok(Value::Str(lipsum_from_seed(words, 97).into()))
} }
/// Create a regular expression. /// Create a regular expression.
pub fn regex(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn regex(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?; let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
Ok(Regex::new(&v).at(span)?.into()) Ok(Regex::new(&v).at(span)?.into())
} }
/// Converts an integer into one or multiple letters. /// Converts an integer into one or multiple letters.
pub fn letter(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn letter(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
convert(Numbering::Letter, args) convert(Numbering::Letter, args)
} }
/// Converts an integer into a roman numeral. /// Converts an integer into a roman numeral.
pub fn roman(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn roman(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
convert(Numbering::Roman, args) convert(Numbering::Roman, args)
} }
/// Convert a number into a symbol. /// Convert a number into a symbol.
pub fn symbol(_: &mut Context, args: &mut Args) -> TypResult<Value> { pub fn symbol(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
convert(Numbering::Symbol, args) convert(Numbering::Symbol, args)
} }

View File

@ -214,7 +214,7 @@ fn typeset(command: TypesetCommand) -> StrResult<()> {
.map_err(|_| "failed to load source file")?; .map_err(|_| "failed to load source file")?;
// Typeset. // Typeset.
match ctx.typeset(id) { match typst::typeset(&mut ctx, id) {
// Export the PDF. // Export the PDF.
Ok(frames) => { Ok(frames) => {
let buffer = export::pdf(&ctx, &frames); let buffer = export::pdf(&ctx, &frames);

View File

@ -19,6 +19,19 @@ use crate::library::text::{
}; };
use crate::util::EcoString; use crate::util::EcoString;
/// Layout content into a collection of pages.
pub fn layout(ctx: &mut Context, content: &Content) -> TypResult<Vec<Arc<Frame>>> {
let copy = ctx.config.styles.clone();
let styles = StyleChain::with_root(&copy);
let scratch = Scratch::default();
let mut builder = Builder::new(ctx, &scratch, true);
builder.accept(content, styles)?;
let (doc, shared) = builder.into_doc(styles)?;
doc.layout(ctx, shared)
}
/// Composable representation of styled content. /// Composable representation of styled content.
/// ///
/// This results from: /// This results from:
@ -207,19 +220,6 @@ impl Content {
Self::sequence(seq) Self::sequence(seq)
} }
/// Layout this content into a collection of pages.
pub fn layout(&self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> {
let copy = ctx.config.styles.clone();
let styles = StyleChain::with_root(&copy);
let scratch = Scratch::default();
let mut builder = Builder::new(ctx, &scratch, true);
builder.accept(self, styles)?;
let (doc, shared) = builder.into_doc(styles)?;
doc.layout(ctx, shared)
}
} }
impl Layout for Content { impl Layout for Content {

View File

@ -1,4 +1,4 @@
//! Structured representation of styled content. //! Styled and structured representation of layoutable content.
#[macro_use] #[macro_use]
mod styles; mod styles;

View File

@ -82,12 +82,12 @@ impl Recipe {
F: FnOnce() -> Value, F: FnOnce() -> Value,
{ {
let args = if self.func.argc() == Some(0) { let args = if self.func.argc() == Some(0) {
Args::new(self.span) Args::new(self.span, [])
} else { } else {
Args::from_values(self.span, [arg()]) Args::new(self.span, [arg()])
}; };
Ok(self.func.call(ctx, args)?.display()) Ok(self.func.call_detached(ctx, args)?.display())
} }
/// What kind of structure the property interrupts. /// What kind of structure the property interrupts.

View File

@ -290,7 +290,7 @@ fn test_part(
ok &= test_reparse(ctx.sources.get(id).src(), i, rng); ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
let (mut frames, mut errors) = match ctx.typeset(id) { let (mut frames, mut errors) = match typst::typeset(ctx, id) {
Ok(frames) => (frames, vec![]), Ok(frames) => (frames, vec![]),
Err(errors) => (vec![], *errors), Err(errors) => (vec![], *errors),
}; };