Reorganize ast types 🏕

This commit is contained in:
Laurenz 2020-10-01 15:03:37 +02:00
parent aafd3c95ca
commit 7fcad452b8
17 changed files with 554 additions and 447 deletions

View File

@ -52,38 +52,38 @@ impl<V> Dict<V> {
/// Get a reference to the value with the given key. /// Get a reference to the value with the given key.
pub fn get<'a, K>(&self, key: K) -> Option<&V> pub fn get<'a, K>(&self, key: K) -> Option<&V>
where where
K: Into<BorrowedKey<'a>>, K: Into<RefKey<'a>>,
{ {
match key.into() { match key.into() {
BorrowedKey::Num(num) => self.nums.get(&num), RefKey::Num(num) => self.nums.get(&num),
BorrowedKey::Str(string) => self.strs.get(string), RefKey::Str(string) => self.strs.get(string),
} }
} }
/// Borrow the value with the given key mutably. /// Borrow the value with the given key mutably.
pub fn get_mut<'a, K>(&mut self, key: K) -> Option<&mut V> pub fn get_mut<'a, K>(&mut self, key: K) -> Option<&mut V>
where where
K: Into<BorrowedKey<'a>>, K: Into<RefKey<'a>>,
{ {
match key.into() { match key.into() {
BorrowedKey::Num(num) => self.nums.get_mut(&num), RefKey::Num(num) => self.nums.get_mut(&num),
BorrowedKey::Str(string) => self.strs.get_mut(string), RefKey::Str(string) => self.strs.get_mut(string),
} }
} }
/// Insert a value into the dictionary. /// Insert a value into the dictionary.
pub fn insert<K>(&mut self, key: K, value: V) pub fn insert<K>(&mut self, key: K, value: V)
where where
K: Into<OwnedKey>, K: Into<DictKey>,
{ {
match key.into() { match key.into() {
OwnedKey::Num(num) => { DictKey::Num(num) => {
self.nums.insert(num, value); self.nums.insert(num, value);
if self.lowest_free == num { if self.lowest_free == num {
self.lowest_free += 1; self.lowest_free += 1;
} }
} }
OwnedKey::Str(string) => { DictKey::Str(string) => {
self.strs.insert(string, value); self.strs.insert(string, value);
} }
} }
@ -92,14 +92,14 @@ impl<V> Dict<V> {
/// Remove the value with the given key from the dictionary. /// Remove the value with the given key from the dictionary.
pub fn remove<'a, K>(&mut self, key: K) -> Option<V> pub fn remove<'a, K>(&mut self, key: K) -> Option<V>
where where
K: Into<BorrowedKey<'a>>, K: Into<RefKey<'a>>,
{ {
match key.into() { match key.into() {
BorrowedKey::Num(num) => { RefKey::Num(num) => {
self.lowest_free = self.lowest_free.min(num); self.lowest_free = self.lowest_free.min(num);
self.nums.remove(&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. /// 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() self.nums()
.map(|(&k, v)| (BorrowedKey::Num(k), v)) .map(|(&k, v)| (RefKey::Num(k), v))
.chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v))) .chain(self.strs().map(|(k, v)| (RefKey::Str(k), v)))
} }
/// Iterate over all values in the dictionary. /// 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. /// 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 self.nums
.into_iter() .into_iter()
.map(|(k, v)| (OwnedKey::Num(k), v)) .map(|(k, v)| (DictKey::Num(k), v))
.chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(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. /// 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> impl<'a, K, V> Index<K> for Dict<V>
where where
K: Into<BorrowedKey<'a>>, K: Into<RefKey<'a>>,
{ {
type Output = V; type Output = V;
@ -230,33 +230,39 @@ impl<V: Debug> Debug for Dict<V> {
/// The owned variant of a dictionary key. /// The owned variant of a dictionary key.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum OwnedKey { pub enum DictKey {
Num(u64), Num(u64),
Str(String), Str(String),
} }
impl From<BorrowedKey<'_>> for OwnedKey { impl From<&Self> for DictKey {
fn from(key: BorrowedKey<'_>) -> Self { fn from(key: &Self) -> Self {
key.clone()
}
}
impl From<RefKey<'_>> for DictKey {
fn from(key: RefKey<'_>) -> Self {
match key { match key {
BorrowedKey::Num(num) => Self::Num(num), RefKey::Num(num) => Self::Num(num),
BorrowedKey::Str(string) => Self::Str(string.to_string()), RefKey::Str(string) => Self::Str(string.to_string()),
} }
} }
} }
impl From<u64> for OwnedKey { impl From<u64> for DictKey {
fn from(num: u64) -> Self { fn from(num: u64) -> Self {
Self::Num(num) Self::Num(num)
} }
} }
impl From<String> for OwnedKey { impl From<String> for DictKey {
fn from(string: String) -> Self { fn from(string: String) -> Self {
Self::Str(string) Self::Str(string)
} }
} }
impl From<&'static str> for OwnedKey { impl From<&'static str> for DictKey {
fn from(string: &'static str) -> Self { fn from(string: &'static str) -> Self {
Self::Str(string.to_string()) Self::Str(string.to_string())
} }
@ -264,24 +270,24 @@ impl From<&'static str> for OwnedKey {
/// The borrowed variant of a dictionary key. /// The borrowed variant of a dictionary key.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum BorrowedKey<'a> { pub enum RefKey<'a> {
Num(u64), Num(u64),
Str(&'a str), Str(&'a str),
} }
impl From<u64> for BorrowedKey<'static> { impl From<u64> for RefKey<'static> {
fn from(num: u64) -> Self { fn from(num: u64) -> Self {
Self::Num(num) 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 { fn from(string: &'a String) -> Self {
Self::Str(&string) 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 { fn from(string: &'a str) -> Self {
Self::Str(string) Self::Str(string)
} }

View File

@ -11,7 +11,7 @@ use crate::color::RgbaColor;
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign}; use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
use crate::length::{Length, ScaleLength}; use crate::length::{Length, ScaleLength};
use crate::paper::Paper; 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}; use crate::{DynFuture, Feedback, Pass};
/// A computational value. /// A computational value.
@ -32,7 +32,7 @@ pub enum Value {
/// A dictionary value: `(false, 12cm, greeting="hi")`. /// A dictionary value: `(false, 12cm, greeting="hi")`.
Dict(DictValue), Dict(DictValue),
/// A syntax tree containing typesetting content. /// A syntax tree containing typesetting content.
Tree(SyntaxTree), Tree(SynTree),
/// An executable function. /// An executable function.
Func(FuncValue), Func(FuncValue),
/// Layouting commands. /// Layouting commands.
@ -76,7 +76,7 @@ impl Spanned<Value> {
for entry in dict.into_values() { for entry in dict.into_values() {
if let Some(last_end) = end { if let Some(last_end) = end {
let span = Span::new(last_end, entry.key.start); 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)); commands.push(Command::LayoutSyntaxTree(tree));
} }
@ -89,7 +89,7 @@ impl Spanned<Value> {
// Format with debug. // Format with debug.
val => { val => {
let fmt = format!("{:?}", 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)] 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!(bool, "bool", &Value::Bool(b) => b);
impl_match!(f64, "number", &Value::Number(n) => n); impl_match!(f64, "number", &Value::Number(n) => n);
impl_match!(Length, "length", &Value::Length(l) => l); 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!(DictValue, "dict", Value::Dict(t) => t.clone());
impl_match!(FuncValue, "function", Value::Func(f) => f.clone()); impl_match!(FuncValue, "function", Value::Func(f) => f.clone());
impl_match!(ScaleLength, "number or length", impl_match!(ScaleLength, "number or length",

View File

@ -25,7 +25,7 @@ use crate::compute::scope::Scope;
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::geom::{Margins, Size}; use crate::geom::{Margins, Size};
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::SyntaxTree; use crate::syntax::SynTree;
use elements::LayoutElements; use elements::LayoutElements;
use prelude::*; use prelude::*;
@ -121,7 +121,7 @@ pub enum Command {
/// This has the effect that the content fits nicely into the active line /// 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 /// layouting, enabling functions to e.g. change the style of some piece of
/// text while keeping it part of the current paragraph. /// text while keeping it part of the current paragraph.
LayoutSyntaxTree(SyntaxTree), LayoutSyntaxTree(SynTree),
/// Add a finished layout. /// Add a finished layout.
Add(BoxLayout), Add(BoxLayout),

View File

@ -5,12 +5,12 @@ use super::text::{layout_text, TextContext};
use super::*; use super::*;
use crate::style::LayoutStyle; use crate::style::LayoutStyle;
use crate::syntax::{ 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}; use crate::{DynFuture, Feedback, Pass};
/// Layout a syntax tree into a collection of boxes. /// 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); let mut layouter = TreeLayouter::new(ctx);
layouter.layout_tree(tree).await; layouter.layout_tree(tree).await;
layouter.finish() layouter.finish()
@ -44,7 +44,7 @@ impl<'a> TreeLayouter<'a> {
Pass::new(self.layouter.finish(), self.feedback) 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 { Box::pin(async move {
for node in tree { for node in tree {
self.layout_node(node).await; 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| { let decorate = |this: &mut Self, deco: Decoration| {
this.feedback.decorations.push(deco.span_with(node.span)); this.feedback.decorations.push(deco.span_with(node.span));
}; };
match &node.v { match &node.v {
SyntaxNode::Spacing => self.layout_space(), SynNode::Spacing => self.layout_space(),
SyntaxNode::Linebreak => self.layouter.finish_line(), SynNode::Linebreak => self.layouter.finish_line(),
SyntaxNode::Parbreak => self.layout_parbreak(), SynNode::Parbreak => self.layout_parbreak(),
SyntaxNode::ToggleItalic => { SynNode::ToggleItalic => {
self.style.text.italic = !self.style.text.italic; self.style.text.italic = !self.style.text.italic;
decorate(self, Decoration::Italic); decorate(self, Decoration::Italic);
} }
SyntaxNode::ToggleBolder => { SynNode::ToggleBolder => {
self.style.text.bolder = !self.style.text.bolder; self.style.text.bolder = !self.style.text.bolder;
decorate(self, Decoration::Bold); decorate(self, Decoration::Bold);
} }
SyntaxNode::Text(text) => { SynNode::Text(text) => {
if self.style.text.italic { if self.style.text.italic {
decorate(self, Decoration::Italic); decorate(self, Decoration::Italic);
} }
@ -81,12 +81,11 @@ impl<'a> TreeLayouter<'a> {
self.layout_text(text).await; 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, SynNode::Expr(expr) => {
self.layout_expr(expr.span_with(node.span)).await;
SyntaxNode::Call(call) => {
self.layout_call(call.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(); 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.font_scale *= 1.5 - 0.1 * heading.level.v.min(5) as f64;
self.style.text.bolder = true; self.style.text.bolder = true;
self.layout_parbreak(); self.layout_parbreak();
self.layout_tree(&heading.tree).await; self.layout_tree(&heading.contents).await;
self.layout_parbreak(); self.layout_parbreak();
self.style.text = style; self.style.text = style;
} }
async fn layout_raw(&mut self, raw: &Raw) { async fn layout_raw(&mut self, raw: &NodeRaw) {
if !raw.inline { if !raw.inline {
self.layout_parbreak(); 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 { let ctx = LayoutContext {
style: &self.style, style: &self.style,
spaces: self.layouter.remaining(), spaces: self.layouter.remaining(),
@ -161,11 +160,11 @@ impl<'a> TreeLayouter<'a> {
..self.ctx ..self.ctx
}; };
let val = call.v.eval(&ctx, &mut self.feedback).await; let val = expr.v.eval(&ctx, &mut self.feedback).await;
let commands = val.span_with(call.span).into_commands(); let commands = val.span_with(expr.span).into_commands();
for command in commands { for command in commands {
self.execute_command(command, call.span).await; self.execute_command(command, expr.span).await;
} }
} }

View File

@ -50,7 +50,7 @@ use crate::diagnostic::Diagnostic;
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::layout::{Commands, MultiLayout}; use crate::layout::{Commands, MultiLayout};
use crate::style::{LayoutStyle, PageStyle, TextStyle}; 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. /// Transforms source code into typesetted layouts.
/// ///
@ -85,12 +85,12 @@ impl Typesetter {
} }
/// Parse source code into a syntax tree. /// 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) parse::parse(src)
} }
/// Layout a syntax tree and return the produced layout. /// 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::*; use crate::layout::prelude::*;
let margins = self.style.page.margins(); let margins = self.style.page.margins();

View File

@ -17,7 +17,7 @@ use super::*;
pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new(); 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 h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut f);
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f); let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f);
let all = args let all = args

View File

@ -13,7 +13,7 @@ pub async fn boxed(
) -> Pass<Value> { ) -> Pass<Value> {
let mut f = Feedback::new(); 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.base = ctx.spaces[0].size;
ctx.spaces.truncate(1); ctx.spaces.truncate(1);

View File

@ -28,7 +28,7 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
let mut text = ctx.style.text.clone(); let mut text = ctx.style.text.clone();
let mut updated_fallback = false; let mut updated_fallback = false;
let content = args.take::<SyntaxTree>(); let content = args.take::<SynTree>();
if let Some(s) = args.take::<ScaleLength>() { if let Some(s) = args.take::<ScaleLength>() {
match s { match s {

View File

@ -12,14 +12,13 @@ pub use tokens::*;
use std::str::FromStr; use std::str::FromStr;
use super::*;
use crate::color::RgbaColor; use crate::color::RgbaColor;
use crate::compute::dict::SpannedEntry; use crate::compute::dict::DictKey;
use crate::syntax::*; use crate::syntax::*;
use crate::{Feedback, Pass}; use crate::{Feedback, Pass};
/// Parse a string of source code. /// Parse a string of source code.
pub fn parse(src: &str) -> Pass<SyntaxTree> { pub fn parse(src: &str) -> Pass<SynTree> {
Parser::new(src).parse() 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(); let tree = self.parse_body_contents();
Pass::new(tree, self.feedback) Pass::new(tree, self.feedback)
} }
@ -50,8 +49,8 @@ impl<'s> Parser<'s> {
// Typesetting content. // Typesetting content.
impl Parser<'_> { impl Parser<'_> {
fn parse_body_contents(&mut self) -> SyntaxTree { fn parse_body_contents(&mut self) -> SynTree {
let mut tree = SyntaxTree::new(); let mut tree = SynTree::new();
self.at_block_or_line_start = true; self.at_block_or_line_start = true;
while !self.eof() { while !self.eof() {
@ -63,7 +62,7 @@ impl Parser<'_> {
tree tree
} }
fn parse_node(&mut self) -> Option<Spanned<SyntaxNode>> { fn parse_node(&mut self) -> Option<Spanned<SynNode>> {
let token = self.peek()?; let token = self.peek()?;
let end = Span::at(token.span.end); let end = Span::at(token.span.end);
@ -83,11 +82,7 @@ impl Parser<'_> {
self.at_block_or_line_start = true; self.at_block_or_line_start = true;
} }
self.with_span(if n >= 2 { self.with_span(if n >= 2 { SynNode::Parbreak } else { SynNode::Spacing })
SyntaxNode::Parbreak
} else {
SyntaxNode::Spacing
})
} }
Token::LineComment(_) | Token::BlockComment(_) => { Token::LineComment(_) | Token::BlockComment(_) => {
@ -99,15 +94,15 @@ impl Parser<'_> {
Token::LeftBracket => { Token::LeftBracket => {
let call = self.parse_bracket_call(false); let call = self.parse_bracket_call(false);
self.at_block_or_line_start = 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::Star => self.with_span(SynNode::ToggleBolder),
Token::Underscore => self.with_span(SyntaxNode::ToggleItalic), Token::Underscore => self.with_span(SynNode::ToggleItalic),
Token::Backslash => self.with_span(SyntaxNode::Linebreak), Token::Backslash => self.with_span(SynNode::Linebreak),
Token::Hashtag if was_at_block_or_line_start => { 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 } => { Token::Raw { raw, backticks, terminated } => {
@ -116,11 +111,11 @@ impl Parser<'_> {
} }
let raw = resolve::resolve_raw(raw, backticks); 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::Text(text) => self.with_span(SynNode::Text(text.to_string())),
Token::Hashtag => self.with_span(SyntaxNode::Text("#".to_string())), Token::Hashtag => self.with_span(SynNode::Text("#".to_string())),
Token::UnicodeEscape { sequence, terminated } => { Token::UnicodeEscape { sequence, terminated } => {
if !terminated { if !terminated {
@ -128,7 +123,7 @@ impl Parser<'_> {
} }
if let Some(c) = resolve::resolve_hex(sequence) { 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 { } else {
error!(@self.feedback, token.span, "invalid unicode escape sequence"); error!(@self.feedback, token.span, "invalid unicode escape sequence");
// TODO: Decide whether to render the 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(); let start = self.pos();
self.assert(Token::Hashtag); self.assert(Token::Hashtag);
@ -167,7 +162,7 @@ impl Parser<'_> {
self.skip_ws(); 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) { while !self.eof() && !matches!(self.peekv(), Some(Token::Space(n)) if n >= 1) {
if let Some(node) = self.parse_node() { if let Some(node) = self.parse_node() {
tree.push(node); tree.push(node);
@ -175,13 +170,13 @@ impl Parser<'_> {
} }
let span = Span::new(start, self.pos()); let span = Span::new(start, self.pos());
Heading { level, tree }.span_with(span) NodeHeading { level, contents: tree }.span_with(span)
} }
} }
// Function calls. // Function calls.
impl Parser<'_> { 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(); let before_bracket = self.pos();
if !chained { if !chained {
self.start_group(Group::Bracket); self.start_group(Group::Bracket);
@ -203,9 +198,9 @@ impl Parser<'_> {
Some(_) => { Some(_) => {
self.expected_at("colon", name.span.end); self.expected_at("colon", name.span.end);
while self.eat().is_some() {} while self.eat().is_some() {}
DictExpr::new() LitDict::default()
} }
None => DictExpr::new(), None => LitDict::default(),
}; };
self.end_group(); self.end_group();
@ -213,8 +208,9 @@ impl Parser<'_> {
let (has_chained_child, end) = if self.peek().is_some() { let (has_chained_child, end) = if self.peek().is_some() {
let item = self.parse_bracket_call(true); let item = self.parse_bracket_call(true);
let span = item.span; let span = item.span;
let t = vec![item.map(SyntaxNode::Call)]; let tree = vec![item.map(|c| SynNode::Expr(Expr::Call(c)))];
args.push(SpannedEntry::val(Expr::Tree(t).span_with(span))); let expr = Expr::Lit(Lit::Content(tree));
args.0.push(LitDictEntry { key: None, value: expr.span_with(span) });
(true, span.end) (true, span.end)
} else { } else {
self.tokens.pop_mode(); self.tokens.pop_mode();
@ -227,40 +223,41 @@ impl Parser<'_> {
if self.check(Token::LeftBracket) && !has_chained_child { if self.check(Token::LeftBracket) && !has_chained_child {
self.start_group(Group::Bracket); self.start_group(Group::Bracket);
self.tokens.push_mode(TokenMode::Body); self.tokens.push_mode(TokenMode::Body);
let body = self.parse_body_contents(); let body = self.parse_body_contents();
self.tokens.pop_mode(); self.tokens.pop_mode();
let body_span = self.end_group(); let body_span = self.end_group();
let expr = Expr::Tree(body); let expr = Expr::Lit(Lit::Content(body));
args.push(SpannedEntry::val(expr.span_with(body_span))); args.0.push(LitDictEntry {
key: None,
value: expr.span_with(body_span),
});
span.expand(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); self.start_group(Group::Paren);
let args = self.parse_dict_contents().0; let args = self.parse_dict_contents().0;
let args_span = self.end_group(); let args_span = self.end_group();
let span = Span::merge(name.span, args_span); let span = Span::merge(name.span, args_span);
CallExpr { name, args }.span_with(span) ExprCall { name, args }.span_with(span)
} }
} }
// Dicts. // Dicts.
impl Parser<'_> { impl Parser<'_> {
fn parse_dict_contents(&mut self) -> (DictExpr, bool) { fn parse_dict_contents(&mut self) -> (LitDict, bool) {
let mut dict = DictExpr::new(); let mut dict = LitDict::default();
let mut comma_and_keyless = true; let mut comma_and_keyless = true;
while { while {
self.skip_ws(); self.skip_ws();
!self.eof() !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(); self.skip_ws();
match self.peekv() { match self.peekv() {
@ -268,7 +265,7 @@ impl Parser<'_> {
self.eat(); self.eat();
self.skip_ws(); self.skip_ws();
if let Some(value) = self.parse_expr() { if let Some(value) = self.parse_expr() {
(Some(ident), value) (Some(ident.map(|id| DictKey::Str(id.0))), value)
} else { } else {
self.expected("value"); self.expected("value");
continue; continue;
@ -280,7 +277,7 @@ impl Parser<'_> {
(None, call.map(Expr::Call)) (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() { } else if let Some(value) = self.parse_expr() {
(None, value) (None, value)
@ -289,17 +286,16 @@ impl Parser<'_> {
continue; continue;
}; };
let behind = val.span.end; if let Some(key) = &key {
if let Some(key) = key {
comma_and_keyless = false; comma_and_keyless = false;
dict.insert(key.v.0, SpannedEntry::new(key.span, val));
self.feedback self.feedback
.decorations .decorations
.push(Decoration::DictKey.span_with(key.span)); .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 { if {
self.skip_ws(); self.skip_ws();
self.eof() self.eof()
@ -311,27 +307,25 @@ impl Parser<'_> {
comma_and_keyless = false; comma_and_keyless = false;
} }
let coercable = comma_and_keyless && !dict.is_empty(); let coercable = comma_and_keyless && !dict.0.is_empty();
(dict, coercable) (dict, coercable)
} }
} }
type Binop = fn(Box<Spanned<Expr>>, Box<Spanned<Expr>>) -> Expr;
// Expressions and values. // Expressions and values.
impl Parser<'_> { impl Parser<'_> {
fn parse_expr(&mut self) -> Option<Spanned<Expr>> { fn parse_expr(&mut self) -> Option<Spanned<Expr>> {
self.parse_binops("summand", Self::parse_term, |token| match token { self.parse_binops("summand", Self::parse_term, |token| match token {
Token::Plus => Some(Expr::Add), Token::Plus => Some(BinOp::Add),
Token::Hyphen => Some(Expr::Sub), Token::Hyphen => Some(BinOp::Sub),
_ => None, _ => None,
}) })
} }
fn parse_term(&mut self) -> Option<Spanned<Expr>> { fn parse_term(&mut self) -> Option<Spanned<Expr>> {
self.parse_binops("factor", Self::parse_factor, |token| match token { self.parse_binops("factor", Self::parse_factor, |token| match token {
Token::Star => Some(Expr::Mul), Token::Star => Some(BinOp::Mul),
Token::Slash => Some(Expr::Div), Token::Slash => Some(BinOp::Div),
_ => None, _ => None,
}) })
} }
@ -341,7 +335,7 @@ impl Parser<'_> {
&mut self, &mut self,
operand_name: &str, operand_name: &str,
mut parse_operand: impl FnMut(&mut Self) -> Option<Spanned<Expr>>, 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>> { ) -> Option<Spanned<Expr>> {
let mut left = parse_operand(self)?; let mut left = parse_operand(self)?;
@ -353,8 +347,12 @@ impl Parser<'_> {
if let Some(right) = parse_operand(self) { if let Some(right) = parse_operand(self) {
let span = Span::merge(left.span, right.span); let span = Span::merge(left.span, right.span);
let v = op(Box::new(left), Box::new(right)); let expr = Expr::Binary(ExprBinary {
left = v.span_with(span); lhs: left.map(Box::new),
op: op.span_with(token.span),
rhs: right.map(Box::new),
});
left = expr.span_with(span);
self.skip_ws(); self.skip_ws();
continue; continue;
} }
@ -375,7 +373,11 @@ impl Parser<'_> {
self.skip_ws(); self.skip_ws();
if let Some(factor) = self.parse_factor() { if let Some(factor) = self.parse_factor() {
let span = Span::merge(hyph.span, factor.span); 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 { } else {
error!(@self.feedback, hyph.span, "dangling minus"); error!(@self.feedback, hyph.span, "dangling minus");
None None
@ -396,7 +398,7 @@ impl Parser<'_> {
if self.check(Token::LeftParen) { if self.check(Token::LeftParen) {
self.parse_paren_call(name).map(Expr::Call) self.parse_paren_call(name).map(Expr::Call)
} else { } else {
name.map(Expr::Ident) name.map(|n| Expr::Lit(Lit::Ident(n)))
} }
} }
@ -404,21 +406,19 @@ impl Parser<'_> {
if !terminated { if !terminated {
self.expected_at("quote", span.end); 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::Bool(b) => self.with_span(Expr::Lit(Lit::Bool(b))),
Token::Number(n) => self.with_span(Expr::Number(n)), Token::Number(n) => self.with_span(Expr::Lit(Lit::Float(n))),
Token::Length(s) => self.with_span(Expr::Length(s)), Token::Length(s) => self.with_span(Expr::Lit(Lit::Length(s))),
Token::Hex(s) => { Token::Hex(s) => {
if let Ok(color) = RgbaColor::from_str(s) { let color = RgbaColor::from_str(s).unwrap_or_else(|_| {
self.with_span(Expr::Color(color))
} else {
// Heal color by assuming black. // Heal color by assuming black.
error!(@self.feedback, span, "invalid color"); error!(@self.feedback, span, "invalid color");
let healed = RgbaColor::new_healed(0, 0, 0, 255); RgbaColor::new_healed(0, 0, 0, 255)
self.with_span(Expr::Color(healed)) });
} self.with_span(Expr::Lit(Lit::Color(color)))
} }
// This could be a dictionary or a parenthesized expression. We // This could be a dictionary or a parenthesized expression. We
@ -430,9 +430,9 @@ impl Parser<'_> {
let span = self.end_group(); let span = self.end_group();
let expr = if coercable { 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 { } else {
Expr::Dict(dict) Expr::Lit(Lit::Dict(dict))
}; };
expr.span_with(span) expr.span_with(span)
@ -442,19 +442,17 @@ impl Parser<'_> {
Token::LeftBrace => { Token::LeftBrace => {
self.start_group(Group::Brace); self.start_group(Group::Brace);
self.tokens.push_mode(TokenMode::Body); self.tokens.push_mode(TokenMode::Body);
let tree = self.parse_body_contents(); let tree = self.parse_body_contents();
self.tokens.pop_mode(); self.tokens.pop_mode();
let span = self.end_group(); 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. // This is a bracketed function call.
Token::LeftBracket => { Token::LeftBracket => {
let call = self.parse_bracket_call(false); let call = self.parse_bracket_call(false);
let tree = vec![call.map(SyntaxNode::Call)]; let tree = vec![call.map(|c| SynNode::Expr(Expr::Call(c)))];
Expr::Tree(tree).span_with(span) Expr::Lit(Lit::Content(tree)).span_with(span)
} }
_ => return None, _ => return None,

View File

@ -1,7 +1,7 @@
//! Resolve strings and raw blocks. //! Resolve strings and raw blocks.
use super::{is_newline, Scanner}; use super::{is_newline, Scanner};
use crate::syntax::{Ident, Raw}; use crate::syntax::{Ident, NodeRaw};
/// Resolves all escape sequences in a string. /// Resolves all escape sequences in a string.
pub fn resolve_string(string: &str) -> 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. /// 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 { if backticks > 1 {
let (tag, inner) = split_at_lang_tag(raw); let (tag, inner) = split_at_lang_tag(raw);
let (lines, had_newline) = trim_and_split_raw(inner); let (lines, had_newline) = trim_and_split_raw(inner);
Raw { NodeRaw {
lang: Ident::new(tag), lang: Ident::new(tag),
lines, lines,
inline: !had_newline, inline: !had_newline,
} }
} else { } else {
Raw { NodeRaw {
lang: None, lang: None,
lines: split_lines(raw), lines: split_lines(raw),
inline: true, inline: true,

View File

@ -6,33 +6,33 @@ use std::fmt::Debug;
use super::parse; use super::parse;
use crate::color::RgbaColor; use crate::color::RgbaColor;
use crate::compute::dict::SpannedEntry; use crate::compute::dict::DictKey;
use crate::length::Length; use crate::length::Length;
use crate::syntax::*; use crate::syntax::*;
// ------------------------------ Construct Syntax Nodes ------------------------------ // // ------------------------------ Construct Syntax Nodes ------------------------------ //
use Decoration::*; use Decoration::*;
use SyntaxNode::{ use SynNode::{
Linebreak as L, Parbreak as P, Spacing as S, ToggleBolder as B, ToggleItalic as I, Linebreak as L, Parbreak as P, Spacing as S, ToggleBolder as B, ToggleItalic as I,
}; };
fn T(text: &str) -> SyntaxNode { fn T(text: &str) -> SynNode {
SyntaxNode::Text(text.to_string()) SynNode::Text(text.to_string())
} }
macro_rules! H { macro_rules! H {
($level:expr, $($tts:tt)*) => { ($level:expr, $($tts:tt)*) => {
SyntaxNode::Heading(Heading { SynNode::Heading(NodeHeading {
level: Spanned::zero($level), level: Spanned::zero($level),
tree: Tree![@$($tts)*], contents: Tree![@$($tts)*],
}) })
}; };
} }
macro_rules! R { macro_rules! R {
($lang:expr, $inline:expr, $($line:expr),* $(,)?) => {{ ($lang:expr, $inline:expr, $($line:expr),* $(,)?) => {{
SyntaxNode::Raw(Raw { SynNode::Raw(NodeRaw {
lang: $lang, lang: $lang,
lines: vec![$($line.to_string()) ,*], lines: vec![$($line.to_string()) ,*],
inline: $inline, inline: $inline,
@ -45,53 +45,73 @@ fn Lang(lang: &str) -> Option<Ident> {
} }
macro_rules! F { macro_rules! F {
($($tts:tt)*) => { SyntaxNode::Call(Call!(@$($tts)*)) } ($($tts:tt)*) => { SynNode::Expr(Expr::Call(Call!(@$($tts)*))) }
} }
// ------------------------------- Construct Expressions ------------------------------ // // ------------------------------- Construct Expressions ------------------------------ //
use Expr::{Bool, Color, Length as Len, Number as Num}; use BinOp::*;
use UnOp::*;
fn Id(ident: &str) -> Expr { 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 { fn Str(string: &str) -> Expr {
Expr::Str(string.to_string()) Expr::Lit(Lit::Str(string.to_string()))
} }
macro_rules! Dict { macro_rules! Dict {
(@dict=$dict:expr,) => {}; (@dict=$dict:expr,) => {};
(@dict=$dict:expr, $key:expr => $value:expr $(, $($tts:tt)*)?) => {{ (@dict=$dict:expr, $key:expr => $value:expr $(, $($tts:tt)*)?) => {{
let key = Into::<Spanned<&str>>::into($key); let key = Into::<Spanned<&str>>::into($key);
let val = Into::<Spanned<Expr>>::into($value); let key = key.map(Into::<DictKey>::into);
$dict.insert(key.v, SpannedEntry::new(key.span, val)); let value = Into::<Spanned<Expr>>::into($value);
$dict.0.push(LitDictEntry { key: Some(key), value });
Dict![@dict=$dict, $($($tts)*)?]; Dict![@dict=$dict, $($($tts)*)?];
}}; }};
(@dict=$dict:expr, $value:expr $(, $($tts:tt)*)?) => { (@dict=$dict:expr, $value:expr $(, $($tts:tt)*)?) => {
let val = Into::<Spanned<Expr>>::into($value); let value = Into::<Spanned<Expr>>::into($value);
$dict.push(SpannedEntry::val(val)); $dict.0.push(LitDictEntry { key: None, value });
Dict![@dict=$dict, $($($tts)*)?]; Dict![@dict=$dict, $($($tts)*)?];
}; };
(@$($tts:tt)*) => {{ (@$($tts:tt)*) => {{
#[allow(unused_mut)] #[allow(unused_mut)]
let mut dict = DictExpr::new(); let mut dict = LitDict::default();
Dict![@dict=dict, $($tts)*]; Dict![@dict=dict, $($tts)*];
dict dict
}}; }};
($($tts:tt)*) => { Expr::Dict(Dict![@$($tts)*]) }; ($($tts:tt)*) => { Expr::Lit(Lit::Dict(Dict![@$($tts)*])) };
} }
macro_rules! Tree { macro_rules! Tree {
(@$($node:expr),* $(,)?) => { (@$($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 { macro_rules! Call {
(@$name:expr $(; $($tts:tt)*)?) => {{ (@$name:expr $(; $($tts:tt)*)?) => {{
let name = Into::<Spanned<&str>>::into($name); let name = Into::<Spanned<&str>>::into($name);
CallExpr { ExprCall {
name: name.map(|n| Ident(n.to_string())), name: name.map(|n| Ident(n.to_string())),
args: Dict![@$($($tts)*)?], args: Dict![@$($($tts)*)?],
} }
@ -99,20 +119,22 @@ macro_rules! Call {
($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) }; ($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) };
} }
fn Neg<T: Into<Spanned<Expr>>>(e1: T) -> Expr { fn Unary(op: impl Into<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> Expr {
Expr::Neg(Box::new(e1.into())) Expr::Unary(ExprUnary {
op: op.into(),
expr: expr.into().map(Box::new),
})
} }
fn Add<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr { fn Binary(
Expr::Add(Box::new(e1.into()), Box::new(e2.into())) op: impl Into<Spanned<BinOp>>,
} lhs: impl Into<Spanned<Expr>>,
fn Sub<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr { rhs: impl Into<Spanned<Expr>>,
Expr::Sub(Box::new(e1.into()), Box::new(e2.into())) ) -> Expr {
} Expr::Binary(ExprBinary {
fn Mul<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr { lhs: lhs.into().map(Box::new),
Expr::Mul(Box::new(e1.into()), Box::new(e2.into())) op: op.into(),
} rhs: rhs.into().map(Box::new),
fn Div<T: Into<Spanned<Expr>>>(e1: T, e2: T) -> Expr { })
Expr::Div(Box::new(e1.into()), Box::new(e2.into()))
} }
// ------------------------------------ Test Macros ----------------------------------- // // ------------------------------------ Test Macros ----------------------------------- //
@ -321,12 +343,12 @@ fn test_parse_function_names() {
#[test] #[test]
fn test_parse_chaining() { fn test_parse_chaining() {
// Things the parser has to make sense of // 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 >>][Hi]" => F!("box"; Tree![T("Hi")]));
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![ t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi"))) 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))))) F!("emph"; Tree!(F!("sub"; Len(Length::cm(1.0)))))
])); ]));
@ -354,7 +376,7 @@ fn test_parse_colon_starting_func_args() {
#[test] #[test]
fn test_parse_function_bodies() { 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")); e!(" [val][ */]" => s(8, 10, "unexpected end of block comment"));
// Raw in body. // Raw in body.
@ -384,9 +406,9 @@ fn test_parse_values() {
v!("\"hi\"" => Str("hi")); v!("\"hi\"" => Str("hi"));
v!("true" => Bool(true)); v!("true" => Bool(true));
v!("false" => Bool(false)); v!("false" => Bool(false));
v!("1.0e-4" => Num(1e-4)); v!("1.0e-4" => Float(1e-4));
v!("3.14" => Num(3.14)); v!("3.14" => Float(3.14));
v!("50%" => Num(0.5)); v!("50%" => Float(0.5));
v!("4.5cm" => Len(Length::cm(4.5))); v!("4.5cm" => Len(Length::cm(4.5)));
v!("12e1pt" => Len(Length::pt(12e1))); v!("12e1pt" => Len(Length::pt(12e1)));
v!("#f7a20500" => Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0x00))); v!("#f7a20500" => Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0x00)));
@ -411,7 +433,7 @@ fn test_parse_values() {
s(13, 13, "expected closing bracket")); s(13, 13, "expected closing bracket"));
// Spanned. // 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] #[test]
@ -420,40 +442,50 @@ fn test_parse_expressions() {
v!("(hi)" => Id("hi")); v!("(hi)" => Id("hi"));
// Operations. // Operations.
v!("-1" => Neg(Num(1.0))); v!("-1" => Unary(Neg, Float(1.0)));
v!("-- 1" => Neg(Neg(Num(1.0)))); v!("-- 1" => Unary(Neg, Unary(Neg, Float(1.0))));
v!("3.2in + 6pt" => Add(Len(Length::inches(3.2)), Len(Length::pt(6.0)))); v!("3.2in + 6pt" => Binary(Add, Len(Length::inches(3.2)), Len(Length::pt(6.0))));
v!("5 - 0.01" => Sub(Num(5.0), Num(0.01))); v!("5 - 0.01" => Binary(Sub, Float(5.0), Float(0.01)));
v!("(3mm * 2)" => Mul(Len(Length::mm(3.0)), Num(2.0))); v!("(3mm * 2)" => Binary(Mul, Len(Length::mm(3.0)), Float(2.0)));
v!("12e-3cm/1pt" => Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0)))); v!("12e-3cm/1pt" => Binary(Div, Len(Length::cm(12e-3)), Len(Length::pt(1.0))));
// More complex. // More complex.
v!("(3.2in + 6pt)*(5/2-1)" => Mul( v!("(3.2in + 6pt)*(5/2-1)" => Binary(
Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))), Mul,
Sub(Div(Num(5.0), Num(2.0)), Num(1.0)) 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( v!("(6.3E+2+4* - 3.2pt)/2" => Binary(
Add(Num(6.3e2), Mul(Num(4.0), Neg(Len(Length::pt(3.2))))), Div,
Num(2.0) 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. // 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. // Spanned.
ts!("[val: 1 + 3]" => s(0, 12, F!( 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. // 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. // Invalid expressions.
v!("4pt--" => Len(Length::pt(4.0))); v!("4pt--" => Len(Length::pt(4.0)));
e!("[val: 4pt--]" => s(10, 11, "dangling minus"), e!("[val: 4pt--]" => s(10, 11, "dangling minus"),
s(6, 10, "missing right summand")); 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")); e!("[val: 3mm+4pt*]" => s(10, 14, "missing right factor"));
} }
@ -464,8 +496,8 @@ fn test_parse_dicts() {
v!("(false)" => Bool(false)); v!("(false)" => Bool(false));
v!("(true,)" => Dict![Bool(true)]); v!("(true,)" => Dict![Bool(true)]);
v!("(key=val)" => Dict!["key" => Id("val")]); v!("(key=val)" => Dict!["key" => Id("val")]);
v!("(1, 2)" => Dict![Num(1.0), Num(2.0)]); v!("(1, 2)" => Dict![Float(1.0), Float(2.0)]);
v!("(1, key=\"value\")" => Dict![Num(1.0), "key" => Str("value")]); v!("(1, key=\"value\")" => Dict![Float(1.0), "key" => Str("value")]);
// Decorations. // Decorations.
d!("[val: key=hi]" => s(6, 9, DictKey)); d!("[val: key=hi]" => s(6, 9, DictKey));
@ -483,7 +515,7 @@ fn test_parse_dicts() {
#[test] #[test]
fn test_parse_dicts_compute_func_calls() { fn test_parse_dicts_compute_func_calls() {
v!("empty()" => Call!("empty")); 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"; v!("items(\"fire\", #f93a6d)" => Call!("items";
Str("fire"), Color(RgbaColor::new(0xf9, 0x3a, 0x6d, 0xff)) 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!( v!("css(1pt, rgb(90, 102, 254), \"solid\")" => Call!(
"css"; "css";
Len(Length::pt(1.0)), 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"), Str("solid"),
)); ));
@ -501,7 +533,7 @@ fn test_parse_dicts_compute_func_calls() {
e!("[val: lang(中文]" => s(17, 17, "expected closing paren")); e!("[val: lang(中文]" => s(17, 17, "expected closing paren"));
// Invalid name. // 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")); 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() { fn test_parse_dicts_nested() {
v!("(1, ( ab=(), d = (3, 14pt) )), false" => v!("(1, ( ab=(), d = (3, 14pt) )), false" =>
Dict![ Dict![
Num(1.0), Float(1.0),
Dict!( Dict!(
"ab" => 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), Bool(false),
@ -546,7 +578,7 @@ fn test_parse_dicts_errors() {
s(10, 11, "expected value, found equals sign")); s(10, 11, "expected value, found equals sign"));
// Unexpected 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]" => e!("[val: z=y=4]" =>
s(9, 9, "expected comma"), s(9, 9, "expected comma"),
s(9, 10, "expected value, found equals sign")); s(9, 10, "expected value, found equals sign"));

124
src/syntax/expr.rs Normal file
View File

@ -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)
}
}
}

58
src/syntax/ident.rs Normal file
View File

@ -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 == '_'
}

99
src/syntax/lit.rs Normal file
View File

@ -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>,
}

View File

@ -1,12 +1,24 @@
//! Syntax types. //! Syntax types.
mod expr;
mod ident;
mod lit;
mod span; mod span;
mod token; mod token;
mod tree; 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 span::*;
pub use token::*; pub use token::*;
pub use tree::*;
/// Decorations for semantic syntax highlighting. /// Decorations for semantic syntax highlighting.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]

View File

@ -42,8 +42,10 @@ impl<T> Offset for SpanVec<T> {
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
pub struct Spanned<T> { pub struct Spanned<T> {
pub span: Span, /// The spanned value.
pub v: T, pub v: T,
/// The location in source code of the value.
pub span: Span,
} }
impl<T> Spanned<T> { impl<T> Spanned<T> {

View File

@ -1,26 +1,15 @@
//! The syntax tree. //! The syntax tree.
use std::fmt::{self, Debug, Formatter}; use super::span::{SpanVec, Spanned};
use std::ops::Deref; use super::{Expr, Ident};
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};
/// A collection of nodes which form a tree together with the nodes' children. /// 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 /// A syntax node, which encompasses a single logical entity of parsed source
/// code. /// code.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum SyntaxNode { pub enum SynNode {
/// Whitespace containing less than two newlines. /// Whitespace containing less than two newlines.
Spacing, Spacing,
/// A forced line break. /// A forced line break.
@ -34,11 +23,11 @@ pub enum SyntaxNode {
/// Plain text. /// Plain text.
Text(String), Text(String),
/// An optionally syntax-highlighted raw block. /// An optionally syntax-highlighted raw block.
Raw(Raw), Raw(NodeRaw),
/// Section headings. /// A section heading.
Heading(Heading), Heading(NodeHeading),
/// A function call. /// An expression.
Call(CallExpr), Expr(Expr),
} }
/// A raw block, rendered in monospace with optional syntax highlighting. /// 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 /// you can always force leading or trailing whitespace simply by adding more
/// spaces. /// spaces.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Raw { pub struct NodeRaw {
/// An optional identifier specifying the language to syntax-highlight in. /// An optional identifier specifying the language to syntax-highlight in.
pub lang: Option<Ident>, pub lang: Option<Ident>,
/// The lines of raw text, determined as the raw string between the /// The lines of raw text, determined as the raw string between the
@ -122,221 +111,9 @@ pub struct Raw {
/// A section heading. /// A section heading.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Heading { pub struct NodeHeading {
/// The section depth (how many hashtags minus 1). /// The section depth (how many hashtags minus 1).
pub level: Spanned<u8>, pub level: Spanned<u8>,
pub tree: SyntaxTree, /// The contents of the heading.
} pub contents: SynTree,
/// 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)
}
}
} }