More spans in AST

This commit is contained in:
Laurenz 2022-10-17 16:47:07 +02:00
parent 54b38c4790
commit 4fd031a256
12 changed files with 1119 additions and 866 deletions

View File

@ -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 {

View File

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

View File

@ -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)? {

View File

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

View File

@ -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)?;

View File

@ -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.

View File

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

View File

@ -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,24 +197,9 @@ impl<'s> Tokens<'s> {
'[' => NodeKind::LeftBracket,
']' => NodeKind::RightBracket,
// Escape sequences.
'\\' => self.backslash(),
// Single-char things.
'~' => NodeKind::Tilde,
'.' if self.s.eat_if("..") => NodeKind::Dot3,
'\'' => NodeKind::Quote { double: false },
'"' => NodeKind::Quote { double: true },
'*' if !self.in_word() => NodeKind::Star,
'_' if !self.in_word() => NodeKind::Underscore,
'$' => NodeKind::Dollar,
'=' => NodeKind::Eq,
'+' => NodeKind::Plus,
'/' => NodeKind::Slash,
':' => NodeKind::Colon,
// 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)
@ -226,6 +209,21 @@ impl<'s> Tokens<'s> {
'<' => self.label(),
'@' => self.reference(start),
// Escape sequences.
'\\' => self.backslash(),
// Single-char things.
'~' => 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,
'=' => NodeKind::Eq,
'+' => NodeKind::Plus,
'/' => NodeKind::Slash,
':' => NodeKind::Colon,
// 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));

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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