Move route from context to VM
This commit is contained in:
parent
6935cf8dfe
commit
c010cbc17d
@ -4,10 +4,10 @@ use std::sync::Arc;
|
||||
use iai::{black_box, main, Iai};
|
||||
use unscanny::Scanner;
|
||||
|
||||
use typst::eval::evaluate;
|
||||
use typst::loading::MemLoader;
|
||||
use typst::parse::{parse, TokenMode, Tokens};
|
||||
use typst::parse::{TokenMode, Tokens};
|
||||
use typst::source::SourceId;
|
||||
use typst::syntax::highlight_node;
|
||||
use typst::{Config, Context};
|
||||
|
||||
const SRC: &str = include_str!("bench.typ");
|
||||
@ -61,7 +61,7 @@ fn bench_tokenize(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) {
|
||||
@ -73,7 +73,7 @@ fn bench_highlight(iai: &mut Iai) {
|
||||
let (ctx, id) = context();
|
||||
let source = ctx.sources.get(id);
|
||||
iai.run(|| {
|
||||
highlight_node(
|
||||
typst::syntax::highlight_node(
|
||||
source.red().as_ref(),
|
||||
0 .. source.len_bytes(),
|
||||
&mut |_, _| {},
|
||||
@ -83,17 +83,17 @@ fn bench_highlight(iai: &mut Iai) {
|
||||
|
||||
fn bench_eval(iai: &mut Iai) {
|
||||
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) {
|
||||
let (mut ctx, id) = context();
|
||||
let module = ctx.evaluate(id).unwrap();
|
||||
iai.run(|| module.content.layout(&mut ctx));
|
||||
let module = evaluate(&mut ctx, id, vec![]).unwrap();
|
||||
iai.run(|| typst::model::layout(&mut ctx, &module.content));
|
||||
}
|
||||
|
||||
fn bench_render(iai: &mut Iai) {
|
||||
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))
|
||||
}
|
||||
|
@ -26,13 +26,8 @@ pub struct Arg {
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
.into_iter()
|
||||
.map(|value| Arg {
|
||||
|
@ -3,11 +3,10 @@ use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::ops::{Add, AddAssign};
|
||||
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::syntax::Spanned;
|
||||
use crate::util::ArcExt;
|
||||
use crate::Context;
|
||||
|
||||
/// Create a new [`Array`] from values.
|
||||
#[allow(unused_macros)]
|
||||
@ -120,21 +119,21 @@ impl Array {
|
||||
}
|
||||
|
||||
/// 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
|
||||
.iter()
|
||||
.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<_>>()?)
|
||||
}
|
||||
|
||||
/// Return a new array with only those elements for which the function
|
||||
/// 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![];
|
||||
for item in self.iter() {
|
||||
if f.v
|
||||
.call(ctx, Args::from_values(f.span, [item.clone()]))?
|
||||
.call(vm, Args::new(f.span, [item.clone()]))?
|
||||
.cast::<bool>()
|
||||
.at(f.span)?
|
||||
{
|
||||
|
@ -3,12 +3,11 @@ use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Args, Array, Func, Value};
|
||||
use super::{Args, Array, Func, Machine, Value};
|
||||
use crate::diag::{StrResult, TypResult};
|
||||
use crate::parse::is_ident;
|
||||
use crate::syntax::Spanned;
|
||||
use crate::util::{ArcExt, EcoString};
|
||||
use crate::Context;
|
||||
|
||||
/// Create a new [`Dict`] from key-value pairs.
|
||||
#[allow(unused_macros)]
|
||||
@ -97,14 +96,12 @@ impl Dict {
|
||||
}
|
||||
|
||||
/// 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
|
||||
.iter()
|
||||
.map(|(key, value)| {
|
||||
f.v.call(
|
||||
ctx,
|
||||
Args::from_values(f.span, [Value::Str(key.clone()), value.clone()]),
|
||||
)
|
||||
let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]);
|
||||
f.v.call(vm, args)
|
||||
})
|
||||
.collect::<TypResult<_>>()?)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl Func {
|
||||
/// Create a new function from a native rust function.
|
||||
pub fn from_fn(
|
||||
name: &'static str,
|
||||
func: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
|
||||
) -> Self {
|
||||
Self(Arc::new(Repr::Native(Native {
|
||||
name,
|
||||
@ -86,19 +86,25 @@ impl Func {
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
Repr::Native(native) => (native.func)(ctx, &mut args)?,
|
||||
Repr::Closure(closure) => closure.call(ctx, &mut args)?,
|
||||
Repr::Native(native) => (native.func)(vm, &mut args)?,
|
||||
Repr::Closure(closure) => closure.call(vm, &mut args)?,
|
||||
Repr::With(wrapped, applied) => {
|
||||
args.items.splice(.. 0, applied.items.iter().cloned());
|
||||
return wrapped.call(ctx, args);
|
||||
return wrapped.call(vm, args);
|
||||
}
|
||||
};
|
||||
args.finish()?;
|
||||
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.
|
||||
pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
|
||||
let styles = match self.0.as_ref() {
|
||||
@ -138,7 +144,7 @@ struct Native {
|
||||
/// The name of the function.
|
||||
pub name: &'static str,
|
||||
/// The function pointer.
|
||||
pub func: fn(&mut Context, &mut Args) -> TypResult<Value>,
|
||||
pub func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
|
||||
/// The set rule.
|
||||
pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>,
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
@ -192,7 +198,7 @@ pub struct Closure {
|
||||
|
||||
impl Closure {
|
||||
/// 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
|
||||
// scope of captured variables we collected earlier.
|
||||
let mut scopes = Scopes::new(None);
|
||||
@ -213,24 +219,20 @@ impl Closure {
|
||||
scopes.top.def_mut(sink, args.take());
|
||||
}
|
||||
|
||||
// Set the new route if we are detached.
|
||||
let detached = ctx.route.is_empty();
|
||||
if detached {
|
||||
ctx.route = self.location.into_iter().collect();
|
||||
}
|
||||
// Determine the route inside the closure.
|
||||
let detached = vm.route.is_empty();
|
||||
let route = if detached {
|
||||
self.location.into_iter().collect()
|
||||
} else {
|
||||
vm.route.clone()
|
||||
};
|
||||
|
||||
// Evaluate the body.
|
||||
let mut vm = Machine::new(ctx, scopes);
|
||||
let result = self.body.eval(&mut vm);
|
||||
let flow = vm.flow;
|
||||
|
||||
// Restore the old route.
|
||||
if detached {
|
||||
ctx.route.clear();
|
||||
}
|
||||
let mut sub = Machine::new(vm.ctx, route, scopes);
|
||||
let result = self.body.eval(&mut sub);
|
||||
|
||||
// Handle control flow.
|
||||
match flow {
|
||||
match sub.flow {
|
||||
Some(Flow::Return(_, Some(explicit))) => return Ok(explicit),
|
||||
Some(Flow::Return(_, None)) => {}
|
||||
Some(flow) => return Err(flow.forbidden())?,
|
||||
|
@ -1,12 +1,18 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Scopes, Value};
|
||||
use crate::diag::TypError;
|
||||
use crate::diag::{StrResult, TypError};
|
||||
use crate::source::SourceId;
|
||||
use crate::syntax::Span;
|
||||
use crate::util::PathExt;
|
||||
use crate::Context;
|
||||
|
||||
/// A virtual machine.
|
||||
pub struct Machine<'a> {
|
||||
/// The core 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.
|
||||
pub scopes: Scopes<'a>,
|
||||
/// A control flow event that is currently happening.
|
||||
@ -15,8 +21,24 @@ pub struct Machine<'a> {
|
||||
|
||||
impl<'a> Machine<'a> {
|
||||
/// Create a new virtual machine.
|
||||
pub fn new(ctx: &'a mut Context, scopes: Scopes<'a>) -> Self {
|
||||
Self { ctx, scopes, flow: None }
|
||||
pub fn new(ctx: &'a mut Context, route: Vec<SourceId>, scopes: Scopes<'a>) -> Self {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
//! Methods on values.
|
||||
|
||||
use super::{Args, Regex, StrExt, Value};
|
||||
use super::{Args, Machine, Regex, StrExt, Value};
|
||||
use crate::diag::{At, TypResult};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::EcoString;
|
||||
use crate::Context;
|
||||
|
||||
/// Call a method on a value.
|
||||
pub fn call(
|
||||
ctx: &mut Context,
|
||||
vm: &mut Machine,
|
||||
value: Value,
|
||||
method: &str,
|
||||
mut args: Args,
|
||||
@ -35,8 +34,8 @@ pub fn call(
|
||||
}
|
||||
Value::Array(array.slice(start, end).at(span)?)
|
||||
}
|
||||
"map" => Value::Array(array.map(ctx, args.expect("function")?)?),
|
||||
"filter" => Value::Array(array.filter(ctx, args.expect("function")?)?),
|
||||
"map" => Value::Array(array.map(vm, args.expect("function")?)?),
|
||||
"filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
|
||||
"flatten" => Value::Array(array.flatten()),
|
||||
"find" => array.find(args.expect("value")?).map_or(Value::None, Value::Int),
|
||||
"join" => {
|
||||
@ -52,7 +51,7 @@ pub fn call(
|
||||
"len" => Value::Int(dict.len()),
|
||||
"keys" => Value::Array(dict.keys()),
|
||||
"values" => Value::Array(dict.values()),
|
||||
"pairs" => Value::Array(dict.map(ctx, args.expect("function")?)?),
|
||||
"pairs" => Value::Array(dict.map(vm, args.expect("function")?)?),
|
||||
_ => missing()?,
|
||||
},
|
||||
|
||||
|
@ -43,13 +43,63 @@ use crate::syntax::{Span, Spanned};
|
||||
use crate::util::EcoString;
|
||||
use crate::Context;
|
||||
|
||||
/// Evaluate an expression.
|
||||
pub trait Eval {
|
||||
/// The output of evaluating the expression.
|
||||
type Output;
|
||||
/// 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(
|
||||
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.
|
||||
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output>;
|
||||
// Check whether the module was already evaluated.
|
||||
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.
|
||||
@ -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 {
|
||||
type Output = Content;
|
||||
|
||||
@ -553,7 +612,7 @@ impl Eval for FuncCall {
|
||||
Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(),
|
||||
Value::Func(func) => {
|
||||
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!(
|
||||
@ -581,7 +640,7 @@ impl Eval for MethodCall {
|
||||
} else {
|
||||
let value = self.receiver().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.
|
||||
Ok(Value::Func(Func::from_closure(Closure {
|
||||
location: vm.ctx.route.last().copied(),
|
||||
location: vm.route.last().copied(),
|
||||
name,
|
||||
captured,
|
||||
params,
|
||||
@ -731,7 +790,7 @@ impl Eval for ShowExpr {
|
||||
let body = self.body();
|
||||
let span = body.span();
|
||||
let func = Func::from_closure(Closure {
|
||||
location: vm.ctx.route.last().copied(),
|
||||
location: vm.route.last().copied(),
|
||||
name: None,
|
||||
captured,
|
||||
params,
|
||||
@ -875,7 +934,7 @@ impl Eval for ImportExpr {
|
||||
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
|
||||
let span = self.path().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() {
|
||||
Imports::Wildcard => {
|
||||
@ -904,16 +963,16 @@ impl Eval for IncludeExpr {
|
||||
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
|
||||
let span = self.path().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())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
let full = ctx.locate(&path).at(span)?;
|
||||
let id = ctx.sources.load(&full).map_err(|err| match err.kind() {
|
||||
let full = vm.locate(&path).at(span)?;
|
||||
let id = vm.ctx.sources.load(&full).map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
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.
|
||||
if ctx.route.contains(&id) {
|
||||
if vm.route.contains(&id) {
|
||||
bail!(span, "cyclic import");
|
||||
}
|
||||
|
||||
// Evaluate the file.
|
||||
let module = ctx.evaluate(id).trace(|| Tracepoint::Import, span)?;
|
||||
ctx.deps.extend(module.deps.iter().cloned());
|
||||
let route = vm.route.clone();
|
||||
let module = evaluate(vm.ctx, id, route).trace(|| Tracepoint::Import, span)?;
|
||||
vm.ctx.deps.extend(module.deps.iter().cloned());
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,9 @@ use std::sync::Arc;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{Args, Func, Node, Value};
|
||||
use super::{Args, Func, Machine, Node, Value};
|
||||
use crate::diag::TypResult;
|
||||
use crate::util::EcoString;
|
||||
use crate::Context;
|
||||
|
||||
/// A slot where a variable is stored.
|
||||
pub type Slot = Arc<RwLock<Value>>;
|
||||
@ -89,7 +88,7 @@ impl Scope {
|
||||
pub fn def_fn(
|
||||
&mut self,
|
||||
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));
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use crate::Context;
|
||||
/// 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
|
||||
/// 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 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,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
frame: &Frame,
|
||||
) {
|
||||
for (pos, element) in &frame.elements {
|
||||
@ -72,7 +72,7 @@ fn render_group(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
group: &Group,
|
||||
) {
|
||||
let ts = ts.pre_concat(group.transform.into());
|
||||
@ -114,7 +114,7 @@ fn render_text(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
text: &Text,
|
||||
) {
|
||||
let mut x = 0.0;
|
||||
@ -136,7 +136,7 @@ fn render_svg_glyph(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
_: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
text: &Text,
|
||||
id: GlyphId,
|
||||
) -> Option<()> {
|
||||
@ -187,7 +187,7 @@ fn render_bitmap_glyph(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
text: &Text,
|
||||
id: GlyphId,
|
||||
) -> Option<()> {
|
||||
@ -213,7 +213,7 @@ fn render_outline_glyph(
|
||||
canvas: &mut sk::Pixmap,
|
||||
ts: sk::Transform,
|
||||
mask: Option<&sk::ClipMask>,
|
||||
ctx: &mut Context,
|
||||
ctx: &Context,
|
||||
text: &Text,
|
||||
id: GlyphId,
|
||||
) -> Option<()> {
|
||||
|
103
src/lib.rs
103
src/lib.rs
@ -21,10 +21,10 @@
|
||||
//! [parsed]: parse::parse
|
||||
//! [green tree]: syntax::GreenNode
|
||||
//! [AST]: syntax::ast
|
||||
//! [evaluate]: eval::Eval
|
||||
//! [evaluate]: eval::evaluate
|
||||
//! [module]: eval::Module
|
||||
//! [content]: model::Content
|
||||
//! [layouted]: model::Content::layout
|
||||
//! [layouted]: model::layout
|
||||
//! [PDF]: export::pdf
|
||||
|
||||
#![allow(clippy::len_without_is_empty)]
|
||||
@ -52,19 +52,27 @@ pub mod source;
|
||||
pub mod syntax;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::diag::{StrResult, TypResult};
|
||||
use crate::eval::{Eval, Machine, Module, Scope, Scopes};
|
||||
use crate::diag::TypResult;
|
||||
use crate::eval::{Module, Scope};
|
||||
use crate::font::FontStore;
|
||||
use crate::frame::Frame;
|
||||
use crate::image::ImageStore;
|
||||
use crate::loading::Loader;
|
||||
use crate::model::StyleMap;
|
||||
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.
|
||||
pub struct Context {
|
||||
@ -78,8 +86,6 @@ pub struct Context {
|
||||
pub config: Config,
|
||||
/// Cached modules.
|
||||
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.
|
||||
deps: Vec<(SourceId, usize)>,
|
||||
}
|
||||
@ -93,90 +99,9 @@ impl Context {
|
||||
images: ImageStore::new(loader),
|
||||
config,
|
||||
modules: HashMap::new(),
|
||||
route: 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.
|
||||
|
@ -6,7 +6,7 @@ pub struct HideNode(pub LayoutNode);
|
||||
|
||||
#[node]
|
||||
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")?)))
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,12 @@ impl ImageNode {
|
||||
/// How the image should adjust itself to a given area.
|
||||
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 } =
|
||||
args.expect::<Spanned<EcoString>>("path to image file")?;
|
||||
|
||||
let full = ctx.locate(&path).at(span)?;
|
||||
let id = ctx.images.load(&full).map_err(|err| match err.kind() {
|
||||
let full = vm.locate(&path).at(span)?;
|
||||
let id = vm.ctx.images.load(&full).map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
error!(span, "file not found (searched at {})", full.display())
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ impl LineNode {
|
||||
#[property(resolve, fold)]
|
||||
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 delta = match args.named::<Spec<Relative<RawLength>>>("to")? {
|
||||
|
@ -37,7 +37,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
||||
#[property(skip, resolve, fold)]
|
||||
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 {
|
||||
SQUARE => args.named::<RawLength>("size")?.map(Relative::from),
|
||||
CIRCLE => args.named::<RawLength>("radius")?.map(|r| 2.0 * Relative::from(r)),
|
||||
|
@ -12,7 +12,7 @@ pub struct MoveNode {
|
||||
|
||||
#[node]
|
||||
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 dy = args.named("dy")?.unwrap_or_default();
|
||||
Ok(Content::inline(Self {
|
||||
@ -62,7 +62,7 @@ impl<const T: TransformKind> TransformNode<T> {
|
||||
#[property(resolve)]
|
||||
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 {
|
||||
ROTATE => {
|
||||
let angle = args.named_or_find("angle")?.unwrap_or_default();
|
||||
|
@ -12,7 +12,7 @@ pub struct AlignNode {
|
||||
|
||||
#[node]
|
||||
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 body: Content = args.expect("body")?;
|
||||
Ok(match (body, aligns) {
|
||||
|
@ -17,7 +17,7 @@ impl ColumnsNode {
|
||||
#[property(resolve)]
|
||||
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 {
|
||||
columns: args.expect("column count")?,
|
||||
child: args.expect("body")?,
|
||||
@ -106,7 +106,7 @@ pub struct ColbreakNode;
|
||||
|
||||
#[node]
|
||||
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);
|
||||
Ok(Content::Colbreak { weak })
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ pub struct BoxNode;
|
||||
|
||||
#[node]
|
||||
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 height = args.named("height")?;
|
||||
let body: LayoutNode = args.eat()?.unwrap_or_default();
|
||||
@ -18,7 +18,7 @@ pub struct BlockNode;
|
||||
|
||||
#[node]
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ pub struct GridNode {
|
||||
|
||||
#[node]
|
||||
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 rows = args.named("rows")?.unwrap_or_default();
|
||||
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
||||
|
@ -11,7 +11,7 @@ pub struct PadNode {
|
||||
|
||||
#[node]
|
||||
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 x = args.named("x")?;
|
||||
let y = args.named("y")?;
|
||||
|
@ -35,7 +35,7 @@ impl PageNode {
|
||||
#[property(referenced)]
|
||||
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")?)))
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ impl PageNode {
|
||||
let w = size.x - padding.left - padding.right;
|
||||
let area = Size::new(w, h);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ pub struct PagebreakNode;
|
||||
|
||||
#[node]
|
||||
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);
|
||||
Ok(Content::Pagebreak { weak })
|
||||
}
|
||||
@ -158,8 +158,8 @@ impl Marginal {
|
||||
Self::None => None,
|
||||
Self::Content(content) => Some(content.clone()),
|
||||
Self::Func(func, span) => {
|
||||
let args = Args::from_values(*span, [Value::Int(page as i64)]);
|
||||
Some(func.call(ctx, args)?.display())
|
||||
let args = Args::new(*span, [Value::Int(page as i64)]);
|
||||
Some(func.call_detached(ctx, args)?.display())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub struct PlaceNode(pub LayoutNode);
|
||||
|
||||
#[node]
|
||||
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 dx = args.named("dx")?.unwrap_or_default();
|
||||
let dy = args.named("dy")?.unwrap_or_default();
|
||||
|
@ -8,7 +8,7 @@ pub struct HNode;
|
||||
|
||||
#[node]
|
||||
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 weak = args.named("weak")?.unwrap_or(false);
|
||||
Ok(Content::Horizontal { amount, weak })
|
||||
@ -20,7 +20,7 @@ pub struct VNode;
|
||||
|
||||
#[node]
|
||||
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 weak = args.named("weak")?.unwrap_or(false);
|
||||
Ok(Content::Vertical { amount, weak, generated: false })
|
||||
|
@ -15,7 +15,7 @@ pub struct StackNode {
|
||||
|
||||
#[node]
|
||||
impl StackNode {
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::block(Self {
|
||||
dir: args.named("dir")?.unwrap_or(Dir::TTB),
|
||||
spacing: args.named("spacing")?,
|
||||
|
@ -28,7 +28,7 @@ impl MathNode {
|
||||
#[property(resolve, shorthand(around))]
|
||||
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 {
|
||||
formula: args.expect("formula")?,
|
||||
display: args.named("display")?.unwrap_or(false),
|
||||
|
@ -9,8 +9,8 @@ pub use typst_macros::node;
|
||||
|
||||
pub use crate::diag::{with_alternative, At, Error, StrResult, TypError, TypResult};
|
||||
pub use crate::eval::{
|
||||
Arg, Args, Array, Cast, Dict, Func, Node, RawAlign, RawLength, RawStroke, Scope,
|
||||
Smart, Value,
|
||||
Arg, Args, Array, Cast, Dict, Func, Machine, Node, RawAlign, RawLength, RawStroke,
|
||||
Scope, Smart, Value,
|
||||
};
|
||||
pub use crate::frame::*;
|
||||
pub use crate::geom::*;
|
||||
|
@ -55,7 +55,7 @@ impl HeadingNode {
|
||||
pub const BELOW: Leveled<Option<BlockSpacing>> =
|
||||
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 {
|
||||
body: args.expect("body")?,
|
||||
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::Mapping(mapping) => mapping(level),
|
||||
Self::Func(func, span) => {
|
||||
let args = Args::from_values(*span, [Value::Int(level.get() as i64)]);
|
||||
func.call(ctx, args)?.cast().at(*span)?
|
||||
let args = Args::new(*span, [Value::Int(level.get() as i64)]);
|
||||
func.call_detached(ctx, args)?.cast().at(*span)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ impl<const L: ListKind> ListNode<L> {
|
||||
#[property(resolve)]
|
||||
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 {
|
||||
start: args.named("start")?.unwrap_or(1),
|
||||
tight: args.named("tight")?.unwrap_or(true),
|
||||
@ -216,8 +216,8 @@ impl Label {
|
||||
}
|
||||
Self::Content(content) => content.clone(),
|
||||
Self::Func(func, span) => {
|
||||
let args = Args::from_values(*span, [Value::Int(number as i64)]);
|
||||
func.call(ctx, args)?.display()
|
||||
let args = Args::new(*span, [Value::Int(number as i64)]);
|
||||
func.call_detached(ctx, args)?.display()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ impl TableNode {
|
||||
#[property(resolve, shorthand(around))]
|
||||
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 rows = args.named("rows")?.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 {
|
||||
Self::Value(value) => value.clone(),
|
||||
Self::Func(func, span) => {
|
||||
let args = Args::from_values(*span, [
|
||||
Value::Int(x as i64),
|
||||
Value::Int(y as i64),
|
||||
]);
|
||||
func.call(ctx, args)?.cast().at(*span)?
|
||||
let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
|
||||
func.call_detached(ctx, args)?.cast().at(*span)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl<const L: DecoLine> DecoNode<L> {
|
||||
/// with the glyphs. Does not apply to strikethrough.
|
||||
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")?)))
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ impl LinkNode {
|
||||
/// Whether to underline link.
|
||||
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 {
|
||||
url: args.expect::<EcoString>("url")?,
|
||||
body: args.eat()?,
|
||||
|
@ -127,7 +127,7 @@ impl TextNode {
|
||||
#[property(skip, fold)]
|
||||
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.
|
||||
// Instead, it leaves the passed argument structurally unchanged, but
|
||||
// styles all text in it.
|
||||
@ -443,12 +443,12 @@ impl Fold for Vec<(Tag, u32)> {
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
@ -482,7 +482,7 @@ impl Case {
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
|
||||
}
|
||||
@ -514,7 +514,7 @@ pub struct StrongNode(pub Content);
|
||||
|
||||
#[node(showable)]
|
||||
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")?)))
|
||||
}
|
||||
}
|
||||
@ -539,7 +539,7 @@ pub struct EmphNode(pub Content);
|
||||
|
||||
#[node(showable)]
|
||||
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")?)))
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ impl ParNode {
|
||||
#[property(resolve)]
|
||||
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
|
||||
// node. Instead, it just ensures that the passed content lives is in a
|
||||
// separate paragraph and styles it.
|
||||
@ -172,7 +172,7 @@ pub struct ParbreakNode;
|
||||
|
||||
#[node]
|
||||
impl ParbreakNode {
|
||||
fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
|
||||
fn construct(_: &mut Machine, _: &mut Args) -> TypResult<Content> {
|
||||
Ok(Content::Parbreak)
|
||||
}
|
||||
}
|
||||
@ -182,7 +182,7 @@ pub struct LinebreakNode;
|
||||
|
||||
#[node]
|
||||
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);
|
||||
Ok(Content::Linebreak { justified })
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl RawNode {
|
||||
#[property(resolve, shorthand(around))]
|
||||
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 {
|
||||
text: args.expect("text")?,
|
||||
block: args.named("block")?.unwrap_or(false),
|
||||
|
@ -6,7 +6,7 @@ pub struct RepeatNode(pub LayoutNode);
|
||||
|
||||
#[node]
|
||||
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")?)))
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||
use crate::library::prelude::*;
|
||||
|
||||
/// 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(
|
||||
if let Some(string) = args.find::<Spanned<EcoString>>()? {
|
||||
match RgbaColor::from_str(&string.v) {
|
||||
@ -37,7 +37,7 @@ pub fn rgb(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
castable! {
|
||||
|
@ -3,7 +3,7 @@ use std::cmp::Ordering;
|
||||
use crate::library::prelude::*;
|
||||
|
||||
/// 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")?;
|
||||
Ok(Value::Int(match v {
|
||||
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.
|
||||
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")?;
|
||||
Ok(Value::Float(match v {
|
||||
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.
|
||||
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")?;
|
||||
Ok(match v {
|
||||
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.
|
||||
pub fn min(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
pub fn min(_: &mut Machine, args: &mut Args) -> TypResult<Value> {
|
||||
minmax(args, Ordering::Less)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
@ -79,17 +79,17 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
/// 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: 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.
|
||||
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 (start, end) = match args.eat::<i64>()? {
|
||||
Some(second) => (first, second),
|
||||
|
@ -8,19 +8,17 @@ pub use color::*;
|
||||
pub use math::*;
|
||||
pub use string::*;
|
||||
|
||||
use std::mem;
|
||||
|
||||
use crate::eval::{Eval, Machine, Scopes};
|
||||
use crate::library::prelude::*;
|
||||
use crate::source::SourceFile;
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
if !v {
|
||||
bail!(span, "assertion failed");
|
||||
@ -29,28 +27,21 @@ pub fn assert(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
|
||||
// Parse the source and set a synthetic span for all nodes.
|
||||
let source = SourceFile::synthesized(src, span);
|
||||
let ast = source.ast()?;
|
||||
|
||||
// Save the old route, then detach it.
|
||||
let prev_route = mem::take(&mut ctx.route);
|
||||
|
||||
// Evaluate the source.
|
||||
let std = ctx.config.std.clone();
|
||||
let std = vm.ctx.config.std.clone();
|
||||
let scopes = Scopes::new(Some(&std));
|
||||
let mut vm = Machine::new(ctx, scopes);
|
||||
let result = ast.eval(&mut vm);
|
||||
let flow = vm.flow;
|
||||
|
||||
// Restore the old route.
|
||||
ctx.route = prev_route;
|
||||
let mut sub = Machine::new(vm.ctx, vec![], scopes);
|
||||
let result = ast.eval(&mut sub);
|
||||
|
||||
// Handle control flow.
|
||||
if let Some(flow) = flow {
|
||||
if let Some(flow) = sub.flow {
|
||||
return Err(flow.forbidden());
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,12 @@ use crate::eval::Regex;
|
||||
use crate::library::prelude::*;
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
Ok(Value::Str(match v {
|
||||
Value::Int(v) => format_eco!("{}", v),
|
||||
@ -20,29 +20,29 @@ pub fn str(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
Ok(Value::Str(lipsum_from_seed(words, 97).into()))
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
Ok(Regex::new(&v).at(span)?.into())
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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 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)
|
||||
}
|
||||
|
||||
|
@ -214,7 +214,7 @@ fn typeset(command: TypesetCommand) -> StrResult<()> {
|
||||
.map_err(|_| "failed to load source file")?;
|
||||
|
||||
// Typeset.
|
||||
match ctx.typeset(id) {
|
||||
match typst::typeset(&mut ctx, id) {
|
||||
// Export the PDF.
|
||||
Ok(frames) => {
|
||||
let buffer = export::pdf(&ctx, &frames);
|
||||
|
@ -19,6 +19,19 @@ use crate::library::text::{
|
||||
};
|
||||
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(©);
|
||||
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.
|
||||
///
|
||||
/// This results from:
|
||||
@ -207,19 +220,6 @@ impl Content {
|
||||
|
||||
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(©);
|
||||
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 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Structured representation of styled content.
|
||||
//! Styled and structured representation of layoutable content.
|
||||
|
||||
#[macro_use]
|
||||
mod styles;
|
||||
|
@ -82,12 +82,12 @@ impl Recipe {
|
||||
F: FnOnce() -> Value,
|
||||
{
|
||||
let args = if self.func.argc() == Some(0) {
|
||||
Args::new(self.span)
|
||||
Args::new(self.span, [])
|
||||
} 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.
|
||||
|
@ -290,7 +290,7 @@ fn test_part(
|
||||
|
||||
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![]),
|
||||
Err(errors) => (vec![], *errors),
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user