Protect Vm

This commit is contained in:
Laurenz 2022-11-24 17:39:08 +01:00
parent 5ae81971f2
commit 8d3c68a1de
38 changed files with 101 additions and 93 deletions

View File

@ -3,7 +3,7 @@ use std::cmp::Ordering;
use crate::prelude::*; use crate::prelude::*;
/// Convert a value to an integer. /// Convert a value to an integer.
pub fn int(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn int(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// Convert a value to a float. /// Convert a value to a float.
pub fn float(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn float(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// The absolute value of a numeric value. /// The absolute value of a numeric value.
pub fn abs(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn abs(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// The minimum of a sequence of values. /// The minimum of a sequence of values.
pub fn min(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn min(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> { pub fn max(_: &Vm, args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Greater) minmax(args, Ordering::Greater)
} }
@ -79,17 +79,17 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
} }
/// Whether an integer is even. /// Whether an integer is even.
pub fn even(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn even(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> { pub fn odd(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> { pub fn mod_(_: &Vm, args: &mut Args) -> SourceResult<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")?;
@ -117,7 +117,7 @@ pub fn mod_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// Create a sequence of numbers. /// Create a sequence of numbers.
pub fn range(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn range(_: &Vm, args: &mut Args) -> SourceResult<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

@ -3,13 +3,13 @@ use std::str::FromStr;
use crate::prelude::*; use crate::prelude::*;
/// Create a grayscale color. /// Create a grayscale color.
pub fn luma(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn luma(_: &Vm, args: &mut Args) -> SourceResult<Value> {
let Component(luma) = args.expect("gray component")?; let Component(luma) = args.expect("gray component")?;
Ok(Value::Color(LumaColor::new(luma).into())) Ok(Value::Color(LumaColor::new(luma).into()))
} }
/// Create an RGB(A) color. /// Create an RGB(A) color.
pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn rgb(_: &Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? { Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) { match RgbaColor::from_str(&string.v) {
Ok(color) => color.into(), Ok(color) => color.into(),
@ -25,7 +25,7 @@ pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// Create a CMYK color. /// Create a CMYK color.
pub fn cmyk(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn cmyk(_: &Vm, args: &mut Args) -> SourceResult<Value> {
let RatioComponent(c) = args.expect("cyan component")?; let RatioComponent(c) = args.expect("cyan component")?;
let RatioComponent(m) = args.expect("magenta component")?; let RatioComponent(m) = args.expect("magenta component")?;
let RatioComponent(y) = args.expect("yellow component")?; let RatioComponent(y) = args.expect("yellow component")?;

View File

@ -5,12 +5,12 @@ use typst::diag::{format_xml_like_error, FileError};
use crate::prelude::*; use crate::prelude::*;
/// Read structured data from a CSV file. /// Read structured data from a CSV file.
pub fn csv(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } = let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to csv file")?; args.expect::<Spanned<EcoString>>("path to csv file")?;
let path = vm.locate(&path).at(span)?; let path = vm.locate(&path).at(span)?;
let data = vm.world.file(&path).at(span)?; let data = vm.world().file(&path).at(span)?;
let mut builder = csv::ReaderBuilder::new(); let mut builder = csv::ReaderBuilder::new();
builder.has_headers(false); builder.has_headers(false);
@ -45,12 +45,12 @@ fn format_csv_error(error: csv::Error) -> String {
} }
/// Read structured data from a JSON file. /// Read structured data from a JSON file.
pub fn json(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn json(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } = let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to json file")?; args.expect::<Spanned<EcoString>>("path to json file")?;
let path = vm.locate(&path).at(span)?; let path = vm.locate(&path).at(span)?;
let data = vm.world.file(&path).at(span)?; let data = vm.world().file(&path).at(span)?;
let value: serde_json::Value = let value: serde_json::Value =
serde_json::from_slice(&data).map_err(format_json_error).at(span)?; serde_json::from_slice(&data).map_err(format_json_error).at(span)?;
@ -85,12 +85,12 @@ fn format_json_error(error: serde_json::Error) -> String {
} }
/// Read structured data from an XML file. /// Read structured data from an XML file.
pub fn xml(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn xml(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: path, span } = let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to xml file")?; args.expect::<Spanned<EcoString>>("path to xml file")?;
let path = vm.locate(&path).at(span)?; let path = vm.locate(&path).at(span)?;
let data = vm.world.file(&path).at(span)?; let data = vm.world().file(&path).at(span)?;
let text = std::str::from_utf8(&data).map_err(FileError::from).at(span)?; let text = std::str::from_utf8(&data).map_err(FileError::from).at(span)?;
let document = roxmltree::Document::parse(text).map_err(format_xml_error).at(span)?; let document = roxmltree::Document::parse(text).map_err(format_xml_error).at(span)?;

View File

@ -17,12 +17,12 @@ use typst::syntax::Source;
use crate::prelude::*; use crate::prelude::*;
/// The name of a value's type. /// The name of a value's type.
pub fn type_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn type_(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> { pub fn assert(_: &Vm, args: &mut Args) -> SourceResult<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");
@ -31,10 +31,10 @@ pub fn assert(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// Evaluate a string as Typst markup. /// Evaluate a string as Typst markup.
pub fn eval(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?; let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
let source = Source::synthesized(text, span); let source = Source::synthesized(text, span);
let route = Route::default(); let route = Route::default();
let module = model::eval(vm.world, route.track(), &source)?; let module = model::eval(vm.world(), route.track(), &source)?;
Ok(Value::Content(module.content)) Ok(Value::Content(module.content))
} }

View File

@ -3,12 +3,12 @@ use typst::model::Regex;
use crate::prelude::*; use crate::prelude::*;
/// The string representation of a value. /// The string representation of a value.
pub fn repr(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn repr(_: &Vm, args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.repr().into()) Ok(args.expect::<Value>("value")?.repr().into())
} }
/// Convert a value to a string. /// Convert a value to a string.
pub fn str(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn str(_: &Vm, args: &mut Args) -> SourceResult<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_str!("{}", v), Value::Int(v) => format_str!("{}", v),
@ -19,29 +19,29 @@ pub fn str(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
} }
/// Create blind text. /// Create blind text.
pub fn lorem(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn lorem(_: &Vm, args: &mut Args) -> SourceResult<Value> {
let words: usize = args.expect("number of words")?; let words: usize = args.expect("number of words")?;
Ok(Value::Str(lipsum::lipsum(words).into())) Ok(Value::Str(lipsum::lipsum(words).into()))
} }
/// Create a regular expression. /// Create a regular expression.
pub fn regex(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn regex(_: &Vm, args: &mut Args) -> SourceResult<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 Vm, args: &mut Args) -> SourceResult<Value> { pub fn letter(_: &Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Letter, args) numbered(Numbering::Letter, args)
} }
/// Converts an integer into a roman numeral. /// Converts an integer into a roman numeral.
pub fn roman(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn roman(_: &Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Roman, args) numbered(Numbering::Roman, args)
} }
/// Convert a number into a symbol. /// Convert a number into a symbol.
pub fn symbol(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn symbol(_: &Vm, args: &mut Args) -> SourceResult<Value> {
numbered(Numbering::Symbol, args) numbered(Numbering::Symbol, args)
} }

View File

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

View File

@ -14,12 +14,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(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(vm: &Vm, args: &mut Args) -> SourceResult<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 = vm.locate(&path).at(span)?; let full = vm.locate(&path).at(span)?;
let buffer = vm.world.file(&full).at(span)?; let buffer = vm.world().file(&full).at(span)?;
let ext = full.extension().and_then(OsStr::to_str).unwrap_or_default(); let ext = full.extension().and_then(OsStr::to_str).unwrap_or_default();
let format = match ext.to_lowercase().as_str() { let format = match ext.to_lowercase().as_str() {
"png" => ImageFormat::Raster(RasterFormat::Png), "png" => ImageFormat::Raster(RasterFormat::Png),

View File

@ -15,7 +15,7 @@ impl LineNode {
#[property(resolve, fold)] #[property(resolve, fold)]
pub const STROKE: PartialStroke = PartialStroke::default(); pub const STROKE: PartialStroke = PartialStroke::default();
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let origin = args.named("origin")?.unwrap_or_default(); let origin = args.named("origin")?.unwrap_or_default();
let delta = match args.named::<Axes<Rel<Length>>>("to")? { let delta = match args.named::<Axes<Rel<Length>>>("to")? {

View File

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

View File

@ -12,7 +12,7 @@ pub struct AlignNode {
#[node(LayoutBlock)] #[node(LayoutBlock)]
impl AlignNode { impl AlignNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let aligns: Axes<Option<GenAlign>> = args.find()?.unwrap_or_default(); let aligns: Axes<Option<GenAlign>> = args.find()?.unwrap_or_default();
let body: Content = args.expect("body")?; let body: Content = args.expect("body")?;

View File

@ -17,7 +17,7 @@ impl ColumnsNode {
#[property(resolve)] #[property(resolve)]
pub const GUTTER: Rel<Length> = Ratio::new(0.04).into(); pub const GUTTER: Rel<Length> = Ratio::new(0.04).into();
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self { Ok(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(Behave)] #[node(Behave)]
impl ColbreakNode { impl ColbreakNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Self { weak }.pack()) Ok(Self { weak }.pack())
} }

View File

@ -12,7 +12,7 @@ pub struct BoxNode {
#[node(LayoutInline)] #[node(LayoutInline)]
impl BoxNode { impl BoxNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let width = args.named("width")?; let width = args.named("width")?;
let height = args.named("height")?; let height = args.named("height")?;
let body = args.eat::<Content>()?.unwrap_or_default(); let body = args.eat::<Content>()?.unwrap_or_default();
@ -70,7 +70,7 @@ impl BlockNode {
#[property(skip)] #[property(skip)]
pub const BELOW: VNode = VNode::block_spacing(Em::new(1.2).into()); pub const BELOW: VNode = VNode::block_spacing(Em::new(1.2).into());
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.eat()?.unwrap_or_default()).pack()) Ok(Self(args.eat()?.unwrap_or_default()).pack())
} }

View File

@ -15,7 +15,7 @@ pub struct GridNode {
#[node(LayoutBlock)] #[node(LayoutBlock)]
impl GridNode { impl GridNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default(); let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
let TrackSizings(rows) = args.named("rows")?.unwrap_or_default(); let TrackSizings(rows) = args.named("rows")?.unwrap_or_default();
let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default(); let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default();

View File

@ -11,7 +11,7 @@ pub struct PadNode {
#[node(LayoutBlock)] #[node(LayoutBlock)]
impl PadNode { impl PadNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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

@ -41,7 +41,7 @@ impl PageNode {
#[property(referenced)] #[property(referenced)]
pub const FOREGROUND: Marginal = Marginal::None; pub const FOREGROUND: Marginal = Marginal::None;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }
@ -150,7 +150,7 @@ pub struct PagebreakNode {
#[node] #[node]
impl PagebreakNode { impl PagebreakNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let weak = args.named("weak")?.unwrap_or(false); let weak = args.named("weak")?.unwrap_or(false);
Ok(Self { weak }.pack()) Ok(Self { weak }.pack())
} }

View File

@ -7,7 +7,7 @@ pub struct PlaceNode(pub Content);
#[node(LayoutBlock, Behave)] #[node(LayoutBlock, Behave)]
impl PlaceNode { impl PlaceNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let aligns = args.find()?.unwrap_or(Axes::with_x(Some(GenAlign::Start))); let aligns = args.find()?.unwrap_or(Axes::with_x(Some(GenAlign::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

@ -13,7 +13,7 @@ pub struct HNode {
#[node(Behave)] #[node(Behave)]
impl HNode { impl HNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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(Self { amount, weak }.pack()) Ok(Self { amount, weak }.pack())
@ -60,7 +60,7 @@ pub struct VNode {
#[node(Behave)] #[node(Behave)]
impl VNode { impl VNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let amount = args.expect("spacing")?; let amount = args.expect("spacing")?;
let node = if args.named("weak")?.unwrap_or(false) { let node = if args.named("weak")?.unwrap_or(false) {
Self::weak(amount) Self::weak(amount)

View File

@ -17,7 +17,7 @@ pub struct StackNode {
#[node(LayoutBlock)] #[node(LayoutBlock)]
impl StackNode { impl StackNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self { Ok(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

@ -13,7 +13,7 @@ pub struct MoveNode {
#[node(LayoutInline)] #[node(LayoutInline)]
impl MoveNode { impl MoveNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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(Self { Ok(Self {
@ -60,7 +60,7 @@ impl<const T: TransformKind> TransformNode<T> {
#[property(resolve)] #[property(resolve)]
pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default(); pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default();
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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

@ -16,7 +16,7 @@ pub struct HeadingNode {
#[node(Show, Finalize)] #[node(Show, Finalize)]
impl HeadingNode { impl HeadingNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self { Ok(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()),

View File

@ -38,7 +38,7 @@ impl<const L: ListKind> ListNode<L> {
/// The spacing between the items of a wide (non-tight) list. /// The spacing between the items of a wide (non-tight) list.
pub const SPACING: Smart<Spacing> = Smart::Auto; pub const SPACING: Smart<Spacing> = Smart::Auto;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let items = match L { let items = match L {
LIST => args LIST => args
.all()? .all()?

View File

@ -7,7 +7,7 @@ pub struct RefNode(pub EcoString);
#[node(Show)] #[node(Show)]
impl RefNode { impl RefNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("target")?).pack()) Ok(Self(args.expect("target")?).pack())
} }

View File

@ -23,7 +23,7 @@ impl TableNode {
/// How much to pad the cells's content. /// How much to pad the cells's content.
pub const PADDING: Rel<Length> = Abs::pt(5.0).into(); pub const PADDING: Rel<Length> = Abs::pt(5.0).into();
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default(); let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
let TrackSizings(rows) = args.named("rows")?.unwrap_or_default(); let TrackSizings(rows) = args.named("rows")?.unwrap_or_default();
let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default(); let TrackSizings(base_gutter) = args.named("gutter")?.unwrap_or_default();

View File

@ -34,7 +34,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 Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }

View File

@ -29,7 +29,7 @@ impl LinkNode {
#[property(skip, referenced)] #[property(skip, referenced)]
pub(crate) const DEST: Option<Destination> = None; pub(crate) const DEST: Option<Destination> = None;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let dest = args.expect::<Destination>("destination")?; let dest = args.expect::<Destination>("destination")?;
Ok(match dest { Ok(match dest {
Destination::Url(url) => match args.eat()? { Destination::Url(url) => match args.eat()? {

View File

@ -128,7 +128,7 @@ impl TextNode {
#[property(skip, fold)] #[property(skip, fold)]
const DECO: Decoration = vec![]; const DECO: Decoration = vec![];
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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.
@ -416,7 +416,7 @@ pub struct SpaceNode;
#[node(Unlabellable, Behave)] #[node(Unlabellable, Behave)]
impl SpaceNode { impl SpaceNode {
fn construct(_: &mut Vm, _: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, _: &mut Args) -> SourceResult<Content> {
Ok(Self.pack()) Ok(Self.pack())
} }
} }
@ -437,7 +437,7 @@ pub struct LinebreakNode {
#[node(Behave)] #[node(Behave)]
impl LinebreakNode { impl LinebreakNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let justify = args.named("justify")?.unwrap_or(false); let justify = args.named("justify")?.unwrap_or(false);
Ok(Self { justify }.pack()) Ok(Self { justify }.pack())
} }
@ -457,19 +457,19 @@ pub struct SmartQuoteNode {
#[node] #[node]
impl SmartQuoteNode { impl SmartQuoteNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let double = args.named("double")?.unwrap_or(true); let double = args.named("double")?.unwrap_or(true);
Ok(Self { double }.pack()) Ok(Self { double }.pack())
} }
} }
/// Convert a string or content to lowercase. /// Convert a string or content to lowercase.
pub fn lower(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn lower(_: &Vm, args: &mut Args) -> SourceResult<Value> {
case(Case::Lower, args) case(Case::Lower, args)
} }
/// Convert a string or content to uppercase. /// Convert a string or content to uppercase.
pub fn upper(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn upper(_: &Vm, args: &mut Args) -> SourceResult<Value> {
case(Case::Upper, args) case(Case::Upper, args)
} }
@ -503,7 +503,7 @@ impl Case {
} }
/// Display text in small capitals. /// Display text in small capitals.
pub fn smallcaps(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn smallcaps(_: &Vm, args: &mut Args) -> SourceResult<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(Show)] #[node(Show)]
impl StrongNode { impl StrongNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }
@ -538,7 +538,7 @@ pub struct EmphNode(pub Content);
#[node(Show)] #[node(Show)]
impl EmphNode { impl EmphNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }

View File

@ -31,7 +31,7 @@ impl ParNode {
/// How to determine line breaks. /// How to determine line breaks.
pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto; pub const LINEBREAKS: Smart<Linebreaks> = Smart::Auto;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<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.
@ -119,7 +119,7 @@ pub struct ParbreakNode;
#[node(Unlabellable)] #[node(Unlabellable)]
impl ParbreakNode { impl ParbreakNode {
fn construct(_: &mut Vm, _: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, _: &mut Args) -> SourceResult<Content> {
Ok(Self.pack()) Ok(Self.pack())
} }
} }
@ -132,7 +132,7 @@ pub struct RepeatNode(pub Content);
#[node(LayoutInline)] #[node(LayoutInline)]
impl RepeatNode { impl RepeatNode {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }
} }

View File

@ -25,7 +25,7 @@ impl RawNode {
#[property(referenced)] #[property(referenced)]
pub const LANG: Option<EcoString> = None; pub const LANG: Option<EcoString> = None;
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self { Ok(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

@ -30,7 +30,7 @@ impl<const S: ShiftKind> ShiftNode<S> {
/// The font size for synthetic sub- and superscripts. /// The font size for synthetic sub- and superscripts.
pub const SIZE: TextSize = TextSize(Em::new(0.6).into()); pub const SIZE: TextSize = TextSize(Em::new(0.6).into());
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack()) Ok(Self(args.expect("body")?).pack())
} }

View File

@ -284,7 +284,7 @@ fn create_node_construct_func(node: &Node) -> syn::ImplItemMethod {
node.construct.clone().unwrap_or_else(|| { node.construct.clone().unwrap_or_else(|| {
parse_quote! { parse_quote! {
fn construct( fn construct(
_: &mut ::typst::model::Vm, _: &::typst::model::Vm,
_: &mut ::typst::model::Args, _: &mut ::typst::model::Args,
) -> ::typst::diag::SourceResult<::typst::model::Content> { ) -> ::typst::diag::SourceResult<::typst::model::Content> {
unimplemented!() unimplemented!()

View File

@ -128,7 +128,7 @@ impl Array {
} }
/// Return the first matching element. /// Return the first matching element.
pub fn find(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Option<Value>> { pub fn find(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<Option<Value>> {
for item in self.iter() { for item in self.iter() {
let args = Args::new(f.span, [item.clone()]); let args = Args::new(f.span, [item.clone()]);
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? { if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
@ -140,7 +140,7 @@ impl Array {
} }
/// Return the index of the first matching element. /// Return the index of the first matching element.
pub fn position(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Option<i64>> { pub fn position(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<Option<i64>> {
for (i, item) in self.iter().enumerate() { for (i, item) in self.iter().enumerate() {
let args = Args::new(f.span, [item.clone()]); let args = Args::new(f.span, [item.clone()]);
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? { if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
@ -153,7 +153,7 @@ impl Array {
/// Return a new array with only those elements for which the function /// Return a new array with only those elements for which the function
/// returns true. /// returns true.
pub fn filter(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Self> { pub fn filter(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<Self> {
let mut kept = vec![]; let mut kept = vec![];
for item in self.iter() { for item in self.iter() {
let args = Args::new(f.span, [item.clone()]); let args = Args::new(f.span, [item.clone()]);
@ -165,7 +165,7 @@ impl Array {
} }
/// Transform each item in the array with a function. /// Transform each item in the array with a function.
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Self> { pub fn map(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<Self> {
let enumerate = f.v.argc() == Some(2); let enumerate = f.v.argc() == Some(2);
self.iter() self.iter()
.enumerate() .enumerate()
@ -181,7 +181,7 @@ impl Array {
} }
/// Whether any element matches. /// Whether any element matches.
pub fn any(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<bool> { pub fn any(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<bool> {
for item in self.iter() { for item in self.iter() {
let args = Args::new(f.span, [item.clone()]); let args = Args::new(f.span, [item.clone()]);
if f.v.call(vm, args)?.cast::<bool>().at(f.span)? { if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
@ -193,7 +193,7 @@ impl Array {
} }
/// Whether all elements match. /// Whether all elements match.
pub fn all(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<bool> { pub fn all(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<bool> {
for item in self.iter() { for item in self.iter() {
let args = Args::new(f.span, [item.clone()]); let args = Args::new(f.span, [item.clone()]);
if !f.v.call(vm, args)?.cast::<bool>().at(f.span)? { if !f.v.call(vm, args)?.cast::<bool>().at(f.span)? {

View File

@ -362,7 +362,7 @@ pub trait Node: 'static + Capable {
/// ///
/// 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(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content>
where where
Self: Sized; Self: Sized;

View File

@ -105,7 +105,7 @@ impl Dict {
} }
/// Transform each pair in the array with a function. /// Transform each pair in the array with a function.
pub fn map(&self, vm: &mut Vm, f: Spanned<Func>) -> SourceResult<Array> { pub fn map(&self, vm: &Vm, f: Spanned<Func>) -> SourceResult<Array> {
self.iter() self.iter()
.map(|(key, value)| { .map(|(key, value)| {
let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]); let args = Args::new(f.span, [Value::Str(key.clone()), value.clone()]);

View File

@ -1030,7 +1030,7 @@ impl Eval for ast::ModuleInclude {
} }
/// Process an import of a module relative to the current location. /// Process an import of a module relative to the current location.
fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> { fn import(vm: &Vm, path: &str, span: Span) -> SourceResult<Module> {
// Load the source file. // Load the source file.
let full = vm.locate(path).at(span)?; let full = vm.locate(path).at(span)?;
let id = vm.world.resolve(&full).at(span)?; let id = vm.world.resolve(&full).at(span)?;

View File

@ -32,7 +32,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 Vm, &mut Args) -> SourceResult<Value>, func: fn(&Vm, &mut Args) -> SourceResult<Value>,
) -> Self { ) -> Self {
Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None }))) Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None })))
} }
@ -77,7 +77,7 @@ impl Func {
} }
/// Call the function with the given arguments. /// Call the function with the given arguments.
pub fn call(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> { pub fn call(&self, vm: &Vm, mut args: Args) -> SourceResult<Value> {
let value = match self.0.as_ref() { let value = match self.0.as_ref() {
Repr::Native(native) => (native.func)(vm, &mut args)?, Repr::Native(native) => (native.func)(vm, &mut args)?,
Repr::Closure(closure) => closure.call(vm, &mut args)?, Repr::Closure(closure) => closure.call(vm, &mut args)?,
@ -99,8 +99,8 @@ impl Func {
let route = Route::default(); let route = Route::default();
let id = SourceId::detached(); let id = SourceId::detached();
let scopes = Scopes::new(None); let scopes = Scopes::new(None);
let mut vm = Vm::new(world, route.track(), id, scopes); let vm = Vm::new(world, route.track(), id, scopes);
self.call(&mut vm, args) self.call(&vm, args)
} }
/// Apply the given arguments to the function. /// Apply the given arguments to the function.
@ -160,7 +160,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 Vm, &mut Args) -> SourceResult<Value>, pub func: fn(&Vm, &mut Args) -> SourceResult<Value>,
/// The set rule. /// The set rule.
pub set: Option<fn(&mut Args) -> SourceResult<StyleMap>>, pub set: Option<fn(&mut Args) -> SourceResult<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.
@ -196,7 +196,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, vm: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn call(&self, vm: &Vm, args: &mut Args) -> SourceResult<Value> {
// Don't leak the scopes from the call site. Instead, we use the scope // Don't leak the scopes from the call site. Instead, we use the scope
// of captured variables we collected earlier. // of captured variables we collected earlier.
let mut scopes = Scopes::new(None); let mut scopes = Scopes::new(None);

View File

@ -7,7 +7,7 @@ use crate::util::EcoString;
/// Call a method on a value. /// Call a method on a value.
pub fn call( pub fn call(
vm: &mut Vm, vm: &Vm,
value: Value, value: Value,
method: &str, method: &str,
mut args: Args, mut args: Args,

View File

@ -78,7 +78,7 @@ impl Scope {
pub fn def_fn( pub fn def_fn(
&mut self, &mut self,
name: &'static str, name: &'static str,
func: fn(&mut Vm, &mut Args) -> SourceResult<Value>, func: fn(&Vm, &mut Args) -> SourceResult<Value>,
) { ) {
self.define(name, Func::from_fn(name, func)); self.define(name, Func::from_fn(name, func));
} }

View File

@ -9,19 +9,22 @@ use crate::util::PathExt;
use crate::World; use crate::World;
/// A virtual machine. /// A virtual machine.
///
/// Holds the state needed to evaluate Typst sources. A new virtual machine is
/// created for each module evaluation and function call.
pub struct Vm<'a> { pub struct Vm<'a> {
/// The core context. /// The core context.
pub world: Tracked<'a, dyn World>, pub(crate) world: Tracked<'a, dyn World>,
/// The route of source ids the VM took to reach its current location. /// The route of source ids the VM took to reach its current location.
pub route: Tracked<'a, Route>, pub(crate) route: Tracked<'a, Route>,
/// The current location. /// The current location.
pub location: SourceId, pub(crate) location: SourceId,
/// The stack of scopes. /// The stack of scopes.
pub scopes: Scopes<'a>, pub(crate) scopes: Scopes<'a>,
/// A control flow event that is currently happening. /// A control flow event that is currently happening.
pub flow: Option<Flow>, pub(crate) flow: Option<Flow>,
/// The language items. /// The language items.
pub items: LangItems, pub(crate) items: LangItems,
} }
impl<'a> Vm<'a> { impl<'a> Vm<'a> {
@ -42,6 +45,11 @@ impl<'a> Vm<'a> {
} }
} }
/// Access the underlying world.
pub fn world(&self) -> Tracked<dyn World> {
self.world
}
/// Resolve a user-entered path to be relative to the compilation /// Resolve a user-entered path to be relative to the compilation
/// environment's root. /// environment's root.
pub fn locate(&self, path: &str) -> StrResult<PathBuf> { pub fn locate(&self, path: &str) -> StrResult<PathBuf> {