summaryrefslogtreecommitdiff
path: root/src/layout
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/layout
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/layout')
-rw-r--r--src/layout/mod.rs27
-rw-r--r--src/layout/tree.rs105
2 files changed, 66 insertions, 66 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 5f5a4859..837c19ec 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -10,9 +10,7 @@ mod tree;
/// Basic types used across the layouting engine.
pub mod prelude {
pub use super::primitive::*;
- pub use super::{
- BoxLayout, layout, Layout, LayoutContext, LayoutSpace, MultiLayout,
- };
+ pub use super::{BoxLayout, layout, LayoutContext, LayoutSpace, MultiLayout};
pub use Dir::*;
pub use GenAlign::*;
pub use GenAxis::*;
@@ -23,13 +21,11 @@ pub mod prelude {
pub use primitive::*;
pub use tree::layout_tree as layout;
-use async_trait::async_trait;
-
+use crate::compute::scope::Scope;
use crate::font::SharedFontLoader;
use crate::geom::{Margins, Size};
use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::tree::SyntaxTree;
-use crate::Pass;
use elements::LayoutElements;
use prelude::*;
@@ -48,18 +44,13 @@ pub struct BoxLayout {
pub elements: LayoutElements,
}
-/// Command-based layouting.
-#[async_trait(?Send)]
-pub trait Layout {
- /// Create a sequence of layouting commands to execute.
- async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
-}
-
/// The context for layouting.
-#[derive(Debug, Clone)]
+#[derive(Debug)]
pub struct LayoutContext<'a> {
/// The font loader to query fonts from when typesetting text.
pub loader: &'a SharedFontLoader,
+ /// The function scope.
+ pub scope: &'a Scope,
/// The style for pages and text.
pub style: &'a LayoutStyle,
/// The unpadded size of this container (the base 100% for relative sizes).
@@ -118,11 +109,11 @@ impl LayoutSpace {
}
/// A sequence of layouting commands.
-pub type Commands<'a> = Vec<Command<'a>>;
+pub type Commands = Vec<Command>;
/// Commands executable by the layouting engine.
-#[derive(Debug, Clone)]
-pub enum Command<'a> {
+#[derive(Debug, Clone, PartialEq)]
+pub enum Command {
/// Layout the given tree in the current context (i.e. not nested). The
/// content of the tree is not laid out into a separate box and then added,
/// but simply laid out flatly in the active layouting process.
@@ -130,7 +121,7 @@ pub enum Command<'a> {
/// This has the effect that the content fits nicely into the active line
/// layouting, enabling functions to e.g. change the style of some piece of
/// text while keeping it part of the current paragraph.
- LayoutSyntaxTree(&'a SyntaxTree),
+ LayoutSyntaxTree(SyntaxTree),
/// Add a finished layout.
Add(BoxLayout),
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 3abdc934..39e111bd 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -1,9 +1,10 @@
//! Layouting of syntax trees.
+use crate::compute::value::Value;
use crate::style::LayoutStyle;
use crate::syntax::decoration::Decoration;
-use crate::syntax::span::{Span, Spanned};
-use crate::syntax::tree::{DynamicNode, SyntaxNode, SyntaxTree};
+use crate::syntax::span::{Offset, Span, Spanned};
+use crate::syntax::tree::{CallExpr, SyntaxNode, SyntaxTree};
use crate::{DynFuture, Feedback, Pass};
use super::line::{LineContext, LineLayouter};
use super::text::{layout_text, TextContext};
@@ -66,60 +67,28 @@ impl<'a> TreeLayouter<'a> {
self.style.text.word_spacing(),
SpacingKind::WORD,
);
- },
-
+ }
SyntaxNode::Linebreak => self.layouter.finish_line(),
SyntaxNode::ToggleItalic => {
self.style.text.italic = !self.style.text.italic;
decorate(self, Decoration::Italic);
}
-
SyntaxNode::ToggleBolder => {
self.style.text.bolder = !self.style.text.bolder;
decorate(self, Decoration::Bold);
}
SyntaxNode::Text(text) => {
- if self.style.text.italic {
- decorate(self, Decoration::Italic);
- }
-
- if self.style.text.bolder {
- decorate(self, Decoration::Bold);
- }
-
+ if self.style.text.italic { decorate(self, Decoration::Italic); }
+ if self.style.text.bolder { decorate(self, Decoration::Bold); }
self.layout_text(text).await;
}
- SyntaxNode::Raw(lines) => {
- // TODO: Make this more efficient.
- let fallback = self.style.text.fallback.clone();
- self.style.text.fallback
- .list_mut()
- .insert(0, "monospace".to_string());
-
- self.style.text.fallback.flatten();
-
- // Layout the first line.
- let mut iter = lines.iter();
- if let Some(line) = iter.next() {
- self.layout_text(line).await;
- }
-
- // Put a newline before each following line.
- for line in iter {
- self.layouter.finish_line();
- self.layout_text(line).await;
- }
-
- self.style.text.fallback = fallback;
- }
-
+ SyntaxNode::Raw(lines) => self.layout_raw(lines).await,
SyntaxNode::Par(par) => self.layout_par(par).await,
-
- SyntaxNode::Dyn(dynamic) => {
- self.layout_dyn(Spanned::new(dynamic.as_ref(), node.span)).await;
+ SyntaxNode::Call(call) => {
+ self.layout_call(Spanned::new(call, node.span)).await;
}
}
}
@@ -133,19 +102,35 @@ impl<'a> TreeLayouter<'a> {
self.layout_tree(par).await;
}
- async fn layout_dyn(&mut self, dynamic: Spanned<&dyn DynamicNode>) {
- // Execute the dynamic node's command-generating layout function.
- let layouted = dynamic.v.layout(LayoutContext {
+ async fn layout_call(&mut self, call: Spanned<&CallExpr>) {
+ let name = call.v.name.v.as_str();
+ let span = call.v.name.span.offset(call.span.start);
+
+ let (func, deco) = if let Some(func) = self.ctx.scope.func(name) {
+ (func, Decoration::Resolved)
+ } else {
+ error!(@self.feedback, span, "unknown function");
+ (self.ctx.scope.fallback(), Decoration::Unresolved)
+ };
+
+ self.feedback.decorations.push(Spanned::new(deco, span));
+
+ let args = call.v.args.eval();
+ let pass = func(args, LayoutContext {
style: &self.style,
spaces: self.layouter.remaining(),
root: true,
..self.ctx
}).await;
- self.feedback.extend_offset(layouted.feedback, dynamic.span.start);
+ self.feedback.extend_offset(pass.feedback, call.span.start);
- for command in layouted.output {
- self.execute_command(command, dynamic.span).await;
+ if let Value::Commands(commands) = pass.output {
+ for command in commands {
+ self.execute_command(command, call.span).await;
+ }
+ } else {
+ self.layout_raw(&[format!("{:?}", pass.output)]).await;
}
}
@@ -163,11 +148,35 @@ impl<'a> TreeLayouter<'a> {
);
}
- async fn execute_command(&mut self, command: Command<'_>, span: Span) {
+ async fn layout_raw(&mut self, lines: &[String]) {
+ // TODO: Make this more efficient.
+ let fallback = self.style.text.fallback.clone();
+ self.style.text.fallback
+ .list_mut()
+ .insert(0, "monospace".to_string());
+
+ self.style.text.fallback.flatten();
+
+ // Layout the first line.
+ let mut iter = lines.iter();
+ if let Some(line) = iter.next() {
+ self.layout_text(line).await;
+ }
+
+ // Put a newline before each following line.
+ for line in iter {
+ self.layouter.finish_line();
+ self.layout_text(line).await;
+ }
+
+ self.style.text.fallback = fallback;
+ }
+
+ async fn execute_command(&mut self, command: Command, span: Span) {
use Command::*;
match command {
- LayoutSyntaxTree(tree) => self.layout_tree(tree).await,
+ LayoutSyntaxTree(tree) => self.layout_tree(&tree).await,
Add(layout) => self.layouter.add(layout),
AddMultiple(layouts) => self.layouter.add_multiple(layouts),