summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-01 15:03:37 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-01 15:03:37 +0200
commit7fcad452b87c8bd31a9b7dfba78c1b1a92d33dd9 (patch)
treec1e82792456be54fd41e7b143be302dcd874e30b /src
parentaafd3c95cacd829b647cfab1533de5d4833b9a04 (diff)
Reorganize ast types πŸ•
Diffstat (limited to 'src')
-rw-r--r--src/compute/dict.rs68
-rw-r--r--src/compute/value.rs10
-rw-r--r--src/layout/mod.rs4
-rw-r--r--src/layout/tree.rs43
-rw-r--r--src/lib.rs6
-rw-r--r--src/library/align.rs2
-rw-r--r--src/library/boxed.rs2
-rw-r--r--src/library/font.rs2
-rw-r--r--src/parse/mod.rs148
-rw-r--r--src/parse/resolve.rs8
-rw-r--r--src/parse/tests.rs160
-rw-r--r--src/syntax/expr.rs124
-rw-r--r--src/syntax/ident.rs58
-rw-r--r--src/syntax/lit.rs99
-rw-r--r--src/syntax/mod.rs14
-rw-r--r--src/syntax/span.rs4
-rw-r--r--src/syntax/tree.rs249
17 files changed, 554 insertions, 447 deletions
diff --git a/src/compute/dict.rs b/src/compute/dict.rs
index e4df0048..e6216572 100644
--- a/src/compute/dict.rs
+++ b/src/compute/dict.rs
@@ -52,38 +52,38 @@ impl<V> Dict<V> {
/// Get a reference to the value with the given key.
pub fn get<'a, K>(&self, key: K) -> Option<&V>
where
- K: Into<BorrowedKey<'a>>,
+ K: Into<RefKey<'a>>,
{
match key.into() {
- BorrowedKey::Num(num) => self.nums.get(&num),
- BorrowedKey::Str(string) => self.strs.get(string),
+ RefKey::Num(num) => self.nums.get(&num),
+ RefKey::Str(string) => self.strs.get(string),
}
}
/// Borrow the value with the given key mutably.
pub fn get_mut<'a, K>(&mut self, key: K) -> Option<&mut V>
where
- K: Into<BorrowedKey<'a>>,
+ K: Into<RefKey<'a>>,
{
match key.into() {
- BorrowedKey::Num(num) => self.nums.get_mut(&num),
- BorrowedKey::Str(string) => self.strs.get_mut(string),
+ RefKey::Num(num) => self.nums.get_mut(&num),
+ RefKey::Str(string) => self.strs.get_mut(string),
}
}
/// Insert a value into the dictionary.
pub fn insert<K>(&mut self, key: K, value: V)
where
- K: Into<OwnedKey>,
+ K: Into<DictKey>,
{
match key.into() {
- OwnedKey::Num(num) => {
+ DictKey::Num(num) => {
self.nums.insert(num, value);
if self.lowest_free == num {
self.lowest_free += 1;
}
}
- OwnedKey::Str(string) => {
+ DictKey::Str(string) => {
self.strs.insert(string, value);
}
}
@@ -92,14 +92,14 @@ impl<V> Dict<V> {
/// Remove the value with the given key from the dictionary.
pub fn remove<'a, K>(&mut self, key: K) -> Option<V>
where
- K: Into<BorrowedKey<'a>>,
+ K: Into<RefKey<'a>>,
{
match key.into() {
- BorrowedKey::Num(num) => {
+ RefKey::Num(num) => {
self.lowest_free = self.lowest_free.min(num);
self.nums.remove(&num)
}
- BorrowedKey::Str(string) => self.strs.remove(string),
+ RefKey::Str(string) => self.strs.remove(string),
}
}
@@ -116,10 +116,10 @@ impl<V> Dict<V> {
}
/// Iterator over all borrowed keys and values.
- pub fn iter(&self) -> impl Iterator<Item = (BorrowedKey, &V)> {
+ pub fn iter(&self) -> impl Iterator<Item = (RefKey, &V)> {
self.nums()
- .map(|(&k, v)| (BorrowedKey::Num(k), v))
- .chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v)))
+ .map(|(&k, v)| (RefKey::Num(k), v))
+ .chain(self.strs().map(|(k, v)| (RefKey::Str(k), v)))
}
/// Iterate over all values in the dictionary.
@@ -138,11 +138,11 @@ impl<V> Dict<V> {
}
/// Move into an owned iterator over owned keys and values.
- pub fn into_iter(self) -> impl Iterator<Item = (OwnedKey, V)> {
+ pub fn into_iter(self) -> impl Iterator<Item = (DictKey, V)> {
self.nums
.into_iter()
- .map(|(k, v)| (OwnedKey::Num(k), v))
- .chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(k), v)))
+ .map(|(k, v)| (DictKey::Num(k), v))
+ .chain(self.strs.into_iter().map(|(k, v)| (DictKey::Str(k), v)))
}
/// Move into an owned iterator over all values in the dictionary.
@@ -166,7 +166,7 @@ impl<V> Dict<V> {
impl<'a, K, V> Index<K> for Dict<V>
where
- K: Into<BorrowedKey<'a>>,
+ K: Into<RefKey<'a>>,
{
type Output = V;
@@ -230,33 +230,39 @@ impl<V: Debug> Debug for Dict<V> {
/// The owned variant of a dictionary key.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum OwnedKey {
+pub enum DictKey {
Num(u64),
Str(String),
}
-impl From<BorrowedKey<'_>> for OwnedKey {
- fn from(key: BorrowedKey<'_>) -> Self {
+impl From<&Self> for DictKey {
+ fn from(key: &Self) -> Self {
+ key.clone()
+ }
+}
+
+impl From<RefKey<'_>> for DictKey {
+ fn from(key: RefKey<'_>) -> Self {
match key {
- BorrowedKey::Num(num) => Self::Num(num),
- BorrowedKey::Str(string) => Self::Str(string.to_string()),
+ RefKey::Num(num) => Self::Num(num),
+ RefKey::Str(string) => Self::Str(string.to_string()),
}
}
}
-impl From<u64> for OwnedKey {
+impl From<u64> for DictKey {
fn from(num: u64) -> Self {
Self::Num(num)
}
}
-impl From<String> for OwnedKey {
+impl From<String> for DictKey {
fn from(string: String) -> Self {
Self::Str(string)
}
}
-impl From<&'static str> for OwnedKey {
+impl From<&'static str> for DictKey {
fn from(string: &'static str) -> Self {
Self::Str(string.to_string())
}
@@ -264,24 +270,24 @@ impl From<&'static str> for OwnedKey {
/// The borrowed variant of a dictionary key.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum BorrowedKey<'a> {
+pub enum RefKey<'a> {
Num(u64),
Str(&'a str),
}
-impl From<u64> for BorrowedKey<'static> {
+impl From<u64> for RefKey<'static> {
fn from(num: u64) -> Self {
Self::Num(num)
}
}
-impl<'a> From<&'a String> for BorrowedKey<'a> {
+impl<'a> From<&'a String> for RefKey<'a> {
fn from(string: &'a String) -> Self {
Self::Str(&string)
}
}
-impl<'a> From<&'a str> for BorrowedKey<'a> {
+impl<'a> From<&'a str> for RefKey<'a> {
fn from(string: &'a str) -> Self {
Self::Str(string)
}
diff --git a/src/compute/value.rs b/src/compute/value.rs
index 1fcc4be2..cfbc302c 100644
--- a/src/compute/value.rs
+++ b/src/compute/value.rs
@@ -11,7 +11,7 @@ use crate::color::RgbaColor;
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
use crate::length::{Length, ScaleLength};
use crate::paper::Paper;
-use crate::syntax::{Ident, Span, SpanWith, Spanned, SyntaxNode, SyntaxTree};
+use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
use crate::{DynFuture, Feedback, Pass};
/// A computational value.
@@ -32,7 +32,7 @@ pub enum Value {
/// A dictionary value: `(false, 12cm, greeting="hi")`.
Dict(DictValue),
/// A syntax tree containing typesetting content.
- Tree(SyntaxTree),
+ Tree(SynTree),
/// An executable function.
Func(FuncValue),
/// Layouting commands.
@@ -76,7 +76,7 @@ impl Spanned<Value> {
for entry in dict.into_values() {
if let Some(last_end) = end {
let span = Span::new(last_end, entry.key.start);
- let tree = vec![SyntaxNode::Spacing.span_with(span)];
+ let tree = vec![SynNode::Spacing.span_with(span)];
commands.push(Command::LayoutSyntaxTree(tree));
}
@@ -89,7 +89,7 @@ impl Spanned<Value> {
// Format with debug.
val => {
let fmt = format!("{:?}", val);
- let tree = vec![SyntaxNode::Text(fmt).span_with(self.span)];
+ let tree = vec![SynNode::Text(fmt).span_with(self.span)];
vec![Command::LayoutSyntaxTree(tree)]
}
}
@@ -340,7 +340,7 @@ impl_match!(String, "string", Value::Str(s) => s.clone());
impl_match!(bool, "bool", &Value::Bool(b) => b);
impl_match!(f64, "number", &Value::Number(n) => n);
impl_match!(Length, "length", &Value::Length(l) => l);
-impl_match!(SyntaxTree, "tree", Value::Tree(t) => t.clone());
+impl_match!(SynTree, "tree", Value::Tree(t) => t.clone());
impl_match!(DictValue, "dict", Value::Dict(t) => t.clone());
impl_match!(FuncValue, "function", Value::Func(f) => f.clone());
impl_match!(ScaleLength, "number or length",
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index c053cda0..768ff160 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -25,7 +25,7 @@ use crate::compute::scope::Scope;
use crate::font::SharedFontLoader;
use crate::geom::{Margins, Size};
use crate::style::{LayoutStyle, PageStyle, TextStyle};
-use crate::syntax::SyntaxTree;
+use crate::syntax::SynTree;
use elements::LayoutElements;
use prelude::*;
@@ -121,7 +121,7 @@ pub enum Command {
/// 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(SyntaxTree),
+ LayoutSyntaxTree(SynTree),
/// Add a finished layout.
Add(BoxLayout),
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 24a00367..14ff4630 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -5,12 +5,12 @@ use super::text::{layout_text, TextContext};
use super::*;
use crate::style::LayoutStyle;
use crate::syntax::{
- CallExpr, Decoration, Heading, Raw, Span, SpanWith, Spanned, SyntaxNode, SyntaxTree,
+ Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
};
use crate::{DynFuture, Feedback, Pass};
/// Layout a syntax tree into a collection of boxes.
-pub async fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
+pub async fn layout_tree(tree: &SynTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
let mut layouter = TreeLayouter::new(ctx);
layouter.layout_tree(tree).await;
layouter.finish()
@@ -44,7 +44,7 @@ impl<'a> TreeLayouter<'a> {
Pass::new(self.layouter.finish(), self.feedback)
}
- fn layout_tree<'t>(&'t mut self, tree: &'t SyntaxTree) -> DynFuture<'t, ()> {
+ fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> {
Box::pin(async move {
for node in tree {
self.layout_node(node).await;
@@ -52,26 +52,26 @@ impl<'a> TreeLayouter<'a> {
})
}
- async fn layout_node(&mut self, node: &Spanned<SyntaxNode>) {
+ async fn layout_node(&mut self, node: &Spanned<SynNode>) {
let decorate = |this: &mut Self, deco: Decoration| {
this.feedback.decorations.push(deco.span_with(node.span));
};
match &node.v {
- SyntaxNode::Spacing => self.layout_space(),
- SyntaxNode::Linebreak => self.layouter.finish_line(),
- SyntaxNode::Parbreak => self.layout_parbreak(),
+ SynNode::Spacing => self.layout_space(),
+ SynNode::Linebreak => self.layouter.finish_line(),
+ SynNode::Parbreak => self.layout_parbreak(),
- SyntaxNode::ToggleItalic => {
+ SynNode::ToggleItalic => {
self.style.text.italic = !self.style.text.italic;
decorate(self, Decoration::Italic);
}
- SyntaxNode::ToggleBolder => {
+ SynNode::ToggleBolder => {
self.style.text.bolder = !self.style.text.bolder;
decorate(self, Decoration::Bold);
}
- SyntaxNode::Text(text) => {
+ SynNode::Text(text) => {
if self.style.text.italic {
decorate(self, Decoration::Italic);
}
@@ -81,12 +81,11 @@ impl<'a> TreeLayouter<'a> {
self.layout_text(text).await;
}
- SyntaxNode::Heading(heading) => self.layout_heading(heading).await,
+ SynNode::Raw(raw) => self.layout_raw(raw).await,
+ SynNode::Heading(heading) => self.layout_heading(heading).await,
- SyntaxNode::Raw(raw) => self.layout_raw(raw).await,
-
- SyntaxNode::Call(call) => {
- self.layout_call(call.span_with(node.span)).await;
+ SynNode::Expr(expr) => {
+ self.layout_expr(expr.span_with(node.span)).await;
}
}
}
@@ -115,19 +114,19 @@ impl<'a> TreeLayouter<'a> {
);
}
- async fn layout_heading(&mut self, heading: &Heading) {
+ async fn layout_heading(&mut self, heading: &NodeHeading) {
let style = self.style.text.clone();
self.style.text.font_scale *= 1.5 - 0.1 * heading.level.v.min(5) as f64;
self.style.text.bolder = true;
self.layout_parbreak();
- self.layout_tree(&heading.tree).await;
+ self.layout_tree(&heading.contents).await;
self.layout_parbreak();
self.style.text = style;
}
- async fn layout_raw(&mut self, raw: &Raw) {
+ async fn layout_raw(&mut self, raw: &NodeRaw) {
if !raw.inline {
self.layout_parbreak();
}
@@ -153,7 +152,7 @@ impl<'a> TreeLayouter<'a> {
}
}
- async fn layout_call(&mut self, call: Spanned<&CallExpr>) {
+ async fn layout_expr(&mut self, expr: Spanned<&Expr>) {
let ctx = LayoutContext {
style: &self.style,
spaces: self.layouter.remaining(),
@@ -161,11 +160,11 @@ impl<'a> TreeLayouter<'a> {
..self.ctx
};
- let val = call.v.eval(&ctx, &mut self.feedback).await;
- let commands = val.span_with(call.span).into_commands();
+ let val = expr.v.eval(&ctx, &mut self.feedback).await;
+ let commands = val.span_with(expr.span).into_commands();
for command in commands {
- self.execute_command(command, call.span).await;
+ self.execute_command(command, expr.span).await;
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 10e1f810..f79acbdf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,7 +50,7 @@ use crate::diagnostic::Diagnostic;
use crate::font::SharedFontLoader;
use crate::layout::{Commands, MultiLayout};
use crate::style::{LayoutStyle, PageStyle, TextStyle};
-use crate::syntax::{Decoration, Offset, Pos, SpanVec, SyntaxTree};
+use crate::syntax::{Decoration, Offset, Pos, SpanVec, SynTree};
/// Transforms source code into typesetted layouts.
///
@@ -85,12 +85,12 @@ impl Typesetter {
}
/// Parse source code into a syntax tree.
- pub fn parse(&self, src: &str) -> Pass<SyntaxTree> {
+ pub fn parse(&self, src: &str) -> Pass<SynTree> {
parse::parse(src)
}
/// Layout a syntax tree and return the produced layout.
- pub async fn layout(&self, tree: &SyntaxTree) -> Pass<MultiLayout> {
+ pub async fn layout(&self, tree: &SynTree) -> Pass<MultiLayout> {
use crate::layout::prelude::*;
let margins = self.style.page.margins();
diff --git a/src/library/align.rs b/src/library/align.rs
index 55259fd5..c909087a 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -17,7 +17,7 @@ use super::*;
pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
- let content = args.take::<SyntaxTree>();
+ let content = args.take::<SynTree>();
let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut f);
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f);
let all = args
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 672e77d1..e02b8c0c 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -13,7 +13,7 @@ pub async fn boxed(
) -> Pass<Value> {
let mut f = Feedback::new();
- let content = args.take::<SyntaxTree>().unwrap_or(SyntaxTree::new());
+ let content = args.take::<SynTree>().unwrap_or(SynTree::new());
ctx.base = ctx.spaces[0].size;
ctx.spaces.truncate(1);
diff --git a/src/library/font.rs b/src/library/font.rs
index e9f6f9b4..e0133822 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -28,7 +28,7 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
let mut text = ctx.style.text.clone();
let mut updated_fallback = false;
- let content = args.take::<SyntaxTree>();
+ let content = args.take::<SynTree>();
if let Some(s) = args.take::<ScaleLength>() {
match s {
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 75ca7eb4..7a0002d6 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -12,14 +12,13 @@ pub use tokens::*;
use std::str::FromStr;
-use super::*;
use crate::color::RgbaColor;
-use crate::compute::dict::SpannedEntry;
+use crate::compute::dict::DictKey;
use crate::syntax::*;
use crate::{Feedback, Pass};
/// Parse a string of source code.
-pub fn parse(src: &str) -> Pass<SyntaxTree> {
+pub fn parse(src: &str) -> Pass<SynTree> {
Parser::new(src).parse()
}
@@ -42,7 +41,7 @@ impl<'s> Parser<'s> {
}
}
- fn parse(mut self) -> Pass<SyntaxTree> {
+ fn parse(mut self) -> Pass<SynTree> {
let tree = self.parse_body_contents();
Pass::new(tree, self.feedback)
}
@@ -50,8 +49,8 @@ impl<'s> Parser<'s> {
// Typesetting content.
impl Parser<'_> {
- fn parse_body_contents(&mut self) -> SyntaxTree {
- let mut tree = SyntaxTree::new();
+ fn parse_body_contents(&mut self) -> SynTree {
+ let mut tree = SynTree::new();
self.at_block_or_line_start = true;
while !self.eof() {
@@ -63,7 +62,7 @@ impl Parser<'_> {
tree
}
- fn parse_node(&mut self) -> Option<Spanned<SyntaxNode>> {
+ fn parse_node(&mut self) -> Option<Spanned<SynNode>> {
let token = self.peek()?;
let end = Span::at(token.span.end);
@@ -83,11 +82,7 @@ impl Parser<'_> {
self.at_block_or_line_start = true;
}
- self.with_span(if n >= 2 {
- SyntaxNode::Parbreak
- } else {
- SyntaxNode::Spacing
- })
+ self.with_span(if n >= 2 { SynNode::Parbreak } else { SynNode::Spacing })
}
Token::LineComment(_) | Token::BlockComment(_) => {
@@ -99,15 +94,15 @@ impl Parser<'_> {
Token::LeftBracket => {
let call = self.parse_bracket_call(false);
self.at_block_or_line_start = false;
- call.map(SyntaxNode::Call)
+ call.map(|c| SynNode::Expr(Expr::Call(c)))
}
- Token::Star => self.with_span(SyntaxNode::ToggleBolder),
- Token::Underscore => self.with_span(SyntaxNode::ToggleItalic),
- Token::Backslash => self.with_span(SyntaxNode::Linebreak),
+ Token::Star => self.with_span(SynNode::ToggleBolder),
+ Token::Underscore => self.with_span(SynNode::ToggleItalic),
+ Token::Backslash => self.with_span(SynNode::Linebreak),
Token::Hashtag if was_at_block_or_line_start => {
- self.parse_heading().map(SyntaxNode::Heading)
+ self.parse_heading().map(SynNode::Heading)
}
Token::Raw { raw, backticks, terminated } => {
@@ -116,11 +111,11 @@ impl Parser<'_> {
}
let raw = resolve::resolve_raw(raw, backticks);
- self.with_span(SyntaxNode::Raw(raw))
+ self.with_span(SynNode::Raw(raw))
}
- Token::Text(text) => self.with_span(SyntaxNode::Text(text.to_string())),
- Token::Hashtag => self.with_span(SyntaxNode::Text("#".to_string())),
+ Token::Text(text) => self.with_span(SynNode::Text(text.to_string())),
+ Token::Hashtag => self.with_span(SynNode::Text("#".to_string())),
Token::UnicodeEscape { sequence, terminated } => {
if !terminated {
@@ -128,7 +123,7 @@ impl Parser<'_> {
}
if let Some(c) = resolve::resolve_hex(sequence) {
- self.with_span(SyntaxNode::Text(c.to_string()))
+ self.with_span(SynNode::Text(c.to_string()))
} else {
error!(@self.feedback, token.span, "invalid unicode escape sequence");
// TODO: Decide whether to render the escape sequence.
@@ -145,7 +140,7 @@ impl Parser<'_> {
})
}
- fn parse_heading(&mut self) -> Spanned<Heading> {
+ fn parse_heading(&mut self) -> Spanned<NodeHeading> {
let start = self.pos();
self.assert(Token::Hashtag);
@@ -167,7 +162,7 @@ impl Parser<'_> {
self.skip_ws();
- let mut tree = SyntaxTree::new();
+ let mut tree = SynTree::new();
while !self.eof() && !matches!(self.peekv(), Some(Token::Space(n)) if n >= 1) {
if let Some(node) = self.parse_node() {
tree.push(node);
@@ -175,13 +170,13 @@ impl Parser<'_> {
}
let span = Span::new(start, self.pos());
- Heading { level, tree }.span_with(span)
+ NodeHeading { level, contents: tree }.span_with(span)
}
}
// Function calls.
impl Parser<'_> {
- fn parse_bracket_call(&mut self, chained: bool) -> Spanned<CallExpr> {
+ fn parse_bracket_call(&mut self, chained: bool) -> Spanned<ExprCall> {
let before_bracket = self.pos();
if !chained {
self.start_group(Group::Bracket);
@@ -203,9 +198,9 @@ impl Parser<'_> {
Some(_) => {
self.expected_at("colon", name.span.end);
while self.eat().is_some() {}
- DictExpr::new()
+ LitDict::default()
}
- None => DictExpr::new(),
+ None => LitDict::default(),
};
self.end_group();
@@ -213,8 +208,9 @@ impl Parser<'_> {
let (has_chained_child, end) = if self.peek().is_some() {
let item = self.parse_bracket_call(true);
let span = item.span;
- let t = vec![item.map(SyntaxNode::Call)];
- args.push(SpannedEntry::val(Expr::Tree(t).span_with(span)));
+ let tree = vec![item.map(|c| SynNode::Expr(Expr::Call(c)))];
+ let expr = Expr::Lit(Lit::Content(tree));
+ args.0.push(LitDictEntry { key: None, value: expr.span_with(span) });
(true, span.end)
} else {
self.tokens.pop_mode();
@@ -227,40 +223,41 @@ impl Parser<'_> {
if self.check(Token::LeftBracket) && !has_chained_child {
self.start_group(Group::Bracket);
self.tokens.push_mode(TokenMode::Body);
-
let body = self.parse_body_contents();
-
self.tokens.pop_mode();
let body_span = self.end_group();
- let expr = Expr::Tree(body);
- args.push(SpannedEntry::val(expr.span_with(body_span)));
+ let expr = Expr::Lit(Lit::Content(body));
+ args.0.push(LitDictEntry {
+ key: None,
+ value: expr.span_with(body_span),
+ });
span.expand(body_span);
}
- CallExpr { name, args }.span_with(span)
+ ExprCall { name, args }.span_with(span)
}
- fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<CallExpr> {
+ fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<ExprCall> {
self.start_group(Group::Paren);
let args = self.parse_dict_contents().0;
let args_span = self.end_group();
let span = Span::merge(name.span, args_span);
- CallExpr { name, args }.span_with(span)
+ ExprCall { name, args }.span_with(span)
}
}
// Dicts.
impl Parser<'_> {
- fn parse_dict_contents(&mut self) -> (DictExpr, bool) {
- let mut dict = DictExpr::new();
+ fn parse_dict_contents(&mut self) -> (LitDict, bool) {
+ let mut dict = LitDict::default();
let mut comma_and_keyless = true;
while {
self.skip_ws();
!self.eof()
} {
- let (key, val) = if let Some(ident) = self.parse_ident() {
+ let (key, value) = if let Some(ident) = self.parse_ident() {
self.skip_ws();
match self.peekv() {
@@ -268,7 +265,7 @@ impl Parser<'_> {
self.eat();
self.skip_ws();
if let Some(value) = self.parse_expr() {
- (Some(ident), value)
+ (Some(ident.map(|id| DictKey::Str(id.0))), value)
} else {
self.expected("value");
continue;
@@ -280,7 +277,7 @@ impl Parser<'_> {
(None, call.map(Expr::Call))
}
- _ => (None, ident.map(Expr::Ident)),
+ _ => (None, ident.map(|id| Expr::Lit(Lit::Ident(id)))),
}
} else if let Some(value) = self.parse_expr() {
(None, value)
@@ -289,17 +286,16 @@ impl Parser<'_> {
continue;
};
- let behind = val.span.end;
- if let Some(key) = key {
+ if let Some(key) = &key {
comma_and_keyless = false;
- dict.insert(key.v.0, SpannedEntry::new(key.span, val));
self.feedback
.decorations
.push(Decoration::DictKey.span_with(key.span));
- } else {
- dict.push(SpannedEntry::val(val));
}
+ let behind = value.span.end;
+ dict.0.push(LitDictEntry { key, value });
+
if {
self.skip_ws();
self.eof()
@@ -311,27 +307,25 @@ impl Parser<'_> {
comma_and_keyless = false;
}
- let coercable = comma_and_keyless && !dict.is_empty();
+ let coercable = comma_and_keyless && !dict.0.is_empty();
(dict, coercable)
}
}
-type Binop = fn(Box<Spanned<Expr>>, Box<Spanned<Expr>>) -> Expr;
-
// Expressions and values.
impl Parser<'_> {
fn parse_expr(&mut self) -> Option<Spanned<Expr>> {
self.parse_binops("summand", Self::parse_term, |token| match token {
- Token::Plus => Some(Expr::Add),
- Token::Hyphen => Some(Expr::Sub),
+ Token::Plus => Some(BinOp::Add),
+ Token::Hyphen => Some(BinOp::Sub),
_ => None,
})
}
fn parse_term(&mut self) -> Option<Spanned<Expr>> {
self.parse_binops("factor", Self::parse_factor, |token| match token {
- Token::Star => Some(Expr::Mul),
- Token::Slash => Some(Expr::Div),
+ Token::Star => Some(BinOp::Mul),
+ Token::Slash => Some(BinOp::Div),
_ => None,
})
}
@@ -341,7 +335,7 @@ impl Parser<'_> {
&mut self,
operand_name: &str,
mut parse_operand: impl FnMut(&mut Self) -> Option<Spanned<Expr>>,
- mut parse_op: impl FnMut(Token) -> Option<Binop>,
+ mut parse_op: impl FnMut(Token) -> Option<BinOp>,
) -> Option<Spanned<Expr>> {
let mut left = parse_operand(self)?;
@@ -353,8 +347,12 @@ impl Parser<'_> {
if let Some(right) = parse_operand(self) {
let span = Span::merge(left.span, right.span);
- let v = op(Box::new(left), Box::new(right));
- left = v.span_with(span);
+ let expr = Expr::Binary(ExprBinary {
+ lhs: left.map(Box::new),
+ op: op.span_with(token.span),
+ rhs: right.map(Box::new),
+ });
+ left = expr.span_with(span);
self.skip_ws();
continue;
}
@@ -375,7 +373,11 @@ impl Parser<'_> {
self.skip_ws();
if let Some(factor) = self.parse_factor() {
let span = Span::merge(hyph.span, factor.span);
- Some(Expr::Neg(Box::new(factor)).span_with(span))
+ let expr = Expr::Unary(ExprUnary {
+ op: UnOp::Neg.span_with(hyph.span),
+ expr: factor.map(Box::new),
+ });
+ Some(expr.span_with(span))
} else {
error!(@self.feedback, hyph.span, "dangling minus");
None
@@ -396,7 +398,7 @@ impl Parser<'_> {
if self.check(Token::LeftParen) {
self.parse_paren_call(name).map(Expr::Call)
} else {
- name.map(Expr::Ident)
+ name.map(|n| Expr::Lit(Lit::Ident(n)))
}
}
@@ -404,21 +406,19 @@ impl Parser<'_> {
if !terminated {
self.expected_at("quote", span.end);
}
- self.with_span(Expr::Str(resolve::resolve_string(string)))
+ self.with_span(Expr::Lit(Lit::Str(resolve::resolve_string(string))))
}
- Token::Bool(b) => self.with_span(Expr::Bool(b)),
- Token::Number(n) => self.with_span(Expr::Number(n)),
- Token::Length(s) => self.with_span(Expr::Length(s)),
+ Token::Bool(b) => self.with_span(Expr::Lit(Lit::Bool(b))),
+ Token::Number(n) => self.with_span(Expr::Lit(Lit::Float(n))),
+ Token::Length(s) => self.with_span(Expr::Lit(Lit::Length(s))),
Token::Hex(s) => {
- if let Ok(color) = RgbaColor::from_str(s) {
- self.with_span(Expr::Color(color))
- } else {
+ let color = RgbaColor::from_str(s).unwrap_or_else(|_| {
// Heal color by assuming black.
error!(@self.feedback, span, "invalid color");
- let healed = RgbaColor::new_healed(0, 0, 0, 255);
- self.with_span(Expr::Color(healed))
- }
+ RgbaColor::new_healed(0, 0, 0, 255)
+ });
+ self.with_span(Expr::Lit(Lit::Color(color)))
}
// This could be a dictionary or a parenthesized expression. We
@@ -430,9 +430,9 @@ impl Parser<'_> {
let span = self.end_group();
let expr = if coercable {
- dict.into_values().next().expect("dict is coercable").val.v
+ dict.0.into_iter().next().expect("dict is coercable").value.v
} else {
- Expr::Dict(dict)
+ Expr::Lit(Lit::Dict(dict))
};
expr.span_with(span)
@@ -442,19 +442,17 @@ impl Parser<'_> {
Token::LeftBrace => {
self.start_group(Group::Brace);
self.tokens.push_mode(TokenMode::Body);
-
let tree = self.parse_body_contents();
-
self.tokens.pop_mode();
let span = self.end_group();
- Expr::Tree(tree).span_with(span)
+ Expr::Lit(Lit::Content(tree)).span_with(span)
}
// This is a bracketed function call.
Token::LeftBracket => {
let call = self.parse_bracket_call(false);
- let tree = vec![call.map(SyntaxNode::Call)];
- Expr::Tree(tree).span_with(span)
+ let tree = vec![call.map(|c| SynNode::Expr(Expr::Call(c)))];
+ Expr::Lit(Lit::Content(tree)).span_with(span)
}
_ => return None,
diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs
index d4babd25..f9919373 100644
--- a/src/parse/resolve.rs
+++ b/src/parse/resolve.rs
@@ -1,7 +1,7 @@
//! Resolve strings and raw blocks.
use super::{is_newline, Scanner};
-use crate::syntax::{Ident, Raw};
+use crate::syntax::{Ident, NodeRaw};
/// Resolves all escape sequences in a string.
pub fn resolve_string(string: &str) -> String {
@@ -49,17 +49,17 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
}
/// Resolves the language tag and trims the raw text.
-pub fn resolve_raw(raw: &str, backticks: usize) -> Raw {
+pub fn resolve_raw(raw: &str, backticks: usize) -> NodeRaw {
if backticks > 1 {
let (tag, inner) = split_at_lang_tag(raw);
let (lines, had_newline) = trim_and_split_raw(inner);
- Raw {
+ NodeRaw {
lang: Ident::new(tag),
lines,
inline: !had_newline,
}
} else {
- Raw {
+ NodeRaw {
lang: None,
lines: split_lines(raw),
inline: true,
diff --git a/src/parse/tests.rs b/src/parse/tests.rs
index e516af32..302e93ee 100644
--- a/src/parse/tests.rs
+++ b/src/parse/tests.rs
@@ -6,33 +6,33 @@ use std::fmt::Debug;
use super::parse;
use crate::color::RgbaColor;
-use crate::compute::dict::SpannedEntry;
+use crate::compute::dict::DictKey;
use crate::length::Length;
use crate::syntax::*;
// ------------------------------ Construct Syntax Nodes ------------------------------ //
use Decoration::*;
-use SyntaxNode::{
+use SynNode::{
Linebreak as L, Parbreak as P, Spacing as S, ToggleBolder as B, ToggleItalic as I,
};
-fn T(text: &str) -> SyntaxNode {
- SyntaxNode::Text(text.to_string())
+fn T(text: &str) -> SynNode {
+ SynNode::Text(text.to_string())
}
macro_rules! H {
($level:expr, $($tts:tt)*) => {
- SyntaxNode::Heading(Heading {
+ SynNode::Heading(NodeHeading {
level: Spanned::zero($level),
- tree: Tree![@$($tts)*],
+ contents: Tree![@$($tts)*],
})
};
}
macro_rules! R {
($lang:expr, $inline:expr, $($line:expr),* $(,)?) => {{
- SyntaxNode::Raw(Raw {
+ SynNode::Raw(NodeRaw {
lang: $lang,
lines: vec![$($line.to_string()) ,*],
inline: $inline,
@@ -45,53 +45,73 @@ fn Lang(lang: &str) -> Option<Ident> {
}
macro_rules! F {
- ($($tts:tt)*) => { SyntaxNode::Call(Call!(@$($tts)*)) }
+ ($($tts:tt)*) => { SynNode::Expr(Expr::Call(Call!(@$($tts)*))) }
}
// ------------------------------- Construct Expressions ------------------------------ //
-use Expr::{Bool, Color, Length as Len, Number as Num};
+use BinOp::*;
+use UnOp::*;
fn Id(ident: &str) -> Expr {
- Expr::Ident(Ident(ident.to_string()))
+ Expr::Lit(Lit::Ident(Ident(ident.to_string())))
+}
+fn Bool(b: bool) -> Expr {
+ Expr::Lit(Lit::Bool(b))
+}
+fn _Int(int: i64) -> Expr {
+ Expr::Lit(Lit::Int(int))
+}
+fn Float(float: f64) -> Expr {
+ Expr::Lit(Lit::Float(float))
+}
+fn _Percent(percent: f64) -> Expr {
+ Expr::Lit(Lit::Percent(percent))
+}
+fn Len(length: Length) -> Expr {
+ Expr::Lit(Lit::Length(length))
+}
+fn Color(color: RgbaColor) -> Expr {
+ Expr::Lit(Lit::Color(color))
}
fn Str(string: &str) -> Expr {
- Expr::Str(string.to_string())
+ Expr::Lit(Lit::Str(string.to_string()))
}
macro_rules! Dict {
(@dict=$dict:expr,) => {};
(@dict=$dict:expr, $key:expr => $value:expr $(, $($tts:tt)*)?) => {{
let key = Into::<Spanned<&str>>::into($key);
- let val = Into::<Spanned<Expr>>::into($value);
- $dict.insert(key.v, SpannedEntry::new(key.span, val));
+ let key = key.map(Into::<DictKey>::into);
+ let value = Into::<Spanned<Expr>>::into($value);
+ $dict.0.push(LitDictEntry { key: Some(key), value });
Dict![@dict=$dict, $($($tts)*)?];
}};
(@dict=$dict:expr, $value:expr $(, $($tts:tt)*)?) => {
- let val = Into::<Spanned<Expr>>::into($value);
- $dict.push(SpannedEntry::val(val));
+ let value = Into::<Spanned<Expr>>::into($value);
+ $dict.0.push(LitDictEntry { key: None, value });
Dict![@dict=$dict, $($($tts)*)?];
};
(@$($tts:tt)*) => {{
#[allow(unused_mut)]
- let mut dict = DictExpr::new();
+ let mut dict = LitDict::default();
Dict![@dict=dict, $($tts)*];
dict
}};
- ($($tts:tt)*) => { Expr::Dict(Dict![@$($tts)*]) };
+ ($($tts:tt)*) => { Expr::Lit(Lit::Dict(Dict![@$($tts)*])) };
}
macro_rules! Tree {
(@$($node:expr),* $(,)?) => {
- vec![$(Into::<Spanned<SyntaxNode>>::into($node)),*]
+ vec![$(Into::<Spanned<SynNode>>::into($node)),*]
};
- ($($tts:tt)*) => { Expr::Tree(Tree![@$($tts)*]) };
+ ($($tts:tt)*) => { Expr::Lit(Lit::Content(Tree![@$($tts)*])) };
}
macro_rules! Call {
(@$name:expr $(; $($tts:tt)*)?) => {{
let name = Into::<Spanned<&str>>::into($name);
- CallExpr {
+ ExprCall {
name: name.map(|n| Ident(n.to_string())),
args: Dict![@$($($tts)*)?],
}
@@ -99,20 +119,22 @@ macro_rules! Call {
($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) };
}
-fn Neg<T: Into<Spanned<Expr>>>(e1: T) -> Expr {
- Expr::Neg(Box::new(e1.into()))
-}
-fn Add<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr {
- Expr::Add(Box::new(e1.into()), Box::new(e2.into()))
-}
-fn Sub<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr {
- Expr::Sub(Box::new(e1.into()), Box::new(e2.into()))
+fn Unary(op: impl Into<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> Expr {
+ Expr::Unary(ExprUnary {
+ op: op.into(),
+ expr: expr.into().map(Box::new),
+ })
}
-fn Mul<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr {
- Expr::Mul(Box::new(e1.into()), Box::new(e2.into()))
-}
-fn Div<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr {
- Expr::Div(Box::new(e1.into()), Box::new(e2.into()))
+fn Binary(
+ op: impl Into<Spanned<BinOp>>,
+ lhs: impl Into<Spanned<Expr>>,
+ rhs: impl Into<Spanned<Expr>>,
+) -> Expr {
+ Expr::Binary(ExprBinary {
+ lhs: lhs.into().map(Box::new),
+ op: op.into(),
+ rhs: rhs.into().map(Box::new),
+ })
}
// ------------------------------------ Test Macros ----------------------------------- //
@@ -321,12 +343,12 @@ fn test_parse_function_names() {
#[test]
fn test_parse_chaining() {
// Things the parser has to make sense of
- t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Dict![Num(5.0), Num(2.1)], Tree![F!("you")]));
+ t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Dict![Float(5.0), Float(2.1)], Tree![F!("you")]));
t!("[box >>][Hi]" => F!("box"; Tree![T("Hi")]));
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi")))
]));
- t!("[bold: 400, >> emph >> sub: 1cm]" => F!("bold"; Num(400.0), Tree![
+ t!("[bold: 400, >> emph >> sub: 1cm]" => F!("bold"; Float(400.0), Tree![
F!("emph"; Tree!(F!("sub"; Len(Length::cm(1.0)))))
]));
@@ -354,7 +376,7 @@ fn test_parse_colon_starting_func_args() {
#[test]
fn test_parse_function_bodies() {
- t!("[val: 1][*Hi*]" => F!("val"; Num(1.0), Tree![B, T("Hi"), B]));
+ t!("[val: 1][*Hi*]" => F!("val"; Float(1.0), Tree![B, T("Hi"), B]));
e!(" [val][ */]" => s(8, 10, "unexpected end of block comment"));
// Raw in body.
@@ -384,9 +406,9 @@ fn test_parse_values() {
v!("\"hi\"" => Str("hi"));
v!("true" => Bool(true));
v!("false" => Bool(false));
- v!("1.0e-4" => Num(1e-4));
- v!("3.14" => Num(3.14));
- v!("50%" => Num(0.5));
+ v!("1.0e-4" => Float(1e-4));
+ v!("3.14" => Float(3.14));
+ v!("50%" => Float(0.5));
v!("4.5cm" => Len(Length::cm(4.5)));
v!("12e1pt" => Len(Length::pt(12e1)));
v!("#f7a20500" => Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0x00)));
@@ -411,7 +433,7 @@ fn test_parse_values() {
s(13, 13, "expected closing bracket"));
// Spanned.
- ts!("[val: 1.4]" => s(0, 10, F!(s(1, 4, "val"); s(6, 9, Num(1.4)))));
+ ts!("[val: 1.4]" => s(0, 10, F!(s(1, 4, "val"); s(6, 9, Float(1.4)))));
}
#[test]
@@ -420,40 +442,50 @@ fn test_parse_expressions() {
v!("(hi)" => Id("hi"));
// Operations.
- v!("-1" => Neg(Num(1.0)));
- v!("-- 1" => Neg(Neg(Num(1.0))));
- v!("3.2in + 6pt" => Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))));
- v!("5 - 0.01" => Sub(Num(5.0), Num(0.01)));
- v!("(3mm * 2)" => Mul(Len(Length::mm(3.0)), Num(2.0)));
- v!("12e-3cm/1pt" => Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0))));
+ v!("-1" => Unary(Neg, Float(1.0)));
+ v!("-- 1" => Unary(Neg, Unary(Neg, Float(1.0))));
+ v!("3.2in + 6pt" => Binary(Add, Len(Length::inches(3.2)), Len(Length::pt(6.0))));
+ v!("5 - 0.01" => Binary(Sub, Float(5.0), Float(0.01)));
+ v!("(3mm * 2)" => Binary(Mul, Len(Length::mm(3.0)), Float(2.0)));
+ v!("12e-3cm/1pt" => Binary(Div, Len(Length::cm(12e-3)), Len(Length::pt(1.0))));
// More complex.
- v!("(3.2in + 6pt)*(5/2-1)" => Mul(
- Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))),
- Sub(Div(Num(5.0), Num(2.0)), Num(1.0))
+ v!("(3.2in + 6pt)*(5/2-1)" => Binary(
+ Mul,
+ Binary(Add, Len(Length::inches(3.2)), Len(Length::pt(6.0))),
+ Binary(Sub, Binary(Div, Float(5.0), Float(2.0)), Float(1.0))
));
- v!("(6.3E+2+4* - 3.2pt)/2" => Div(
- Add(Num(6.3e2), Mul(Num(4.0), Neg(Len(Length::pt(3.2))))),
- Num(2.0)
+ v!("(6.3E+2+4* - 3.2pt)/2" => Binary(
+ Div,
+ Binary(Add, Float(6.3e2), Binary(
+ Mul,
+ Float(4.0),
+ Unary(Neg, Len(Length::pt(3.2)))
+ )),
+ Float(2.0)
));
// Associativity of multiplication and division.
- v!("3/4*5" => Mul(Div(Num(3.0), Num(4.0)), Num(5.0)));
+ v!("3/4*5" => Binary(Mul, Binary(Div, Float(3.0), Float(4.0)), Float(5.0)));
// Spanned.
ts!("[val: 1 + 3]" => s(0, 12, F!(
- s(1, 4, "val"); s(6, 11, Add(s(6, 7, Num(1.0)), s(10, 11, Num(3.0))))
+ s(1, 4, "val"); s(6, 11, Binary(
+ s(8, 9, Add),
+ s(6, 7, Float(1.0)),
+ s(10, 11, Float(3.0))
+ ))
)));
// Span of parenthesized expression contains parens.
- ts!("[val: (1)]" => s(0, 10, F!(s(1, 4, "val"); s(6, 9, Num(1.0)))));
+ ts!("[val: (1)]" => s(0, 10, F!(s(1, 4, "val"); s(6, 9, Float(1.0)))));
// Invalid expressions.
v!("4pt--" => Len(Length::pt(4.0)));
e!("[val: 4pt--]" => s(10, 11, "dangling minus"),
s(6, 10, "missing right summand"));
- v!("3mm+4pt*" => Add(Len(Length::mm(3.0)), Len(Length::pt(4.0))));
+ v!("3mm+4pt*" => Binary(Add, Len(Length::mm(3.0)), Len(Length::pt(4.0))));
e!("[val: 3mm+4pt*]" => s(10, 14, "missing right factor"));
}
@@ -464,8 +496,8 @@ fn test_parse_dicts() {
v!("(false)" => Bool(false));
v!("(true,)" => Dict![Bool(true)]);
v!("(key=val)" => Dict!["key" => Id("val")]);
- v!("(1, 2)" => Dict![Num(1.0), Num(2.0)]);
- v!("(1, key=\"value\")" => Dict![Num(1.0), "key" => Str("value")]);
+ v!("(1, 2)" => Dict![Float(1.0), Float(2.0)]);
+ v!("(1, key=\"value\")" => Dict![Float(1.0), "key" => Str("value")]);
// Decorations.
d!("[val: key=hi]" => s(6, 9, DictKey));
@@ -483,7 +515,7 @@ fn test_parse_dicts() {
#[test]
fn test_parse_dicts_compute_func_calls() {
v!("empty()" => Call!("empty"));
- v!("add ( 1 , 2 )" => Call!("add"; Num(1.0), Num(2.0)));
+ v!("add ( 1 , 2 )" => Call!("add"; Float(1.0), Float(2.0)));
v!("items(\"fire\", #f93a6d)" => Call!("items";
Str("fire"), Color(RgbaColor::new(0xf9, 0x3a, 0x6d, 0xff))
));
@@ -492,7 +524,7 @@ fn test_parse_dicts_compute_func_calls() {
v!("css(1pt, rgb(90, 102, 254), \"solid\")" => Call!(
"css";
Len(Length::pt(1.0)),
- Call!("rgb"; Num(90.0), Num(102.0), Num(254.0)),
+ Call!("rgb"; Float(90.0), Float(102.0), Float(254.0)),
Str("solid"),
));
@@ -501,7 +533,7 @@ fn test_parse_dicts_compute_func_calls() {
e!("[val: lang(δΈ­ζ–‡]" => s(17, 17, "expected closing paren"));
// Invalid name.
- v!("πŸ‘ (\"abc\", 13e-5)" => Dict!(Str("abc"), Num(13.0e-5)));
+ v!("πŸ‘ (\"abc\", 13e-5)" => Dict!(Str("abc"), Float(13.0e-5)));
e!("[val: πŸ‘ (\"abc\", 13e-5)]" => s(6, 10, "expected value, found invalid token"));
}
@@ -509,10 +541,10 @@ fn test_parse_dicts_compute_func_calls() {
fn test_parse_dicts_nested() {
v!("(1, ( ab=(), d = (3, 14pt) )), false" =>
Dict![
- Num(1.0),
+ Float(1.0),
Dict!(
"ab" => Dict![],
- "d" => Dict!(Num(3.0), Len(Length::pt(14.0))),
+ "d" => Dict!(Float(3.0), Len(Length::pt(14.0))),
),
],
Bool(false),
@@ -546,7 +578,7 @@ fn test_parse_dicts_errors() {
s(10, 11, "expected value, found equals sign"));
// Unexpected equals sign.
- v!("z=y=4" => Num(4.0), "z" => Id("y"));
+ v!("z=y=4" => "z" => Id("y"), Float(4.0));
e!("[val: z=y=4]" =>
s(9, 9, "expected comma"),
s(9, 10, "expected value, found equals sign"));
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
new file mode 100644
index 00000000..817b3d6e
--- /dev/null
+++ b/src/syntax/expr.rs
@@ -0,0 +1,124 @@
+//! Expressions.
+
+use super::span::{SpanWith, Spanned};
+use super::{Decoration, Ident, Lit, LitDict};
+use crate::compute::value::Value;
+use crate::layout::LayoutContext;
+use crate::Feedback;
+
+/// An expression.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Expr {
+ /// A literal: `true`, `1cm`, `"hi"`, `{_Hey!_}`.
+ Lit(Lit),
+ /// A unary operation: `-x`.
+ Unary(ExprUnary),
+ /// A binary operation: `a + b`, `a / b`.
+ Binary(ExprBinary),
+ /// An invocation of a function: `[foo: ...]`, `foo(...)`.
+ Call(ExprCall),
+}
+
+impl Expr {
+ /// Evaluate the expression to a value.
+ pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
+ match self {
+ Self::Lit(lit) => lit.eval(ctx, f).await,
+ Self::Unary(unary) => unary.eval(ctx, f).await,
+ Self::Binary(binary) => binary.eval(ctx, f).await,
+ Self::Call(call) => call.eval(ctx, f).await,
+ }
+ }
+}
+
+/// A unary operation: `-x`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprUnary {
+ /// The operator: `-`.
+ pub op: Spanned<UnOp>,
+ /// The expression to operator on: `x`.
+ pub expr: Spanned<Box<Expr>>,
+}
+
+impl ExprUnary {
+ /// Evaluate the expression to a value.
+ pub async fn eval(&self, _: &LayoutContext<'_>, _: &mut Feedback) -> Value {
+ match self.op.v {
+ UnOp::Neg => todo!("eval neg"),
+ }
+ }
+}
+
+/// A unary operator.
+#[derive(Debug, Clone, PartialEq)]
+pub enum UnOp {
+ /// The negation operator: `-`.
+ Neg,
+}
+
+/// A binary operation: `a + b`, `a / b`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprBinary {
+ /// The left-hand side of the operation: `a`.
+ pub lhs: Spanned<Box<Expr>>,
+ /// The operator: `+`.
+ pub op: Spanned<BinOp>,
+ /// The right-hand side of the operation: `b`.
+ pub rhs: Spanned<Box<Expr>>,
+}
+
+impl ExprBinary {
+ /// Evaluate the expression to a value.
+ pub async fn eval(&self, _: &LayoutContext<'_>, _: &mut Feedback) -> Value {
+ match self.op.v {
+ BinOp::Add => todo!("eval add"),
+ BinOp::Sub => todo!("eval sub"),
+ BinOp::Mul => todo!("eval mul"),
+ BinOp::Div => todo!("eval div"),
+ }
+ }
+}
+
+/// A binary operator.
+#[derive(Debug, Clone, PartialEq)]
+pub enum BinOp {
+ /// The addition operator: `+`.
+ Add,
+ /// The subtraction operator: `-`.
+ Sub,
+ /// The multiplication operator: `*`.
+ Mul,
+ /// The division operator: `/`.
+ Div,
+}
+
+/// An invocation of a function: `[foo: ...]`, `foo(...)`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprCall {
+ /// The name of the function.
+ pub name: Spanned<Ident>,
+ /// The arguments to the function.
+ pub args: LitDict,
+}
+
+impl ExprCall {
+ /// Evaluate the call expression to a value.
+ pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
+ let name = &self.name.v;
+ let span = self.name.span;
+ let args = self.args.eval(ctx, f).await;
+
+ if let Some(func) = ctx.scope.func(name) {
+ let pass = func(span, args, ctx.clone()).await;
+ f.extend(pass.feedback);
+ f.decorations.push(Decoration::Resolved.span_with(span));
+ pass.output
+ } else {
+ if !name.is_empty() {
+ error!(@f, span, "unknown function");
+ f.decorations.push(Decoration::Unresolved.span_with(span));
+ }
+ Value::Dict(args)
+ }
+ }
+}
diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs
new file mode 100644
index 00000000..5ab7c73c
--- /dev/null
+++ b/src/syntax/ident.rs
@@ -0,0 +1,58 @@
+//! Unicode identifiers.
+
+use std::ops::Deref;
+
+use unicode_xid::UnicodeXID;
+
+/// An identifier as defined by unicode with a few extra permissible characters.
+///
+/// This is defined as in the [Unicode Standard], but allows additionally
+/// `-` and `_` as starting and continuing characters.
+///
+/// [Unicode Standard]: http://www.unicode.org/reports/tr31/
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Ident(pub String);
+
+impl Ident {
+ /// Create a new identifier from a string checking that it is a valid.
+ pub fn new(ident: impl AsRef<str> + Into<String>) -> Option<Self> {
+ if is_ident(ident.as_ref()) {
+ Some(Self(ident.into()))
+ } else {
+ None
+ }
+ }
+
+ /// Return a reference to the underlying string.
+ pub fn as_str(&self) -> &str {
+ self
+ }
+}
+
+impl AsRef<str> for Ident {
+ fn as_ref(&self) -> &str {
+ self
+ }
+}
+
+impl Deref for Ident {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ self.0.as_str()
+ }
+}
+
+/// Whether the string is a valid identifier.
+pub fn is_ident(string: &str) -> bool {
+ let mut chars = string.chars();
+ if matches!(chars.next(), Some(c) if c.is_xid_start() || is_also_ok(c)) {
+ chars.all(|c| c.is_xid_continue() || is_also_ok(c))
+ } else {
+ false
+ }
+}
+
+fn is_also_ok(c: char) -> bool {
+ c == '-' || c == '_'
+}
diff --git a/src/syntax/lit.rs b/src/syntax/lit.rs
new file mode 100644
index 00000000..e9807a17
--- /dev/null
+++ b/src/syntax/lit.rs
@@ -0,0 +1,99 @@
+//! Literals.
+
+use super::{Expr, Ident, SpanWith, Spanned, SynTree};
+use crate::color::RgbaColor;
+use crate::compute::dict::{DictKey, SpannedEntry};
+use crate::compute::value::{DictValue, Value};
+use crate::layout::LayoutContext;
+use crate::length::Length;
+use crate::{DynFuture, Feedback};
+
+/// A literal.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Lit {
+ /// A identifier literal: `left`.
+ Ident(Ident),
+ /// A boolean literal: `true`, `false`.
+ Bool(bool),
+ /// An integer literal: `120`.
+ Int(i64),
+ /// A floating-point literal: `1.2`, `10e-4`.
+ Float(f64),
+ /// A percent literal: `50%`.
+ Percent(f64),
+ /// A length literal: `12pt`, `3cm`.
+ Length(Length),
+ /// A color literal: `#ffccee`.
+ Color(RgbaColor),
+ /// A string literal: `"hello!"`.
+ Str(String),
+ /// A dictionary literal: `(false, 12cm, greeting = "hi")`.
+ Dict(LitDict),
+ /// A content literal: `{*Hello* there!}`.
+ Content(SynTree),
+}
+
+impl Lit {
+ /// Evaluate the dictionary literal to a dictionary value.
+ pub async fn eval<'a>(
+ &'a self,
+ ctx: &'a LayoutContext<'a>,
+ f: &'a mut Feedback,
+ ) -> Value {
+ match *self {
+ Lit::Ident(ref i) => Value::Ident(i.clone()),
+ Lit::Bool(b) => Value::Bool(b),
+ Lit::Int(i) => Value::Number(i as f64),
+ Lit::Float(f) => Value::Number(f as f64),
+ Lit::Percent(p) => Value::Number(p as f64 / 100.0),
+ Lit::Length(l) => Value::Length(l),
+ Lit::Color(c) => Value::Color(c),
+ Lit::Str(ref s) => Value::Str(s.clone()),
+ Lit::Dict(ref d) => Value::Dict(d.eval(ctx, f).await),
+ Lit::Content(ref c) => Value::Tree(c.clone()),
+ }
+ }
+}
+
+/// A dictionary literal: `(false, 12cm, greeting = "hi")`.
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct LitDict(pub Vec<LitDictEntry>);
+
+impl LitDict {
+ /// Create an empty dict literal.
+ pub fn new() -> Self {
+ Self(vec![])
+ }
+
+ /// Evaluate the dictionary literal to a dictionary value.
+ pub fn eval<'a>(
+ &'a self,
+ ctx: &'a LayoutContext<'a>,
+ f: &'a mut Feedback,
+ ) -> DynFuture<'a, DictValue> {
+ Box::pin(async move {
+ let mut dict = DictValue::new();
+
+ for entry in &self.0 {
+ let val = entry.value.v.eval(ctx, f).await;
+ let spanned = val.span_with(entry.value.span);
+ if let Some(key) = &entry.key {
+ dict.insert(&key.v, SpannedEntry::new(key.span, spanned));
+ } else {
+ dict.push(SpannedEntry::val(spanned));
+ }
+ }
+
+ dict
+ })
+ }
+}
+
+/// An entry in a dictionary literal: `false` or `greeting = "hi"`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct LitDictEntry {
+ /// The key of the entry if there was one: `greeting`.
+ pub key: Option<Spanned<DictKey>>,
+ /// The value of the entry: `"hi"`.
+ pub value: Spanned<Expr>,
+}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 0809d33f..f4472df5 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -1,12 +1,24 @@
//! Syntax types.
+mod expr;
+mod ident;
+mod lit;
mod span;
mod token;
mod tree;
+/// Abstract syntax tree definition.
+pub mod ast {
+ use super::*;
+ pub use expr::*;
+ pub use lit::*;
+ pub use tree::*;
+}
+
+pub use ast::*;
+pub use ident::*;
pub use span::*;
pub use token::*;
-pub use tree::*;
/// Decorations for semantic syntax highlighting.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index eb029479..62929706 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -42,8 +42,10 @@ impl<T> Offset for SpanVec<T> {
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Spanned<T> {
- pub span: Span,
+ /// The spanned value.
pub v: T,
+ /// The location in source code of the value.
+ pub span: Span,
}
impl<T> Spanned<T> {
diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs
index c4ce3ad2..80bca399 100644
--- a/src/syntax/tree.rs
+++ b/src/syntax/tree.rs
@@ -1,26 +1,15 @@
//! The syntax tree.
-use std::fmt::{self, Debug, Formatter};
-use std::ops::Deref;
-
-use unicode_xid::UnicodeXID;
-
-use super::span::{SpanVec, SpanWith, Spanned};
-use super::Decoration;
-use crate::color::RgbaColor;
-use crate::compute::dict::{Dict, SpannedEntry};
-use crate::compute::value::{DictValue, Value};
-use crate::layout::LayoutContext;
-use crate::length::Length;
-use crate::{DynFuture, Feedback};
+use super::span::{SpanVec, Spanned};
+use super::{Expr, Ident};
/// A collection of nodes which form a tree together with the nodes' children.
-pub type SyntaxTree = SpanVec<SyntaxNode>;
+pub type SynTree = SpanVec<SynNode>;
/// A syntax node, which encompasses a single logical entity of parsed source
/// code.
#[derive(Debug, Clone, PartialEq)]
-pub enum SyntaxNode {
+pub enum SynNode {
/// Whitespace containing less than two newlines.
Spacing,
/// A forced line break.
@@ -34,11 +23,11 @@ pub enum SyntaxNode {
/// Plain text.
Text(String),
/// An optionally syntax-highlighted raw block.
- Raw(Raw),
- /// Section headings.
- Heading(Heading),
- /// A function call.
- Call(CallExpr),
+ Raw(NodeRaw),
+ /// A section heading.
+ Heading(NodeHeading),
+ /// An expression.
+ Expr(Expr),
}
/// A raw block, rendered in monospace with optional syntax highlighting.
@@ -103,7 +92,7 @@ pub enum SyntaxNode {
/// you can always force leading or trailing whitespace simply by adding more
/// spaces.
#[derive(Debug, Clone, PartialEq)]
-pub struct Raw {
+pub struct NodeRaw {
/// An optional identifier specifying the language to syntax-highlight in.
pub lang: Option<Ident>,
/// The lines of raw text, determined as the raw string between the
@@ -122,221 +111,9 @@ pub struct Raw {
/// A section heading.
#[derive(Debug, Clone, PartialEq)]
-pub struct Heading {
+pub struct NodeHeading {
/// The section depth (how many hashtags minus 1).
pub level: Spanned<u8>,
- pub tree: SyntaxTree,
-}
-
-/// An expression.
-#[derive(Clone, PartialEq)]
-pub enum Expr {
- /// An identifier: `ident`.
- Ident(Ident),
- /// A string: `"string"`.
- Str(String),
- /// A boolean: `true, false`.
- Bool(bool),
- /// A number: `1.2, 200%`.
- Number(f64),
- /// A length: `2cm, 5.2in`.
- Length(Length),
- /// A color value with alpha channel: `#f79143ff`.
- Color(RgbaColor),
- /// A dictionary expression: `(false, 12cm, greeting="hi")`.
- Dict(DictExpr),
- /// A syntax tree containing typesetting content.
- Tree(SyntaxTree),
- /// A function call expression: `cmyk(37.7, 0, 3.9, 1.1)`.
- Call(CallExpr),
- /// An operation that negates the contained expression.
- Neg(Box<Spanned<Expr>>),
- /// An operation that adds the contained expressions.
- Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operation that subtracts the contained expressions.
- Sub(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operation that multiplies the contained expressions.
- Mul(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
- /// An operation that divides the contained expressions.
- Div(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
-}
-
-impl Expr {
- /// A natural-language name of the type of this expression, e.g.
- /// "identifier".
- pub fn name(&self) -> &'static str {
- use Expr::*;
- match self {
- Ident(_) => "identifier",
- Str(_) => "string",
- Bool(_) => "bool",
- Number(_) => "number",
- Length(_) => "length",
- Color(_) => "color",
- Dict(_) => "dictg",
- Tree(_) => "syntax tree",
- Call(_) => "function call",
- Neg(_) => "negation",
- Add(_, _) => "addition",
- Sub(_, _) => "subtraction",
- Mul(_, _) => "multiplication",
- Div(_, _) => "division",
- }
- }
-
- /// Evaluate the expression to a value.
- pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
- use Expr::*;
- match self {
- Ident(i) => Value::Ident(i.clone()),
- Str(s) => Value::Str(s.clone()),
- &Bool(b) => Value::Bool(b),
- &Number(n) => Value::Number(n),
- &Length(s) => Value::Length(s),
- &Color(c) => Value::Color(c),
- Dict(t) => Value::Dict(t.eval(ctx, f).await),
- Tree(t) => Value::Tree(t.clone()),
- Call(call) => call.eval(ctx, f).await,
- Neg(_) => todo!("eval neg"),
- Add(_, _) => todo!("eval add"),
- Sub(_, _) => todo!("eval sub"),
- Mul(_, _) => todo!("eval mul"),
- Div(_, _) => todo!("eval div"),
- }
- }
-}
-
-impl Debug for Expr {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- use Expr::*;
- match self {
- Ident(i) => i.fmt(f),
- Str(s) => s.fmt(f),
- Bool(b) => b.fmt(f),
- Number(n) => n.fmt(f),
- Length(s) => s.fmt(f),
- Color(c) => c.fmt(f),
- Dict(t) => t.fmt(f),
- Tree(t) => t.fmt(f),
- Call(c) => c.fmt(f),
- Neg(e) => write!(f, "-{:?}", e),
- Add(a, b) => write!(f, "({:?} + {:?})", a, b),
- Sub(a, b) => write!(f, "({:?} - {:?})", a, b),
- Mul(a, b) => write!(f, "({:?} * {:?})", a, b),
- Div(a, b) => write!(f, "({:?} / {:?})", a, b),
- }
- }
-}
-
-/// An identifier as defined by unicode with a few extra permissible characters.
-#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Ident(pub String);
-
-impl Ident {
- /// Create a new identifier from a string checking that it is a valid.
- pub fn new(ident: impl AsRef<str> + Into<String>) -> Option<Self> {
- if is_ident(ident.as_ref()) {
- Some(Self(ident.into()))
- } else {
- None
- }
- }
-
- /// Return a reference to the underlying string.
- pub fn as_str(&self) -> &str {
- self
- }
-}
-
-impl AsRef<str> for Ident {
- fn as_ref(&self) -> &str {
- self
- }
-}
-
-impl Deref for Ident {
- type Target = str;
-
- fn deref(&self) -> &Self::Target {
- self.0.as_str()
- }
-}
-
-impl Debug for Ident {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "`{}`", self.0)
- }
-}
-
-/// Whether the string is a valid identifier.
-pub fn is_ident(string: &str) -> bool {
- fn is_ok(c: char) -> bool {
- c == '-' || c == '_'
- }
-
- let mut chars = string.chars();
- if matches!(chars.next(), Some(c) if c.is_xid_start() || is_ok(c)) {
- chars.all(|c| c.is_xid_continue() || is_ok(c))
- } else {
- false
- }
-}
-
-/// A dictionary of expressions.
-///
-/// # Example
-/// ```typst
-/// (false, 12cm, greeting="hi")
-/// ```
-pub type DictExpr = Dict<SpannedEntry<Expr>>;
-
-impl DictExpr {
- /// Evaluate the dictionary expression to a dictionary value.
- pub fn eval<'a>(
- &'a self,
- ctx: &'a LayoutContext<'a>,
- f: &'a mut Feedback,
- ) -> DynFuture<'a, DictValue> {
- Box::pin(async move {
- let mut dict = DictValue::new();
-
- for (key, entry) in self.iter() {
- let val = entry.val.v.eval(ctx, f).await;
- let spanned = val.span_with(entry.val.span);
- let entry = SpannedEntry::new(entry.key, spanned);
- dict.insert(key, entry);
- }
-
- dict
- })
- }
-}
-
-/// An invocation of a function.
-#[derive(Debug, Clone, PartialEq)]
-pub struct CallExpr {
- pub name: Spanned<Ident>,
- pub args: DictExpr,
-}
-
-impl CallExpr {
- /// Evaluate the call expression to a value.
- pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
- let name = &self.name.v;
- let span = self.name.span;
- let args = self.args.eval(ctx, f).await;
-
- if let Some(func) = ctx.scope.func(name) {
- let pass = func(span, args, ctx.clone()).await;
- f.extend(pass.feedback);
- f.decorations.push(Decoration::Resolved.span_with(span));
- pass.output
- } else {
- if !name.is_empty() {
- error!(@f, span, "unknown function");
- f.decorations.push(Decoration::Unresolved.span_with(span));
- }
- Value::Dict(args)
- }
- }
+ /// The contents of the heading.
+ pub contents: SynTree,
}