diff --git a/src/diag.rs b/src/diag.rs index ed4d47561..f4725f00a 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -9,7 +9,7 @@ use std::string::FromUtf8Error; use comemo::Tracked; -use crate::syntax::{Span, Spanned}; +use crate::syntax::{ErrorPos, Span, Spanned}; use crate::util::EcoString; use crate::World; @@ -83,17 +83,6 @@ impl SourceError { } } -/// Where in a node an error should be annotated, -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum ErrorPos { - /// Over the full width of the node. - Full, - /// At the start of the node. - Start, - /// At the end of the node. - End, -} - /// A part of an error's [trace](SourceError::trace). #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Tracepoint { diff --git a/src/eval/capture.rs b/src/eval/capture.rs index b7570fe9d..289d31e18 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -1,6 +1,6 @@ use super::{Scope, Scopes, Value}; -use crate::syntax::ast::{ClosureParam, Expr, Ident, Imports, TypedNode}; -use crate::syntax::SyntaxNode; +use crate::syntax::ast::TypedNode; +use crate::syntax::{ast, SyntaxNode}; /// A visitor that captures variable slots. pub struct CapturesVisitor<'a> { @@ -25,12 +25,12 @@ impl<'a> CapturesVisitor<'a> { } /// Bind a new internal variable. - pub fn bind(&mut self, ident: Ident) { + pub fn bind(&mut self, ident: ast::Ident) { self.internal.top.define(ident.take(), Value::None); } /// Capture a variable if it isn't internal. - pub fn capture(&mut self, ident: Ident) { + pub fn capture(&mut self, ident: ast::Ident) { if self.internal.get(&ident).is_err() { if let Ok(value) = self.external.get(&ident) { self.captures.define_captured(ident.take(), value.clone()); @@ -45,10 +45,10 @@ impl<'a> CapturesVisitor<'a> { // Identifiers that shouldn't count as captures because they // actually bind a new name are handled below (individually through // the expressions that contain them). - Some(Expr::Ident(ident)) => self.capture(ident), + Some(ast::Expr::Ident(ident)) => self.capture(ident), // Code and content blocks create a scope. - Some(Expr::Code(_) | Expr::Content(_)) => { + Some(ast::Expr::Code(_) | ast::Expr::Content(_)) => { self.internal.enter(); for child in node.children() { self.visit(child); @@ -59,18 +59,18 @@ impl<'a> CapturesVisitor<'a> { // A closure contains parameter bindings, which are bound before the // body is evaluated. Care must be taken so that the default values // of named parameters cannot access previous parameter bindings. - Some(Expr::Closure(expr)) => { + Some(ast::Expr::Closure(expr)) => { for param in expr.params() { - if let ClosureParam::Named(named) = param { + if let ast::Param::Named(named) = param { self.visit(named.expr().as_untyped()); } } for param in expr.params() { match param { - ClosureParam::Pos(ident) => self.bind(ident), - ClosureParam::Named(named) => self.bind(named.name()), - ClosureParam::Sink(ident) => self.bind(ident), + ast::Param::Pos(ident) => self.bind(ident), + ast::Param::Named(named) => self.bind(named.name()), + ast::Param::Sink(ident) => self.bind(ident), } } @@ -79,7 +79,7 @@ impl<'a> CapturesVisitor<'a> { // A let expression contains a binding, but that binding is only // active after the body is evaluated. - Some(Expr::Let(expr)) => { + Some(ast::Expr::Let(expr)) => { if let Some(init) = expr.init() { self.visit(init.as_untyped()); } @@ -88,7 +88,7 @@ impl<'a> CapturesVisitor<'a> { // A show rule contains a binding, but that binding is only active // after the target has been evaluated. - Some(Expr::Show(show)) => { + Some(ast::Expr::Show(show)) => { self.visit(show.pattern().as_untyped()); if let Some(binding) = show.binding() { self.bind(binding); @@ -99,7 +99,7 @@ impl<'a> CapturesVisitor<'a> { // A for loop contains one or two bindings in its pattern. These are // active after the iterable is evaluated but before the body is // evaluated. - Some(Expr::For(expr)) => { + Some(ast::Expr::For(expr)) => { self.visit(expr.iter().as_untyped()); let pattern = expr.pattern(); if let Some(key) = pattern.key() { @@ -111,9 +111,9 @@ impl<'a> CapturesVisitor<'a> { // An import contains items, but these are active only after the // path is evaluated. - Some(Expr::Import(expr)) => { + Some(ast::Expr::Import(expr)) => { self.visit(expr.path().as_untyped()); - if let Imports::Items(items) = expr.imports() { + if let ast::Imports::Items(items) = expr.imports() { for item in items { self.bind(item); } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 26b4130bc..0a3d6545a 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -43,8 +43,8 @@ use crate::geom::{Angle, Em, Fraction, Length, Ratio}; use crate::library; use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap}; use crate::source::SourceId; -use crate::syntax::ast::*; -use crate::syntax::{Span, Spanned}; +use crate::syntax::ast::TypedNode; +use crate::syntax::{ast, Span, Spanned, Unit}; use crate::util::EcoString; use crate::World; @@ -133,25 +133,25 @@ pub trait Eval { fn eval(&self, vm: &mut Vm) -> SourceResult; } -impl Eval for MarkupNode { +impl Eval for ast::Markup { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - 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, + nodes: &mut impl Iterator, ) -> SourceResult { 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 { - 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 { + 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 { + Ok(Content::Linebreak { justify: false }) + } +} + +impl Eval for ast::Text { + type Output = Content; + + fn eval(&self, _: &mut Vm) -> SourceResult { + Ok(Content::Text(self.get().clone())) + } +} + +impl Eval for ast::Escape { + type Output = Content; + + fn eval(&self, _: &mut Vm) -> SourceResult { + Ok(Content::Text(self.get().into())) + } +} + +impl Eval for ast::Shorthand { + type Output = Content; + + fn eval(&self, _: &mut Vm) -> SourceResult { + Ok(Content::Text(self.get().into())) + } +} + +impl Eval for ast::SmartQuote { + type Output = Content; + + fn eval(&self, _: &mut Vm) -> SourceResult { + Ok(Content::Quote { double: self.double() }) + } +} + +impl Eval for ast::Strong { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -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 { @@ -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 { + 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 { 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 { - let nodes = - self.items().map(|node| node.eval(vm)).collect::>()?; + let nodes = self + .children() + .map(|node| node.eval(vm)) + .collect::>()?; 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 { 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::>()?, ), @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { + Ok(Content::Empty) + } +} + +impl Eval for ast::Ref { + type Output = Content; + + fn eval(&self, _: &mut Vm) -> SourceResult { + Ok(Content::show(library::structure::RefNode( + self.get().clone(), + ))) + } +} + +impl Eval for ast::Expr { type Output = Value; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -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 { 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 { @@ -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 { @@ -462,14 +544,17 @@ impl Eval for CodeBlock { } /// Evaluate a stream of expressions. -fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator) -> SourceResult { +fn eval_code( + vm: &mut Vm, + exprs: &mut impl Iterator, +) -> SourceResult { 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) -> 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) -> 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) -> SourceResul Ok(output) } -impl Eval for ContentBlock { +impl Eval for ast::ContentBlock { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -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 { @@ -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 { @@ -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 { @@ -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 { 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 { 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 { - 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 { @@ -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 { @@ -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 { @@ -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 { // 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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -1071,7 +1156,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult { Ok(module) } -impl Eval for BreakExpr { +impl Eval for ast::BreakStmt { type Output = Value; fn eval(&self, vm: &mut Vm) -> SourceResult { @@ -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 { @@ -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 { @@ -1111,29 +1196,29 @@ pub trait Access { fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>; } -impl Access for Expr { +impl Access for ast::Expr { fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { match self { - Expr::Ident(v) => v.access(vm), - Expr::FieldAccess(v) => v.access(vm), - Expr::FuncCall(v) => v.access(vm), + Self::Ident(v) => v.access(vm), + Self::FieldAccess(v) => v.access(vm), + Self::FuncCall(v) => v.access(vm), _ => bail!(self.span(), "cannot mutate a temporary value"), } } } -impl Access for Ident { +impl Access for ast::Ident { fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { vm.scopes.get_mut(self).at(self.span()) } } -impl Access for FieldAccess { +impl Access for ast::FieldAccess { fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { - Ok(match self.object().access(vm)? { + Ok(match self.target().access(vm)? { Value::Dict(dict) => dict.get_mut(self.field().take().into()), v => bail!( - self.object().span(), + self.target().span(), "expected dictionary, found {}", v.type_name(), ), @@ -1141,7 +1226,7 @@ impl Access for FieldAccess { } } -impl Access for FuncCall { +impl Access for ast::FuncCall { fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { let args = self.args().eval(vm)?; Ok(match self.callee().access(vm)? { diff --git a/src/parse/incremental.rs b/src/parse/incremental.rs index e0be9b6d3..4651a7843 100644 --- a/src/parse/incremental.rs +++ b/src/parse/incremental.rs @@ -389,16 +389,12 @@ fn is_bounded(kind: &NodeKind) -> bool { match kind { NodeKind::CodeBlock | NodeKind::ContentBlock - | NodeKind::Backslash - | NodeKind::Tilde - | NodeKind::HyphQuest - | NodeKind::Hyph2 - | NodeKind::Hyph3 - | NodeKind::Dot3 - | NodeKind::Quote { .. } + | NodeKind::Linebreak + | NodeKind::SmartQuote { .. } | NodeKind::BlockComment | NodeKind::Space { .. } - | NodeKind::Escape(_) => true, + | NodeKind::Escape(_) + | NodeKind::Shorthand(_) => true, _ => false, } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 4f42442f1..ac8ec6eb8 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -11,9 +11,8 @@ pub use tokens::*; use std::collections::HashSet; -use crate::diag::ErrorPos; use crate::syntax::ast::{Assoc, BinOp, UnOp}; -use crate::syntax::{NodeKind, SyntaxNode}; +use crate::syntax::{ErrorPos, NodeKind, SyntaxNode}; use crate::util::EcoString; /// Parse a source file. @@ -240,14 +239,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { // Text and markup. NodeKind::Text(_) - | NodeKind::Backslash - | NodeKind::Tilde - | NodeKind::HyphQuest - | NodeKind::Hyph2 - | NodeKind::Hyph3 - | NodeKind::Dot3 - | NodeKind::Quote { .. } + | NodeKind::Linebreak + | NodeKind::SmartQuote { .. } | NodeKind::Escape(_) + | NodeKind::Shorthand(_) | NodeKind::Link(_) | NodeKind::Raw(_) | NodeKind::Label(_) @@ -475,15 +470,15 @@ fn math_primary(p: &mut Parser) { match token { // Spaces, atoms and expressions. NodeKind::Space { .. } - | NodeKind::Backslash + | NodeKind::Linebreak | NodeKind::Escape(_) | NodeKind::Atom(_) | NodeKind::Ident(_) => p.eat(), // Groups. - NodeKind::LeftParen => group(p, Group::Paren), - NodeKind::LeftBracket => group(p, Group::Bracket), - NodeKind::LeftBrace => group(p, Group::Brace), + NodeKind::LeftParen => group(p, Group::Paren, '(', ')'), + NodeKind::LeftBracket => group(p, Group::Bracket, '[', ']'), + NodeKind::LeftBrace => group(p, Group::Brace, '{', '}'), // Alignment indactor. NodeKind::Amp => align(p), @@ -493,13 +488,17 @@ fn math_primary(p: &mut Parser) { } /// Parse grouped math. -fn group(p: &mut Parser, group: Group) { +fn group(p: &mut Parser, group: Group, l: char, r: char) { p.perform(NodeKind::Math, |p| { + let marker = p.marker(); p.start_group(group); + marker.convert(p, NodeKind::Atom(l.into())); while !p.eof() { math_node(p); } + let marker = p.marker(); p.end_group(); + marker.convert(p, NodeKind::Atom(r.into())); }) } @@ -532,7 +531,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { p.eat(); let prec = op.precedence(); expr_prec(p, atomic, prec)?; - marker.end(p, NodeKind::UnaryExpr); + marker.end(p, NodeKind::Unary); } _ => primary(p, atomic)?, }; @@ -585,7 +584,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { Assoc::Right => {} } - marker.perform(p, NodeKind::BinaryExpr, |p| expr_prec(p, atomic, prec))?; + marker.perform(p, NodeKind::Binary, |p| expr_prec(p, atomic, prec))?; } Ok(()) @@ -605,9 +604,9 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { // Arrow means this is a closure's lone parameter. if !atomic && p.at(NodeKind::Arrow) { - marker.end(p, NodeKind::ClosureParams); + marker.end(p, NodeKind::Params); p.assert(NodeKind::Arrow); - marker.perform(p, NodeKind::ClosureExpr, expr) + marker.perform(p, NodeKind::Closure, expr) } else { Ok(()) } @@ -703,12 +702,12 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult { if !atomic && p.at(NodeKind::Arrow) { params(p, marker); p.assert(NodeKind::Arrow); - return marker.perform(p, NodeKind::ClosureExpr, expr); + return marker.perform(p, NodeKind::Closure, expr); } // Transform into the identified collection. match kind { - CollectionKind::Group => marker.end(p, NodeKind::GroupExpr), + CollectionKind::Group => marker.end(p, NodeKind::Parenthesized), CollectionKind::Positional => array(p, marker), CollectionKind::Named => dict(p, marker), } @@ -833,7 +832,7 @@ fn array(p: &mut Parser, marker: Marker) { NodeKind::Named | NodeKind::Keyed => Err("expected expression"), _ => Ok(()), }); - marker.end(p, NodeKind::ArrayExpr); + marker.end(p, NodeKind::Array); } /// Convert a collection into a dictionary, producing errors for anything other @@ -855,7 +854,7 @@ fn dict(p: &mut Parser, marker: Marker) { NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()), _ => Err("expected named or keyed pair"), }); - marker.end(p, NodeKind::DictExpr); + marker.end(p, NodeKind::Dict); } /// Convert a collection into a list of parameters, producing errors for @@ -874,7 +873,7 @@ fn params(p: &mut Parser, marker: Marker) { } _ => Err("expected identifier, named pair or argument sink"), }); - marker.end(p, NodeKind::ClosureParams); + marker.end(p, NodeKind::Params); } /// Parse a code block: `{...}`. @@ -920,7 +919,7 @@ fn args(p: &mut Parser) -> ParseResult { } } - p.perform(NodeKind::CallArgs, |p| { + p.perform(NodeKind::Args, |p| { if p.at(NodeKind::LeftParen) { let marker = p.marker(); p.start_group(Group::Paren); @@ -953,7 +952,7 @@ fn args(p: &mut Parser) -> ParseResult { /// Parse a let expression. fn let_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::LetExpr, |p| { + p.perform(NodeKind::LetBinding, |p| { p.assert(NodeKind::Let); let marker = p.marker(); @@ -978,7 +977,7 @@ fn let_expr(p: &mut Parser) -> ParseResult { // Rewrite into a closure expression if it's a function definition. if has_params { - marker.end(p, NodeKind::ClosureExpr); + marker.end(p, NodeKind::Closure); } Ok(()) @@ -987,7 +986,7 @@ fn let_expr(p: &mut Parser) -> ParseResult { /// Parse a set expression. fn set_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::SetExpr, |p| { + p.perform(NodeKind::SetRule, |p| { p.assert(NodeKind::Set); ident(p)?; args(p) @@ -996,7 +995,7 @@ fn set_expr(p: &mut Parser) -> ParseResult { /// Parse a show expression. fn show_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ShowExpr, |p| { + p.perform(NodeKind::ShowRule, |p| { p.assert(NodeKind::Show); let marker = p.marker(); expr(p)?; @@ -1014,7 +1013,7 @@ fn show_expr(p: &mut Parser) -> ParseResult { /// Parse a wrap expression. fn wrap_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::WrapExpr, |p| { + p.perform(NodeKind::WrapRule, |p| { p.assert(NodeKind::Wrap); ident(p)?; p.expect(NodeKind::In)?; @@ -1024,7 +1023,7 @@ fn wrap_expr(p: &mut Parser) -> ParseResult { /// Parse an if-else expresion. fn if_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::IfExpr, |p| { + p.perform(NodeKind::Conditional, |p| { p.assert(NodeKind::If); expr(p)?; @@ -1044,7 +1043,7 @@ fn if_expr(p: &mut Parser) -> ParseResult { /// Parse a while expresion. fn while_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::WhileExpr, |p| { + p.perform(NodeKind::WhileLoop, |p| { p.assert(NodeKind::While); expr(p)?; body(p) @@ -1053,7 +1052,7 @@ fn while_expr(p: &mut Parser) -> ParseResult { /// Parse a for-in expression. fn for_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ForExpr, |p| { + p.perform(NodeKind::ForLoop, |p| { p.assert(NodeKind::For); for_pattern(p)?; p.expect(NodeKind::In)?; @@ -1075,7 +1074,7 @@ fn for_pattern(p: &mut Parser) -> ParseResult { /// Parse an import expression. fn import_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ImportExpr, |p| { + p.perform(NodeKind::ModuleImport, |p| { p.assert(NodeKind::Import); if !p.eat_if(NodeKind::Star) { @@ -1103,7 +1102,7 @@ fn import_expr(p: &mut Parser) -> ParseResult { /// Parse an include expression. fn include_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::IncludeExpr, |p| { + p.perform(NodeKind::ModuleInclude, |p| { p.assert(NodeKind::Include); expr(p) }) @@ -1111,7 +1110,7 @@ fn include_expr(p: &mut Parser) -> ParseResult { /// Parse a break expression. fn break_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::BreakExpr, |p| { + p.perform(NodeKind::BreakStmt, |p| { p.assert(NodeKind::Break); Ok(()) }) @@ -1119,7 +1118,7 @@ fn break_expr(p: &mut Parser) -> ParseResult { /// Parse a continue expression. fn continue_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ContinueExpr, |p| { + p.perform(NodeKind::ContinueStmt, |p| { p.assert(NodeKind::Continue); Ok(()) }) @@ -1127,7 +1126,7 @@ fn continue_expr(p: &mut Parser) -> ParseResult { /// Parse a return expression. fn return_expr(p: &mut Parser) -> ParseResult { - p.perform(NodeKind::ReturnExpr, |p| { + p.perform(NodeKind::ReturnStmt, |p| { p.assert(NodeKind::Return); if !p.at(NodeKind::Comma) && !p.eof() { expr(p)?; diff --git a/src/parse/parser.rs b/src/parse/parser.rs index fe04f29ea..3dbb7d50c 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -3,8 +3,7 @@ use std::mem; use std::ops::Range; use super::{TokenMode, Tokens}; -use crate::diag::ErrorPos; -use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode}; +use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode}; use crate::util::EcoString; /// A convenient token-based parser. diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs index d68282c0a..9fde0cf40 100644 --- a/src/parse/resolve.rs +++ b/src/parse/resolve.rs @@ -1,7 +1,7 @@ use unscanny::Scanner; use super::{is_ident, is_newline}; -use crate::syntax::ast::RawNode; +use crate::syntax::RawKind; use crate::util::EcoString; /// Resolve all escape sequences in a string. @@ -46,17 +46,17 @@ pub fn resolve_hex(sequence: &str) -> Option { } /// Resolve the language tag and trim the raw text. -pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawNode { +pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawKind { if backticks > 1 { let (tag, inner) = split_at_lang_tag(text); let (text, block) = trim_and_split_raw(column, inner); - RawNode { + RawKind { lang: is_ident(tag).then(|| tag.into()), text: text.into(), block, } } else { - RawNode { + RawKind { lang: None, text: split_lines(text).join("\n").into(), block: false, diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 7cba18237..73c64d1e1 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -4,10 +4,8 @@ use unicode_xid::UnicodeXID; use unscanny::Scanner; use super::resolve::{resolve_hex, resolve_raw, resolve_string}; -use crate::diag::ErrorPos; use crate::geom::{AngleUnit, LengthUnit}; -use crate::syntax::ast::{RawNode, Unit}; -use crate::syntax::NodeKind; +use crate::syntax::{ErrorPos, NodeKind, RawKind, Unit}; use crate::util::EcoString; /// An iterator over the tokens of a string of source code. @@ -199,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| 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::() { - 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::() { + 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| 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 { @@ -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, &'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, &str, NodeKind)] = &[ - // Whitespace suffixes. - (' ', None, " ", Space(0)), - (' ', None, "\n", Space(1)), - (' ', None, "\r", Space(1)), - (' ', None, "\r\n", Space(1)), - // Letter suffixes. - ('a', Some(Markup), "hello", Text("hello")), - ('a', Some(Markup), "💚", Text("💚")), - ('a', Some(Code), "val", Ident("val")), - ('a', Some(Code), "α", Ident("α")), - ('a', Some(Code), "_", Ident("_")), - // Number suffixes. - ('1', Some(Code), "2", Int(2)), - ('1', Some(Code), ".2", Float(0.2)), - // Symbol suffixes. - ('/', None, "[", LeftBracket), - ('/', None, "//", LineComment), - ('/', None, "/**/", BlockComment), - ('/', Some(Markup), "*", Star), - ('/', Some(Markup), r"\\", Escape('\\')), - ('/', Some(Markup), "#let", Let), - ('/', Some(Code), "(", LeftParen), - ('/', Some(Code), ":", Colon), - ('/', Some(Code), "+=", PlusEq), - ]; - // Test with each applicable suffix. - for &(block, mode, suffix, ref token) in suffixes { + for (block, mode, suffix, ref token) in suffixes() { let text = $text; #[allow(unused_variables)] let blocks = BLOCKS; @@ -872,14 +875,14 @@ mod tests { t!(Markup[" /"]: "reha-world" => Text("reha-world")); // Test code symbols in text. - t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, Quote { double: true }, Text("b")); + t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, SmartQuote { double: true }, Text("b")); t!(Markup[" /"]: ";,|/+" => Text(";,|/+")); t!(Markup[" /"]: "=-a" => Eq, Minus, Text("a")); t!(Markup[" "]: "#123" => Text("#123")); // Test text ends. t!(Markup[""]: "hello " => Text("hello"), Space(0)); - t!(Markup[""]: "hello~" => Text("hello"), Tilde); + t!(Markup[""]: "hello~" => Text("hello"), Shorthand('\u{00A0}')); } #[test] @@ -924,10 +927,10 @@ mod tests { t!(Markup: "_" => Underscore); t!(Markup[""]: "===" => Eq, Eq, Eq); t!(Markup["a1/"]: "= " => Eq, Space(0)); - t!(Markup[" "]: r"\" => Backslash); - t!(Markup: "~" => Tilde); - t!(Markup["a1/"]: "-?" => HyphQuest); - t!(Markup["a "]: r"a--" => Text("a"), Hyph2); + t!(Markup[" "]: r"\" => Linebreak); + t!(Markup: "~" => Shorthand('\u{00A0}')); + t!(Markup["a1/"]: "-?" => Shorthand('\u{00AD}')); + t!(Markup["a "]: r"a--" => Text("a"), Shorthand('\u{2013}')); t!(Markup["a1/"]: "- " => Minus, Space(0)); t!(Markup[" "]: "+" => Plus); t!(Markup[" "]: "1." => EnumNumbering(1)); diff --git a/src/source.rs b/src/source.rs index 8e22c01d4..69e72d6bc 100644 --- a/src/source.rs +++ b/src/source.rs @@ -10,7 +10,7 @@ use unscanny::Scanner; use crate::diag::SourceResult; use crate::parse::{is_newline, parse, reparse}; -use crate::syntax::ast::MarkupNode; +use crate::syntax::ast::Markup; use crate::syntax::{Span, SyntaxNode}; use crate::util::{PathExt, StrExt}; @@ -66,7 +66,7 @@ impl Source { } /// The root node of the file's typed abstract syntax tree. - pub fn ast(&self) -> SourceResult { + pub fn ast(&self) -> SourceResult { let errors = self.root.errors(); if errors.is_empty() { Ok(self.root.cast().expect("root node must be markup")) diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index aa590da29..ecfa9a5b8 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -5,8 +5,7 @@ use std::num::NonZeroUsize; use std::ops::Deref; -use super::{NodeData, NodeKind, Span, SyntaxNode}; -use crate::geom::{AngleUnit, LengthUnit}; +use super::{NodeData, NodeKind, RawKind, Span, SyntaxNode, Unit}; use crate::util::EcoString; /// A typed AST node. @@ -25,10 +24,7 @@ pub trait TypedNode: Sized { macro_rules! node { ($(#[$attr:meta])* $name:ident) => { - node!{$(#[$attr])* $name: $name} - }; - ($(#[$attr:meta])* $name:ident: $variant:ident) => { - node!{$(#[$attr])* $name: NodeKind::$variant} + node!{ $(#[$attr])* $name: NodeKind::$name { .. } } }; ($(#[$attr:meta])* $name:ident: $variants:pat) => { #[derive(Debug, Clone, PartialEq, Hash)] @@ -54,254 +50,296 @@ macro_rules! node { node! { /// The syntactical root capable of representing a full parsed document. - MarkupNode: NodeKind::Markup { .. } + Markup } -impl MarkupNode { +impl Markup { /// The children. - pub fn items(&self) -> impl Iterator + '_ { + pub fn children(&self) -> impl Iterator + '_ { self.0.children().filter_map(SyntaxNode::cast) } } /// A single piece of markup. #[derive(Debug, Clone, PartialEq)] -pub enum MarkupItem { +pub enum MarkupNode { /// Whitespace containing less than two newlines. - Space, + Space(Space), /// A forced line break. - Linebreak, - /// A paragraph break: Two or more newlines. - Parbreak, + Linebreak(Linebreak), /// Plain text. - Text(EcoString), + Text(Text), + /// An escape sequence: `\#`, `\u{1F5FA}`. + Escape(Escape), + /// A shorthand for a unicode codepoint. For example, `~` for non-breaking + /// space or `-?` for a soft hyphen. + Shorthand(Shorthand), /// A smart quote: `'` or `"`. - Quote { double: bool }, - /// Strong content: `*Strong*`. - Strong(StrongNode), - /// Emphasized content: `_Emphasized_`. - Emph(EmphNode), - /// A hyperlink: `https://typst.org`. - Link(EcoString), + SmartQuote(SmartQuote), + /// Strong markup: `*Strong*`. + Strong(Strong), + /// Emphasized markup: `_Emphasized_`. + Emph(Emph), /// A raw block with optional syntax highlighting: `` `...` ``. - Raw(RawNode), - /// A math formula: `$x$`, `$ x^2 $`. - Math(MathNode), + Raw(Raw), + /// A hyperlink: `https://typst.org`. + Link(Link), + /// A label: `