summaryrefslogtreecommitdiff
path: root/src/syntax/ast.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-02 12:13:45 +0100
committerMartin Haug <mhaug@live.de>2021-11-05 13:46:41 +0100
commit65fac0e57c9852eb2131aa06c0bac43b70bfbfbc (patch)
tree8ed11d7cefd4e64f523b975f077e4b10f67a7cb9 /src/syntax/ast.rs
parent42afb27cef5540535420fb6d8d9d2fcda7300a47 (diff)
Refactoring
Co-Authored-By: Martin <mhaug@live.de>
Diffstat (limited to 'src/syntax/ast.rs')
-rw-r--r--src/syntax/ast.rs141
1 files changed, 107 insertions, 34 deletions
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 6ca271a9..9ad04be5 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -1,7 +1,18 @@
-use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode};
+//! 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.
+pub trait TypedNode: Sized {
+ /// Convert from a red node to a typed node.
+ fn from_red(value: RedRef) -> Option<Self>;
+}
+
macro_rules! node {
($(#[$attr:meta])* $name:ident) => {
node!{$(#[$attr])* $name => $name}
@@ -13,7 +24,7 @@ macro_rules! node {
pub struct $name(RedNode);
impl TypedNode for $name {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
if node.kind() != &NodeKind::$variant {
return None;
}
@@ -23,10 +34,12 @@ macro_rules! node {
}
impl $name {
+ /// The source code location.
pub fn span(&self) -> Span {
self.0.span()
}
+ /// The underlying red node.
pub fn underlying(&self) -> RedRef {
self.0.as_ref()
}
@@ -40,7 +53,8 @@ node! {
}
impl Markup {
- pub fn nodes<'a>(&'a self) -> impl Iterator<Item = MarkupNode> + 'a {
+ /// The markup nodes.
+ pub fn nodes(&self) -> impl Iterator<Item = MarkupNode> + '_ {
self.0.children().filter_map(RedRef::cast)
}
}
@@ -73,7 +87,7 @@ pub enum MarkupNode {
}
impl TypedNode for MarkupNode {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::Space(_) => Some(MarkupNode::Space),
NodeKind::Linebreak => Some(MarkupNode::Linebreak),
@@ -81,17 +95,14 @@ impl TypedNode for MarkupNode {
NodeKind::Strong => Some(MarkupNode::Strong),
NodeKind::Emph => Some(MarkupNode::Emph),
NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())),
- NodeKind::UnicodeEscape(u) => Some(MarkupNode::Text(u.character.into())),
- NodeKind::EnDash => Some(MarkupNode::Text(EcoString::from("\u{2013}"))),
- NodeKind::EmDash => Some(MarkupNode::Text(EcoString::from("\u{2014}"))),
- NodeKind::NonBreakingSpace => {
- Some(MarkupNode::Text(EcoString::from("\u{00A0}")))
- }
+ NodeKind::UnicodeEscape(c) => Some(MarkupNode::Text((*c).into())),
+ NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())),
+ NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())),
+ NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())),
NodeKind::Raw(_) => node.cast().map(MarkupNode::Raw),
NodeKind::Heading => node.cast().map(MarkupNode::Heading),
NodeKind::List => node.cast().map(MarkupNode::List),
NodeKind::Enum => node.cast().map(MarkupNode::Enum),
- NodeKind::Error(_, _) => None,
_ => node.cast().map(MarkupNode::Expr),
}
}
@@ -111,16 +122,16 @@ pub struct RawNode {
}
impl TypedNode for RawNode {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::Raw(raw) => {
- let span = node.span();
- let start = span.start + raw.backticks as usize;
+ let full = node.span();
+ let start = full.start + raw.backticks as usize;
Some(Self {
block: raw.block,
- lang: raw.lang.as_ref().and_then(|x| {
- let span = Span::new(span.source, start, start + x.len());
- Ident::new(x, span)
+ 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(),
})
@@ -272,7 +283,7 @@ impl Expr {
}
impl TypedNode for Expr {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::Ident(_) => node.cast().map(Self::Ident),
NodeKind::Array => node.cast().map(Self::Array),
@@ -325,7 +336,7 @@ pub enum Lit {
}
impl TypedNode for Lit {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::None => Some(Self::None(node.span())),
NodeKind::Auto => Some(Self::Auto(node.span())),
@@ -336,13 +347,14 @@ impl TypedNode for Lit {
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.string.clone())),
+ NodeKind::Str(s) => Some(Self::Str(node.span(), s.clone())),
_ => None,
}
}
}
impl Lit {
+ /// The source code location.
pub fn span(&self) -> Span {
match self {
Self::None(span) => *span,
@@ -366,7 +378,7 @@ node! {
impl ArrayExpr {
/// The array items.
- pub fn items<'a>(&'a self) -> impl Iterator<Item = Expr> + 'a {
+ pub fn items(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(RedRef::cast)
}
}
@@ -378,7 +390,7 @@ node! {
impl DictExpr {
/// The named dictionary items.
- pub fn items<'a>(&'a self) -> impl Iterator<Item = Named> + 'a {
+ pub fn items(&self) -> impl Iterator<Item = Named> + '_ {
self.0.children().filter_map(RedRef::cast)
}
}
@@ -439,7 +451,7 @@ node! {
impl BlockExpr {
/// The list of expressions contained in the block.
- pub fn exprs<'a>(&'a self) -> impl Iterator<Item = Expr> + 'a {
+ pub fn exprs(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(RedRef::cast)
}
}
@@ -477,7 +489,7 @@ pub enum UnOp {
}
impl TypedNode for UnOp {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
Self::from_token(node.kind())
}
}
@@ -581,7 +593,7 @@ pub enum BinOp {
}
impl TypedNode for BinOp {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
Self::from_token(node.kind())
}
}
@@ -709,7 +721,7 @@ node! {
impl CallArgs {
/// The positional and named arguments.
- pub fn items<'a>(&'a self) -> impl Iterator<Item = CallArg> + 'a {
+ pub fn items(&self) -> impl Iterator<Item = CallArg> + '_ {
self.0.children().filter_map(RedRef::cast)
}
}
@@ -726,7 +738,7 @@ pub enum CallArg {
}
impl TypedNode for CallArg {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::Named => Some(CallArg::Named(
node.cast().expect("named call argument is missing name"),
@@ -767,7 +779,7 @@ impl ClosureExpr {
}
/// The parameter bindings.
- pub fn params<'a>(&'a self) -> impl Iterator<Item = ClosureParam> + 'a {
+ pub fn params(&self) -> impl Iterator<Item = ClosureParam> + '_ {
self.0
.children()
.find(|x| x.kind() == &NodeKind::ClosureParams)
@@ -805,10 +817,10 @@ pub enum ClosureParam {
}
impl TypedNode for ClosureParam {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
- NodeKind::Ident(i) => {
- Some(ClosureParam::Pos(Ident::new(i, node.span()).unwrap()))
+ 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"),
@@ -921,7 +933,7 @@ pub enum Imports {
}
impl TypedNode for Imports {
- fn cast_from(node: RedRef) -> Option<Self> {
+ fn from_red(node: RedRef) -> Option<Self> {
match node.kind() {
NodeKind::Star => Some(Imports::Wildcard),
NodeKind::ImportItems => {
@@ -1043,14 +1055,75 @@ node! {
}
impl ForPattern {
+ /// The key part of the pattern: index for arrays, name for dictionaries.
pub fn key(&self) -> Option<Ident> {
- let mut items: Vec<_> = self.0.children().filter_map(RedRef::cast).collect();
- if items.len() > 1 { Some(items.remove(0)) } else { None }
+ let mut children = self.0.children().filter_map(RedRef::cast);
+ let key = children.next();
+ if children.next().is_some() { key } else { None }
}
+ /// The value part of the pattern.
pub fn value(&self) -> Ident {
self.0
.cast_last_child()
.expect("for-in 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/
+#[derive(Debug, Clone, PartialEq)]
+pub struct Ident {
+ /// The source code location.
+ pub span: Span,
+ /// The identifier string.
+ pub string: EcoString,
+}
+
+impl Ident {
+ /// Create a new identifier from a string checking that it is a valid.
+ pub fn new(
+ string: impl AsRef<str> + Into<EcoString>,
+ span: impl Into<Span>,
+ ) -> Option<Self> {
+ 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<EcoString>, 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<Self> {
+ match node.kind() {
+ NodeKind::Ident(string) => Some(Ident::new_unchecked(string, node.span())),
+ _ => None,
+ }
+ }
+}