summaryrefslogtreecommitdiff
path: root/src/syntax
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/syntax
parentaafd3c95cacd829b647cfab1533de5d4833b9a04 (diff)
Reorganize ast types 🏕
Diffstat (limited to 'src/syntax')
-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
6 files changed, 310 insertions, 238 deletions
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,
}