From 95866d5fc9ae89a23c5754193c7de5d4fe4873b1 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 7 Nov 2021 22:05:48 +0100 Subject: [PATCH] Tidy up AST --- src/eval/capture.rs | 80 +--------- src/eval/mod.rs | 33 ++-- src/parse/mod.rs | 75 ++++------ src/parse/parser.rs | 37 +++-- src/parse/resolve.rs | 14 +- src/parse/scanner.rs | 8 +- src/parse/tokens.rs | 19 ++- src/syntax/ast.rs | 350 +++++++++++++------------------------------ src/syntax/mod.rs | 309 ++++++++++++++++++++------------------ src/syntax/pretty.rs | 6 +- 10 files changed, 370 insertions(+), 561 deletions(-) diff --git a/src/eval/capture.rs b/src/eval/capture.rs index e46103c8b..786da36e7 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -1,93 +1,27 @@ use std::rc::Rc; -use super::{Scope, Scopes, Value}; -use crate::syntax::ast::{ClosureParam, Expr, Imports}; -use crate::syntax::RedRef; +use super::{Scope, Scopes}; +use crate::syntax::{NodeKind, RedRef}; /// A visitor that captures variable slots. pub struct CapturesVisitor<'a> { external: &'a Scopes<'a>, - internal: Scopes<'a>, captures: Scope, } impl<'a> CapturesVisitor<'a> { /// Create a new visitor for the given external scopes. pub fn new(external: &'a Scopes) -> Self { - Self { - external, - internal: Scopes::new(None), - captures: Scope::new(), - } + Self { external, captures: Scope::new() } } pub fn visit(&mut self, node: RedRef) { - let expr: Option = node.cast(); - - match expr.as_ref() { - Some(Expr::Let(expr)) => { - self.visit(expr.init_ref()); - let ident = expr.binding(); - self.internal.def_mut(ident.as_str(), Value::None); - } - Some(Expr::Closure(closure)) => { - for arg in closure.params() { - match arg { - ClosureParam::Pos(ident) | ClosureParam::Sink(ident) => { - self.internal.def_mut(ident.as_str(), Value::None); - } - ClosureParam::Named(name) => { - self.internal.def_mut(name.name().as_str(), Value::None); - } - } - } - self.visit(closure.body_ref()); - } - Some(Expr::For(forloop)) => { - let pattern = forloop.pattern(); - self.internal.def_mut(pattern.value().as_str(), Value::None); - - if let Some(key) = pattern.key() { - self.internal.def_mut(key.as_str(), Value::None); - } - self.visit(forloop.body_ref()); - } - Some(Expr::Import(import)) => { - if let Imports::Idents(idents) = import.imports() { - for ident in idents { - self.internal.def_mut(ident.as_str(), Value::None); - } + match node.kind() { + NodeKind::Ident(ident) => { + if let Some(slot) = self.external.get(ident.as_str()) { + self.captures.def_slot(ident.as_str(), Rc::clone(slot)); } } - Some(Expr::Ident(ident)) => { - if self.internal.get(ident.as_str()).is_none() { - if let Some(slot) = self.external.get(ident.as_str()) { - self.captures.def_slot(ident.as_str(), Rc::clone(slot)); - } - } - } - _ => {} - } - - match expr.as_ref() { - Some(Expr::Let(_)) | Some(Expr::For(_)) | Some(Expr::Closure(_)) => {} - - Some(Expr::Block(_)) => { - self.internal.enter(); - for child in node.children() { - self.visit(child); - } - self.internal.exit(); - } - - Some(Expr::Template(_)) => { - self.internal.enter(); - for child in node.children() { - self.visit(child); - } - self.internal.exit(); - } - _ => { for child in node.children() { self.visit(child); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 809209f46..7c984691a 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -219,7 +219,7 @@ impl Eval for Ident { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - match ctx.scopes.get(self) { + match ctx.scopes.get(&self.string) { Some(slot) => Ok(slot.borrow().clone()), None => bail!(self.span, "unknown variable"), } @@ -401,7 +401,7 @@ impl Eval for CallArgs { CallArg::Named(x) => { items.push(Arg { span, - name: Some((&x.name().string).into()), + name: Some(x.name().string.into()), value: Spanned::new(x.expr().eval(ctx)?, x.expr().span()), }); } @@ -443,12 +443,10 @@ impl Eval for ClosureExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - let name = self.name().as_ref().map(|name| name.string.clone()); - // Collect captured variables. let captured = { let mut visitor = CapturesVisitor::new(&ctx.scopes); - visitor.visit(self.underlying()); + visitor.visit(self.as_red()); visitor.finish() }; @@ -459,23 +457,24 @@ impl Eval for ClosureExpr { for param in self.params() { match param { ClosureParam::Pos(name) => { - params.push((name.string.clone(), None)); + params.push((name.string, None)); } - ClosureParam::Named(x) => { - params.push((x.name().string.clone(), Some(x.expr().eval(ctx)?))); + ClosureParam::Named(named) => { + params.push((named.name().string, Some(named.expr().eval(ctx)?))); } ClosureParam::Sink(name) => { if sink.is_some() { bail!(name.span, "only one argument sink is allowed"); } - sink = Some(name.string.clone()); + sink = Some(name.string); } } } // Clone the body expression so that we don't have a lifetime // dependence on the AST. - let body = self.body().clone(); + let name = self.name().map(|name| name.string); + let body = self.body(); // Define the actual function. let func = Function::new(name, move |ctx, args| { @@ -534,7 +533,7 @@ impl Eval for LetExpr { Some(expr) => expr.eval(ctx)?, None => Value::None, }; - ctx.scopes.def_mut(self.binding().as_str(), value); + ctx.scopes.def_mut(self.binding().string, value); Ok(Value::None) } } @@ -590,7 +589,7 @@ impl Eval for ForExpr { #[allow(unused_parens)] for ($($value),*) in $iter { - $(ctx.scopes.def_mut($binding.as_str(), $value);)* + $(ctx.scopes.def_mut(&$binding.string, $value);)* let value = self.body().eval(ctx)?; output = ops::join(output, value) @@ -637,16 +636,16 @@ impl Eval for ImportExpr { let file = ctx.import(&path, self.path().span())?; let module = &ctx.modules[&file]; - match &self.imports() { + match self.imports() { Imports::Wildcard => { for (var, slot) in module.scope.iter() { ctx.scopes.def_mut(var, slot.borrow().clone()); } } - Imports::Idents(idents) => { + Imports::Items(idents) => { for ident in idents { - if let Some(slot) = module.scope.get(&ident) { - ctx.scopes.def_mut(ident.as_str(), slot.borrow().clone()); + if let Some(slot) = module.scope.get(&ident.string) { + ctx.scopes.def_mut(ident.string, slot.borrow().clone()); } else { bail!(ident.span, "unresolved import"); } @@ -692,7 +691,7 @@ impl Access for Expr { impl Access for Ident { fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult> { - match ctx.scopes.get(self) { + match ctx.scopes.get(&self.string) { Some(slot) => match slot.try_borrow_mut() { Ok(guard) => Ok(guard), Err(_) => bail!(self.span, "cannot mutate a constant"), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index aa616fdf8..505482cad 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -13,7 +13,7 @@ pub use tokens::*; use std::rc::Rc; use crate::syntax::ast::{Associativity, BinOp, UnOp}; -use crate::syntax::{ErrorPosition, Green, GreenNode, NodeKind}; +use crate::syntax::{ErrorPos, Green, GreenNode, NodeKind}; /// Parse a source file. pub fn parse(source: &str) -> Rc { @@ -197,7 +197,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { p.peek_direct(), Some(NodeKind::LeftParen | NodeKind::LeftBracket) ) { - call(p, &marker)?; + call(p, marker)?; continue; } @@ -206,7 +206,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { } if p.at(&NodeKind::With) { - with_expr(p, &marker)?; + with_expr(p, marker)?; } let op = match p.peek().and_then(BinOp::from_token) { @@ -248,7 +248,7 @@ 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); - p.eat(); + p.eat_assert(&NodeKind::Arrow); marker.perform(p, NodeKind::Closure, expr) } else { Ok(()) @@ -326,14 +326,13 @@ fn parenthesized(p: &mut Parser) -> ParseResult { // Leading colon makes this a (empty) dictionary. if colon { - dict(p, &marker); + dict(p, marker); return Ok(()); } // Arrow means this is a closure's parameter list. if p.at(&NodeKind::Arrow) { - params(p, &marker, true); - marker.end(p, NodeKind::ClosureParams); + params(p, marker); p.eat_assert(&NodeKind::Arrow); return marker.perform(p, NodeKind::Closure, expr); } @@ -341,8 +340,8 @@ fn parenthesized(p: &mut Parser) -> ParseResult { // Transform into the identified collection. match kind { CollectionKind::Group => marker.end(p, NodeKind::Group), - CollectionKind::Positional => array(p, &marker), - CollectionKind::Named => dict(p, &marker), + CollectionKind::Positional => array(p, marker), + CollectionKind::Named => dict(p, marker), } Ok(()) @@ -384,7 +383,7 @@ fn collection(p: &mut Parser) -> (CollectionKind, usize) { items += 1; if let Some(marker) = missing_coma.take() { - marker.expected_at(p, "comma"); + marker.expected(p, "comma"); } if p.eof() { @@ -419,12 +418,11 @@ fn item(p: &mut Parser) -> ParseResult { if p.at(&NodeKind::Colon) { marker.perform(p, NodeKind::Named, |p| { - if matches!(marker.child_at(p).unwrap().kind(), &NodeKind::Ident(_)) { + if matches!(marker.peek(p).unwrap().kind(), &NodeKind::Ident(_)) { p.eat(); expr(p) } else { - let error = - NodeKind::Error(ErrorPosition::Full, "expected identifier".into()); + let error = NodeKind::Error(ErrorPos::Full, "expected identifier".into()); marker.end(p, error); p.eat(); expr(p).ok(); @@ -440,15 +438,10 @@ fn item(p: &mut Parser) -> ParseResult { /// Convert a collection into an array, producing errors for anything other than /// expressions. -fn array(p: &mut Parser, marker: &Marker) { +fn array(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { - NodeKind::Named => Err(( - ErrorPosition::Full, - "expected expression, found named pair".into(), - )), - NodeKind::Spread => { - Err((ErrorPosition::Full, "spreading is not allowed here".into())) - } + NodeKind::Named => Err("expected expression, found named pair"), + NodeKind::Spread => Err("spreading is not allowed here"), _ => Ok(()), }); marker.end(p, NodeKind::Array); @@ -456,25 +449,21 @@ fn array(p: &mut Parser, marker: &Marker) { /// Convert a collection into a dictionary, producing errors for anything other /// than named pairs. -fn dict(p: &mut Parser, marker: &Marker) { +fn dict(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { + kind if kind.is_paren() => Ok(()), NodeKind::Named | NodeKind::Comma | NodeKind::Colon => Ok(()), - NodeKind::Spread => { - Err((ErrorPosition::Full, "spreading is not allowed here".into())) - } - _ if x.kind().is_paren() => Ok(()), - _ => Err(( - ErrorPosition::Full, - "expected named pair, found expression".into(), - )), + NodeKind::Spread => Err("spreading is not allowed here"), + _ => Err("expected named pair, found expression"), }); marker.end(p, NodeKind::Dict); } /// Convert a collection into a list of parameters, producing errors for /// anything other than identifiers, spread operations and named pairs. -fn params(p: &mut Parser, marker: &Marker, allow_parens: bool) { +fn params(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { + kind if kind.is_paren() => Ok(()), NodeKind::Named | NodeKind::Comma | NodeKind::Ident(_) => Ok(()), NodeKind::Spread if matches!( @@ -484,9 +473,9 @@ fn params(p: &mut Parser, marker: &Marker, allow_parens: bool) { { Ok(()) } - _ if allow_parens && x.kind().is_paren() => Ok(()), - _ => Err((ErrorPosition::Full, "expected identifier".into())), + _ => Err("expected identifier"), }); + marker.end(p, NodeKind::ClosureParams); } // Parse a template block: `[...]`. @@ -517,7 +506,7 @@ fn block(p: &mut Parser) { } /// Parse a function call. -fn call(p: &mut Parser, callee: &Marker) -> ParseResult { +fn call(p: &mut Parser, callee: Marker) -> ParseResult { callee.perform(p, NodeKind::Call, |p| match p.peek_direct() { Some(NodeKind::LeftParen | NodeKind::LeftBracket) => { args(p, true); @@ -546,7 +535,7 @@ fn args(p: &mut Parser, allow_template: bool) { } /// Parse a with expression. -fn with_expr(p: &mut Parser, marker: &Marker) -> ParseResult { +fn with_expr(p: &mut Parser, marker: Marker) -> ParseResult { marker.perform(p, NodeKind::WithExpr, |p| { p.eat_assert(&NodeKind::With); @@ -569,18 +558,16 @@ fn let_expr(p: &mut Parser) -> ParseResult { ident(p)?; if p.at(&NodeKind::With) { - with_expr(p, &marker)?; + with_expr(p, marker)?; } else { // If a parenthesis follows, this is a function definition. let has_params = p.peek_direct() == Some(&NodeKind::LeftParen); if has_params { - p.perform(NodeKind::ClosureParams, |p| { - p.start_group(Group::Paren); - let marker = p.marker(); - collection(p); - params(p, &marker, true); - p.end_group(); - }); + let marker = p.marker(); + p.start_group(Group::Paren); + collection(p); + p.end_group(); + params(p, marker); } if p.eat_if(&NodeKind::Eq) { @@ -671,7 +658,7 @@ fn import_expr(p: &mut Parser) -> ParseResult { marker.filter_children(p, |n| match n.kind() { NodeKind::Ident(_) | NodeKind::Comma => Ok(()), - _ => Err((ErrorPosition::Full, "expected identifier".into())), + _ => Err("expected identifier"), }); }); }; diff --git a/src/parse/parser.rs b/src/parse/parser.rs index a30895ad5..5ebc2c17e 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,7 +1,7 @@ use std::mem; use super::{TokenMode, Tokens}; -use crate::syntax::{ErrorPosition, Green, GreenData, GreenNode, NodeKind}; +use crate::syntax::{ErrorPos, Green, GreenData, GreenNode, NodeKind}; use crate::util::EcoString; /// Allows parser methods to use the try operator. Not exposed as the parser @@ -131,11 +131,9 @@ impl<'s> Parser<'s> { /// Eat the current token, but change its type. pub fn convert(&mut self, kind: NodeKind) { - let idx = self.children.len(); + let marker = self.marker(); self.eat(); - if let Some(child) = self.children.get_mut(idx) { - child.set_kind(kind); - } + marker.convert(self, kind); } /// Whether the current token is of the given type. @@ -321,7 +319,7 @@ impl<'s> Parser<'s> { impl Parser<'_> { /// Push an error into the children list. pub fn push_error(&mut self, msg: impl Into) { - let error = NodeKind::Error(ErrorPosition::Full, msg.into()); + let error = NodeKind::Error(ErrorPos::Full, msg.into()); self.children.push(GreenData::new(error, 0).into()); } @@ -330,7 +328,7 @@ impl Parser<'_> { match self.peek() { Some(found) => { let msg = format!("unexpected {}", found); - let error = NodeKind::Error(ErrorPosition::Full, msg.into()); + let error = NodeKind::Error(ErrorPos::Full, msg.into()); self.perform(error, Self::eat); } None => self.push_error("unexpected end of file"), @@ -342,7 +340,7 @@ impl Parser<'_> { match self.peek() { Some(found) => { let msg = format!("expected {}, found {}", thing, found); - let error = NodeKind::Error(ErrorPosition::Full, msg.into()); + let error = NodeKind::Error(ErrorPos::Full, msg.into()); self.perform(error, Self::eat); } None => self.expected_at(thing), @@ -352,7 +350,7 @@ impl Parser<'_> { /// Add an error that the `thing` was expected at the end of the last /// non-trivia token. pub fn expected_at(&mut self, thing: &str) { - Marker(self.trivia_start()).expected_at(self, thing); + Marker(self.trivia_start()).expected(self, thing); } } @@ -384,15 +382,15 @@ impl Marker { /// Wrap all children that do not fulfill the predicate in error nodes. pub fn filter_children(self, p: &mut Parser, f: F) where - F: Fn(&Green) -> Result<(), (ErrorPosition, EcoString)>, + F: Fn(&Green) -> Result<(), &'static str>, { for child in &mut p.children[self.0 ..] { if (p.tokens.mode() == TokenMode::Markup || !Parser::is_trivia_ext(child.kind(), false)) && !child.kind().is_error() { - if let Err((pos, msg)) = f(child) { - let error = NodeKind::Error(pos, msg); + if let Err(msg) = f(child) { + let error = NodeKind::Error(ErrorPos::Full, msg.into()); let inner = mem::take(child); *child = GreenNode::with_child(error, inner).into(); } @@ -401,16 +399,23 @@ impl Marker { } /// Insert an error message that `what` was expected at the marker position. - pub fn expected_at(self, p: &mut Parser, what: &str) { + pub fn expected(self, p: &mut Parser, what: &str) { let msg = format!("expected {}", what); - let error = NodeKind::Error(ErrorPosition::Full, msg.into()); + let error = NodeKind::Error(ErrorPos::Full, msg.into()); p.children.insert(self.0, GreenData::new(error, 0).into()); } - /// Return a reference to the child directly after the marker. - pub fn child_at<'a>(self, p: &'a Parser) -> Option<&'a Green> { + /// Peek at the child directly after the marker. + pub fn peek<'a>(self, p: &'a Parser) -> Option<&'a Green> { p.children.get(self.0) } + + /// Convert the child directly after marker. + pub fn convert(self, p: &mut Parser, kind: NodeKind) { + if let Some(child) = p.children.get_mut(self.0) { + child.convert(kind); + } + } } /// A logical group of tokens, e.g. `[...]`. diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs index b330dbd6a..6719f41df 100644 --- a/src/parse/resolve.rs +++ b/src/parse/resolve.rs @@ -1,4 +1,4 @@ -use super::{is_newline, Scanner}; +use super::{is_ident, is_newline, Scanner}; use crate::syntax::RawData; use crate::util::EcoString; @@ -51,7 +51,7 @@ pub fn resolve_raw(column: usize, backticks: u8, text: &str) -> RawData { let (tag, inner) = split_at_lang_tag(text); let (text, block) = trim_and_split_raw(column, inner); RawData { - lang: Some(tag.into()), + lang: is_ident(tag).then(|| tag.into()), text: text.into(), backticks, block, @@ -201,15 +201,15 @@ mod tests { // More than one backtick with lang tag. test(0, 2, "js alert()", Some("js"), "alert()", false); test(0, 3, "py quit(\n\n)", Some("py"), "quit(\n\n)", true); - test(0, 2, "♥", Some("♥"), "", false); + test(0, 2, "♥", None, "", false); // Trimming of whitespace (tested more thoroughly in separate test). - test(0, 2, " a", Some(""), "a", false); - test(0, 2, " a", Some(""), " a", false); - test(0, 2, " \na", Some(""), "a", true); + test(0, 2, " a", None, "a", false); + test(0, 2, " a", None, " a", false); + test(0, 2, " \na", None, "a", true); // Dedenting - test(2, 3, " def foo():\n bar()", Some(""), "def foo():\n bar()", true); + test(2, 3, " def foo():\n bar()", None, "def foo():\n bar()", true); } #[test] diff --git a/src/parse/scanner.rs b/src/parse/scanner.rs index 92a2333d4..ea06a2e06 100644 --- a/src/parse/scanner.rs +++ b/src/parse/scanner.rs @@ -182,7 +182,13 @@ pub fn is_newline(character: char) -> bool { ) } -/// Whether a string is a valid identifier. +/// Whether a string is a valid unicode identifier. +/// +/// In addition to what is specified in the [Unicode Standard][uax31], we allow: +/// - `_` as a starting character, +/// - `_` and `-` as continuing characters. +/// +/// [uax31]: http://www.unicode.org/reports/tr31/ #[inline] pub fn is_ident(string: &str) -> bool { let mut chars = string.chars(); diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 494a9f0b9..1523cd643 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -236,20 +236,19 @@ impl<'s> Tokens<'s> { 'u' if self.s.rest().starts_with("u{") => { self.s.eat_assert('u'); self.s.eat_assert('{'); - let sequence: EcoString = self.s.eat_while(|c| c.is_ascii_alphanumeric()).into(); - + let sequence = self.s.eat_while(|c| c.is_ascii_alphanumeric()); if self.s.eat_if('}') { if let Some(c) = resolve_hex(&sequence) { NodeKind::UnicodeEscape(c) } else { NodeKind::Error( - ErrorPosition::Full, + ErrorPos::Full, "invalid unicode escape sequence".into(), ) } } else { NodeKind::Error( - ErrorPosition::End, + ErrorPos::End, "expected closing brace".into(), ) } @@ -348,7 +347,7 @@ impl<'s> Tokens<'s> { let noun = if remaining == 1 { "backtick" } else { "backticks" }; NodeKind::Error( - ErrorPosition::End, + ErrorPos::End, if found == 0 { format!("expected {} {}", remaining, noun) } else { @@ -396,7 +395,7 @@ impl<'s> Tokens<'s> { })) } else { NodeKind::Error( - ErrorPosition::End, + ErrorPos::End, if !display || (!escaped && dollar) { "expected closing dollar sign" } else { @@ -487,7 +486,7 @@ impl<'s> Tokens<'s> { if self.s.eat_if('"') { NodeKind::Str(string) } else { - NodeKind::Error(ErrorPosition::End, "expected quote".into()) + NodeKind::Error(ErrorPos::End, "expected quote".into()) } } @@ -555,7 +554,7 @@ mod tests { use super::*; - use ErrorPosition::*; + use ErrorPos::*; use NodeKind::*; use Option::None; use TokenMode::{Code, Markup}; @@ -564,7 +563,7 @@ mod tests { NodeKind::UnicodeEscape(c) } - fn Error(pos: ErrorPosition, message: &str) -> NodeKind { + fn Error(pos: ErrorPos, message: &str) -> NodeKind { NodeKind::Error(pos, message.into()) } @@ -881,7 +880,7 @@ mod tests { // Test more backticks. t!(Markup: "``nope``" => Raw("", None, 1, false), Text("nope"), Raw("", None, 1, false)); - t!(Markup: "````🚀````" => Raw("", Some("🚀"), 4, false)); + t!(Markup: "````🚀````" => Raw("", None, 4, false)); t!(Markup[""]: "`````👩‍🚀````noend" => Error(End, "expected 5 backticks")); t!(Markup[""]: "````raw``````" => Raw("", Some("raw"), 4, false), Raw("", None, 1, false)); } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 1198d6b1e..dc71e2295 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,10 +1,7 @@ //! A typed layer over the red-green tree. -use std::ops::Deref; - use super::{NodeKind, RedNode, RedRef, Span}; use crate::geom::{AngularUnit, LengthUnit}; -use crate::parse::is_ident; use crate::util::EcoString; /// A typed AST node. @@ -40,7 +37,7 @@ macro_rules! node { } /// The underlying red node. - pub fn underlying(&self) -> RedRef { + pub fn as_red(&self) -> RedRef { self.0.as_ref() } } @@ -112,7 +109,7 @@ impl TypedNode for MarkupNode { #[derive(Debug, Clone, PartialEq)] pub struct RawNode { /// An optional identifier specifying the language to syntax-highlight in. - pub lang: Option, + pub lang: Option, /// The raw text, determined as the raw string between the backticks trimmed /// according to the above rules. pub text: EcoString, @@ -124,18 +121,11 @@ pub struct RawNode { impl TypedNode for RawNode { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Raw(raw) => { - let full = node.span(); - let start = full.start + raw.backticks as usize; - Some(Self { - block: raw.block, - lang: raw.lang.as_ref().and_then(|lang| { - let span = Span::new(full.source, start, start + lang.len()); - Ident::new(lang, span) - }), - text: raw.text.clone(), - }) - } + NodeKind::Raw(raw) => Some(Self { + block: raw.block, + lang: raw.lang.clone(), + text: raw.text.clone(), + }), _ => None, } } @@ -149,9 +139,7 @@ node! { impl HeadingNode { /// The contents of the heading. pub fn body(&self) -> Markup { - self.0 - .cast_first_child() - .expect("heading node is missing markup body") + self.0.cast_first_child().expect("heading is missing markup body") } /// The section depth (numer of equals signs). @@ -184,7 +172,7 @@ node! { impl EnumNode { /// The contents of the list item. pub fn body(&self) -> Markup { - self.0.cast_first_child().expect("enumeration node is missing body") + self.0.cast_first_child().expect("enum node is missing body") } /// The number, if any. @@ -195,7 +183,7 @@ impl EnumNode { NodeKind::EnumNumbering(num) => Some(num.clone()), _ => None, }) - .expect("enumeration node is missing number") + .expect("enum node is missing number") } } @@ -240,6 +228,31 @@ pub enum Expr { Include(IncludeExpr), } +impl TypedNode for Expr { + fn from_red(node: RedRef) -> Option { + match node.kind() { + NodeKind::Ident(_) => node.cast().map(Self::Ident), + NodeKind::Array => node.cast().map(Self::Array), + NodeKind::Dict => node.cast().map(Self::Dict), + NodeKind::Template => node.cast().map(Self::Template), + NodeKind::Group => node.cast().map(Self::Group), + NodeKind::Block => node.cast().map(Self::Block), + NodeKind::Unary => node.cast().map(Self::Unary), + NodeKind::Binary => node.cast().map(Self::Binary), + NodeKind::Call => node.cast().map(Self::Call), + NodeKind::Closure => node.cast().map(Self::Closure), + NodeKind::WithExpr => node.cast().map(Self::With), + NodeKind::LetExpr => node.cast().map(Self::Let), + NodeKind::IfExpr => node.cast().map(Self::If), + NodeKind::WhileExpr => node.cast().map(Self::While), + NodeKind::ForExpr => node.cast().map(Self::For), + NodeKind::ImportExpr => node.cast().map(Self::Import), + NodeKind::IncludeExpr => node.cast().map(Self::Include), + _ => node.cast().map(Self::Lit), + } + } +} + impl Expr { /// Whether the expression can be shortened in markup with a hashtag. pub fn has_short_form(&self) -> bool { @@ -280,31 +293,6 @@ impl Expr { } } -impl TypedNode for Expr { - fn from_red(node: RedRef) -> Option { - match node.kind() { - NodeKind::Ident(_) => node.cast().map(Self::Ident), - NodeKind::Array => node.cast().map(Self::Array), - NodeKind::Dict => node.cast().map(Self::Dict), - NodeKind::Template => node.cast().map(Self::Template), - NodeKind::Group => node.cast().map(Self::Group), - NodeKind::Block => node.cast().map(Self::Block), - NodeKind::Unary => node.cast().map(Self::Unary), - NodeKind::Binary => node.cast().map(Self::Binary), - NodeKind::Call => node.cast().map(Self::Call), - NodeKind::Closure => node.cast().map(Self::Closure), - NodeKind::WithExpr => node.cast().map(Self::With), - NodeKind::LetExpr => node.cast().map(Self::Let), - NodeKind::IfExpr => node.cast().map(Self::If), - NodeKind::WhileExpr => node.cast().map(Self::While), - NodeKind::ForExpr => node.cast().map(Self::For), - NodeKind::ImportExpr => node.cast().map(Self::Import), - NodeKind::IncludeExpr => node.cast().map(Self::Include), - _ => node.cast().map(Self::Lit), - } - } -} - /// A literal: `1`, `true`, ... #[derive(Debug, Clone, PartialEq)] pub enum Lit { @@ -335,17 +323,17 @@ pub enum Lit { impl TypedNode for Lit { fn from_red(node: RedRef) -> Option { - match node.kind() { + match *node.kind() { NodeKind::None => Some(Self::None(node.span())), NodeKind::Auto => Some(Self::Auto(node.span())), - NodeKind::Bool(b) => Some(Self::Bool(node.span(), *b)), - NodeKind::Int(i) => Some(Self::Int(node.span(), *i)), - NodeKind::Float(f) => Some(Self::Float(node.span(), *f)), - NodeKind::Length(f, unit) => Some(Self::Length(node.span(), *f, *unit)), - NodeKind::Angle(f, unit) => Some(Self::Angle(node.span(), *f, *unit)), - NodeKind::Percentage(f) => Some(Self::Percent(node.span(), *f)), - NodeKind::Fraction(f) => Some(Self::Fractional(node.span(), *f)), - NodeKind::Str(s) => Some(Self::Str(node.span(), s.clone())), + NodeKind::Bool(v) => Some(Self::Bool(node.span(), v)), + NodeKind::Int(v) => Some(Self::Int(node.span(), v)), + NodeKind::Float(v) => Some(Self::Float(node.span(), v)), + NodeKind::Length(v, unit) => Some(Self::Length(node.span(), v, unit)), + NodeKind::Angle(v, unit) => Some(Self::Angle(node.span(), v, unit)), + NodeKind::Percentage(v) => Some(Self::Percent(node.span(), v)), + NodeKind::Fraction(v) => Some(Self::Fractional(node.span(), v)), + NodeKind::Str(ref v) => Some(Self::Str(node.span(), v.clone())), _ => None, } } @@ -354,17 +342,17 @@ impl TypedNode for Lit { impl Lit { /// The source code location. pub fn span(&self) -> Span { - match self { - Self::None(span) => *span, - Self::Auto(span) => *span, - Self::Bool(span, _) => *span, - Self::Int(span, _) => *span, - Self::Float(span, _) => *span, - Self::Length(span, _, _) => *span, - Self::Angle(span, _, _) => *span, - Self::Percent(span, _) => *span, - Self::Fractional(span, _) => *span, - Self::Str(span, _) => *span, + match *self { + Self::None(span) => span, + Self::Auto(span) => span, + Self::Bool(span, _) => span, + Self::Int(span, _) => span, + Self::Float(span, _) => span, + Self::Length(span, _, _) => span, + Self::Angle(span, _, _) => span, + Self::Percent(span, _) => span, + Self::Fractional(span, _) => span, + Self::Str(span, _) => span, } } } @@ -401,16 +389,12 @@ node! { impl Named { /// The name: `pattern`. pub fn name(&self) -> Ident { - self.0.cast_first_child().expect("named pair is missing name ident") + self.0.cast_first_child().expect("named pair is missing name") } /// The right-hand side of the pair: `dashed`. pub fn expr(&self) -> Expr { - self.0 - .children() - .filter_map(RedRef::cast) - .nth(1) - .expect("named pair is missing expression") + self.0.cast_last_child().expect("named pair is missing expression") } } @@ -422,9 +406,7 @@ node! { impl TemplateExpr { /// The contents of the template. pub fn body(&self) -> Markup { - self.0 - .cast_first_child() - .expect("template expression is missing body") + self.0.cast_first_child().expect("template is missing body") } } @@ -436,9 +418,7 @@ node! { impl GroupExpr { /// The wrapped expression. pub fn expr(&self) -> Expr { - self.0 - .cast_first_child() - .expect("group expression is missing expression") + self.0.cast_first_child().expect("group is missing expression") } } @@ -469,9 +449,7 @@ impl UnaryExpr { /// The expression to operator on: `x`. pub fn expr(&self) -> Expr { - self.0 - .cast_first_child() - .expect("unary expression is missing expression") + self.0.cast_last_child().expect("unary expression is missing child") } } @@ -506,7 +484,7 @@ impl UnOp { /// The precedence of this operator. pub fn precedence(self) -> usize { match self { - Self::Pos | Self::Neg => 8, + Self::Pos | Self::Neg => 7, Self::Not => 4, } } @@ -544,9 +522,7 @@ impl BinaryExpr { /// The right-hand side of the operation: `b`. pub fn rhs(&self) -> Expr { self.0 - .children() - .filter_map(RedRef::cast) - .nth(1) + .cast_last_child() .expect("binary expression is missing right-hand side") } } @@ -701,14 +677,12 @@ node! { impl CallExpr { /// The function to call. pub fn callee(&self) -> Expr { - self.0.cast_first_child().expect("call expression is missing callee") + self.0.cast_first_child().expect("call is missing callee") } /// The arguments to the function. pub fn args(&self) -> CallArgs { - self.0 - .cast_first_child() - .expect("call expression is missing argument list") + self.0.cast_last_child().expect("call is missing argument list") } } @@ -738,14 +712,9 @@ pub enum CallArg { impl TypedNode for CallArg { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Named => Some(CallArg::Named( - node.cast().expect("named call argument is missing name"), - )), - NodeKind::Spread => Some(CallArg::Spread( - node.cast_first_child() - .expect("call argument sink is missing expression"), - )), - _ => Some(CallArg::Pos(node.cast()?)), + NodeKind::Named => node.cast().map(CallArg::Named), + NodeKind::Spread => node.cast_first_child().map(CallArg::Spread), + _ => node.cast().map(CallArg::Pos), } } } @@ -754,8 +723,8 @@ impl CallArg { /// The name of this argument. pub fn span(&self) -> Span { match self { - Self::Named(named) => named.span(), Self::Pos(expr) => expr.span(), + Self::Named(named) => named.span(), Self::Spread(expr) => expr.span(), } } @@ -771,8 +740,6 @@ impl ClosureExpr { /// /// This only exists if you use the function syntax sugar: `let f(x) = y`. pub fn name(&self) -> Option { - // `first_convert_child` does not work here because of the Option in the - // Result. self.0.cast_first_child() } @@ -788,22 +755,11 @@ impl ClosureExpr { /// The body of the closure. pub fn body(&self) -> Expr { - // The filtering for the NodeKind is necessary here because otherwise, - // `first_convert_child` will use the Ident if present. self.0.cast_last_child().expect("closure is missing body") } - - /// The red node reference of the body of the closure. - pub fn body_ref(&self) -> RedRef { - self.0 - .children() - .filter(|x| x.cast::().is_some()) - .last() - .unwrap() - } } -/// An parameter to a closure. +/// A parameter to a closure. #[derive(Debug, Clone, PartialEq)] pub enum ClosureParam { /// A positional parameter: `x`. @@ -817,17 +773,10 @@ pub enum ClosureParam { impl TypedNode for ClosureParam { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(id) => { - Some(ClosureParam::Pos(Ident::new_unchecked(id, node.span()))) - } - NodeKind::Named => Some(ClosureParam::Named( - node.cast().expect("named closure parameter is missing name"), - )), - NodeKind::Spread => Some(ClosureParam::Sink( - node.cast_first_child() - .expect("closure parameter sink is missing identifier"), - )), - _ => Some(ClosureParam::Pos(node.cast()?)), + NodeKind::Ident(_) => node.cast().map(ClosureParam::Pos), + NodeKind::Named => node.cast().map(ClosureParam::Named), + NodeKind::Spread => node.cast_first_child().map(ClosureParam::Sink), + _ => None, } } } @@ -840,9 +789,7 @@ node! { impl WithExpr { /// The function to apply the arguments to. pub fn callee(&self) -> Expr { - self.0 - .cast_first_child() - .expect("with expression is missing callee expression") + self.0.cast_first_child().expect("with expression is missing callee") } /// The arguments to apply to the function. @@ -861,17 +808,16 @@ node! { impl LetExpr { /// The binding to assign to. pub fn binding(&self) -> Ident { - if let Some(c) = self.0.cast_first_child() { - c - } else if let Some(w) = self.0.typed_child(&NodeKind::WithExpr) { - // Can't do an `first_convert_child` here because the WithExpr's - // callee has to be an identifier. - w.cast_first_child() - .expect("with expression is missing an identifier callee") - } else if let Some(Expr::Closure(c)) = self.0.cast_last_child() { - c.name().expect("closure is missing an identifier name") - } else { - panic!("let expression is missing either an identifier or a with expression") + match self.0.cast_first_child() { + Some(Expr::Ident(binding)) => binding, + Some(Expr::With(with)) => match with.callee() { + Expr::Ident(binding) => binding, + _ => panic!("let .. with callee must be identifier"), + }, + Some(Expr::Closure(closure)) => { + closure.name().expect("let-bound closure is missing name") + } + _ => panic!("let expression is missing binding"), } } @@ -880,24 +826,10 @@ impl LetExpr { if self.0.cast_first_child::().is_some() { self.0.children().filter_map(RedRef::cast).nth(1) } else { - Some( - self.0 - .cast_first_child() - .expect("let expression is missing a with expression"), - ) + // This is a let .. with expression. + self.0.cast_first_child() } } - - /// The red node reference for the expression the binding is initialized - /// with. - pub fn init_ref(&self) -> RedRef { - if self.0.cast_first_child::().is_some() { - self.0.children().filter(|x| x.cast::().is_some()).nth(1) - } else { - self.0.children().find(|x| x.cast::().is_some()) - } - .unwrap() - } } node! { @@ -908,16 +840,12 @@ node! { impl ImportExpr { /// The items to be imported. pub fn imports(&self) -> Imports { - self.0 - .cast_first_child() - .expect("import expression is missing import list") + self.0.cast_first_child().expect("import is missing items") } /// The location of the importable file. pub fn path(&self) -> Expr { - self.0 - .cast_first_child() - .expect("import expression is missing path expression") + self.0.cast_last_child().expect("import is missing path") } } @@ -926,8 +854,8 @@ impl ImportExpr { pub enum Imports { /// All items in the scope of the file should be imported. Wildcard, - /// The specified identifiers from the file should be imported. - Idents(Vec), + /// The specified items from the file should be imported. + Items(Vec), } impl TypedNode for Imports { @@ -935,8 +863,8 @@ impl TypedNode for Imports { match node.kind() { NodeKind::Star => Some(Imports::Wildcard), NodeKind::ImportItems => { - let idents = node.children().filter_map(RedRef::cast).collect(); - Some(Imports::Idents(idents)) + let items = node.children().filter_map(RedRef::cast).collect(); + Some(Imports::Items(items)) } _ => None, } @@ -951,9 +879,7 @@ node! { impl IncludeExpr { /// The location of the file to be included. pub fn path(&self) -> Expr { - self.0 - .cast_first_child() - .expect("include expression is missing path expression") + self.0.cast_last_child().expect("include is missing path") } } @@ -965,9 +891,7 @@ node! { impl IfExpr { /// The condition which selects the body to evaluate. pub fn condition(&self) -> Expr { - self.0 - .cast_first_child() - .expect("if expression is missing condition expression") + self.0.cast_first_child().expect("if expression is missing condition") } /// The expression to evaluate if the condition is true. @@ -976,7 +900,7 @@ impl IfExpr { .children() .filter_map(RedRef::cast) .nth(1) - .expect("if expression is missing if body") + .expect("if expression is missing body") } /// The expression to evaluate if the condition is false. @@ -993,18 +917,12 @@ node! { impl WhileExpr { /// The condition which selects whether to evaluate the body. pub fn condition(&self) -> Expr { - self.0 - .cast_first_child() - .expect("while loop expression is missing condition expression") + self.0.cast_first_child().expect("while loop is missing condition") } /// The expression to evaluate while the condition is true. pub fn body(&self) -> Expr { - self.0 - .children() - .filter_map(RedRef::cast) - .nth(1) - .expect("while loop expression is missing body") + self.0.cast_last_child().expect("while loop is missing body") } } @@ -1016,34 +934,17 @@ node! { impl ForExpr { /// The pattern to assign to. pub fn pattern(&self) -> ForPattern { - self.0 - .cast_first_child() - .expect("for loop expression is missing pattern") + self.0.cast_first_child().expect("for loop is missing pattern") } /// The expression to iterate over. pub fn iter(&self) -> Expr { - self.0 - .cast_first_child() - .expect("for loop expression is missing iterable expression") + self.0.cast_first_child().expect("for loop is missing iterable") } /// The expression to evaluate for each iteration. pub fn body(&self) -> Expr { - self.0 - .children() - .filter_map(RedRef::cast) - .last() - .expect("for loop expression is missing body") - } - - /// The red node reference for the expression to evaluate for each iteration. - pub fn body_ref(&self) -> RedRef { - self.0 - .children() - .filter(|x| x.cast::().is_some()) - .last() - .unwrap() + self.0.cast_last_child().expect("for loop is missing body") } } @@ -1062,19 +963,11 @@ impl ForPattern { /// The value part of the pattern. pub fn value(&self) -> Ident { - self.0 - .cast_last_child() - .expect("for-in loop pattern is missing value") + self.0.cast_last_child().expect("for loop pattern is missing value") } } -/// An unicode identifier with a few extra permissible characters. -/// -/// In addition to what is specified in the [Unicode Standard][uax31], we allow: -/// - `_` as a starting character, -/// - `_` and `-` as continuing characters. -/// -/// [uax31]: http://www.unicode.org/reports/tr31/ +/// An identifier. #[derive(Debug, Clone, PartialEq)] pub struct Ident { /// The source code location. @@ -1083,44 +976,13 @@ pub struct Ident { pub string: EcoString, } -impl Ident { - /// Create a new identifier from a string checking that it is a valid. - pub fn new( - string: impl AsRef + Into, - span: impl Into, - ) -> Option { - is_ident(string.as_ref()) - .then(|| Self { span: span.into(), string: string.into() }) - } - - /// Create a new identifier from a string and a span. - /// - /// The `string` must be a valid identifier. - #[track_caller] - pub fn new_unchecked(string: impl Into, span: Span) -> Self { - let string = string.into(); - debug_assert!(is_ident(&string), "`{}` is not a valid identifier", string); - Self { span, string } - } - - /// Return a reference to the underlying string. - pub fn as_str(&self) -> &str { - &self.string - } -} - -impl Deref for Ident { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.as_str() - } -} - impl TypedNode for Ident { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(string) => Some(Ident::new_unchecked(string, node.span())), + NodeKind::Ident(string) => Some(Ident { + span: node.span(), + string: string.clone(), + }), _ => None, } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 022b51de0..fc05ad50c 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -5,7 +5,6 @@ mod pretty; mod span; use std::fmt::{self, Debug, Display, Formatter}; -use std::mem; use std::rc::Rc; pub use pretty::*; @@ -40,14 +39,6 @@ impl Green { self.data().kind() } - /// Set the type of the node. - pub fn set_kind(&mut self, kind: NodeKind) { - match self { - Self::Node(node) => Rc::make_mut(node).data.set_kind(kind), - Self::Token(data) => data.set_kind(kind), - } - } - /// The length of the node. pub fn len(&self) -> usize { self.data().len() @@ -68,6 +59,18 @@ impl Green { Green::Token(_) => &[], } } + + /// Change the type of the node. + pub fn convert(&mut self, kind: NodeKind) { + match self { + Self::Node(node) => { + let node = Rc::make_mut(node); + node.erroneous |= kind.is_error(); + node.data.kind = kind; + } + Self::Token(data) => data.kind = kind, + } + } } impl Default for Green { @@ -161,11 +164,6 @@ impl GreenData { &self.kind } - /// Set the type of the node. - pub fn set_kind(&mut self, kind: NodeKind) { - self.kind = kind; - } - /// The length of the node. pub fn len(&self) -> usize { self.len @@ -178,123 +176,7 @@ impl From for Green { } } -/// A borrowed wrapper for a [`GreenNode`] with span information. -/// -/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node. -#[derive(Copy, Clone, PartialEq)] -pub struct RedRef<'a> { - id: SourceId, - offset: usize, - green: &'a Green, -} - -impl<'a> RedRef<'a> { - /// Convert to an owned representation. - pub fn own(self) -> RedNode { - RedNode { - id: self.id, - offset: self.offset, - green: self.green.clone(), - } - } - - /// The type of the node. - pub fn kind(self) -> &'a NodeKind { - self.green.kind() - } - - /// The length of the node. - pub fn len(self) -> usize { - self.green.len() - } - - /// The span of the node. - pub fn span(self) -> Span { - Span::new(self.id, self.offset, self.offset + self.green.len()) - } - - /// Whether the node or its children contain an error. - pub fn erroneous(self) -> bool { - self.green.erroneous() - } - - /// The error messages for this node and its descendants. - pub fn errors(self) -> Vec { - if !self.erroneous() { - return vec![]; - } - - match self.kind() { - NodeKind::Error(pos, msg) => { - let span = match pos { - ErrorPosition::Start => self.span().at_start(), - ErrorPosition::Full => self.span(), - ErrorPosition::End => self.span().at_end(), - }; - - vec![Error::new(span, msg.to_string())] - } - _ => self - .children() - .filter(|red| red.erroneous()) - .flat_map(|red| red.errors()) - .collect(), - } - } - - /// Convert the node to a typed AST node. - pub fn cast(self) -> Option - where - T: TypedNode, - { - T::from_red(self) - } - - /// The node's children. - pub fn children(self) -> impl Iterator> { - let children = match &self.green { - Green::Node(node) => node.children(), - Green::Token(_) => &[], - }; - - let mut cursor = self.offset; - children.iter().map(move |green| { - let offset = cursor; - cursor += green.len(); - RedRef { id: self.id, offset, green } - }) - } - - /// Get the first child of some type. - pub(crate) fn typed_child(self, kind: &NodeKind) -> Option> { - self.children() - .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) - } - - /// Get the first child that can cast to some AST type. - pub(crate) fn cast_first_child(self) -> Option { - self.children().find_map(RedRef::cast) - } - - /// Get the last child that can cast to some AST type. - pub(crate) fn cast_last_child(self) -> Option { - self.children().filter_map(RedRef::cast).last() - } -} - -impl Debug for RedRef<'_> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}: {:?}", self.kind(), self.span())?; - let mut children = self.children().peekable(); - if children.peek().is_some() { - f.write_str(" ")?; - f.debug_list().entries(children.map(RedRef::own)).finish()?; - } - Ok(()) - } -} - -/// A owned wrapper for a [`GreenNode`] with span information. +/// A owned wrapper for a green node with span information. /// /// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes. #[derive(Clone, PartialEq)] @@ -348,22 +230,17 @@ impl RedNode { } /// The children of the node. - pub fn children(&self) -> impl Iterator> { + pub fn children(&self) -> Children<'_> { self.as_ref().children() } - /// Get the first child of some type. - pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { - self.as_ref().typed_child(kind).map(RedRef::own) - } - /// Get the first child that can cast to some AST type. - pub(crate) fn cast_first_child(&self) -> Option { + pub fn cast_first_child(&self) -> Option { self.as_ref().cast_first_child() } /// Get the last child that can cast to some AST type. - pub(crate) fn cast_last_child(&self) -> Option { + pub fn cast_last_child(&self) -> Option { self.as_ref().cast_last_child() } } @@ -374,6 +251,146 @@ impl Debug for RedNode { } } +/// A borrowed wrapper for a green node with span information. +/// +/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node. +#[derive(Copy, Clone, PartialEq)] +pub struct RedRef<'a> { + id: SourceId, + offset: usize, + green: &'a Green, +} + +impl<'a> RedRef<'a> { + /// Convert to an owned representation. + pub fn own(self) -> RedNode { + RedNode { + id: self.id, + offset: self.offset, + green: self.green.clone(), + } + } + + /// The type of the node. + pub fn kind(self) -> &'a NodeKind { + self.green.kind() + } + + /// The length of the node. + pub fn len(self) -> usize { + self.green.len() + } + + /// The span of the node. + pub fn span(self) -> Span { + Span::new(self.id, self.offset, self.offset + self.green.len()) + } + + /// The error messages for this node and its descendants. + pub fn errors(self) -> Vec { + if !self.green.erroneous() { + return vec![]; + } + + match self.kind() { + NodeKind::Error(pos, msg) => { + let span = match pos { + ErrorPos::Start => self.span().at_start(), + ErrorPos::Full => self.span(), + ErrorPos::End => self.span().at_end(), + }; + + vec![Error::new(span, msg.to_string())] + } + _ => self + .children() + .filter(|red| red.green.erroneous()) + .flat_map(|red| red.errors()) + .collect(), + } + } + + /// Convert the node to a typed AST node. + pub fn cast(self) -> Option + where + T: TypedNode, + { + T::from_red(self) + } + + /// The node's children. + pub fn children(self) -> Children<'a> { + let children = match &self.green { + Green::Node(node) => node.children(), + Green::Token(_) => &[], + }; + + Children { + id: self.id, + iter: children.iter(), + front: self.offset, + back: self.offset + self.len(), + } + } + + /// Get the first child that can cast to some AST type. + pub fn cast_first_child(self) -> Option { + self.children().find_map(RedRef::cast) + } + + /// Get the last child that can cast to some AST type. + pub fn cast_last_child(self) -> Option { + self.children().rev().find_map(RedRef::cast) + } +} + +impl Debug for RedRef<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}: {:?}", self.kind(), self.span())?; + let mut children = self.children().peekable(); + if children.peek().is_some() { + f.write_str(" ")?; + f.debug_list().entries(children.map(RedRef::own)).finish()?; + } + Ok(()) + } +} + +/// An iterator over the children of a red node. +pub struct Children<'a> { + id: SourceId, + iter: std::slice::Iter<'a, Green>, + front: usize, + back: usize, +} + +impl<'a> Iterator for Children<'a> { + type Item = RedRef<'a>; + + fn next(&mut self) -> Option { + self.iter.next().map(|green| { + let offset = self.front; + self.front += green.len(); + RedRef { id: self.id, offset, green } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for Children<'_> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|green| { + self.back -= green.len(); + RedRef { id: self.id, offset: self.back, green } + }) + } +} + +impl ExactSizeIterator for Children<'_> {} + /// All syntactical building blocks that can be part of a Typst document. /// /// Can be emitted as a token by the tokenizer or as part of a green node by @@ -533,7 +550,7 @@ pub enum NodeKind { Array, /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. Dict, - /// A named argument: `thickness: 3pt`. + /// A named pair: `thickness: 3pt`. Named, /// A grouped expression: `(1 + 2)`. Group, @@ -582,12 +599,12 @@ pub enum NodeKind { /// The comment can contain nested block comments. BlockComment, /// Tokens that appear in the wrong place. - Error(ErrorPosition, EcoString), + Error(ErrorPos, EcoString), /// Unknown character sequences. Unknown(EcoString), } -/// Payload of a raw block: `` `...` ``. +/// Payload of a raw block node. #[derive(Debug, Clone, PartialEq)] pub struct RawData { /// The raw text in the block. @@ -600,19 +617,19 @@ pub struct RawData { pub block: bool, } -/// Payload of a math formula: `$2pi + x$` or `$[f'(x) = x^2]$`. +/// Payload of a math formula node. #[derive(Debug, Clone, PartialEq)] pub struct MathData { - /// The formula between the dollars. + /// The formula between the dollars / brackets. pub formula: EcoString, /// Whether the formula is display-level, that is, it is surrounded by - /// `$[..]`. + /// `$[..]$`. pub display: bool, } /// Where in a node an error should be annotated. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ErrorPosition { +pub enum ErrorPos { /// At the start of the node. Start, /// Over the full width of the node. diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index b396a39c7..fa423e94b 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -141,7 +141,7 @@ impl Pretty for RawNode { // Language tag. if let Some(lang) = &self.lang { - lang.pretty(p); + p.push_str(lang); } // Start untrimming. @@ -492,7 +492,7 @@ impl Pretty for Imports { fn pretty(&self, p: &mut Printer) { match self { Self::Wildcard => p.push('*'), - Self::Idents(idents) => { + Self::Items(idents) => { p.join(idents, ", ", |item, p| item.pretty(p)); } } @@ -508,7 +508,7 @@ impl Pretty for IncludeExpr { impl Pretty for Ident { fn pretty(&self, p: &mut Printer) { - p.push_str(self.as_str()); + p.push_str(&self.string); } }