summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/diag.rs13
-rw-r--r--src/eval/capture.rs32
-rw-r--r--src/eval/mod.rs371
-rw-r--r--src/parse/incremental.rs12
-rw-r--r--src/parse/mod.rs73
-rw-r--r--src/parse/parser.rs3
-rw-r--r--src/parse/resolve.rs8
-rw-r--r--src/parse/tokens.rs179
-rw-r--r--src/source.rs4
-rw-r--r--src/syntax/ast.rs823
-rw-r--r--src/syntax/highlight.rs114
-rw-r--r--src/syntax/kind.rs335
12 files changed, 1110 insertions, 857 deletions
diff --git a/src/diag.rs b/src/diag.rs
index ed4d4756..f4725f00 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -9,7 +9,7 @@ use std::string::FromUtf8Error;
use comemo::Tracked;
-use crate::syntax::{Span, Spanned};
+use crate::syntax::{ErrorPos, Span, Spanned};
use crate::util::EcoString;
use crate::World;
@@ -83,17 +83,6 @@ impl SourceError {
}
}
-/// Where in a node an error should be annotated,
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum ErrorPos {
- /// Over the full width of the node.
- Full,
- /// At the start of the node.
- Start,
- /// At the end of the node.
- End,
-}
-
/// A part of an error's [trace](SourceError::trace).
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Tracepoint {
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
index b7570fe9..289d31e1 100644
--- a/src/eval/capture.rs
+++ b/src/eval/capture.rs
@@ -1,6 +1,6 @@
use super::{Scope, Scopes, Value};
-use crate::syntax::ast::{ClosureParam, Expr, Ident, Imports, TypedNode};
-use crate::syntax::SyntaxNode;
+use crate::syntax::ast::TypedNode;
+use crate::syntax::{ast, SyntaxNode};
/// A visitor that captures variable slots.
pub struct CapturesVisitor<'a> {
@@ -25,12 +25,12 @@ impl<'a> CapturesVisitor<'a> {
}
/// Bind a new internal variable.
- pub fn bind(&mut self, ident: Ident) {
+ pub fn bind(&mut self, ident: ast::Ident) {
self.internal.top.define(ident.take(), Value::None);
}
/// Capture a variable if it isn't internal.
- pub fn capture(&mut self, ident: Ident) {
+ pub fn capture(&mut self, ident: ast::Ident) {
if self.internal.get(&ident).is_err() {
if let Ok(value) = self.external.get(&ident) {
self.captures.define_captured(ident.take(), value.clone());
@@ -45,10 +45,10 @@ impl<'a> CapturesVisitor<'a> {
// Identifiers that shouldn't count as captures because they
// actually bind a new name are handled below (individually through
// the expressions that contain them).
- Some(Expr::Ident(ident)) => self.capture(ident),
+ Some(ast::Expr::Ident(ident)) => self.capture(ident),
// Code and content blocks create a scope.
- Some(Expr::Code(_) | Expr::Content(_)) => {
+ Some(ast::Expr::Code(_) | ast::Expr::Content(_)) => {
self.internal.enter();
for child in node.children() {
self.visit(child);
@@ -59,18 +59,18 @@ impl<'a> CapturesVisitor<'a> {
// A closure contains parameter bindings, which are bound before the
// body is evaluated. Care must be taken so that the default values
// of named parameters cannot access previous parameter bindings.
- Some(Expr::Closure(expr)) => {
+ Some(ast::Expr::Closure(expr)) => {
for param in expr.params() {
- if let ClosureParam::Named(named) = param {
+ if let ast::Param::Named(named) = param {
self.visit(named.expr().as_untyped());
}
}
for param in expr.params() {
match param {
- ClosureParam::Pos(ident) => self.bind(ident),
- ClosureParam::Named(named) => self.bind(named.name()),
- ClosureParam::Sink(ident) => self.bind(ident),
+ ast::Param::Pos(ident) => self.bind(ident),
+ ast::Param::Named(named) => self.bind(named.name()),
+ ast::Param::Sink(ident) => self.bind(ident),
}
}
@@ -79,7 +79,7 @@ impl<'a> CapturesVisitor<'a> {
// A let expression contains a binding, but that binding is only
// active after the body is evaluated.
- Some(Expr::Let(expr)) => {
+ Some(ast::Expr::Let(expr)) => {
if let Some(init) = expr.init() {
self.visit(init.as_untyped());
}
@@ -88,7 +88,7 @@ impl<'a> CapturesVisitor<'a> {
// A show rule contains a binding, but that binding is only active
// after the target has been evaluated.
- Some(Expr::Show(show)) => {
+ Some(ast::Expr::Show(show)) => {
self.visit(show.pattern().as_untyped());
if let Some(binding) = show.binding() {
self.bind(binding);
@@ -99,7 +99,7 @@ impl<'a> CapturesVisitor<'a> {
// A for loop contains one or two bindings in its pattern. These are
// active after the iterable is evaluated but before the body is
// evaluated.
- Some(Expr::For(expr)) => {
+ Some(ast::Expr::For(expr)) => {
self.visit(expr.iter().as_untyped());
let pattern = expr.pattern();
if let Some(key) = pattern.key() {
@@ -111,9 +111,9 @@ impl<'a> CapturesVisitor<'a> {
// An import contains items, but these are active only after the
// path is evaluated.
- Some(Expr::Import(expr)) => {
+ Some(ast::Expr::Import(expr)) => {
self.visit(expr.path().as_untyped());
- if let Imports::Items(items) = expr.imports() {
+ if let ast::Imports::Items(items) = expr.imports() {
for item in items {
self.bind(item);
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 26b4130b..0a3d6545 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -43,8 +43,8 @@ use crate::geom::{Angle, Em, Fraction, Length, Ratio};
use crate::library;
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
use crate::source::SourceId;
-use crate::syntax::ast::*;
-use crate::syntax::{Span, Spanned};
+use crate::syntax::ast::TypedNode;
+use crate::syntax::{ast, Span, Spanned, Unit};
use crate::util::EcoString;
use crate::World;
@@ -133,25 +133,25 @@ pub trait Eval {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output>;
}
-impl Eval for MarkupNode {
+impl Eval for ast::Markup {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- eval_markup(vm, &mut self.items())
+ eval_markup(vm, &mut self.children())
}
}
/// Evaluate a stream of markup nodes.
fn eval_markup(
vm: &mut Vm,
- nodes: &mut impl Iterator<Item = MarkupItem>,
+ nodes: &mut impl Iterator<Item = ast::MarkupNode>,
) -> SourceResult<Content> {
let flow = vm.flow.take();
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
while let Some(node) = nodes.next() {
seq.push(match node {
- MarkupItem::Expr(Expr::Set(set)) => {
+ ast::MarkupNode::Expr(ast::Expr::Set(set)) => {
let styles = set.eval(vm)?;
if vm.flow.is_some() {
break;
@@ -159,7 +159,7 @@ fn eval_markup(
eval_markup(vm, nodes)?.styled_with_map(styles)
}
- MarkupItem::Expr(Expr::Show(show)) => {
+ ast::MarkupNode::Expr(ast::Expr::Show(show)) => {
let recipe = show.eval(vm)?;
if vm.flow.is_some() {
break;
@@ -168,7 +168,7 @@ fn eval_markup(
eval_markup(vm, nodes)?
.styled_with_entry(StyleEntry::Recipe(recipe).into())
}
- MarkupItem::Expr(Expr::Wrap(wrap)) => {
+ ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => {
let tail = eval_markup(vm, nodes)?;
vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)?.display()
@@ -189,35 +189,86 @@ fn eval_markup(
Ok(Content::sequence(seq))
}
-impl Eval for MarkupItem {
+impl Eval for ast::MarkupNode {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok(match self {
- Self::Space => Content::Space,
- Self::Parbreak => Content::Parbreak,
- &Self::Linebreak => Content::Linebreak { justify: false },
- Self::Text(text) => Content::Text(text.clone()),
- &Self::Quote { double } => Content::Quote { double },
- Self::Strong(strong) => strong.eval(vm)?,
- Self::Emph(emph) => emph.eval(vm)?,
- Self::Link(url) => {
- Content::show(library::text::LinkNode::from_url(url.clone()))
- }
- Self::Raw(raw) => raw.eval(vm)?,
- Self::Math(math) => math.eval(vm)?,
- Self::Heading(heading) => heading.eval(vm)?,
- Self::List(list) => list.eval(vm)?,
- Self::Enum(enum_) => enum_.eval(vm)?,
- Self::Desc(desc) => desc.eval(vm)?,
- Self::Label(_) => Content::Empty,
- Self::Ref(label) => Content::show(library::structure::RefNode(label.clone())),
- Self::Expr(expr) => expr.eval(vm)?.display(),
+ match self {
+ Self::Space(v) => v.eval(vm),
+ Self::Linebreak(v) => v.eval(vm),
+ Self::Text(v) => v.eval(vm),
+ Self::Escape(v) => v.eval(vm),
+ Self::Shorthand(v) => v.eval(vm),
+ Self::SmartQuote(v) => v.eval(vm),
+ Self::Strong(v) => v.eval(vm),
+ Self::Emph(v) => v.eval(vm),
+ Self::Link(v) => v.eval(vm),
+ Self::Raw(v) => v.eval(vm),
+ Self::Math(v) => v.eval(vm),
+ Self::Heading(v) => v.eval(vm),
+ Self::List(v) => v.eval(vm),
+ Self::Enum(v) => v.eval(vm),
+ Self::Desc(v) => v.eval(vm),
+ Self::Label(v) => v.eval(vm),
+ Self::Ref(v) => v.eval(vm),
+ Self::Expr(v) => v.eval(vm).map(Value::display),
+ }
+ }
+}
+
+impl Eval for ast::Space {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(if self.newlines() < 2 {
+ Content::Space
+ } else {
+ Content::Parbreak
})
}
}
-impl Eval for StrongNode {
+impl Eval for ast::Linebreak {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Linebreak { justify: false })
+ }
+}
+
+impl Eval for ast::Text {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Text(self.get().clone()))
+ }
+}
+
+impl Eval for ast::Escape {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Text(self.get().into()))
+ }
+}
+
+impl Eval for ast::Shorthand {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Text(self.get().into()))
+ }
+}
+
+impl Eval for ast::SmartQuote {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Quote { double: self.double() })
+ }
+}
+
+impl Eval for ast::Strong {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -227,7 +278,7 @@ impl Eval for StrongNode {
}
}
-impl Eval for EmphNode {
+impl Eval for ast::Emph {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -237,27 +288,39 @@ impl Eval for EmphNode {
}
}
-impl Eval for RawNode {
+impl Eval for ast::Link {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::show(library::text::LinkNode::from_url(
+ self.url().clone(),
+ )))
+ }
+}
+
+impl Eval for ast::Raw {
type Output = Content;
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
let content = Content::show(library::text::RawNode {
- text: self.text.clone(),
- block: self.block,
+ text: self.text().clone(),
+ block: self.block(),
});
- Ok(match self.lang {
- Some(_) => content.styled(library::text::RawNode::LANG, self.lang.clone()),
+ Ok(match self.lang() {
+ Some(_) => content.styled(library::text::RawNode::LANG, self.lang().cloned()),
None => content,
})
}
}
-impl Eval for MathNode {
+impl Eval for ast::Math {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let nodes =
- self.items().map(|node| node.eval(vm)).collect::<SourceResult<_>>()?;
+ let nodes = self
+ .children()
+ .map(|node| node.eval(vm))
+ .collect::<SourceResult<_>>()?;
Ok(Content::show(library::math::MathNode::Row(
Arc::new(nodes),
self.span(),
@@ -265,20 +328,21 @@ impl Eval for MathNode {
}
}
-impl Eval for MathItem {
+impl Eval for ast::MathNode {
type Output = library::math::MathNode;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
Ok(match self {
- Self::Space => library::math::MathNode::Space,
- Self::Linebreak => library::math::MathNode::Linebreak,
- Self::Atom(atom) => library::math::MathNode::Atom(atom.clone()),
+ Self::Space(_) => library::math::MathNode::Space,
+ Self::Linebreak(_) => library::math::MathNode::Linebreak,
+ Self::Escape(c) => library::math::MathNode::Atom(c.get().into()),
+ Self::Atom(atom) => library::math::MathNode::Atom(atom.get().clone()),
Self::Script(node) => node.eval(vm)?,
Self::Frac(node) => node.eval(vm)?,
Self::Align(node) => node.eval(vm)?,
Self::Group(node) => library::math::MathNode::Row(
Arc::new(
- node.items()
+ node.children()
.map(|node| node.eval(vm))
.collect::<SourceResult<_>>()?,
),
@@ -292,7 +356,7 @@ impl Eval for MathItem {
}
}
-impl Eval for ScriptNode {
+impl Eval for ast::Script {
type Output = library::math::MathNode;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -314,7 +378,7 @@ impl Eval for ScriptNode {
}
}
-impl Eval for FracNode {
+impl Eval for ast::Frac {
type Output = library::math::MathNode;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -327,7 +391,7 @@ impl Eval for FracNode {
}
}
-impl Eval for AlignNode {
+impl Eval for ast::Align {
type Output = library::math::MathNode;
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
@@ -335,7 +399,7 @@ impl Eval for AlignNode {
}
}
-impl Eval for HeadingNode {
+impl Eval for ast::Heading {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -346,7 +410,7 @@ impl Eval for HeadingNode {
}
}
-impl Eval for ListItem {
+impl Eval for ast::ListItem {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -355,7 +419,7 @@ impl Eval for ListItem {
}
}
-impl Eval for EnumItem {
+impl Eval for ast::EnumItem {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -367,7 +431,7 @@ impl Eval for EnumItem {
}
}
-impl Eval for DescItem {
+impl Eval for ast::DescItem {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -379,7 +443,25 @@ impl Eval for DescItem {
}
}
-impl Eval for Expr {
+impl Eval for ast::Label {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::Empty)
+ }
+}
+
+impl Eval for ast::Ref {
+ type Output = Content;
+
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(Content::show(library::structure::RefNode(
+ self.get().clone(),
+ )))
+ }
+}
+
+impl Eval for ast::Expr {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -397,7 +479,7 @@ impl Eval for Expr {
Self::Content(v) => v.eval(vm).map(Value::Content),
Self::Array(v) => v.eval(vm).map(Value::Array),
Self::Dict(v) => v.eval(vm).map(Value::Dict),
- Self::Group(v) => v.eval(vm),
+ Self::Parenthesized(v) => v.eval(vm),
Self::FieldAccess(v) => v.eval(vm),
Self::FuncCall(v) => v.eval(vm),
Self::MethodCall(v) => v.eval(vm),
@@ -408,7 +490,7 @@ impl Eval for Expr {
Self::Set(_) => bail!(forbidden("set")),
Self::Show(_) => bail!(forbidden("show")),
Self::Wrap(_) => bail!(forbidden("wrap")),
- Self::If(v) => v.eval(vm),
+ Self::Conditional(v) => v.eval(vm),
Self::While(v) => v.eval(vm),
Self::For(v) => v.eval(vm),
Self::Import(v) => v.eval(vm),
@@ -420,29 +502,29 @@ impl Eval for Expr {
}
}
-impl Eval for Lit {
+impl Eval for ast::Lit {
type Output = Value;
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
Ok(match self.kind() {
- LitKind::None => Value::None,
- LitKind::Auto => Value::Auto,
- LitKind::Bool(v) => Value::Bool(v),
- LitKind::Int(v) => Value::Int(v),
- LitKind::Float(v) => Value::Float(v),
- LitKind::Numeric(v, unit) => match unit {
+ ast::LitKind::None => Value::None,
+ ast::LitKind::Auto => Value::Auto,
+ ast::LitKind::Bool(v) => Value::Bool(v),
+ ast::LitKind::Int(v) => Value::Int(v),
+ ast::LitKind::Float(v) => Value::Float(v),
+ ast::LitKind::Numeric(v, unit) => match unit {
Unit::Length(unit) => Length::with_unit(v, unit).into(),
Unit::Angle(unit) => Angle::with_unit(v, unit).into(),
Unit::Em => Em::new(v).into(),
Unit::Fr => Fraction::new(v).into(),
Unit::Percent => Ratio::new(v / 100.0).into(),
},
- LitKind::Str(v) => Value::Str(v.into()),
+ ast::LitKind::Str(v) => Value::Str(v.into()),
})
}
}
-impl Eval for Ident {
+impl Eval for ast::Ident {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -450,7 +532,7 @@ impl Eval for Ident {
}
}
-impl Eval for CodeBlock {
+impl Eval for ast::CodeBlock {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -462,14 +544,17 @@ impl Eval for CodeBlock {
}
/// Evaluate a stream of expressions.
-fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResult<Value> {
+fn eval_code(
+ vm: &mut Vm,
+ exprs: &mut impl Iterator<Item = ast::Expr>,
+) -> SourceResult<Value> {
let flow = vm.flow.take();
let mut output = Value::None;
while let Some(expr) = exprs.next() {
let span = expr.span();
let value = match expr {
- Expr::Set(set) => {
+ ast::Expr::Set(set) => {
let styles = set.eval(vm)?;
if vm.flow.is_some() {
break;
@@ -478,7 +563,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
let tail = eval_code(vm, exprs)?.display();
Value::Content(tail.styled_with_map(styles))
}
- Expr::Show(show) => {
+ ast::Expr::Show(show) => {
let recipe = show.eval(vm)?;
let entry = StyleEntry::Recipe(recipe).into();
if vm.flow.is_some() {
@@ -488,7 +573,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
let tail = eval_code(vm, exprs)?.display();
Value::Content(tail.styled_with_entry(entry))
}
- Expr::Wrap(wrap) => {
+ ast::Expr::Wrap(wrap) => {
let tail = eval_code(vm, exprs)?;
vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)?
@@ -511,7 +596,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
Ok(output)
}
-impl Eval for ContentBlock {
+impl Eval for ast::ContentBlock {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -522,7 +607,7 @@ impl Eval for ContentBlock {
}
}
-impl Eval for GroupExpr {
+impl Eval for ast::Parenthesized {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -530,7 +615,7 @@ impl Eval for GroupExpr {
}
}
-impl Eval for ArrayExpr {
+impl Eval for ast::Array {
type Output = Array;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -539,8 +624,8 @@ impl Eval for ArrayExpr {
let mut vec = Vec::with_capacity(items.size_hint().0);
for item in items {
match item {
- ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
- ArrayItem::Spread(expr) => match expr.eval(vm)? {
+ ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
+ ast::ArrayItem::Spread(expr) => match expr.eval(vm)? {
Value::None => {}
Value::Array(array) => vec.extend(array.into_iter()),
v => bail!(expr.span(), "cannot spread {} into array", v.type_name()),
@@ -552,7 +637,7 @@ impl Eval for ArrayExpr {
}
}
-impl Eval for DictExpr {
+impl Eval for ast::Dict {
type Output = Dict;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -560,13 +645,13 @@ impl Eval for DictExpr {
for item in self.items() {
match item {
- DictItem::Named(named) => {
+ ast::DictItem::Named(named) => {
map.insert(named.name().take().into(), named.expr().eval(vm)?);
}
- DictItem::Keyed(keyed) => {
+ ast::DictItem::Keyed(keyed) => {
map.insert(keyed.key().into(), keyed.expr().eval(vm)?);
}
- DictItem::Spread(expr) => match expr.eval(vm)? {
+ ast::DictItem::Spread(expr) => match expr.eval(vm)? {
Value::None => {}
Value::Dict(dict) => map.extend(dict.into_iter()),
v => bail!(
@@ -582,49 +667,49 @@ impl Eval for DictExpr {
}
}
-impl Eval for UnaryExpr {
+impl Eval for ast::Unary {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let value = self.expr().eval(vm)?;
let result = match self.op() {
- UnOp::Pos => ops::pos(value),
- UnOp::Neg => ops::neg(value),
- UnOp::Not => ops::not(value),
+ ast::UnOp::Pos => ops::pos(value),
+ ast::UnOp::Neg => ops::neg(value),
+ ast::UnOp::Not => ops::not(value),
};
Ok(result.at(self.span())?)
}
}
-impl Eval for BinaryExpr {
+impl Eval for ast::Binary {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
match self.op() {
- BinOp::Add => self.apply(vm, ops::add),
- BinOp::Sub => self.apply(vm, ops::sub),
- BinOp::Mul => self.apply(vm, ops::mul),
- BinOp::Div => self.apply(vm, ops::div),
- BinOp::And => self.apply(vm, ops::and),
- BinOp::Or => self.apply(vm, ops::or),
- BinOp::Eq => self.apply(vm, ops::eq),
- BinOp::Neq => self.apply(vm, ops::neq),
- BinOp::Lt => self.apply(vm, ops::lt),
- BinOp::Leq => self.apply(vm, ops::leq),
- BinOp::Gt => self.apply(vm, ops::gt),
- BinOp::Geq => self.apply(vm, ops::geq),
- BinOp::In => self.apply(vm, ops::in_),
- BinOp::NotIn => self.apply(vm, ops::not_in),
- BinOp::Assign => self.assign(vm, |_, b| Ok(b)),
- BinOp::AddAssign => self.assign(vm, ops::add),
- BinOp::SubAssign => self.assign(vm, ops::sub),
- BinOp::MulAssign => self.assign(vm, ops::mul),
- BinOp::DivAssign => self.assign(vm, ops::div),
+ ast::BinOp::Add => self.apply(vm, ops::add),
+ ast::BinOp::Sub => self.apply(vm, ops::sub),
+ ast::BinOp::Mul => self.apply(vm, ops::mul),
+ ast::BinOp::Div => self.apply(vm, ops::div),
+ ast::BinOp::And => self.apply(vm, ops::and),
+ ast::BinOp::Or => self.apply(vm, ops::or),
+ ast::BinOp::Eq => self.apply(vm, ops::eq),
+ ast::BinOp::Neq => self.apply(vm, ops::neq),
+ ast::BinOp::Lt => self.apply(vm, ops::lt),
+ ast::BinOp::Leq => self.apply(vm, ops::leq),
+ ast::BinOp::Gt => self.apply(vm, ops::gt),
+ ast::BinOp::Geq => self.apply(vm, ops::geq),
+ ast::BinOp::In => self.apply(vm, ops::in_),
+ ast::BinOp::NotIn => self.apply(vm, ops::not_in),
+ ast::BinOp::Assign => self.assign(vm, |_, b| Ok(b)),
+ ast::BinOp::AddAssign => self.assign(vm, ops::add),
+ ast::BinOp::SubAssign => self.assign(vm, ops::sub),
+ ast::BinOp::MulAssign => self.assign(vm, ops::mul),
+ ast::BinOp::DivAssign => self.assign(vm, ops::div),
}
}
}
-impl BinaryExpr {
+impl ast::Binary {
/// Apply a basic binary operation.
fn apply(
&self,
@@ -634,8 +719,8 @@ impl BinaryExpr {
let lhs = self.lhs().eval(vm)?;
// Short-circuit boolean operations.
- if (self.op() == BinOp::And && lhs == Value::Bool(false))
- || (self.op() == BinOp::Or && lhs == Value::Bool(true))
+ if (self.op() == ast::BinOp::And && lhs == Value::Bool(false))
+ || (self.op() == ast::BinOp::Or && lhs == Value::Bool(true))
{
return Ok(lhs);
}
@@ -658,11 +743,11 @@ impl BinaryExpr {
}
}
-impl Eval for FieldAccess {
+impl Eval for ast::FieldAccess {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let object = self.object().eval(vm)?;
+ let object = self.target().eval(vm)?;
let span = self.field().span();
let field = self.field().take();
@@ -676,7 +761,7 @@ impl Eval for FieldAccess {
.clone(),
v => bail!(
- self.object().span(),
+ self.target().span(),
"cannot access field on {}",
v.type_name()
),
@@ -684,7 +769,7 @@ impl Eval for FieldAccess {
}
}
-impl Eval for FuncCall {
+impl Eval for ast::FuncCall {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -708,7 +793,7 @@ impl Eval for FuncCall {
}
}
-impl Eval for MethodCall {
+impl Eval for ast::MethodCall {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -718,19 +803,19 @@ impl Eval for MethodCall {
Ok(if methods::is_mutating(&method) {
let args = self.args().eval(vm)?;
- let mut value = self.receiver().access(vm)?;
+ let mut value = self.target().access(vm)?;
methods::call_mut(&mut value, &method, args, span)
.trace(vm.world, point, span)?;
Value::None
} else {
- let value = self.receiver().eval(vm)?;
+ let value = self.target().eval(vm)?;
let args = self.args().eval(vm)?;
methods::call(vm, value, &method, args, span).trace(vm.world, point, span)?
})
}
}
-impl Eval for CallArgs {
+impl Eval for ast::Args {
type Output = Args;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -739,21 +824,21 @@ impl Eval for CallArgs {
for arg in self.items() {
let span = arg.span();
match arg {
- CallArg::Pos(expr) => {
+ ast::Arg::Pos(expr) => {
items.push(Arg {
span,
name: None,
value: Spanned::new(expr.eval(vm)?, expr.span()),
});
}
- CallArg::Named(named) => {
+ ast::Arg::Named(named) => {
items.push(Arg {
span,
name: Some(named.name().take().into()),
value: Spanned::new(named.expr().eval(vm)?, named.expr().span()),
});
}
- CallArg::Spread(expr) => match expr.eval(vm)? {
+ ast::Arg::Spread(expr) => match expr.eval(vm)? {
Value::None => {}
Value::Array(array) => {
items.extend(array.into_iter().map(|value| Arg {
@@ -779,12 +864,12 @@ impl Eval for CallArgs {
}
}
-impl Eval for ClosureExpr {
+impl Eval for ast::Closure {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
// The closure's name is defined by its let binding if there's one.
- let name = self.name().map(Ident::take);
+ let name = self.name().map(ast::Ident::take);
// Collect captured variables.
let captured = {
@@ -799,13 +884,13 @@ impl Eval for ClosureExpr {
// Collect parameters and an optional sink parameter.
for param in self.params() {
match param {
- ClosureParam::Pos(name) => {
+ ast::Param::Pos(name) => {
params.push((name.take(), None));
}
- ClosureParam::Named(named) => {
+ ast::Param::Named(named) => {
params.push((named.name().take(), Some(named.expr().eval(vm)?)));
}
- ClosureParam::Sink(name) => {
+ ast::Param::Sink(name) => {
if sink.is_some() {
bail!(name.span(), "only one argument sink is allowed");
}
@@ -826,7 +911,7 @@ impl Eval for ClosureExpr {
}
}
-impl Eval for LetExpr {
+impl Eval for ast::LetBinding {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -839,7 +924,7 @@ impl Eval for LetExpr {
}
}
-impl Eval for SetExpr {
+impl Eval for ast::SetRule {
type Output = StyleMap;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -850,7 +935,7 @@ impl Eval for SetExpr {
}
}
-impl Eval for ShowExpr {
+impl Eval for ast::ShowRule {
type Output = Recipe;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -887,7 +972,7 @@ impl Eval for ShowExpr {
}
}
-impl Eval for IfExpr {
+impl Eval for ast::Conditional {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -902,7 +987,7 @@ impl Eval for IfExpr {
}
}
-impl Eval for WhileExpr {
+impl Eval for ast::WhileLoop {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -934,7 +1019,7 @@ impl Eval for WhileExpr {
}
}
-impl Eval for ForExpr {
+impl Eval for ast::ForLoop {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -968,7 +1053,7 @@ impl Eval for ForExpr {
let iter = self.iter().eval(vm)?;
let pattern = self.pattern();
- let key = pattern.key().map(Ident::take);
+ let key = pattern.key().map(ast::Ident::take);
let value = pattern.value().take();
match (key, value, iter) {
@@ -1013,7 +1098,7 @@ impl Eval for ForExpr {
}
}
-impl Eval for ImportExpr {
+impl Eval for ast::ModuleImport {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -1022,12 +1107,12 @@ impl Eval for ImportExpr {
let module = import(vm, &path, span)?;
match self.imports() {
- Imports::Wildcard => {
+ ast::Imports::Wildcard => {
for (var, value) in module.scope.iter() {
vm.scopes.top.define(var, value.clone());
}
}
- Imports::Items(idents) => {
+ ast::Imports::Items(idents) => {
for ident in idents {
if let Some(value) = module.scope.get(&ident) {
vm.scopes.top.define(ident.take(), value.clone());
@@ -1042,7 +1127,7 @@ impl Eval for ImportExpr {
}
}
-impl Eval for IncludeExpr {
+impl Eval for ast::ModuleInclude {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -1071,7 +1156,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
Ok(module)
}
-impl Eval for BreakExpr {
+impl Eval for ast::BreakStmt {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -1082,7 +1167,7 @@ impl Eval for BreakExpr {
}
}
-impl Eval for ContinueExpr {
+impl Eval for ast::ContinueStmt {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -1093,7 +1178,7 @@ impl Eval for ContinueExpr {
}
}
-impl Eval for ReturnExpr {
+impl Eval for ast::ReturnStmt {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
@@ -1111,29 +1196,29 @@ pub trait Access {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>;
}
-impl Access for Expr {
+impl Access for ast::Expr {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
match self {
- Expr::Ident(v) => v.access(vm),
- Expr::FieldAccess(v) => v.access(vm),
- Expr::FuncCall(v) => v.access(vm),
+ Self::Ident(v) => v.access(vm),
+ Self::FieldAccess(v) => v.access(vm),
+ Self::FuncCall(v) => v.access(vm),
_ => bail!(self.span(), "cannot mutate a temporary value"),
}
}
}
-impl Access for Ident {
+impl Access for ast::Ident {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
vm.scopes.get_mut(self).at(self.span())
}
}
-impl Access for FieldAccess {
+impl Access for ast::FieldAccess {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
- Ok(match self.object().access(vm)? {
+ Ok(match self.target().access(vm)? {
Value::Dict(dict) => dict.get_mut(self.field().take().into()),
v => bail!(
- self.object().span(),
+ self.target().span(),
"expected dictionary, found {}",
v.type_name(),
),
@@ -1141,7 +1226,7 @@ impl Access for FieldAccess {
}
}
-impl Access for FuncCall {
+impl Access for ast::FuncCall {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
let args = self.args().eval(vm)?;
Ok(match self.callee().access(vm)? {
diff --git a/src/parse/incremental.rs b/src/parse/incremental.rs
index e0be9b6d..4651a784 100644
--- a/src/parse/incremental.rs
+++ b/src/parse/incremental.rs
@@ -389,16 +389,12 @@ fn is_bounded(kind: &NodeKind) -> bool {
match kind {
NodeKind::CodeBlock
| NodeKind::ContentBlock
- | NodeKind::Backslash
- | NodeKind::Tilde
- | NodeKind::HyphQuest
- | NodeKind::Hyph2
- | NodeKind::Hyph3
- | NodeKind::Dot3
- | NodeKind::Quote { .. }
+ | NodeKind::Linebreak
+ | NodeKind::SmartQuote { .. }
| NodeKind::BlockComment
| NodeKind::Space { .. }
- | NodeKind::Escape(_) => true,
+ | NodeKind::Escape(_)
+ | NodeKind::Shorthand(_) => true,
_ => false,
}
}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 4f42442f..ac8ec6eb 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -11,9 +11,8 @@ pub use tokens::*;
use std::collections::HashSet;
-use crate::diag::ErrorPos;
use crate::syntax::ast::{Assoc, BinOp, UnOp};
-use crate::syntax::{NodeKind, SyntaxNode};
+use crate::syntax::{ErrorPos, NodeKind, SyntaxNode};
use crate::util::EcoString;
/// Parse a source file.
@@ -240,14 +239,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
// Text and markup.
NodeKind::Text(_)
- | NodeKind::Backslash
- | NodeKind::Tilde
- | NodeKind::HyphQuest
- | NodeKind::Hyph2
- | NodeKind::Hyph3
- | NodeKind::Dot3
- | NodeKind::Quote { .. }
+ | NodeKind::Linebreak
+ | NodeKind::SmartQuote { .. }
| NodeKind::Escape(_)
+ | NodeKind::Shorthand(_)
| NodeKind::Link(_)
| NodeKind::Raw(_)
| NodeKind::Label(_)
@@ -475,15 +470,15 @@ fn math_primary(p: &mut Parser) {
match token {
// Spaces, atoms and expressions.
NodeKind::Space { .. }
- | NodeKind::Backslash
+ | NodeKind::Linebreak
| NodeKind::Escape(_)
| NodeKind::Atom(_)
| NodeKind::Ident(_) => p.eat(),
// Groups.
- NodeKind::LeftParen => group(p, Group::Paren),
- NodeKind::LeftBracket => group(p, Group::Bracket),
- NodeKind::LeftBrace => group(p, Group::Brace),
+ NodeKind::LeftParen => group(p, Group::Paren, '(', ')'),
+ NodeKind::LeftBracket => group(p, Group::Bracket, '[', ']'),
+ NodeKind::LeftBrace => group(p, Group::Brace, '{', '}'),
// Alignment indactor.
NodeKind::Amp => align(p),
@@ -493,13 +488,17 @@ fn math_primary(p: &mut Parser) {
}
/// Parse grouped math.
-fn group(p: &mut Parser, group: Group) {
+fn group(p: &mut Parser, group: Group, l: char, r: char) {
p.perform(NodeKind::Math, |p| {
+ let marker = p.marker();
p.start_group(group);
+ marker.convert(p, NodeKind::Atom(l.into()));
while !p.eof() {
math_node(p);
}
+ let marker = p.marker();
p.end_group();
+ marker.convert(p, NodeKind::Atom(r.into()));
})
}
@@ -532,7 +531,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
p.eat();
let prec = op.precedence();
expr_prec(p, atomic, prec)?;
- marker.end(p, NodeKind::UnaryExpr);
+ marker.end(p, NodeKind::Unary);
}
_ => primary(p, atomic)?,
};
@@ -585,7 +584,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
Assoc::Right => {}
}
- marker.perform(p, NodeKind::BinaryExpr, |p| expr_prec(p, atomic, prec))?;
+ marker.perform(p, NodeKind::Binary, |p| expr_prec(p, atomic, prec))?;
}
Ok(())
@@ -605,9 +604,9 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
// Arrow means this is a closure's lone parameter.
if !atomic && p.at(NodeKind::Arrow) {
- marker.end(p, NodeKind::ClosureParams);
+ marker.end(p, NodeKind::Params);
p.assert(NodeKind::Arrow);
- marker.perform(p, NodeKind::ClosureExpr, expr)
+ marker.perform(p, NodeKind::Closure, expr)
} else {
Ok(())
}
@@ -703,12 +702,12 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult {
if !atomic && p.at(NodeKind::Arrow) {
params(p, marker);
p.assert(NodeKind::Arrow);
- return marker.perform(p, NodeKind::ClosureExpr, expr);
+ return marker.perform(p, NodeKind::Closure, expr);
}
// Transform into the identified collection.
match kind {
- CollectionKind::Group => marker.end(p, NodeKind::GroupExpr),
+ CollectionKind::Group => marker.end(p, NodeKind::Parenthesized),
CollectionKind::Positional => array(p, marker),
CollectionKind::Named => dict(p, marker),
}
@@ -833,7 +832,7 @@ fn array(p: &mut Parser, marker: Marker) {
NodeKind::Named | NodeKind::Keyed => Err("expected expression"),
_ => Ok(()),
});
- marker.end(p, NodeKind::ArrayExpr);
+ marker.end(p, NodeKind::Array);
}
/// Convert a collection into a dictionary, producing errors for anything other
@@ -855,7 +854,7 @@ fn dict(p: &mut Parser, marker: Marker) {
NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()),
_ => Err("expected named or keyed pair"),
});
- marker.end(p, NodeKind::DictExpr);
+ marker.end(p, NodeKind::Dict);
}
/// Convert a collection into a list of parameters, producing errors for
@@ -874,7 +873,7 @@ fn params(p: &mut Parser, marker: Marker) {
}
_ => Err("expected identifier, named pair or argument sink"),
});
- marker.end(p, NodeKind::ClosureParams);
+ marker.end(p, NodeKind::Params);
}
/// Parse a code block: `{...}`.
@@ -920,7 +919,7 @@ fn args(p: &mut Parser) -> ParseResult {
}
}
- p.perform(NodeKind::CallArgs, |p| {
+ p.perform(NodeKind::Args, |p| {
if p.at(NodeKind::LeftParen) {
let marker = p.marker();
p.start_group(Group::Paren);
@@ -953,7 +952,7 @@ fn args(p: &mut Parser) -> ParseResult {
/// Parse a let expression.
fn let_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::LetExpr, |p| {
+ p.perform(NodeKind::LetBinding, |p| {
p.assert(NodeKind::Let);
let marker = p.marker();
@@ -978,7 +977,7 @@ fn let_expr(p: &mut Parser) -> ParseResult {
// Rewrite into a closure expression if it's a function definition.
if has_params {
- marker.end(p, NodeKind::ClosureExpr);
+ marker.end(p, NodeKind::Closure);
}
Ok(())
@@ -987,7 +986,7 @@ fn let_expr(p: &mut Parser) -> ParseResult {
/// Parse a set expression.
fn set_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::SetExpr, |p| {
+ p.perform(NodeKind::SetRule, |p| {
p.assert(NodeKind::Set);
ident(p)?;
args(p)
@@ -996,7 +995,7 @@ fn set_expr(p: &mut Parser) -> ParseResult {
/// Parse a show expression.
fn show_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::ShowExpr, |p| {
+ p.perform(NodeKind::ShowRule, |p| {
p.assert(NodeKind::Show);
let marker = p.marker();
expr(p)?;
@@ -1014,7 +1013,7 @@ fn show_expr(p: &mut Parser) -> ParseResult {
/// Parse a wrap expression.
fn wrap_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::WrapExpr, |p| {
+ p.perform(NodeKind::WrapRule, |p| {
p.assert(NodeKind::Wrap);
ident(p)?;
p.expect(NodeKind::In)?;
@@ -1024,7 +1023,7 @@ fn wrap_expr(p: &mut Parser) -> ParseResult {
/// Parse an if-else expresion.
fn if_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::IfExpr, |p| {
+ p.perform(NodeKind::Conditional, |p| {
p.assert(NodeKind::If);
expr(p)?;
@@ -1044,7 +1043,7 @@ fn if_expr(p: &mut Parser) -> ParseResult {
/// Parse a while expresion.
fn while_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::WhileExpr, |p| {
+ p.perform(NodeKind::WhileLoop, |p| {
p.assert(NodeKind::While);
expr(p)?;
body(p)
@@ -1053,7 +1052,7 @@ fn while_expr(p: &mut Parser) -> ParseResult {
/// Parse a for-in expression.
fn for_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::ForExpr, |p| {
+ p.perform(NodeKind::ForLoop, |p| {
p.assert(NodeKind::For);
for_pattern(p)?;
p.expect(NodeKind::In)?;
@@ -1075,7 +1074,7 @@ fn for_pattern(p: &mut Parser) -> ParseResult {
/// Parse an import expression.
fn import_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::ImportExpr, |p| {
+ p.perform(NodeKind::ModuleImport, |p| {
p.assert(NodeKind::Import);
if !p.eat_if(NodeKind::Star) {
@@ -1103,7 +1102,7 @@ fn import_expr(p: &mut Parser) -> ParseResult {
/// Parse an include expression.
fn include_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::IncludeExpr, |p| {
+ p.perform(NodeKind::ModuleInclude, |p| {
p.assert(NodeKind::Include);
expr(p)
})
@@ -1111,7 +1110,7 @@ fn include_expr(p: &mut Parser) -> ParseResult {
/// Parse a break expression.
fn break_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::BreakExpr, |p| {
+ p.perform(NodeKind::BreakStmt, |p| {
p.assert(NodeKind::Break);
Ok(())
})
@@ -1119,7 +1118,7 @@ fn break_expr(p: &mut Parser) -> ParseResult {
/// Parse a continue expression.
fn continue_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::ContinueExpr, |p| {
+ p.perform(NodeKind::ContinueStmt, |p| {
p.assert(NodeKind::Continue);
Ok(())
})
@@ -1127,7 +1126,7 @@ fn continue_expr(p: &mut Parser) -> ParseResult {
/// Parse a return expression.
fn return_expr(p: &mut Parser) -> ParseResult {
- p.perform(NodeKind::ReturnExpr, |p| {
+ p.perform(NodeKind::ReturnStmt, |p| {
p.assert(NodeKind::Return);
if !p.at(NodeKind::Comma) && !p.eof() {
expr(p)?;
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index fe04f29e..3dbb7d50 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -3,8 +3,7 @@ use std::mem;
use std::ops::Range;
use super::{TokenMode, Tokens};
-use crate::diag::ErrorPos;
-use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode};
+use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode};
use crate::util::EcoString;
/// A convenient token-based parser.
diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs
index d68282c0..9fde0cf4 100644
--- a/src/parse/resolve.rs
+++ b/src/parse/resolve.rs
@@ -1,7 +1,7 @@
use unscanny::Scanner;
use super::{is_ident, is_newline};
-use crate::syntax::ast::RawNode;
+use crate::syntax::RawKind;
use crate::util::EcoString;
/// Resolve all escape sequences in a string.
@@ -46,17 +46,17 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
}
/// Resolve the language tag and trim the raw text.
-pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawNode {
+pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawKind {
if backticks > 1 {
let (tag, inner) = split_at_lang_tag(text);
let (text, block) = trim_and_split_raw(column, inner);
- RawNode {
+ RawKind {
lang: is_ident(tag).then(|| tag.into()),
text: text.into(),
block,
}
} else {
- RawNode {
+ RawKind {
lang: None,
text: split_lines(text).join("\n").into(),
block: false,
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index 7cba1823..73c64d1e 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -4,10 +4,8 @@ use unicode_xid::UnicodeXID;
use unscanny::Scanner;
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
-use crate::diag::ErrorPos;
use crate::geom::{AngleUnit, LengthUnit};
-use crate::syntax::ast::{RawNode, Unit};
-use crate::syntax::NodeKind;
+use crate::syntax::{ErrorPos, NodeKind, RawKind, Unit};
use crate::util::EcoString;
/// An iterator over the tokens of a string of source code.
@@ -199,14 +197,25 @@ impl<'s> Tokens<'s> {
'[' => NodeKind::LeftBracket,
']' => NodeKind::RightBracket,
+ // Multi-char things.
+ '#' => self.hash(start),
+ '.' if self.s.eat_if("..") => NodeKind::Shorthand('\u{2026}'),
+ '-' => self.hyph(),
+ 'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => {
+ self.link(start)
+ }
+ '`' => self.raw(),
+ c if c.is_ascii_digit() => self.numbering(start),
+ '<' => self.label(),
+ '@' => self.reference(start),
+
// Escape sequences.
'\\' => self.backslash(),
// Single-char things.
- '~' => NodeKind::Tilde,
- '.' if self.s.eat_if("..") => NodeKind::Dot3,
- '\'' => NodeKind::Quote { double: false },
- '"' => NodeKind::Quote { double: true },
+ '~' => NodeKind::Shorthand('\u{00A0}'),
+ '\'' => NodeKind::SmartQuote { double: false },
+ '"' => NodeKind::SmartQuote { double: true },
'*' if !self.in_word() => NodeKind::Star,
'_' if !self.in_word() => NodeKind::Underscore,
'$' => NodeKind::Dollar,
@@ -215,17 +224,6 @@ impl<'s> Tokens<'s> {
'/' => NodeKind::Slash,
':' => NodeKind::Colon,
- // Multi-char things.
- '#' => self.hash(start),
- '-' => self.hyph(),
- 'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => {
- self.link(start)
- }
- '`' => self.raw(),
- c if c.is_ascii_digit() => self.numbering(start),
- '<' => self.label(),
- '@' => self.reference(start),
-
// Plain text.
_ => self.text(start),
}
@@ -291,8 +289,8 @@ impl<'s> Tokens<'s> {
}
// Linebreaks.
- Some(c) if c.is_whitespace() => NodeKind::Backslash,
- None => NodeKind::Backslash,
+ Some(c) if c.is_whitespace() => NodeKind::Linebreak,
+ None => NodeKind::Linebreak,
// Escapes.
Some(c) => {
@@ -317,24 +315,17 @@ impl<'s> Tokens<'s> {
fn hyph(&mut self) -> NodeKind {
if self.s.eat_if('-') {
if self.s.eat_if('-') {
- NodeKind::Hyph3
+ NodeKind::Shorthand('\u{2014}')
} else {
- NodeKind::Hyph2
+ NodeKind::Shorthand('\u{2013}')
}
} else if self.s.eat_if('?') {
- NodeKind::HyphQuest
+ NodeKind::Shorthand('\u{00AD}')
} else {
NodeKind::Minus
}
}
- fn in_word(&self) -> bool {
- let alphanumeric = |c: Option<char>| c.map_or(false, |c| c.is_alphanumeric());
- let prev = self.s.scout(-2);
- let next = self.s.peek();
- alphanumeric(prev) && alphanumeric(next)
- }
-
fn link(&mut self, start: usize) -> NodeKind {
#[rustfmt::skip]
self.s.eat_while(|c: char| matches!(c,
@@ -360,7 +351,7 @@ impl<'s> Tokens<'s> {
// Special case for empty inline block.
if backticks == 2 {
- return NodeKind::Raw(Arc::new(RawNode {
+ return NodeKind::Raw(Arc::new(RawKind {
text: EcoString::new(),
lang: None,
block: false,
@@ -567,22 +558,23 @@ impl<'s> Tokens<'s> {
}
}
- if let Ok(f) = number.parse::<f64>() {
- match suffix {
- "" => NodeKind::Float(f),
- "pt" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Pt)),
- "mm" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Mm)),
- "cm" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Cm)),
- "in" => NodeKind::Numeric(f, Unit::Length(LengthUnit::In)),
- "deg" => NodeKind::Numeric(f, Unit::Angle(AngleUnit::Deg)),
- "rad" => NodeKind::Numeric(f, Unit::Angle(AngleUnit::Rad)),
- "em" => NodeKind::Numeric(f, Unit::Em),
- "fr" => NodeKind::Numeric(f, Unit::Fr),
- "%" => NodeKind::Numeric(f, Unit::Percent),
- _ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()),
- }
- } else {
- NodeKind::Error(ErrorPos::Full, "invalid number".into())
+ let v = match number.parse::<f64>() {
+ Ok(v) => v,
+ Err(_) => return NodeKind::Error(ErrorPos::Full, "invalid number".into()),
+ };
+
+ match suffix {
+ "" => NodeKind::Float(v),
+ "pt" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Pt)),
+ "mm" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Mm)),
+ "cm" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Cm)),
+ "in" => NodeKind::Numeric(v, Unit::Length(LengthUnit::In)),
+ "deg" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Deg)),
+ "rad" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Rad)),
+ "em" => NodeKind::Numeric(v, Unit::Em),
+ "fr" => NodeKind::Numeric(v, Unit::Fr),
+ "%" => NodeKind::Numeric(v, Unit::Percent),
+ _ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()),
}
}
@@ -605,6 +597,13 @@ impl<'s> Tokens<'s> {
NodeKind::Error(ErrorPos::End, "expected quote".into())
}
}
+
+ fn in_word(&self) -> bool {
+ let alphanumeric = |c: Option<char>| c.map_or(false, |c| c.is_alphanumeric());
+ let prev = self.s.scout(-2);
+ let next = self.s.peek();
+ alphanumeric(prev) && alphanumeric(next)
+ }
}
fn keyword(ident: &str) -> Option<NodeKind> {
@@ -724,7 +723,7 @@ mod tests {
}
fn Raw(text: &str, lang: Option<&str>, block: bool) -> NodeKind {
- NodeKind::Raw(Arc::new(RawNode {
+ NodeKind::Raw(Arc::new(RawKind {
text: text.into(),
lang: lang.map(Into::into),
block,
@@ -762,6 +761,43 @@ mod tests {
/// - '/': symbols
const BLOCKS: &str = " a1/";
+ // Suffixes described by four-tuples of:
+ //
+ // - block the suffix is part of
+ // - mode in which the suffix is applicable
+ // - the suffix string
+ // - the resulting suffix NodeKind
+ fn suffixes()
+ -> impl Iterator<Item = (char, Option<TokenMode>, &'static str, NodeKind)> {
+ [
+ // Whitespace suffixes.
+ (' ', None, " ", Space(0)),
+ (' ', None, "\n", Space(1)),
+ (' ', None, "\r", Space(1)),
+ (' ', None, "\r\n", Space(1)),
+ // Letter suffixes.
+ ('a', Some(Markup), "hello", Text("hello")),
+ ('a', Some(Markup), "💚", Text("💚")),
+ ('a', Some(Code), "val", Ident("val")),
+ ('a', Some(Code), "α", Ident("α")),
+ ('a', Some(Code), "_", Ident("_")),
+ // Number suffixes.
+ ('1', Some(Code), "2", Int(2)),
+ ('1', Some(Code), ".2", Float(0.2)),
+ // Symbol suffixes.
+ ('/', None, "[", LeftBracket),
+ ('/', None, "//", LineComment),
+ ('/', None, "/**/", BlockComment),
+ ('/', Some(Markup), "*", Star),
+ ('/', Some(Markup), r"\\", Escape('\\')),
+ ('/', Some(Markup), "#let", Let),
+ ('/', Some(Code), "(", LeftParen),
+ ('/', Some(Code), ":", Colon),
+ ('/', Some(Code), "+=", PlusEq),
+ ]
+ .into_iter()
+ }
+
macro_rules! t {
(Both $($tts:tt)*) => {
t!(Markup $($tts)*);
@@ -771,41 +807,8 @@ mod tests {
// Test without suffix.
t!(@$mode: $text => $($token),*);
- // Suffixes described by four-tuples of:
- //
- // - block the suffix is part of
- // - mode in which the suffix is applicable
- // - the suffix string
- // - the resulting suffix NodeKind
- let suffixes: &[(char, Option<TokenMode>, &str, NodeKind)] = &[
- // Whitespace suffixes.
- (' ', None, " ", Space(0)),
- (' ', None, "\n", Space(1)),
- (' ', None, "\r", Space(1)),
- (' ', None, "\r\n", Space(1)),
- // Letter suffixes.
- ('a', Some(Markup), "hello", Text("hello")),
- ('a', Some(Markup), "💚", Text("💚")),
- ('a', Some(Code), "val", Ident("val")),
- ('a', Some(Code), "α", Ident("α")),
- ('a', Some(Code), "_", Ident("_")),
- // Number suffixes.
- ('1', Some(Code), "2", Int(2)),
- ('1', Some(Code), ".2", Float(0.2)),
- // Symbol suffixes.
- ('/', None, "[", LeftBracket),
- ('/', None, "//", LineComment),
- ('/', None, "/**/", BlockComment),
- ('/', Some(Markup), "*", Star),
- ('/', Some(Markup), r"\\", Escape('\\')),
- ('/', Some(Markup), "#let", Let),
- ('/', Some(Code), "(", LeftParen),
- ('/', Some(Code), ":", Colon),
- ('/', Some(Code), "+=", PlusEq),
- ];
-
// Test with each applicable suffix.
- for &(block, mode, suffix, ref token) in suffixes {
+ for (block, mode, suffix, ref token) in suffixes() {
let text = $text;
#[allow(unused_variables)]
let blocks = BLOCKS;
@@ -872,14 +875,14 @@ mod tests {
t!(Markup[" /"]: "reha-world" => Text("reha-world"));
// Test code symbols in text.
- t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, Quote { double: true }, Text("b"));
+ t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, SmartQuote { double: true }, Text("b"));
t!(Markup[" /"]: ";,|/+" => Text(";,|/+"));
t!(Markup[" /"]: "=-a" => Eq, Minus, Text("a"));
t!(Markup[" "]: "#123" => Text("#123"));
// Test text ends.
t!(Markup[""]: "hello " => Text("hello"), Space(0));
- t!(Markup[""]: "hello~" => Text("hello"), Tilde);
+ t!(Markup[""]: "hello~" => Text("hello"), Shorthand('\u{00A0}'));
}
#[test]
@@ -924,10 +927,10 @@ mod tests {
t!(Markup: "_" => Underscore);
t!(Markup[""]: "===" => Eq, Eq, Eq);
t!(Markup["a1/"]: "= " => Eq, Space(0));
- t!(Markup[" "]: r"\" => Backslash);
- t!(Markup: "~" => Tilde);
- t!(Markup["a1/"]: "-?" => HyphQuest);
- t!(Markup["a "]: r"a--" => Text("a"), Hyph2);
+ t!(Markup[" "]: r"\" => Linebreak);
+ t!(Markup: "~" => Shorthand('\u{00A0}'));
+ t!(Markup["a1/"]: "-?" => Shorthand('\u{00AD}'));
+ t!(Markup["a "]: r"a--" => Text("a"), Shorthand('\u{2013}'));
t!(Markup["a1/"]: "- " => Minus, Space(0));
t!(Markup[" "]: "+" => Plus);
t!(Markup[" "]: "1." => EnumNumbering(1));
diff --git a/src/source.rs b/src/source.rs
index 8e22c01d..69e72d6b 100644
--- a/src/source.rs
+++ b/src/source.rs
@@ -10,7 +10,7 @@ use unscanny::Scanner;
use crate::diag::SourceResult;
use crate::parse::{is_newline, parse, reparse};
-use crate::syntax::ast::MarkupNode;
+use crate::syntax::ast::Markup;
use crate::syntax::{Span, SyntaxNode};
use crate::util::{PathExt, StrExt};
@@ -66,7 +66,7 @@ impl Source {
}
/// The root node of the file's typed abstract syntax tree.
- pub fn ast(&self) -> SourceResult<MarkupNode> {
+ pub fn ast(&self) -> SourceResult<Markup> {
let errors = self.root.errors();
if errors.is_empty() {
Ok(self.root.cast().expect("root node must be markup"))
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index aa590da2..ecfa9a5b 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -5,8 +5,7 @@
use std::num::NonZeroUsize;
use std::ops::Deref;
-use super::{NodeData, NodeKind, Span, SyntaxNode};
-use crate::geom::{AngleUnit, LengthUnit};
+use super::{NodeData, NodeKind, RawKind, Span, SyntaxNode, Unit};
use crate::util::EcoString;
/// A typed AST node.
@@ -25,10 +24,7 @@ pub trait TypedNode: Sized {
macro_rules! node {
($(#[$attr:meta])* $name:ident) => {
- node!{$(#[$attr])* $name: $name}
- };
- ($(#[$attr:meta])* $name:ident: $variant:ident) => {
- node!{$(#[$attr])* $name: NodeKind::$variant}
+ node!{ $(#[$attr])* $name: NodeKind::$name { .. } }
};
($(#[$attr:meta])* $name:ident: $variants:pat) => {
#[derive(Debug, Clone, PartialEq, Hash)]
@@ -54,254 +50,296 @@ macro_rules! node {
node! {
/// The syntactical root capable of representing a full parsed document.
- MarkupNode: NodeKind::Markup { .. }
+ Markup
}
-impl MarkupNode {
+impl Markup {
/// The children.
- pub fn items(&self) -> impl Iterator<Item = MarkupItem> + '_ {
+ pub fn children(&self) -> impl Iterator<Item = MarkupNode> + '_ {
self.0.children().filter_map(SyntaxNode::cast)
}
}
/// A single piece of markup.
#[derive(Debug, Clone, PartialEq)]
-pub enum MarkupItem {
+pub enum MarkupNode {
/// Whitespace containing less than two newlines.
- Space,
+ Space(Space),
/// A forced line break.
- Linebreak,
- /// A paragraph break: Two or more newlines.
- Parbreak,
+ Linebreak(Linebreak),
/// Plain text.
- Text(EcoString),
+ Text(Text),
+ /// An escape sequence: `\#`, `\u{1F5FA}`.
+ Escape(Escape),
+ /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
+ /// space or `-?` for a soft hyphen.
+ Shorthand(Shorthand),
/// A smart quote: `'` or `"`.
- Quote { double: bool },
- /// Strong content: `*Strong*`.
- Strong(StrongNode),
- /// Emphasized content: `_Emphasized_`.
- Emph(EmphNode),
- /// A hyperlink: `https://typst.org`.
- Link(EcoString),
+ SmartQuote(SmartQuote),
+ /// Strong markup: `*Strong*`.
+ Strong(Strong),
+ /// Emphasized markup: `_Emphasized_`.
+ Emph(Emph),
/// A raw block with optional syntax highlighting: `` `...` ``.
- Raw(RawNode),
- /// A math formula: `$x$`, `$ x^2 $`.
- Math(MathNode),
+ Raw(Raw),
+ /// A hyperlink: `https://typst.org`.
+ Link(Link),
+ /// A label: `<label>`.
+ Label(Label),
+ /// A reference: `@target`.
+ Ref(Ref),
/// A section heading: `= Introduction`.
- Heading(HeadingNode),
+ Heading(Heading),
/// An item in an unordered list: `- ...`.
List(ListItem),
/// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
Enum(EnumItem),
/// An item in a description list: `/ Term: Details`.
Desc(DescItem),
- /// A label: `<label>`.
- Label(EcoString),
- /// A reference: `@label`.
- Ref(EcoString),
+ /// A math formula: `$x$`, `$ x^2 $`.
+ Math(Math),
/// An expression.
Expr(Expr),
}
-impl TypedNode for MarkupItem {
+impl TypedNode for MarkupNode {
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() {
- NodeKind::Space { newlines: (2 ..) } => Some(Self::Parbreak),
- NodeKind::Space { .. } => Some(Self::Space),
- NodeKind::Backslash => Some(Self::Linebreak),
- NodeKind::Text(s) => Some(Self::Text(s.clone())),
- NodeKind::Escape(c) => Some(Self::Text((*c).into())),
- NodeKind::Tilde => Some(Self::Text('\u{00A0}'.into())),
- NodeKind::HyphQuest => Some(Self::Text('\u{00AD}'.into())),
- NodeKind::Hyph2 => Some(Self::Text('\u{2013}'.into())),
- NodeKind::Hyph3 => Some(Self::Text('\u{2014}'.into())),
- NodeKind::Dot3 => Some(Self::Text('\u{2026}'.into())),
- NodeKind::Quote { double } => Some(Self::Quote { double: *double }),
+ NodeKind::Space { .. } => node.cast().map(Self::Space),
+ NodeKind::Linebreak => node.cast().map(Self::Linebreak),
+ NodeKind::Text(_) => node.cast().map(Self::Text),
+ NodeKind::Escape(_) => node.cast().map(Self::Escape),
+ NodeKind::Shorthand(_) => node.cast().map(Self::Shorthand),
+ NodeKind::SmartQuote { .. } => node.cast().map(Self::SmartQuote),
NodeKind::Strong => node.cast().map(Self::Strong),
NodeKind::Emph => node.cast().map(Self::Emph),
- NodeKind::Link(url) => Some(Self::Link(url.clone())),
- NodeKind::Raw(raw) => Some(Self::Raw(raw.as_ref().clone())),
- NodeKind::Math => node.cast().map(Self::Math),
+ NodeKind::Raw(_) => node.cast().map(Self::Raw),
+ NodeKind::Link(_) => node.cast().map(Self::Link),
+ NodeKind::Label(_) => node.cast().map(Self::Label),
+ NodeKind::Ref(_) => node.cast().map(Self::Ref),
NodeKind::Heading => node.cast().map(Self::Heading),
NodeKind::ListItem => node.cast().map(Self::List),
NodeKind::EnumItem => node.cast().map(Self::Enum),
NodeKind::DescItem => node.cast().map(Self::Desc),
- NodeKind::Label(v) => Some(Self::Label(v.clone())),
- NodeKind::Ref(v) => Some(Self::Ref(v.clone())),
+ NodeKind::Math => node.cast().map(Self::Math),
_ => node.cast().map(Self::Expr),
}
}
fn as_untyped(&self) -> &SyntaxNode {
- unimplemented!("MarkupNode::as_untyped")
+ match self {
+ Self::Space(v) => v.as_untyped(),
+ Self::Linebreak(v) => v.as_untyped(),
+ Self::Text(v) => v.as_untyped(),
+ Self::Escape(v) => v.as_untyped(),
+ Self::Shorthand(v) => v.as_untyped(),
+ Self::SmartQuote(v) => v.as_untyped(),
+ Self::Strong(v) => v.as_untyped(),
+ Self::Emph(v) => v.as_untyped(),
+ Self::Raw(v) => v.as_untyped(),
+ Self::Link(v) => v.as_untyped(),
+ Self::Label(v) => v.as_untyped(),
+ Self::Ref(v) => v.as_untyped(),
+ Self::Heading(v) => v.as_untyped(),
+ Self::List(v) => v.as_untyped(),
+ Self::Enum(v) => v.as_untyped(),
+ Self::Desc(v) => v.as_untyped(),
+ Self::Math(v) => v.as_untyped(),
+ Self::Expr(v) => v.as_untyped(),
+ }
}
}
node! {
- /// Strong content: `*Strong*`.
- StrongNode: Strong
+ /// Whitespace.
+ Space
}
-impl StrongNode {
- /// The contents of the strong node.
- pub fn body(&self) -> MarkupNode {
- self.0.cast_first_child().expect("strong node is missing markup body")
+impl Space {
+ /// Get the number of newlines.
+ pub fn newlines(&self) -> usize {
+ match self.0.kind() {
+ &NodeKind::Space { newlines } => newlines,
+ _ => panic!("space is of wrong kind"),
+ }
}
}
node! {
- /// Emphasized content: `_Emphasized_`.
- EmphNode: Emph
+ /// A forced line break.
+ Linebreak
}
-impl EmphNode {
- /// The contents of the emphasis node.
- pub fn body(&self) -> MarkupNode {
- self.0
- .cast_first_child()
- .expect("emphasis node is missing markup body")
- }
+node! {
+ /// Plain text.
+ Text
}
-/// A raw block with optional syntax highlighting: `` `...` ``.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub struct RawNode {
- /// An optional identifier specifying the language to syntax-highlight in.
- pub lang: Option<EcoString>,
- /// The raw text, determined as the raw string between the backticks trimmed
- /// according to the above rules.
- pub text: EcoString,
- /// Whether the element is block-level, that is, it has 3+ backticks
- /// and contains at least one newline.
- pub block: bool,
+impl Text {
+ /// Get the text.
+ pub fn get(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Text(v) => v,
+ _ => panic!("text is of wrong kind"),
+ }
+ }
}
node! {
- /// A math formula: `$x$`, `$ x^2 $`.
- MathNode: NodeKind::Math { .. }
+ /// An escape sequence: `\#`, `\u{1F5FA}`.
+ Escape
}
-impl MathNode {
- /// The children.
- pub fn items(&self) -> impl Iterator<Item = MathItem> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
+impl Escape {
+ /// Get the escaped character.
+ pub fn get(&self) -> char {
+ match self.0.kind() {
+ &NodeKind::Escape(v) => v,
+ _ => panic!("escape is of wrong kind"),
+ }
}
}
-/// A single piece of a math formula.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum MathItem {
- /// Whitespace.
- Space,
- /// A forced line break.
- Linebreak,
- /// An atom: `x`, `+`, `12`.
- Atom(EcoString),
- /// A base with an optional sub- and superscript: `a_1^2`.
- Script(ScriptNode),
- /// A fraction: `x/2`.
- Frac(FracNode),
- /// An alignment indicator: `&`, `&&`.
- Align(AlignNode),
- /// Grouped mathematical material.
- Group(MathNode),
- /// An expression.
- Expr(Expr),
+node! {
+ /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
+ /// space or `-?` for a soft hyphen.
+ Shorthand
}
-impl TypedNode for MathItem {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- NodeKind::Space { .. } => Some(Self::Space),
- NodeKind::LeftBrace => Some(Self::Atom('{'.into())),
- NodeKind::RightBrace => Some(Self::Atom('}'.into())),
- NodeKind::LeftBracket => Some(Self::Atom('['.into())),
- NodeKind::RightBracket => Some(Self::Atom(']'.into())),
- NodeKind::LeftParen => Some(Self::Atom('('.into())),
- NodeKind::RightParen => Some(Self::Atom(')'.into())),
- NodeKind::Backslash => Some(Self::Linebreak),
- NodeKind::Escape(c) => Some(Self::Atom((*c).into())),
- NodeKind::Atom(atom) => Some(Self::Atom(atom.clone())),
- NodeKind::Script => node.cast().map(Self::Script),
- NodeKind::Frac => node.cast().map(Self::Frac),
- NodeKind::Align => node.cast().map(Self::Align),
- NodeKind::Math => node.cast().map(Self::Group),
- _ => node.cast().map(Self::Expr),
+impl Shorthand {
+ /// Get the shorthanded character.
+ pub fn get(&self) -> char {
+ match self.0.kind() {
+ &NodeKind::Shorthand(v) => v,
+ _ => panic!("shorthand is of wrong kind"),
}
}
+}
- fn as_untyped(&self) -> &SyntaxNode {
- unimplemented!("MathNode::as_untyped")
+node! {
+ /// A smart quote: `'` or `"`.
+ SmartQuote
+}
+
+impl SmartQuote {
+ /// Whether this is a double quote.
+ pub fn double(&self) -> bool {
+ match self.0.kind() {
+ &NodeKind::SmartQuote { double } => double,
+ _ => panic!("smart quote is of wrong kind"),
+ }
}
}
node! {
- /// A base with an optional sub- and superscript in a formula: `a_1^2`.
- ScriptNode: Script
+ /// Strong content: `*Strong*`.
+ Strong
}
-impl ScriptNode {
- /// The base of the script.
- pub fn base(&self) -> MathItem {
- self.0.cast_first_child().expect("subscript is missing base")
+impl Strong {
+ /// The contents of the strong node.
+ pub fn body(&self) -> Markup {
+ self.0.cast_first_child().expect("strong node is missing markup body")
}
+}
- /// The subscript.
- pub fn sub(&self) -> Option<MathItem> {
+node! {
+ /// Emphasized content: `_Emphasized_`.
+ Emph
+}
+
+impl Emph {
+ /// The contents of the emphasis node.
+ pub fn body(&self) -> Markup {
self.0
- .children()
- .skip_while(|node| !matches!(node.kind(), NodeKind::Underscore))
- .nth(1)
- .map(|node| node.cast().expect("script node has invalid subscript"))
+ .cast_first_child()
+ .expect("emphasis node is missing markup body")
}
+}
- /// The superscript.
- pub fn sup(&self) -> Option<MathItem> {
- self.0
- .children()
- .skip_while(|node| !matches!(node.kind(), NodeKind::Hat))
- .nth(1)
- .map(|node| node.cast().expect("script node has invalid superscript"))
+node! {
+ /// A raw block with optional syntax highlighting: `` `...` ``.
+ Raw
+}
+
+impl Raw {
+ /// The raw text.
+ pub fn text(&self) -> &EcoString {
+ &self.get().text
+ }
+
+ /// An optional identifier specifying the language to syntax-highlight in.
+ pub fn lang(&self) -> Option<&EcoString> {
+ self.get().lang.as_ref()
+ }
+
+ /// Whether the raw block is block-level.
+ pub fn block(&self) -> bool {
+ self.get().block
+ }
+
+ /// The raw fields.
+ fn get(&self) -> &RawKind {
+ match self.0.kind() {
+ NodeKind::Raw(v) => v.as_ref(),
+ _ => panic!("raw is of wrong kind"),
+ }
}
}
node! {
- /// A fraction in a formula: `x/2`
- FracNode: Frac
+ /// A hyperlink: `https://typst.org`.
+ Link
}
-impl FracNode {
- /// The numerator.
- pub fn num(&self) -> MathItem {
- self.0.cast_first_child().expect("fraction is missing numerator")
+impl Link {
+ /// Get the URL.
+ pub fn url(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Link(url) => url,
+ _ => panic!("link is of wrong kind"),
+ }
}
+}
- /// The denominator.
- pub fn denom(&self) -> MathItem {
- self.0.cast_last_child().expect("fraction is missing denominator")
+node! {
+ /// A label: `<label>`.
+ Label
+}
+
+impl Label {
+ /// Get the label.
+ pub fn get(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Label(v) => v,
+ _ => panic!("label is of wrong kind"),
+ }
}
}
node! {
- /// A math alignment indicator: `&`, `&&`.
- AlignNode: Align
+ /// A reference: `@target`.
+ Ref
}
-impl AlignNode {
- /// The number of ampersands.
- pub fn count(&self) -> usize {
- self.0.children().filter(|n| n.kind() == &NodeKind::Amp).count()
+impl Ref {
+ /// Get the target.
+ pub fn get(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Ref(v) => v,
+ _ => panic!("reference is of wrong kind"),
+ }
}
}
node! {
/// A section heading: `= Introduction`.
- HeadingNode: Heading
+ Heading
}
-impl HeadingNode {
+impl Heading {
/// The contents of the heading.
- pub fn body(&self) -> MarkupNode {
+ pub fn body(&self) -> Markup {
self.0.cast_first_child().expect("heading is missing markup body")
}
@@ -318,19 +356,19 @@ impl HeadingNode {
node! {
/// An item in an unordered list: `- ...`.
- ListItem: ListItem
+ ListItem
}
impl ListItem {
/// The contents of the list item.
- pub fn body(&self) -> MarkupNode {
+ pub fn body(&self) -> Markup {
self.0.cast_first_child().expect("list item is missing body")
}
}
node! {
/// An item in an enumeration (ordered list): `1. ...`.
- EnumItem: EnumItem
+ EnumItem
}
impl EnumItem {
@@ -343,32 +381,171 @@ impl EnumItem {
}
/// The contents of the list item.
- pub fn body(&self) -> MarkupNode {
+ pub fn body(&self) -> Markup {
self.0.cast_first_child().expect("enum item is missing body")
}
}
node! {
/// An item in a description list: `/ Term: Details`.
- DescItem: DescItem
+ DescItem
}
impl DescItem {
/// The term described by the item.
- pub fn term(&self) -> MarkupNode {
+ pub fn term(&self) -> Markup {
self.0
.cast_first_child()
.expect("description list item is missing term")
}
/// The description of the term.
- pub fn body(&self) -> MarkupNode {
+ pub fn body(&self) -> Markup {
self.0
.cast_last_child()
.expect("description list item is missing body")
}
}
+node! {
+ /// A math formula: `$x$`, `$ x^2 $`.
+ Math
+}
+
+impl Math {
+ /// The children.
+ pub fn children(&self) -> impl Iterator<Item = MathNode> + '_ {
+ self.0.children().filter_map(SyntaxNode::cast)
+ }
+}
+
+/// A single piece of a math formula.
+#[derive(Debug, Clone, PartialEq, Hash)]
+pub enum MathNode {
+ /// Whitespace.
+ Space(Space),
+ /// A forced line break.
+ Linebreak(Linebreak),
+ /// An escape sequence: `\#`, `\u{1F5FA}`.
+ Escape(Escape),
+ /// A atom: `x`, `+`, `12`.
+ Atom(Atom),
+ /// A base with an optional sub- and superscript: `a_1^2`.
+ Script(Script),
+ /// A fraction: `x/2`.
+ Frac(Frac),
+ /// An alignment indicator: `&`, `&&`.
+ Align(Align),
+ /// Grouped mathematical material.
+ Group(Math),
+ /// An expression.
+ Expr(Expr),
+}
+
+impl TypedNode for MathNode {
+ fn from_untyped(node: &SyntaxNode) -> Option<Self> {
+ match node.kind() {
+ NodeKind::Space { .. } => node.cast().map(Self::Space),
+ NodeKind::Linebreak => node.cast().map(Self::Linebreak),
+ NodeKind::Escape(_) => node.cast().map(Self::Escape),
+ NodeKind::Atom(_) => node.cast().map(Self::Atom),
+ NodeKind::Script => node.cast().map(Self::Script),
+ NodeKind::Frac => node.cast().map(Self::Frac),
+ NodeKind::Align => node.cast().map(Self::Align),
+ NodeKind::Math => node.cast().map(Self::Group),
+ _ => node.cast().map(Self::Expr),
+ }
+ }
+
+ fn as_untyped(&self) -> &SyntaxNode {
+ match self {
+ Self::Space(v) => v.as_untyped(),
+ Self::Linebreak(v) => v.as_untyped(),
+ Self::Escape(v) => v.as_untyped(),
+ Self::Atom(v) => v.as_untyped(),
+ Self::Script(v) => v.as_untyped(),
+ Self::Frac(v) => v.as_untyped(),
+ Self::Align(v) => v.as_untyped(),
+ Self::Group(v) => v.as_untyped(),
+ Self::Expr(v) => v.as_untyped(),
+ }
+ }
+}
+
+node! {
+ /// A atom in a formula: `x`, `+`, `12`.
+ Atom
+}
+
+impl Atom {
+ /// Get the atom's text.
+ pub fn get(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Atom(v) => v,
+ _ => panic!("atom is of wrong kind"),
+ }
+ }
+}
+
+node! {
+ /// A base with an optional sub- and superscript in a formula: `a_1^2`.
+ Script
+}
+
+impl Script {
+ /// The base of the script.
+ pub fn base(&self) -> MathNode {
+ self.0.cast_first_child().expect("script node is missing base")
+ }
+
+ /// The subscript.
+ pub fn sub(&self) -> Option<MathNode> {
+ self.0
+ .children()
+ .skip_while(|node| !matches!(node.kind(), NodeKind::Underscore))
+ .nth(1)
+ .map(|node| node.cast().expect("script node has invalid subscript"))
+ }
+
+ /// The superscript.
+ pub fn sup(&self) -> Option<MathNode> {
+ self.0
+ .children()
+ .skip_while(|node| !matches!(node.kind(), NodeKind::Hat))
+ .nth(1)
+ .map(|node| node.cast().expect("script node has invalid superscript"))
+ }
+}
+
+node! {
+ /// A fraction in a formula: `x/2`
+ Frac
+}
+
+impl Frac {
+ /// The numerator.
+ pub fn num(&self) -> MathNode {
+ self.0.cast_first_child().expect("fraction is missing numerator")
+ }
+
+ /// The denominator.
+ pub fn denom(&self) -> MathNode {
+ self.0.cast_last_child().expect("fraction is missing denominator")
+ }
+}
+
+node! {
+ /// A math alignment indicator: `&`, `&&`.
+ Align
+}
+
+impl Align {
+ /// The number of ampersands.
+ pub fn count(&self) -> usize {
+ self.0.children().filter(|n| n.kind() == &NodeKind::Amp).count()
+ }
+}
+
/// An expression.
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Expr {
@@ -381,15 +558,15 @@ pub enum Expr {
/// A content block: `[*Hi* there!]`.
Content(ContentBlock),
/// A grouped expression: `(1 + 2)`.
- Group(GroupExpr),
+ Parenthesized(Parenthesized),
/// An array expression: `(1, "hi", 12cm)`.
- Array(ArrayExpr),
+ Array(Array),
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
- Dict(DictExpr),
+ Dict(Dict),
/// A unary operation: `-x`.
- Unary(UnaryExpr),
+ Unary(Unary),
/// A binary operation: `a + b`.
- Binary(BinaryExpr),
+ Binary(Binary),
/// A field access: `properties.age`.
FieldAccess(FieldAccess),
/// An invocation of a function: `f(x, y)`.
@@ -397,31 +574,31 @@ pub enum Expr {
/// An invocation of a method: `array.push(v)`.
MethodCall(MethodCall),
/// A closure expression: `(x, y) => z`.
- Closure(ClosureExpr),
- /// A let expression: `let x = 1`.
- Let(LetExpr),
- /// A set expression: `set text(...)`.
- Set(SetExpr),
- /// A show expression: `show node: heading as [*{nody.body}*]`.
- Show(ShowExpr),
- /// A wrap expression: `wrap body in columns(2, body)`.
- Wrap(WrapExpr),
- /// An if-else expression: `if x { y } else { z }`.
- If(IfExpr),
- /// A while loop expression: `while x { y }`.
- While(WhileExpr),
- /// A for loop expression: `for x in y { z }`.
- For(ForExpr),
- /// An import expression: `import a, b, c from "utils.typ"`.
- Import(ImportExpr),
- /// An include expression: `include "chapter1.typ"`.
- Include(IncludeExpr),
- /// A break expression: `break`.
- Break(BreakExpr),
- /// A continue expression: `continue`.
- Continue(ContinueExpr),
- /// A return expression: `return`.
- Return(ReturnExpr),
+ Closure(Closure),
+ /// A let binding: `let x = 1`.
+ Let(LetBinding),
+ /// A set rule: `set text(...)`.
+ Set(SetRule),
+ /// A show rule: `show node: heading as [*{nody.body}*]`.
+ Show(ShowRule),
+ /// A wrap rule: `wrap body in columns(2, body)`.
+ Wrap(WrapRule),
+ /// An if-else conditional: `if x { y } else { z }`.
+ Conditional(Conditional),
+ /// A while loop: `while x { y }`.
+ While(WhileLoop),
+ /// A for loop: `for x in y { z }`.
+ For(ForLoop),
+ /// A module import: `import a, b, c from "utils.typ"`.
+ Import(ModuleImport),
+ /// A module include: `include "chapter1.typ"`.
+ Include(ModuleInclude),
+ /// A break statement: `break`.
+ Break(BreakStmt),
+ /// A continue statement: `continue`.
+ Continue(ContinueStmt),
+ /// A return statement: `return`, `return x + 1`.
+ Return(ReturnStmt),
}
impl TypedNode for Expr {
@@ -430,27 +607,27 @@ impl TypedNode for Expr {
NodeKind::Ident(_) => node.cast().map(Self::Ident),
NodeKind::CodeBlock => node.cast().map(Self::Code),
NodeKind::ContentBlock => node.cast().map(Self::Content),
- NodeKind::GroupExpr => node.cast().map(Self::Group),
- NodeKind::ArrayExpr => node.cast().map(Self::Array),
- NodeKind::DictExpr => node.cast().map(Self::Dict),
- NodeKind::UnaryExpr => node.cast().map(Self::Unary),
- NodeKind::BinaryExpr => node.cast().map(Self::Binary),
+ NodeKind::Parenthesized => node.cast().map(Self::Parenthesized),
+ NodeKind::Array => node.cast().map(Self::Array),
+ NodeKind::Dict => node.cast().map(Self::Dict),
+ NodeKind::Unary => node.cast().map(Self::Unary),
+ NodeKind::Binary => node.cast().map(Self::Binary),
NodeKind::FieldAccess => node.cast().map(Self::FieldAccess),
NodeKind::FuncCall => node.cast().map(Self::FuncCall),
NodeKind::MethodCall => node.cast().map(Self::MethodCall),
- NodeKind::ClosureExpr => node.cast().map(Self::Closure),
- NodeKind::LetExpr => node.cast().map(Self::Let),
- NodeKind::SetExpr => node.cast().map(Self::Set),
- NodeKind::ShowExpr => node.cast().map(Self::Show),
- NodeKind::WrapExpr => node.cast().map(Self::Wrap),
- NodeKind::IfExpr => node.cast().map(Self::If),
- NodeKind::WhileExpr => node.cast().map(Self::While),
- NodeKind::ForExpr => node.cast().map(Self::For),
- NodeKind::ImportExpr => node.cast().map(Self::Import),
- NodeKind::IncludeExpr => node.cast().map(Self::Include),
- NodeKind::BreakExpr => node.cast().map(Self::Break),
- NodeKind::ContinueExpr => node.cast().map(Self::Continue),
- NodeKind::ReturnExpr => node.cast().map(Self::Return),
+ NodeKind::Closure => node.cast().map(Self::Closure),
+ NodeKind::LetBinding => node.cast().map(Self::Let),
+ NodeKind::SetRule => node.cast().map(Self::Set),
+ NodeKind::ShowRule => node.cast().map(Self::Show),
+ NodeKind::WrapRule => node.cast().map(Self::Wrap),
+ NodeKind::Conditional => node.cast().map(Self::Conditional),
+ NodeKind::WhileLoop => node.cast().map(Self::While),
+ NodeKind::ForLoop => node.cast().map(Self::For),
+ NodeKind::ModuleImport => node.cast().map(Self::Import),
+ NodeKind::ModuleInclude => node.cast().map(Self::Include),
+ NodeKind::BreakStmt => node.cast().map(Self::Break),
+ NodeKind::ContinueStmt => node.cast().map(Self::Continue),
+ NodeKind::ReturnStmt => node.cast().map(Self::Return),
_ => node.cast().map(Self::Lit),
}
}
@@ -463,7 +640,7 @@ impl TypedNode for Expr {
Self::Ident(v) => v.as_untyped(),
Self::Array(v) => v.as_untyped(),
Self::Dict(v) => v.as_untyped(),
- Self::Group(v) => v.as_untyped(),
+ Self::Parenthesized(v) => v.as_untyped(),
Self::Unary(v) => v.as_untyped(),
Self::Binary(v) => v.as_untyped(),
Self::FieldAccess(v) => v.as_untyped(),
@@ -474,7 +651,7 @@ impl TypedNode for Expr {
Self::Set(v) => v.as_untyped(),
Self::Show(v) => v.as_untyped(),
Self::Wrap(v) => v.as_untyped(),
- Self::If(v) => v.as_untyped(),
+ Self::Conditional(v) => v.as_untyped(),
Self::While(v) => v.as_untyped(),
Self::For(v) => v.as_untyped(),
Self::Import(v) => v.as_untyped(),
@@ -497,7 +674,7 @@ impl Expr {
| Self::Set(_)
| Self::Show(_)
| Self::Wrap(_)
- | Self::If(_)
+ | Self::Conditional(_)
| Self::While(_)
| Self::For(_)
| Self::Import(_)
@@ -552,24 +729,9 @@ pub enum LitKind {
Str(EcoString),
}
-/// Unit of a numeric value.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Unit {
- /// An absolute length unit.
- Length(LengthUnit),
- /// An angular unit.
- Angle(AngleUnit),
- /// Font-relative: `1em` is the same as the font size.
- Em,
- /// Fractions: `fr`.
- Fr,
- /// Percentage: `%`.
- Percent,
-}
-
node! {
/// A code block: `{ let x = 1; x + 2 }`.
- CodeBlock: CodeBlock
+ CodeBlock
}
impl CodeBlock {
@@ -581,46 +743,48 @@ impl CodeBlock {
node! {
/// A content block: `[*Hi* there!]`.
- ContentBlock: ContentBlock
+ ContentBlock
}
impl ContentBlock {
/// The contained markup.
- pub fn body(&self) -> MarkupNode {
- self.0.cast_first_child().expect("content is missing body")
+ pub fn body(&self) -> Markup {
+ self.0.cast_first_child().expect("content block is missing body")
}
}
node! {
- /// A grouped expression: `(1 + 2)`.
- GroupExpr: GroupExpr
+ /// A parenthesized expression: `(1 + 2)`.
+ Parenthesized
}
-impl GroupExpr {
+impl Parenthesized {
/// The wrapped expression.
pub fn expr(&self) -> Expr {
- self.0.cast_first_child().expect("group is missing expression")
+ self.0
+ .cast_first_child()
+ .expect("parenthesized expression is missing expression")
}
}
node! {
- /// An array expression: `(1, "hi", 12cm)`.
- ArrayExpr: ArrayExpr
+ /// An array: `(1, "hi", 12cm)`.
+ Array
}
-impl ArrayExpr {
- /// The array items.
+impl Array {
+ /// The array's items.
pub fn items(&self) -> impl Iterator<Item = ArrayItem> + '_ {
self.0.children().filter_map(SyntaxNode::cast)
}
}
-/// An item in an array expresssion.
+/// An item in an array.
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum ArrayItem {
- /// A bare value: `12`.
+ /// A bare expression: `12`.
Pos(Expr),
- /// A spreaded value: `..things`.
+ /// A spreaded expression: `..things`.
Spread(Expr),
}
@@ -641,12 +805,12 @@ impl TypedNode for ArrayItem {
}
node! {
- /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
- DictExpr: DictExpr
+ /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
+ Dict
}
-impl DictExpr {
- /// The named dictionary items.
+impl Dict {
+ /// The dictionary's items.
pub fn items(&self) -> impl Iterator<Item = DictItem> + '_ {
self.0.children().filter_map(SyntaxNode::cast)
}
@@ -659,7 +823,7 @@ pub enum DictItem {
Named(Named),
/// A keyed pair: `"spacy key": true`.
Keyed(Keyed),
- /// A spreaded value: `..things`.
+ /// A spreaded expression: `..things`.
Spread(Expr),
}
@@ -724,21 +888,21 @@ impl Keyed {
node! {
/// A unary operation: `-x`.
- UnaryExpr: UnaryExpr
+ Unary
}
-impl UnaryExpr {
+impl Unary {
/// The operator: `-`.
pub fn op(&self) -> UnOp {
self.0
.children()
.find_map(|node| UnOp::from_token(node.kind()))
- .expect("unary expression is missing operator")
+ .expect("unary operation is missing operator")
}
/// The expression to operate on: `x`.
pub fn expr(&self) -> Expr {
- self.0.cast_last_child().expect("unary expression is missing child")
+ self.0.cast_last_child().expect("unary operation is missing child")
}
}
@@ -784,10 +948,10 @@ impl UnOp {
node! {
/// A binary operation: `a + b`.
- BinaryExpr: BinaryExpr
+ Binary
}
-impl BinaryExpr {
+impl Binary {
/// The binary operator: `+`.
pub fn op(&self) -> BinOp {
let mut not = false;
@@ -801,21 +965,21 @@ impl BinaryExpr {
NodeKind::In if not => Some(BinOp::NotIn),
_ => BinOp::from_token(node.kind()),
})
- .expect("binary expression is missing operator")
+ .expect("binary operation is missing operator")
}
/// The left-hand side of the operation: `a`.
pub fn lhs(&self) -> Expr {
self.0
.cast_first_child()
- .expect("binary expression is missing left-hand side")
+ .expect("binary operation is missing left-hand side")
}
/// The right-hand side of the operation: `b`.
pub fn rhs(&self) -> Expr {
self.0
.cast_last_child()
- .expect("binary expression is missing right-hand side")
+ .expect("binary operation is missing right-hand side")
}
}
@@ -975,24 +1139,24 @@ pub enum Assoc {
node! {
/// A field access: `properties.age`.
- FieldAccess: FieldAccess
+ FieldAccess
}
impl FieldAccess {
- /// The object with the field.
- pub fn object(&self) -> Expr {
+ /// The expression to access the field on.
+ pub fn target(&self) -> Expr {
self.0.cast_first_child().expect("field access is missing object")
}
/// The name of the field.
pub fn field(&self) -> Ident {
- self.0.cast_last_child().expect("field access call is missing name")
+ self.0.cast_last_child().expect("field access is missing name")
}
}
node! {
/// An invocation of a function: `f(x, y)`.
- FuncCall: FuncCall
+ FuncCall
}
impl FuncCall {
@@ -1002,7 +1166,7 @@ impl FuncCall {
}
/// The arguments to the function.
- pub fn args(&self) -> CallArgs {
+ pub fn args(&self) -> Args {
self.0
.cast_last_child()
.expect("function call is missing argument list")
@@ -1011,13 +1175,13 @@ impl FuncCall {
node! {
/// An invocation of a method: `array.push(v)`.
- MethodCall: MethodCall
+ MethodCall
}
impl MethodCall {
- /// The value to call the method on.
- pub fn receiver(&self) -> Expr {
- self.0.cast_first_child().expect("method call is missing callee")
+ /// The expresion to call the method on.
+ pub fn target(&self) -> Expr {
+ self.0.cast_first_child().expect("method call is missing target")
}
/// The name of the method.
@@ -1026,7 +1190,7 @@ impl MethodCall {
}
/// The arguments to the method.
- pub fn args(&self) -> CallArgs {
+ pub fn args(&self) -> Args {
self.0
.cast_last_child()
.expect("method call is missing argument list")
@@ -1035,19 +1199,19 @@ impl MethodCall {
node! {
/// The arguments to a function: `12, draw: false`.
- CallArgs
+ Args
}
-impl CallArgs {
+impl Args {
/// The positional and named arguments.
- pub fn items(&self) -> impl Iterator<Item = CallArg> + '_ {
+ pub fn items(&self) -> impl Iterator<Item = Arg> + '_ {
self.0.children().filter_map(SyntaxNode::cast)
}
}
/// An argument to a function call.
#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum CallArg {
+pub enum Arg {
/// A positional argument: `12`.
Pos(Expr),
/// A named argument: `draw: false`.
@@ -1056,7 +1220,7 @@ pub enum CallArg {
Spread(Expr),
}
-impl TypedNode for CallArg {
+impl TypedNode for Arg {
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() {
NodeKind::Named => node.cast().map(Self::Named),
@@ -1076,10 +1240,10 @@ impl TypedNode for CallArg {
node! {
/// A closure expression: `(x, y) => z`.
- ClosureExpr: ClosureExpr
+ Closure
}
-impl ClosureExpr {
+impl Closure {
/// The name of the closure.
///
/// This only exists if you use the function syntax sugar: `let f(x) = y`.
@@ -1088,10 +1252,10 @@ impl ClosureExpr {
}
/// The parameter bindings.
- pub fn params(&self) -> impl Iterator<Item = ClosureParam> + '_ {
+ pub fn params(&self) -> impl Iterator<Item = Param> + '_ {
self.0
.children()
- .find(|x| x.kind() == &NodeKind::ClosureParams)
+ .find(|x| x.kind() == &NodeKind::Params)
.expect("closure is missing parameter list")
.children()
.filter_map(SyntaxNode::cast)
@@ -1105,7 +1269,7 @@ impl ClosureExpr {
/// A parameter to a closure.
#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum ClosureParam {
+pub enum Param {
/// A positional parameter: `x`.
Pos(Ident),
/// A named parameter with a default value: `draw: false`.
@@ -1114,7 +1278,7 @@ pub enum ClosureParam {
Sink(Ident),
}
-impl TypedNode for ClosureParam {
+impl TypedNode for Param {
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() {
NodeKind::Ident(_) => node.cast().map(Self::Pos),
@@ -1134,11 +1298,11 @@ impl TypedNode for ClosureParam {
}
node! {
- /// A let expression: `let x = 1`.
- LetExpr
+ /// A let binding: `let x = 1`.
+ LetBinding
}
-impl LetExpr {
+impl LetBinding {
/// The binding to assign to.
pub fn binding(&self) -> Ident {
match self.0.cast_first_child() {
@@ -1146,7 +1310,7 @@ impl LetExpr {
Some(Expr::Closure(closure)) => {
closure.name().expect("let-bound closure is missing name")
}
- _ => panic!("let expression is missing binding"),
+ _ => panic!("let is missing binding"),
}
}
@@ -1163,28 +1327,28 @@ impl LetExpr {
}
node! {
- /// A set expression: `set text(...)`.
- SetExpr
+ /// A set rule: `set text(...)`.
+ SetRule
}
-impl SetExpr {
+impl SetRule {
/// The function to set style properties for.
pub fn target(&self) -> Ident {
self.0.cast_first_child().expect("set rule is missing target")
}
/// The style properties to set.
- pub fn args(&self) -> CallArgs {
+ pub fn args(&self) -> Args {
self.0.cast_last_child().expect("set rule is missing argument list")
}
}
node! {
- /// A show expression: `show node: heading as [*{nody.body}*]`.
- ShowExpr
+ /// A show rule: `show node: heading as [*{nody.body}*]`.
+ ShowRule
}
-impl ShowExpr {
+impl ShowRule {
/// The binding to assign to.
pub fn binding(&self) -> Option<Ident> {
let mut children = self.0.children();
@@ -1210,31 +1374,31 @@ impl ShowExpr {
}
node! {
- /// A wrap expression: `wrap body in columns(2, body)`.
- WrapExpr
+ /// A wrap rule: `wrap body in columns(2, body)`.
+ WrapRule
}
-impl WrapExpr {
+impl WrapRule {
/// The binding to assign the remaining markup to.
pub fn binding(&self) -> Ident {
- self.0.cast_first_child().expect("wrap expression is missing binding")
+ self.0.cast_first_child().expect("wrap rule is missing binding")
}
/// The expression to evaluate.
pub fn body(&self) -> Expr {
- self.0.cast_last_child().expect("wrap expression is missing body")
+ self.0.cast_last_child().expect("wrap rule is missing body")
}
}
node! {
- /// An if-else expression: `if x { y } else { z }`.
- IfExpr
+ /// An if-else conditional: `if x { y } else { z }`.
+ Conditional
}
-impl IfExpr {
+impl Conditional {
/// The condition which selects the body to evaluate.
pub fn condition(&self) -> Expr {
- self.0.cast_first_child().expect("if expression is missing condition")
+ self.0.cast_first_child().expect("conditional is missing condition")
}
/// The expression to evaluate if the condition is true.
@@ -1243,7 +1407,7 @@ impl IfExpr {
.children()
.filter_map(SyntaxNode::cast)
.nth(1)
- .expect("if expression is missing body")
+ .expect("conditional is missing body")
}
/// The expression to evaluate if the condition is false.
@@ -1253,11 +1417,11 @@ impl IfExpr {
}
node! {
- /// A while loop expression: `while x { y }`.
- WhileExpr
+ /// A while loop: `while x { y }`.
+ WhileLoop
}
-impl WhileExpr {
+impl WhileLoop {
/// The condition which selects whether to evaluate the body.
pub fn condition(&self) -> Expr {
self.0.cast_first_child().expect("while loop is missing condition")
@@ -1270,11 +1434,11 @@ impl WhileExpr {
}
node! {
- /// A for loop expression: `for x in y { z }`.
- ForExpr
+ /// A for loop: `for x in y { z }`.
+ ForLoop
}
-impl ForExpr {
+impl ForLoop {
/// The pattern to assign to.
pub fn pattern(&self) -> ForPattern {
self.0.cast_first_child().expect("for loop is missing pattern")
@@ -1292,7 +1456,7 @@ impl ForExpr {
}
node! {
- /// A for-in loop expression: `for x in y { z }`.
+ /// A for-in loop: `for x in y { z }`.
ForPattern
}
@@ -1311,11 +1475,11 @@ impl ForPattern {
}
node! {
- /// An import expression: `import a, b, c from "utils.typ"`.
- ImportExpr
+ /// A module import: `import a, b, c from "utils.typ"`.
+ ModuleImport
}
-impl ImportExpr {
+impl ModuleImport {
/// The items to be imported.
pub fn imports(&self) -> Imports {
self.0
@@ -1328,12 +1492,12 @@ impl ImportExpr {
}
_ => None,
})
- .expect("import is missing items")
+ .expect("module import is missing items")
}
/// The path to the file that should be imported.
pub fn path(&self) -> Expr {
- self.0.cast_last_child().expect("import is missing path")
+ self.0.cast_last_child().expect("module import is missing path")
}
}
@@ -1347,33 +1511,33 @@ pub enum Imports {
}
node! {
- /// An include expression: `include "chapter1.typ"`.
- IncludeExpr
+ /// A module include: `include "chapter1.typ"`.
+ ModuleInclude
}
-impl IncludeExpr {
+impl ModuleInclude {
/// The path to the file that should be included.
pub fn path(&self) -> Expr {
- self.0.cast_last_child().expect("include is missing path")
+ self.0.cast_last_child().expect("module include is missing path")
}
}
node! {
/// A break expression: `break`.
- BreakExpr
+ BreakStmt
}
node! {
/// A continue expression: `continue`.
- ContinueExpr
+ ContinueStmt
}
node! {
/// A return expression: `return`, `return x + 1`.
- ReturnExpr
+ ReturnStmt
}
-impl ReturnExpr {
+impl ReturnStmt {
/// The expression to return.
pub fn body(&self) -> Option<Expr> {
self.0.cast_last_child()
@@ -1382,11 +1546,19 @@ impl ReturnExpr {
node! {
/// An identifier.
- Ident: NodeKind::Ident(_)
+ Ident
}
impl Ident {
- /// Take out the contained [`EcoString`].
+ /// Get the identifier.
+ pub fn get(&self) -> &EcoString {
+ match self.0.kind() {
+ NodeKind::Ident(id) => id,
+ _ => panic!("identifier is of wrong kind"),
+ }
+ }
+
+ /// Take out the container identifier.
pub fn take(self) -> EcoString {
match self.0 {
SyntaxNode::Leaf(NodeData { kind: NodeKind::Ident(id), .. }) => id,
@@ -1399,9 +1571,6 @@ impl Deref for Ident {
type Target = str;
fn deref(&self) -> &Self::Target {
- match &self.0 {
- SyntaxNode::Leaf(NodeData { kind: NodeKind::Ident(id), .. }) => id,
- _ => panic!("identifier is of wrong kind"),
- }
+ self.get()
}
}
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index 60d349d1..bfb36078 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -138,23 +138,21 @@ pub enum Category {
/// An escape sequence.
Escape,
/// An easily typable shortcut to a unicode codepoint.
- Shortcut,
+ Shorthand,
/// A smart quote.
- Quote,
- /// Strong text.
+ SmartQuote,
+ /// Strong markup.
Strong,
- /// Emphasized text.
+ /// Emphasized markup.
Emph,
/// A hyperlink.
Link,
/// Raw text or code.
Raw,
- /// A full math formula.
- Math,
- /// The delimiters of a math formula.
- MathDelimiter,
- /// A symbol with special meaning in a math formula.
- MathSymbol,
+ /// A label.
+ Label,
+ /// A reference.
+ Ref,
/// A section heading.
Heading,
/// A full item of a list, enumeration or description list.
@@ -163,10 +161,12 @@ pub enum Category {
ListMarker,
/// A term in a description list.
ListTerm,
- /// A label.
- Label,
- /// A reference.
- Ref,
+ /// A full math formula.
+ Math,
+ /// The delimiters of a math formula.
+ MathDelimiter,
+ /// An operator with special meaning in a math formula.
+ MathOperator,
/// A keyword.
Keyword,
/// A literal defined by a keyword like `none`, `auto` or a boolean.
@@ -212,17 +212,10 @@ impl Category {
_ => Some(Category::Operator),
},
NodeKind::Underscore => match parent.kind() {
- NodeKind::Script => Some(Category::MathSymbol),
+ NodeKind::Script => Some(Category::MathOperator),
_ => None,
},
NodeKind::Dollar => Some(Category::MathDelimiter),
- NodeKind::Backslash => Some(Category::Shortcut),
- NodeKind::Tilde => Some(Category::Shortcut),
- NodeKind::HyphQuest => Some(Category::Shortcut),
- NodeKind::Hyph2 => Some(Category::Shortcut),
- NodeKind::Hyph3 => Some(Category::Shortcut),
- NodeKind::Dot3 => Some(Category::Shortcut),
- NodeKind::Quote { .. } => Some(Category::Quote),
NodeKind::Plus => Some(match parent.kind() {
NodeKind::EnumItem => Category::ListMarker,
_ => Category::Operator,
@@ -233,11 +226,11 @@ impl Category {
}),
NodeKind::Slash => Some(match parent.kind() {
NodeKind::DescItem => Category::ListMarker,
- NodeKind::Frac => Category::MathSymbol,
+ NodeKind::Frac => Category::MathOperator,
_ => Category::Operator,
}),
- NodeKind::Hat => Some(Category::MathSymbol),
- NodeKind::Amp => Some(Category::MathSymbol),
+ NodeKind::Hat => Some(Category::MathOperator),
+ NodeKind::Amp => Some(Category::MathOperator),
NodeKind::Dot => Some(Category::Punctuation),
NodeKind::Eq => match parent.kind() {
NodeKind::Heading => None,
@@ -291,32 +284,35 @@ impl Category {
_ => None,
},
NodeKind::Text(_) => None,
+ NodeKind::Linebreak => Some(Category::Escape),
NodeKind::Escape(_) => Some(Category::Escape),
+ NodeKind::Shorthand(_) => Some(Category::Shorthand),
+ NodeKind::SmartQuote { .. } => Some(Category::SmartQuote),
NodeKind::Strong => Some(Category::Strong),
NodeKind::Emph => Some(Category::Emph),
- NodeKind::Link(_) => Some(Category::Link),
NodeKind::Raw(_) => Some(Category::Raw),
- NodeKind::Math => Some(Category::Math),
- NodeKind::Atom(_) => None,
- NodeKind::Script => None,
- NodeKind::Frac => None,
- NodeKind::Align => None,
+ NodeKind::Link(_) => Some(Category::Link),
+ NodeKind::Label(_) => Some(Category::Label),
+ NodeKind::Ref(_) => Some(Category::Ref),
NodeKind::Heading => Some(Category::Heading),
NodeKind::ListItem => Some(Category::ListItem),
NodeKind::EnumItem => Some(Category::ListItem),
NodeKind::EnumNumbering(_) => Some(Category::ListMarker),
NodeKind::DescItem => Some(Category::ListItem),
- NodeKind::Label(_) => Some(Category::Label),
- NodeKind::Ref(_) => Some(Category::Ref),
+ NodeKind::Math => Some(Category::Math),
+ NodeKind::Atom(_) => None,
+ NodeKind::Script => None,
+ NodeKind::Frac => None,
+ NodeKind::Align => None,
NodeKind::Ident(_) => match parent.kind() {
NodeKind::Markup { .. } => Some(Category::Interpolated),
NodeKind::Math => Some(Category::Interpolated),
NodeKind::FuncCall => Some(Category::Function),
NodeKind::MethodCall if i > 0 => Some(Category::Function),
- NodeKind::ClosureExpr if i == 0 => Some(Category::Function),
- NodeKind::SetExpr => Some(Category::Function),
- NodeKind::ShowExpr
+ NodeKind::Closure if i == 0 => Some(Category::Function),
+ NodeKind::SetRule => Some(Category::Function),
+ NodeKind::ShowRule
if parent
.children()
.rev()
@@ -336,34 +332,34 @@ impl Category {
NodeKind::Str(_) => Some(Category::String),
NodeKind::CodeBlock => None,
NodeKind::ContentBlock => None,
- NodeKind::GroupExpr => None,
- NodeKind::ArrayExpr => None,
- NodeKind::DictExpr => None,
+ NodeKind::Parenthesized => None,
+ NodeKind::Array => None,
+ NodeKind::Dict => None,
NodeKind::Named => None,
NodeKind::Keyed => None,
- NodeKind::UnaryExpr => None,
- NodeKind::BinaryExpr => None,
+ NodeKind::Unary => None,
+ NodeKind::Binary => None,
NodeKind::FieldAccess => None,
NodeKind::FuncCall => None,
NodeKind::MethodCall => None,
- NodeKind::CallArgs => None,
+ NodeKind::Args => None,
NodeKind::Spread => None,
- NodeKind::ClosureExpr => None,
- NodeKind::ClosureParams => None,
- NodeKind::LetExpr => None,
- NodeKind::SetExpr => None,
- NodeKind::ShowExpr => None,
- NodeKind::WrapExpr => None,
- NodeKind::IfExpr => None,
- NodeKind::WhileExpr => None,
- NodeKind::ForExpr => None,
+ NodeKind::Closure => None,
+ NodeKind::Params => None,
+ NodeKind::LetBinding => None,
+ NodeKind::SetRule => None,
+ NodeKind::ShowRule => None,
+ NodeKind::WrapRule => None,
+ NodeKind::Conditional => None,
+ NodeKind::WhileLoop => None,
+ NodeKind::ForLoop => None,
NodeKind::ForPattern => None,
- NodeKind::ImportExpr => None,
+ NodeKind::ModuleImport => None,
NodeKind::ImportItems => None,
- NodeKind::IncludeExpr => None,
- NodeKind::BreakExpr => None,
- NodeKind::ContinueExpr => None,
- NodeKind::ReturnExpr => None,
+ NodeKind::ModuleInclude => None,
+ NodeKind::BreakStmt => None,
+ NodeKind::ContinueStmt => None,
+ NodeKind::ReturnStmt => None,
NodeKind::Error(_, _) => Some(Category::Error),
}
@@ -376,15 +372,15 @@ impl Category {
Self::Bracket => "punctuation.definition.bracket.typst",
Self::Punctuation => "punctuation.typst",
Self::Escape => "constant.character.escape.typst",
- Self::Shortcut => "constant.character.shortcut.typst",
- Self::Quote => "constant.character.quote.typst",
+ Self::Shorthand => "constant.character.shorthand.typst",
+ Self::SmartQuote => "constant.character.quote.typst",
Self::Strong => "markup.bold.typst",
Self::Emph => "markup.italic.typst",
Self::Link => "markup.underline.link.typst",
Self::Raw => "markup.raw.typst",
Self::Math => "string.other.math.typst",
Self::MathDelimiter => "punctuation.definition.math.typst",
- Self::MathSymbol => "keyword.operator.math.typst",
+ Self::MathOperator => "keyword.operator.math.typst",
Self::Heading => "markup.heading.typst",
Self::ListItem => "markup.list.typst",
Self::ListMarker => "punctuation.definition.list.typst",
diff --git a/src/syntax/kind.rs b/src/syntax/kind.rs
index 77d4cd99..3273e72d 100644
--- a/src/syntax/kind.rs
+++ b/src/syntax/kind.rs
@@ -1,8 +1,7 @@
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::ast::{RawNode, Unit};
-use crate::diag::ErrorPos;
+use crate::geom::{AngleUnit, LengthUnit};
use crate::util::EcoString;
/// All syntactical building blocks that can be part of a Typst document.
@@ -11,13 +10,9 @@ use crate::util::EcoString;
/// the parser.
#[derive(Debug, Clone, PartialEq)]
pub enum NodeKind {
- /// A line comment, two slashes followed by inner contents, terminated with
- /// a newline: `//<str>\n`.
+ /// A line comment: `// ...`.
LineComment,
- /// A block comment, a slash and a star followed by inner contents,
- /// terminated with a star and a slash: `/*<str>*/`.
- ///
- /// The comment can contain nested block comments.
+ /// A block comment: `/* ... */`.
BlockComment,
/// One or more whitespace characters. Single spaces are collapsed into text
/// nodes if they would otherwise be surrounded by text nodes.
@@ -43,7 +38,7 @@ pub enum NodeKind {
Comma,
/// A semicolon terminating an expression: `;`.
Semicolon,
- /// A colon between name / key and value in a dictionary, argument or
+ /// A colon between name/key and value in a dictionary, argument or
/// parameter list, or between the term and body of a description list
/// term: `:`.
Colon,
@@ -52,22 +47,8 @@ pub enum NodeKind {
Star,
/// Toggles emphasized text and indicates a subscript in a formula: `_`.
Underscore,
- /// Starts and ends a math formula.
+ /// Starts and ends a math formula: `$`.
Dollar,
- /// A forced line break: `\`.
- Backslash,
- /// The non-breaking space: `~`.
- Tilde,
- /// The soft hyphen: `-?`.
- HyphQuest,
- /// The en-dash: `--`.
- Hyph2,
- /// The em-dash: `---`.
- Hyph3,
- /// The ellipsis: `...`.
- Dot3,
- /// A smart quote: `'` or `"`.
- Quote { double: bool },
/// The unary plus, binary addition operator, and start of enum items: `+`.
Plus,
/// The unary negation, binary subtraction operator, and start of list
@@ -160,18 +141,37 @@ pub enum NodeKind {
Markup { min_indent: usize },
/// Consecutive text without markup.
Text(EcoString),
- /// A unicode escape sequence, written as a slash and the letter "u"
- /// followed by a hexadecimal unicode entity enclosed in curly braces:
- /// `\u{1F5FA}`.
+ /// A forced line break: `\`.
+ Linebreak,
+ /// An escape sequence: `\#`, `\u{1F5FA}`.
Escape(char),
- /// Strong content: `*Strong*`.
+ /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
+ /// space or `-?` for a soft hyphen.
+ Shorthand(char),
+ /// A smart quote: `'` or `"`.
+ SmartQuote { double: bool },
+ /// Strong markup: `*Strong*`.
Strong,
- /// Emphasized content: `_Emphasized_`.
+ /// Emphasized markup: `_Emphasized_`.
Emph,
+ /// A raw block with optional syntax highlighting: `` `...` ``.
+ Raw(Arc<RawKind>),
/// A hyperlink: `https://typst.org`.
Link(EcoString),
- /// A raw block with optional syntax highlighting: `` `...` ``.
- Raw(Arc<RawNode>),
+ /// A label: `<label>`.
+ Label(EcoString),
+ /// A reference: `@target`.
+ Ref(EcoString),
+ /// A section heading: `= Introduction`.
+ Heading,
+ /// An item of an unordered list: `- ...`.
+ ListItem,
+ /// An item of an enumeration (ordered list): `+ ...` or `1. ...`.
+ EnumItem,
+ /// An explicit enumeration numbering: `23.`.
+ EnumNumbering(usize),
+ /// An item of a description list: `/ Term: Details.
+ DescItem,
/// A math formula: `$x$`, `$ x^2 $`.
Math,
/// An atom in a math formula: `x`, `+`, `12`.
@@ -182,20 +182,6 @@ pub enum NodeKind {
Frac,
/// An alignment indicator in a math formula: `&`, `&&`.
Align,
- /// A section heading: `= Introduction`.
- Heading,
- /// An item in an unordered list: `- ...`.
- ListItem,
- /// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
- EnumItem,
- /// An explicit enumeration numbering: `23.`.
- EnumNumbering(usize),
- /// An item in a description list: `/ Term: Details.
- DescItem,
- /// A label: `<label>`.
- Label(EcoString),
- /// A reference: `@label`.
- Ref(EcoString),
/// An identifier: `center`.
Ident(EcoString),
@@ -214,19 +200,19 @@ pub enum NodeKind {
/// A content block: `[*Hi* there!]`.
ContentBlock,
/// A grouped expression: `(1 + 2)`.
- GroupExpr,
- /// An array expression: `(1, "hi", 12cm)`.
- ArrayExpr,
- /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
- DictExpr,
+ Parenthesized,
+ /// An array: `(1, "hi", 12cm)`.
+ Array,
+ /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
+ Dict,
/// A named pair: `thickness: 3pt`.
Named,
/// A keyed pair: `"spacy key": true`.
Keyed,
/// A unary operation: `-x`.
- UnaryExpr,
+ Unary,
/// A binary operation: `a + b`.
- BinaryExpr,
+ Binary,
/// A field access: `properties.age`.
FieldAccess,
/// An invocation of a function: `f(x, y)`.
@@ -234,50 +220,89 @@ pub enum NodeKind {
/// An invocation of a method: `array.push(v)`.
MethodCall,
/// A function call's argument list: `(x, y)`.
- CallArgs,
+ Args,
/// Spreaded arguments or a argument sink: `..x`.
Spread,
- /// A closure expression: `(x, y) => z`.
- ClosureExpr,
+ /// A closure: `(x, y) => z`.
+ Closure,
/// A closure's parameters: `(x, y)`.
- ClosureParams,
- /// A let expression: `let x = 1`.
- LetExpr,
- /// A set expression: `set text(...)`.
- SetExpr,
- /// A show expression: `show node: heading as [*{nody.body}*]`.
- ShowExpr,
- /// A wrap expression: `wrap body in columns(2, body)`.
- WrapExpr,
- /// An if-else expression: `if x { y } else { z }`.
- IfExpr,
- /// A while loop expression: `while x { ... }`.
- WhileExpr,
- /// A for loop expression: `for x in y { ... }`.
- ForExpr,
+ Params,
+ /// A let binding: `let x = 1`.
+ LetBinding,
+ /// A set rule: `set text(...)`.
+ SetRule,
+ /// A show rule: `show node: heading as [*{nody.body}*]`.
+ ShowRule,
+ /// A wrap rule: `wrap body in columns(2, body)`.
+ WrapRule,
+ /// An if-else conditional: `if x { y } else { z }`.
+ Conditional,
+ /// A while loop: `while x { ... }`.
+ WhileLoop,
+ /// A for loop: `for x in y { ... }`.
+ ForLoop,
/// A for loop's destructuring pattern: `x` or `x, y`.
ForPattern,
- /// An import expression: `import a, b, c from "utils.typ"`.
- ImportExpr,
- /// Items to import: `a, b, c`.
+ /// A module import: `import a, b, c from "utils.typ"`.
+ ModuleImport,
+ /// Items to import from a module: `a, b, c`.
ImportItems,
- /// An include expression: `include "chapter1.typ"`.
- IncludeExpr,
- /// A break expression: `break`.
- BreakExpr,
- /// A continue expression: `continue`.
- ContinueExpr,
- /// A return expression: `return x + 1`.
- ReturnExpr,
+ /// A module include: `include "chapter1.typ"`.
+ ModuleInclude,
+ /// A break statement: `break`.
+ BreakStmt,
+ /// A continue statement: `continue`.
+ ContinueStmt,
+ /// A return statement: `return x + 1`.
+ ReturnStmt,
/// An invalid sequence of characters.
Error(ErrorPos, EcoString),
}
+/// Fields of the node kind `Raw`.
+#[derive(Debug, Clone, PartialEq, Hash)]
+pub struct RawKind {
+ /// An optional identifier specifying the language to syntax-highlight in.
+ pub lang: Option<EcoString>,
+ /// The raw text, determined as the raw string between the backticks trimmed
+ /// according to the above rules.
+ pub text: EcoString,
+ /// Whether the element is block-level, that is, it has 3+ backticks
+ /// and contains at least one newline.
+ pub block: bool,
+}
+
+/// Unit of a numeric value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum Unit {
+ /// An absolute length unit.
+ Length(LengthUnit),
+ /// An angular unit.
+ Angle(AngleUnit),
+ /// Font-relative: `1em` is the same as the font size.
+ Em,
+ /// Fractions: `fr`.
+ Fr,
+ /// Percentage: `%`.
+ Percent,
+}
+
+/// Where in a node an error should be annotated,
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum ErrorPos {
+ /// Over the full width of the node.
+ Full,
+ /// At the start of the node.
+ Start,
+ /// At the end of the node.
+ End,
+}
+
impl NodeKind {
- /// Whether this is a kind of parenthesis.
- pub fn is_paren(&self) -> bool {
- matches!(self, Self::LeftParen | Self::RightParen)
+ /// Whether this is trivia.
+ pub fn is_trivia(&self) -> bool {
+ self.is_space() || matches!(self, Self::LineComment | Self::BlockComment)
}
/// Whether this is a space.
@@ -285,12 +310,12 @@ impl NodeKind {
matches!(self, Self::Space { .. })
}
- /// Whether this is trivia.
- pub fn is_trivia(&self) -> bool {
- self.is_space() || matches!(self, Self::LineComment | Self::BlockComment)
+ /// Whether this is a left or right parenthesis.
+ pub fn is_paren(&self) -> bool {
+ matches!(self, Self::LeftParen | Self::RightParen)
}
- /// Whether this is a kind of error.
+ /// Whether this is an error.
pub fn is_error(&self) -> bool {
matches!(self, NodeKind::Error(_, _))
}
@@ -313,14 +338,8 @@ impl NodeKind {
Self::Star => "star",
Self::Underscore => "underscore",
Self::Dollar => "dollar sign",
- Self::Backslash => "linebreak",
- Self::Tilde => "non-breaking space",
- Self::HyphQuest => "soft hyphen",
- Self::Hyph2 => "en dash",
- Self::Hyph3 => "em dash",
- Self::Dot3 => "ellipsis",
- Self::Quote { double: false } => "single quote",
- Self::Quote { double: true } => "double quote",
+ Self::SmartQuote { double: false } => "single quote",
+ Self::SmartQuote { double: true } => "double quote",
Self::Plus => "plus",
Self::Minus => "minus",
Self::Slash => "slash",
@@ -363,23 +382,25 @@ impl NodeKind {
Self::As => "keyword `as`",
Self::Markup { .. } => "markup",
Self::Text(_) => "text",
+ Self::Linebreak => "linebreak",
Self::Escape(_) => "escape sequence",
+ Self::Shorthand(_) => "shorthand",
Self::Strong => "strong content",
Self::Emph => "emphasized content",
- Self::Link(_) => "link",
Self::Raw(_) => "raw block",
- Self::Math => "math formula",
- Self::Atom(_) => "math atom",
- Self::Script => "script",
- Self::Frac => "fraction",
- Self::Align => "alignment indicator",
+ Self::Link(_) => "link",
+ Self::Label(_) => "label",
+ Self::Ref(_) => "reference",
Self::Heading => "heading",
Self::ListItem => "list item",
Self::EnumItem => "enumeration item",
Self::EnumNumbering(_) => "enumeration item numbering",
Self::DescItem => "description list item",
- Self::Label(_) => "label",
- Self::Ref(_) => "reference",
+ Self::Math => "math formula",
+ Self::Atom(_) => "math atom",
+ Self::Script => "script",
+ Self::Frac => "fraction",
+ Self::Align => "alignment indicator",
Self::Ident(_) => "identifier",
Self::Bool(_) => "boolean",
Self::Int(_) => "integer",
@@ -388,34 +409,34 @@ impl NodeKind {
Self::Str(_) => "string",
Self::CodeBlock => "code block",
Self::ContentBlock => "content block",
- Self::GroupExpr => "group",
- Self::ArrayExpr => "array",
- Self::DictExpr => "dictionary",
+ Self::Parenthesized => "group",
+ Self::Array => "array",
+ Self::Dict => "dictionary",
Self::Named => "named pair",
Self::Keyed => "keyed pair",
- Self::UnaryExpr => "unary expression",
- Self::BinaryExpr => "binary expression",
+ Self::Unary => "unary expression",
+ Self::Binary => "binary expression",
Self::FieldAccess => "field access",
Self::FuncCall => "function call",
Self::MethodCall => "method call",
- Self::CallArgs => "call arguments",
+ Self::Args => "call arguments",
Self::Spread => "spread",
- Self::ClosureExpr => "closure",
- Self::ClosureParams => "closure parameters",
- Self::LetExpr => "`let` expression",
- Self::SetExpr => "`set` expression",
- Self::ShowExpr => "`show` expression",
- Self::WrapExpr => "`wrap` expression",
- Self::IfExpr => "`if` expression",
- Self::WhileExpr => "while-loop expression",
- Self::ForExpr => "for-loop expression",
+ Self::Closure => "closure",
+ Self::Params => "closure parameters",
+ Self::LetBinding => "`let` expression",
+ Self::SetRule => "`set` expression",
+ Self::ShowRule => "`show` expression",
+ Self::WrapRule => "`wrap` expression",
+ Self::Conditional => "`if` expression",
+ Self::WhileLoop => "while-loop expression",
+ Self::ForLoop => "for-loop expression",
Self::ForPattern => "for-loop destructuring pattern",
- Self::ImportExpr => "`import` expression",
+ Self::ModuleImport => "`import` expression",
Self::ImportItems => "import items",
- Self::IncludeExpr => "`include` expression",
- Self::BreakExpr => "`break` expression",
- Self::ContinueExpr => "`continue` expression",
- Self::ReturnExpr => "`return` expression",
+ Self::ModuleInclude => "`include` expression",
+ Self::BreakStmt => "`break` expression",
+ Self::ContinueStmt => "`continue` expression",
+ Self::ReturnStmt => "`return` expression",
Self::Error(_, _) => "syntax error",
}
}
@@ -440,13 +461,6 @@ impl Hash for NodeKind {
Self::Star => {}
Self::Underscore => {}
Self::Dollar => {}
- Self::Backslash => {}
- Self::Tilde => {}
- Self::HyphQuest => {}
- Self::Hyph2 => {}
- Self::Hyph3 => {}
- Self::Dot3 => {}
- Self::Quote { double } => double.hash(state),
Self::Plus => {}
Self::Minus => {}
Self::Slash => {}
@@ -489,23 +503,26 @@ impl Hash for NodeKind {
Self::As => {}
Self::Markup { min_indent } => min_indent.hash(state),
Self::Text(s) => s.hash(state),
+ Self::Linebreak => {}
Self::Escape(c) => c.hash(state),
+ Self::Shorthand(c) => c.hash(state),
+ Self::SmartQuote { double } => double.hash(state),
Self::Strong => {}
Self::Emph => {}
- Self::Link(link) => link.hash(state),
Self::Raw(raw) => raw.hash(state),
- Self::Math => {}
- Self::Atom(c) => c.hash(state),
- Self::Script => {}
- Self::Frac => {}
- Self::Align => {}
+ Self::Link(link) => link.hash(state),
+ Self::Label(c) => c.hash(state),
+ Self::Ref(c) => c.hash(state),
Self::Heading => {}
Self::ListItem => {}
Self::EnumItem => {}
Self::EnumNumbering(num) => num.hash(state),
Self::DescItem => {}
- Self::Label(c) => c.hash(state),
- Self::Ref(c) => c.hash(state),
+ Self::Math => {}
+ Self::Atom(c) => c.hash(state),
+ Self::Script => {}
+ Self::Frac => {}
+ Self::Align => {}
Self::Ident(v) => v.hash(state),
Self::Bool(v) => v.hash(state),
Self::Int(v) => v.hash(state),
@@ -514,34 +531,34 @@ impl Hash for NodeKind {
Self::Str(v) => v.hash(state),
Self::CodeBlock => {}
Self::ContentBlock => {}
- Self::GroupExpr => {}
- Self::ArrayExpr => {}
- Self::DictExpr => {}
+ Self::Parenthesized => {}
+ Self::Array => {}
+ Self::Dict => {}
Self::Named => {}
Self::Keyed => {}
- Self::UnaryExpr => {}
- Self::BinaryExpr => {}
+ Self::Unary => {}
+ Self::Binary => {}
Self::FieldAccess => {}
Self::FuncCall => {}
Self::MethodCall => {}
- Self::CallArgs => {}
+ Self::Args => {}
Self::Spread => {}
- Self::ClosureExpr => {}
- Self::ClosureParams => {}
- Self::LetExpr => {}
- Self::SetExpr => {}
- Self::ShowExpr => {}
- Self::WrapExpr => {}
- Self::IfExpr => {}
- Self::WhileExpr => {}
- Self::ForExpr => {}
+ Self::Closure => {}
+ Self::Params => {}
+ Self::LetBinding => {}
+ Self::SetRule => {}
+ Self::ShowRule => {}
+ Self::WrapRule => {}
+ Self::Conditional => {}
+ Self::WhileLoop => {}
+ Self::ForLoop => {}
Self::ForPattern => {}
- Self::ImportExpr => {}
+ Self::ModuleImport => {}
Self::ImportItems => {}
- Self::IncludeExpr => {}
- Self::BreakExpr => {}
- Self::ContinueExpr => {}
- Self::ReturnExpr => {}
+ Self::ModuleInclude => {}
+ Self::BreakStmt => {}
+ Self::ContinueStmt => {}
+ Self::ReturnStmt => {}
Self::Error(pos, msg) => (pos, msg).hash(state),
}
}