summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-21 17:46:09 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-21 17:50:56 +0100
commit5e08028fb36aa766957cba64c5c665edf9b96fb7 (patch)
tree912799dad3c1e25b7032f3e3bee009537c6f555b /src/syntax
parent898728f260923a91444eb23b522d0abf01a4299b (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.rs98
-rw-r--r--src/syntax/node.rs121
-rw-r--r--src/syntax/token.rs2
-rw-r--r--src/syntax/visit.rs22
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),