More spans in AST
This commit is contained in:
parent
54b38c4790
commit
4fd031a256
13
src/diag.rs
13
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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
371
src/eval/mod.rs
371
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)? {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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
@ -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",
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user