summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-16 22:14:27 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-16 22:39:21 +0200
commit30f16bbf6431ca0c174ca0a1abaa6a13ef50ab06 (patch)
treef5a5c0adad15840ebe24b39e77ff467862067c91 /src/library
parent9f6137d8a829fe8f34554623495fa620252a0184 (diff)
Add Value type and replace dyn-nodes with call-exprs 🏗
- In addition to syntax trees there are now `Value`s, which syntax trees can be evaluated into (e.g. the tree is `5+5` and the value is `10`) - Parsing is completely pure, function calls are not parsed into nodes, but into simple call expressions, which are resolved later - Functions aren't dynamic nodes anymore, but simply functions which receive their arguments as a table and the layouting context - Functions may return any `Value` - Layouting is powered by functions which return the new `Commands` value, which informs the layouting engine what to do - When a function returns a non-`Commands` value, the layouter simply dumps the value into the document in monospace
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs94
-rw-r--r--src/library/boxed.rs60
-rw-r--r--src/library/font.rs110
-rw-r--r--src/library/mod.rs62
-rw-r--r--src/library/page.rs94
-rw-r--r--src/library/spacing.rs46
-rw-r--r--src/library/val.rs28
7 files changed, 199 insertions, 295 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index b4cfd2e2..c716faef 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -10,67 +10,49 @@ use super::*;
/// - `vertical`: Any of `top`, `bottom` or `center`.
///
/// There may not be two alignment specifications for the same axis.
-pub fn align(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
+pub async fn align(mut args: TableValue, mut ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- let mut args = call.args;
- let node = AlignNode {
- content: args.take::<SyntaxTree>(),
- aligns: args.take_all_num_vals::<Spanned<SpecAlign>>().collect(),
- h: args.take_with_key::<_, Spanned<SpecAlign>>("horizontal", &mut f),
- v: args.take_with_key::<_, Spanned<SpecAlign>>("vertical", &mut f),
- };
- args.unexpected(&mut f);
- Pass::node(node, f)
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct AlignNode {
- content: Option<SyntaxTree>,
- aligns: SpanVec<SpecAlign>,
- h: Option<Spanned<SpecAlign>>,
- v: Option<Spanned<SpecAlign>>,
-}
-#[async_trait(?Send)]
-impl Layout for AlignNode {
- async fn layout<'a>(&'a self, mut ctx: LayoutContext<'_>) -> Pass<Commands<'a>> {
- let mut f = Feedback::new();
+ let content = args.take::<SyntaxTree>();
+ let aligns: Vec<_> = args.take_all_num_vals::<Spanned<SpecAlign>>().collect();
+ let h = args.take_with_key::<_, Spanned<SpecAlign>>("horizontal", &mut f);
+ let v = args.take_with_key::<_, Spanned<SpecAlign>>("vertical", &mut f);
+ args.unexpected(&mut f);
- ctx.base = ctx.spaces[0].size;
+ ctx.base = ctx.spaces[0].size;
- let axes = ctx.axes;
- let all = self.aligns.iter()
- .map(|align| {
- let spec = align.v.axis().unwrap_or(axes.primary.axis());
- (spec, align)
- })
- .chain(self.h.iter().map(|align| (Horizontal, align)))
- .chain(self.v.iter().map(|align| (Vertical, align)));
+ let axes = ctx.axes;
+ let all = aligns.iter()
+ .map(|align| {
+ let spec = align.v.axis().unwrap_or(axes.primary.axis());
+ (spec, align)
+ })
+ .chain(h.iter().map(|align| (Horizontal, align)))
+ .chain(v.iter().map(|align| (Vertical, align)));
- let mut had = [false; 2];
- for (axis, align) in all {
- if align.v.axis().map(|a| a != axis).unwrap_or(false) {
- error!(
- @f, align.span,
- "invalid alignment {} for {} axis", align.v, axis,
- );
- } else if had[axis as usize] {
- error!(@f, align.span, "duplicate alignment for {} axis", axis);
- } else {
- had[axis as usize] = true;
- let gen_axis = axis.to_generic(ctx.axes);
- let gen_align = align.v.to_generic(ctx.axes);
- *ctx.align.get_mut(gen_axis) = gen_align;
- }
+ let mut had = [false; 2];
+ for (axis, align) in all {
+ if align.v.axis().map(|a| a != axis).unwrap_or(false) {
+ error!(
+ @f, align.span,
+ "invalid alignment {} for {} axis", align.v, axis,
+ );
+ } else if had[axis as usize] {
+ error!(@f, align.span, "duplicate alignment for {} axis", axis);
+ } else {
+ had[axis as usize] = true;
+ let gen_axis = axis.to_generic(ctx.axes);
+ let gen_align = align.v.to_generic(ctx.axes);
+ *ctx.align.get_mut(gen_axis) = gen_align;
}
-
- Pass::new(match &self.content {
- Some(tree) => {
- let layouted = layout(tree, ctx).await;
- f.extend(layouted.feedback);
- vec![AddMultiple(layouted.output)]
- }
- None => vec![SetAlignment(ctx.align)],
- }, f)
}
+
+ Pass::commands(match content {
+ Some(tree) => {
+ let layouted = layout(&tree, ctx).await;
+ f.extend(layouted.feedback);
+ vec![AddMultiple(layouted.output)]
+ }
+ None => vec![SetAlignment(ctx.align)],
+ }, f)
}
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 3637f072..c03043ae 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -6,48 +6,32 @@ use super::*;
/// # Keyword arguments
/// - `width`: The width of the box (length of relative to parent's width).
/// - `height`: The height of the box (length of relative to parent's height).
-pub fn boxed(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
+pub async fn boxed(mut args: TableValue, mut ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- let mut args = call.args;
- let node = BoxNode {
- content: args.take::<SyntaxTree>().unwrap_or(SyntaxTree::new()),
- width: args.take_with_key::<_, ScaleLength>("width", &mut f),
- height: args.take_with_key::<_, ScaleLength>("height", &mut f),
- };
+ let content = args.take::<SyntaxTree>().unwrap_or(SyntaxTree::new());
+ let width = args.take_with_key::<_, ScaleLength>("width", &mut f);
+ let height = args.take_with_key::<_, ScaleLength>("height", &mut f);
args.unexpected(&mut f);
- Pass::node(node, f)
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct BoxNode {
- content: SyntaxTree,
- width: Option<ScaleLength>,
- height: Option<ScaleLength>,
-}
-#[async_trait(?Send)]
-impl Layout for BoxNode {
- async fn layout<'a>(&'a self, mut ctx: LayoutContext<'_>) -> Pass<Commands<'a>> {
- ctx.spaces.truncate(1);
- ctx.repeat = false;
+ ctx.spaces.truncate(1);
+ ctx.repeat = false;
- self.width.with(|v| {
- let length = v.raw_scaled(ctx.base.x);
- ctx.base.x = length;
- ctx.spaces[0].size.x = length;
- ctx.spaces[0].expansion.horizontal = true;
- });
+ width.with(|v| {
+ let length = v.raw_scaled(ctx.base.x);
+ ctx.base.x = length;
+ ctx.spaces[0].size.x = length;
+ ctx.spaces[0].expansion.horizontal = true;
+ });
- self.height.with(|v| {
- let length = v.raw_scaled(ctx.base.y);
- ctx.base.y = length;
- ctx.spaces[0].size.y = length;
- ctx.spaces[0].expansion.vertical = true;
- });
+ height.with(|v| {
+ let length = v.raw_scaled(ctx.base.y);
+ ctx.base.y = length;
+ ctx.spaces[0].size.y = length;
+ ctx.spaces[0].expansion.vertical = true;
+ });
- layout(&self.content, ctx).await.map(|out| {
- let layout = out.into_iter().next().unwrap();
- vec![Add(layout)]
- })
- }
+ let layouted = layout(&content, ctx).await;
+ let layout = layouted.output.into_iter().next().unwrap();
+ f.extend(layouted.feedback);
+ Pass::commands(vec![Add(layout)], f)
}
diff --git a/src/library/font.rs b/src/library/font.rs
index 8787cc91..71e9552f 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -18,80 +18,60 @@ use super::*;
/// ```typst
/// serif = ("Source Serif Pro", "Noto Serif")
/// ```
-pub fn font(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
+pub async fn font(mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- let mut args = call.args;
- let node = FontNode {
- content: args.take::<SyntaxTree>(),
- size: args.take::<ScaleLength>(),
- style: args.take_with_key::<_, FontStyle>("style", &mut f),
- weight: args.take_with_key::<_, FontWeight>("weight", &mut f),
- width: args.take_with_key::<_, FontWidth>("width", &mut f),
- list: args.take_all_num_vals::<StringLike>()
- .map(|s| s.0.to_lowercase())
- .collect(),
- classes: args.take_all_str::<TableExpr>()
- .map(|(class, mut table)| {
- let fallback = table.take_all_num_vals::<StringLike>()
- .map(|s| s.0.to_lowercase())
- .collect();
- (class, fallback)
- })
- .collect()
- };
+ let content = args.take::<SyntaxTree>();
+ let size = args.take::<ScaleLength>();
+ let style = args.take_with_key::<_, FontStyle>("style", &mut f);
+ let weight = args.take_with_key::<_, FontWeight>("weight", &mut f);
+ let width = args.take_with_key::<_, FontWidth>("width", &mut f);
+ let list: Vec<_> = args.take_all_num_vals::<StringLike>()
+ .map(|s| s.0.to_lowercase())
+ .collect();
+ let classes: Vec<(_, Vec<_>)> = args.take_all_str::<TableValue>()
+ .map(|(class, mut table)| {
+ let fallback = table.take_all_num_vals::<StringLike>()
+ .map(|s| s.0.to_lowercase())
+ .collect();
+ (class, fallback)
+ })
+ .collect();
args.unexpected(&mut f);
- Pass::node(node, f)
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct FontNode {
- content: Option<SyntaxTree>,
- size: Option<ScaleLength>,
- style: Option<FontStyle>,
- weight: Option<FontWeight>,
- width: Option<FontWidth>,
- list: Vec<String>,
- classes: Vec<(String, Vec<String>)>,
-}
-
-#[async_trait(?Send)]
-impl Layout for FontNode {
- async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>> {
- let mut text = ctx.style.text.clone();
-
- self.size.with(|s| match s {
- ScaleLength::Absolute(length) => {
- text.base_font_size = length.as_raw();
- text.font_scale = 1.0;
- }
- ScaleLength::Scaled(scale) => text.font_scale = scale,
- });
- self.style.with(|s| text.variant.style = s);
- self.weight.with(|w| text.variant.weight = w);
- self.width.with(|w| text.variant.width = w);
+ let mut text = ctx.style.text.clone();
- if !self.list.is_empty() {
- *text.fallback.list_mut() = self.list.iter()
- .map(|s| s.to_lowercase())
- .collect();
+ size.with(|s| match s {
+ ScaleLength::Absolute(length) => {
+ text.base_font_size = length.as_raw();
+ text.font_scale = 1.0;
}
+ ScaleLength::Scaled(scale) => text.font_scale = scale,
+ });
- for (class, fallback) in &self.classes {
- text.fallback.set_class_list(class.clone(), fallback.clone());
- }
+ style.with(|s| text.variant.style = s);
+ weight.with(|w| text.variant.weight = w);
+ width.with(|w| text.variant.width = w);
- text.fallback.flatten();
+ if !list.is_empty() {
+ *text.fallback.list_mut() = list.iter()
+ .map(|s| s.to_lowercase())
+ .collect();
+ }
- Pass::okay(match &self.content {
- Some(tree) => vec![
- SetTextStyle(text),
- LayoutSyntaxTree(tree),
- SetTextStyle(ctx.style.text.clone()),
- ],
- None => vec![SetTextStyle(text)],
- })
+ for (class, fallback) in classes {
+ text.fallback.set_class_list(class.clone(), fallback.clone());
}
+
+ text.fallback.flatten();
+
+ Pass::commands(match content {
+ Some(tree) => vec![
+ SetTextStyle(text),
+ LayoutSyntaxTree(tree),
+ SetTextStyle(ctx.style.text.clone()),
+ ],
+ None => vec![SetTextStyle(text)],
+ }, f)
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index ef24d74f..1999ba31 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -5,30 +5,60 @@ mod boxed;
mod font;
mod page;
mod spacing;
-mod val;
pub use align::*;
pub use boxed::*;
pub use font::*;
pub use page::*;
pub use spacing::*;
-pub use val::*;
-use crate::func::prelude::*;
-use crate::syntax::scope::Scope;
+use std::rc::Rc;
-/// Create a scope with all standard library functions.
-pub fn _std() -> Scope {
- let mut std = Scope::new(Box::new(val));
+use crate::compute::scope::Scope;
+use crate::prelude::*;
- std.insert("val", Box::new(val));
- std.insert("font", Box::new(font));
- std.insert("page", Box::new(page));
- std.insert("align", Box::new(align));
- std.insert("box", Box::new(boxed));
- std.insert("pagebreak", Box::new(pagebreak));
- std.insert("h", Box::new(h));
- std.insert("v", Box::new(v));
+macro_rules! std {
+ (fallback: $fallback:expr $(, $name:literal => $func:expr)* $(,)?) => {
+ /// Create a scope with all standard library functions.
+ pub fn _std() -> Scope {
+ let mut std = Scope::new(wrap!(val));
+ $(std.insert($name, wrap!($func));)*
+ std
+ }
+ };
+}
+
+macro_rules! wrap {
+ ($func:expr) => {
+ Rc::new(|args, ctx| Box::pin($func(args, ctx)))
+ };
+}
+
+std! {
+ fallback: val,
+ "align" => align,
+ "box" => boxed,
+ "dump" => dump,
+ "font" => font,
+ "h" => h,
+ "page" => page,
+ "pagebreak" => pagebreak,
+ "v" => v,
+ "val" => val,
+}
+
+/// `val`: Layouts its body flatly, ignoring other arguments.
+///
+/// This is also the fallback function, which is used when a function name
+/// cannot be resolved.
+pub async fn val(mut args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
+ Pass::commands(match args.take::<SyntaxTree>() {
+ Some(tree) => vec![LayoutSyntaxTree(tree)],
+ None => vec![],
+ }, Feedback::new())
+}
- std
+/// `dump`: Dumps its arguments.
+pub async fn dump(args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
+ Pass::okay(Value::Table(args))
}
diff --git a/src/library/page.rs b/src/library/page.rs
index 7e4e6e54..42f29dbb 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -16,78 +16,46 @@ use super::*;
/// - `top`: The top margin (length or relative to height).
/// - `bottom`: The bottom margin (length or relative to height).
/// - `flip`: Flips custom or paper-defined width and height (boolean).
-pub fn page(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
+pub async fn page(mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- let mut args = call.args;
- let node = PageNode {
- paper: args.take::<Paper>(),
- width: args.take_with_key::<_, Length>("width", &mut f),
- height: args.take_with_key::<_, Length>("height", &mut f),
- margins: args.take_with_key::<_, ScaleLength>("margins", &mut f),
- left: args.take_with_key::<_, ScaleLength>("left", &mut f),
- right: args.take_with_key::<_, ScaleLength>("right", &mut f),
- top: args.take_with_key::<_, ScaleLength>("top", &mut f),
- bottom: args.take_with_key::<_, ScaleLength>("bottom", &mut f),
- flip: args.take_with_key::<_, bool>("flip", &mut f).unwrap_or(false),
- };
+ let paper = args.take::<Paper>();
+ let width = args.take_with_key::<_, Length>("width", &mut f);
+ let height = args.take_with_key::<_, Length>("height", &mut f);
+ let margins = args.take_with_key::<_, ScaleLength>("margins", &mut f);
+ let left = args.take_with_key::<_, ScaleLength>("left", &mut f);
+ let right = args.take_with_key::<_, ScaleLength>("right", &mut f);
+ let top = args.take_with_key::<_, ScaleLength>("top", &mut f);
+ let bottom = args.take_with_key::<_, ScaleLength>("bottom", &mut f);
+ let flip = args.take_with_key::<_, bool>("flip", &mut f).unwrap_or(false);
args.unexpected(&mut f);
- Pass::node(node, f)
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct PageNode {
- paper: Option<Paper>,
- width: Option<Length>,
- height: Option<Length>,
- margins: Option<ScaleLength>,
- left: Option<ScaleLength>,
- right: Option<ScaleLength>,
- top: Option<ScaleLength>,
- bottom: Option<ScaleLength>,
- flip: bool,
-}
-#[async_trait(?Send)]
-impl Layout for PageNode {
- async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>> {
- let mut style = ctx.style.page;
+ let mut style = ctx.style.page;
- if let Some(paper) = self.paper {
- style.class = paper.class;
- style.size = paper.size();
- } else if self.width.is_some() || self.height.is_some() {
- style.class = PaperClass::Custom;
- }
-
- self.width.with(|v| style.size.x = v.as_raw());
- self.height.with(|v| style.size.y = v.as_raw());
- self.margins.with(|v| style.margins.set_all(Some(v)));
- self.left.with(|v| style.margins.left = Some(v));
- self.right.with(|v| style.margins.right = Some(v));
- self.top.with(|v| style.margins.top = Some(v));
- self.bottom.with(|v| style.margins.bottom = Some(v));
+ if let Some(paper) = paper {
+ style.class = paper.class;
+ style.size = paper.size();
+ } else if width.is_some() || height.is_some() {
+ style.class = PaperClass::Custom;
+ }
- if self.flip {
- style.size.swap();
- }
+ width.with(|v| style.size.x = v.as_raw());
+ height.with(|v| style.size.y = v.as_raw());
+ margins.with(|v| style.margins.set_all(Some(v)));
+ left.with(|v| style.margins.left = Some(v));
+ right.with(|v| style.margins.right = Some(v));
+ top.with(|v| style.margins.top = Some(v));
+ bottom.with(|v| style.margins.bottom = Some(v));
- Pass::okay(vec![SetPageStyle(style)])
+ if flip {
+ style.size.swap();
}
+
+ Pass::commands(vec![SetPageStyle(style)], f)
}
/// `pagebreak`: Ends the current page.
-pub fn pagebreak(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
+pub async fn pagebreak(args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- call.args.unexpected(&mut f);
- Pass::node(PageBreakNode, f)
-}
-
-#[derive(Debug, Default, Clone, PartialEq)]
-struct PageBreakNode;
-
-#[async_trait(?Send)]
-impl Layout for PageBreakNode {
- async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
- Pass::okay(vec![BreakPage])
- }
+ args.unexpected(&mut f);
+ Pass::commands(vec![BreakPage], f)
}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index 81112cbd..3cd775c9 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -6,44 +6,32 @@ use super::*;
///
/// # Positional arguments
/// - The spacing (length or relative to font size).
-pub fn h(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
- spacing(call, Horizontal)
+pub async fn h(args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
+ spacing(args, ctx, Horizontal).await
}
/// `v`: Add vertical spacing.
///
/// # Positional arguments
/// - The spacing (length or relative to font size).
-pub fn v(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
- spacing(call, Vertical)
+pub async fn v(args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
+ spacing(args, ctx, Vertical).await
}
-fn spacing(call: FuncCall, axis: SpecAxis) -> Pass<SyntaxNode> {
+async fn spacing(
+ mut args: TableValue,
+ ctx: LayoutContext<'_>,
+ axis: SpecAxis,
+) -> Pass<Value> {
let mut f = Feedback::new();
- let mut args = call.args;
- let node = SpacingNode {
- spacing: args.expect::<ScaleLength>(&mut f)
- .map(|s| (axis, s))
- .or_missing(call.name.span, "spacing", &mut f),
- };
+ let spacing = args.expect::<ScaleLength>(&mut f).map(|s| (axis, s));
args.unexpected(&mut f);
- Pass::node(node, f)
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct SpacingNode {
- spacing: Option<(SpecAxis, ScaleLength)>,
-}
-#[async_trait(?Send)]
-impl Layout for SpacingNode {
- async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>> {
- Pass::okay(if let Some((axis, spacing)) = self.spacing {
- let axis = axis.to_generic(ctx.axes);
- let spacing = spacing.raw_scaled(ctx.style.text.font_size());
- vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
- } else {
- vec![]
- })
- }
+ Pass::commands(if let Some((axis, spacing)) = spacing {
+ let axis = axis.to_generic(ctx.axes);
+ let spacing = spacing.raw_scaled(ctx.style.text.font_size());
+ vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
+ } else {
+ vec![]
+ }, f)
}
diff --git a/src/library/val.rs b/src/library/val.rs
deleted file mode 100644
index 9df55401..00000000
--- a/src/library/val.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use super::*;
-
-/// `val`: Ignores all arguments and layouts its body flatly.
-///
-/// This is also the fallback function, which is used when a function name
-/// cannot be resolved.
-pub fn val(call: FuncCall, _: &ParseState) -> Pass<SyntaxNode> {
- let mut args = call.args;
- let node = ValNode {
- content: args.take::<SyntaxTree>(),
- };
- Pass::node(node, Feedback::new())
-}
-
-#[derive(Debug, Clone, PartialEq)]
-struct ValNode {
- content: Option<SyntaxTree>,
-}
-
-#[async_trait(?Send)]
-impl Layout for ValNode {
- async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
- Pass::okay(match &self.content {
- Some(tree) => vec![LayoutSyntaxTree(tree)],
- None => vec![],
- })
- }
-}