diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-21 17:46:09 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-21 17:50:56 +0100 |
| commit | 5e08028fb36aa766957cba64c5c665edf9b96fb7 (patch) | |
| tree | 912799dad3c1e25b7032f3e3bee009537c6f555b /src/syntax | |
| parent | 898728f260923a91444eb23b522d0abf01a4299b (diff) | |
Syntax functions 🚀
This adds overridable functions that markup desugars into. Specifically:
- \ desugars into linebreak
- Two newlines desugar into parbreak
- * desugars into strong
- _ desugars into emph
- = .. desugars into heading
- `..` desugars into raw
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/expr.rs | 98 | ||||
| -rw-r--r-- | src/syntax/node.rs | 121 | ||||
| -rw-r--r-- | src/syntax/token.rs | 2 | ||||
| -rw-r--r-- | src/syntax/visit.rs | 22 |
4 files changed, 165 insertions, 78 deletions
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 2c631991..97361fc3 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -7,8 +7,27 @@ use crate::geom::{AngularUnit, LengthUnit}; /// An expression. #[derive(Debug, Clone, PartialEq)] pub enum Expr { - /// A literal, like `11pt` or `"hi"`. - Lit(Lit), + /// The none literal: `none`. + None(Span), + /// A boolean literal: `true`, `false`. + Bool(Span, bool), + /// An integer literal: `120`. + Int(Span, i64), + /// A floating-point literal: `1.2`, `10e-4`. + Float(Span, f64), + /// A length literal: `12pt`, `3cm`. + Length(Span, f64, LengthUnit), + /// An angle literal: `1.5rad`, `90deg`. + Angle(Span, f64, AngularUnit), + /// A percent literal: `50%`. + /// + /// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the + /// corresponding [value](crate::geom::Relative). + Percent(Span, f64), + /// A color literal: `#ffccee`. + Color(Span, RgbaColor), + /// A string literal: `"hello!"`. + Str(Span, String), /// An identifier: `left`. Ident(Ident), /// An array expression: `(1, "hi", 12cm)`. @@ -42,22 +61,30 @@ pub enum Expr { impl Expr { /// The source code location. pub fn span(&self) -> Span { - match self { - Self::Lit(v) => v.span, - Self::Ident(v) => v.span, - Self::Array(v) => v.span, - Self::Dict(v) => v.span, - Self::Template(v) => v.span, - Self::Group(v) => v.span, - Self::Block(v) => v.span, - Self::Unary(v) => v.span, - Self::Binary(v) => v.span, - Self::Call(v) => v.span, - Self::Closure(v) => v.span, - Self::Let(v) => v.span, - Self::If(v) => v.span, - Self::While(v) => v.span, - Self::For(v) => v.span, + match *self { + Self::None(span) => span, + Self::Bool(span, _) => span, + Self::Int(span, _) => span, + Self::Float(span, _) => span, + Self::Length(span, _, _) => span, + Self::Angle(span, _, _) => span, + Self::Percent(span, _) => span, + Self::Color(span, _) => span, + Self::Str(span, _) => span, + Self::Ident(ref v) => v.span, + Self::Array(ref v) => v.span, + Self::Dict(ref v) => v.span, + Self::Template(ref v) => v.span, + Self::Group(ref v) => v.span, + Self::Block(ref v) => v.span, + Self::Unary(ref v) => v.span, + Self::Binary(ref v) => v.span, + Self::Call(ref v) => v.span, + Self::Closure(ref v) => v.span, + Self::Let(ref v) => v.span, + Self::If(ref v) => v.span, + Self::While(ref v) => v.span, + Self::For(ref v) => v.span, } } @@ -74,41 +101,6 @@ impl Expr { } } -/// A literal, like `11pt` or `"hi"`. -#[derive(Debug, Clone, PartialEq)] -pub struct Lit { - /// The source code location. - pub span: Span, - /// The kind of literal. - pub kind: LitKind, -} - -/// A kind of literal. -#[derive(Debug, Clone, PartialEq)] -pub enum LitKind { - /// The none literal: `none`. - None, - /// A boolean literal: `true`, `false`. - Bool(bool), - /// An integer literal: `120`. - Int(i64), - /// A floating-point literal: `1.2`, `10e-4`. - Float(f64), - /// A length literal: `12pt`, `3cm`. - Length(f64, LengthUnit), - /// An angle literal: `1.5rad`, `90deg`. - Angle(f64, AngularUnit), - /// A percent literal: `50%`. - /// - /// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the - /// corresponding [value](crate::geom::Relative). - Percent(f64), - /// A color literal: `#ffccee`. - Color(RgbaColor), - /// A string literal: `"hello!"`. - Str(String), -} - /// An array expression: `(1, "hi", 12cm)`. #[derive(Debug, Clone, PartialEq)] pub struct ArrayExpr { diff --git a/src/syntax/node.rs b/src/syntax/node.rs index c94ee5b0..537a5686 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -1,35 +1,84 @@ +use std::rc::Rc; + use super::*; /// A syntax node, encompassing a single logical entity of parsed source code. #[derive(Debug, Clone, PartialEq)] pub enum Node { - /// Strong text was enabled / disabled. - Strong, - /// Emphasized text was enabled / disabled. - Emph, - /// Whitespace containing less than two newlines. - Space, - /// A forced line break. - Linebreak, - /// A paragraph break. - Parbreak, /// Plain text. Text(String), - /// A section heading. + /// Whitespace containing less than two newlines. + Space, + /// A forced line break: `\`. + Linebreak(Span), + /// A paragraph break: Two or more newlines. + Parbreak(Span), + /// Strong text was enabled / disabled: `*`. + Strong(Span), + /// Emphasized text was enabled / disabled: `_`. + Emph(Span), + /// A section heading: `= Introduction`. Heading(HeadingNode), - /// An optionally syntax-highlighted raw block. + /// A raw block with optional syntax highlighting: `` `...` ``. Raw(RawNode), /// An expression. Expr(Expr), } +impl Node { + // The names of the corresponding library functions. + pub const LINEBREAK: &'static str = "linebreak"; + pub const PARBREAK: &'static str = "parbreak"; + pub const STRONG: &'static str = "strong"; + pub const EMPH: &'static str = "emph"; + pub const HEADING: &'static str = "heading"; + pub const RAW: &'static str = "raw"; + + /// Desugar markup into a function call. + pub fn desugar(&self) -> Option<CallExpr> { + match *self { + Node::Text(_) => None, + Node::Space => None, + Node::Linebreak(span) => Some(call(span, Self::LINEBREAK)), + Node::Parbreak(span) => Some(call(span, Self::PARBREAK)), + Node::Strong(span) => Some(call(span, Self::STRONG)), + Node::Emph(span) => Some(call(span, Self::EMPH)), + Self::Heading(ref heading) => Some(heading.desugar()), + Self::Raw(ref raw) => Some(raw.desugar()), + Node::Expr(_) => None, + } + } +} + /// A section heading: `= Introduction`. #[derive(Debug, Clone, PartialEq)] pub struct HeadingNode { - /// The section depth (numer of equals signs minus 1). + /// The source code location. + pub span: Span, + /// The section depth (numer of equals signs). pub level: usize, /// The contents of the heading. - pub contents: Tree, + pub contents: Rc<Tree>, +} + +impl HeadingNode { + pub const LEVEL: &'static str = "level"; + pub const BODY: &'static str = "body"; + + /// Desugar into a function call. + pub fn desugar(&self) -> CallExpr { + let Self { span, level, ref contents } = *self; + let mut call = call(span, Node::HEADING); + call.args.items.push(CallArg::Named(Named { + name: ident(span, Self::LEVEL), + expr: Expr::Int(span, level as i64), + })); + call.args.items.push(CallArg::Pos(Expr::Template(TemplateExpr { + span, + tree: Rc::clone(&contents), + }))); + call + } } /// A raw block with optional syntax highlighting: `` `...` ``. @@ -97,12 +146,50 @@ pub struct HeadingNode { /// whitespace simply by adding more spaces. #[derive(Debug, Clone, PartialEq)] pub struct RawNode { + /// The source code location. + pub span: Span, /// 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 - /// backticks trimmed according to the above rules and split at newlines. - pub lines: Vec<String>, + /// The raw text, determined as the raw string between the backticks trimmed + /// according to the above rules. + pub text: String, /// Whether the element is block-level, that is, it has 3+ backticks /// and contains at least one newline. pub block: bool, } + +impl RawNode { + pub const LANG: &'static str = "lang"; + pub const BLOCK: &'static str = "block"; + pub const TEXT: &'static str = "text"; + + /// Desugar into a function call. + pub fn desugar(&self) -> CallExpr { + let Self { span, ref lang, ref text, block } = *self; + let mut call = call(span, Node::RAW); + if let Some(lang) = lang { + call.args.items.push(CallArg::Named(Named { + name: ident(span, Self::LANG), + expr: Expr::Str(span, lang.string.clone()), + })); + } + call.args.items.push(CallArg::Named(Named { + name: ident(span, Self::BLOCK), + expr: Expr::Bool(span, block), + })); + call.args.items.push(CallArg::Pos(Expr::Str(span, text.clone()))); + call + } +} + +fn call(span: Span, name: &str) -> CallExpr { + CallExpr { + span, + callee: Box::new(Expr::Ident(Ident { span, string: name.into() })), + args: CallArgs { span, items: vec![] }, + } +} + +fn ident(span: Span, string: &str) -> Ident { + Ident { span, string: string.into() } +} diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 832c923d..40e1d6d2 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -121,7 +121,7 @@ pub enum Token<'s> { /// A percentage: `50%`. /// /// _Note_: `50%` is stored as `50.0` here, as in the corresponding - /// [literal](super::LitKind::Percent). + /// [literal](super::Expr::Percent). Percent(f64), /// A color value: `#20d82a`. Color(RgbaColor), diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 04546c5d..9c1272ee 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -50,13 +50,13 @@ visit! { fn visit_node(v, node: &Node) { match node { - Node::Strong => {} - Node::Emph => {} - Node::Space => {} - Node::Linebreak => {} - Node::Parbreak => {} Node::Text(_) => {} - Node::Heading(n) => v.visit_tree(&n.contents), + Node::Space => {} + Node::Strong(_) => {} + Node::Linebreak(_) => {} + Node::Parbreak(_) => {} + Node::Emph(_) => {} + Node::Heading(heading) => v.visit_tree(&heading.contents), Node::Raw(_) => {} Node::Expr(expr) => v.visit_expr(expr), } @@ -64,7 +64,15 @@ visit! { fn visit_expr(v, node: &Expr) { match node { - Expr::Lit(_) => {} + Expr::None(_) => {} + Expr::Bool(_, _) => {} + Expr::Int(_, _) => {} + Expr::Float(_, _) => {} + Expr::Length(_, _, _) => {} + Expr::Angle(_, _, _) => {} + Expr::Percent(_, _) => {} + Expr::Color(_, _) => {} + Expr::Str(_, _) => {} Expr::Ident(_) => {} Expr::Array(e) => v.visit_array(e), Expr::Dict(e) => v.visit_dict(e), |
