use super::cst::{SyntaxKind, SyntaxNode, SyntaxToken};
use rowan::TextRange;
pub fn token_src<'q>(token: &SyntaxToken, source: &'q str) -> &'q str {
let range = token.text_range();
&source[range.start().into()..range.end().into()]
}
macro_rules! ast_node {
($name:ident, $kind:ident) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name(SyntaxNode);
impl $name {
pub fn cast(node: SyntaxNode) -> Option<Self> {
Self::can_cast(node.kind()).then(|| Self(node))
}
pub fn can_cast(kind: SyntaxKind) -> bool {
kind == SyntaxKind::$kind
}
pub fn as_cst(&self) -> &SyntaxNode {
&self.0
}
pub fn text_range(&self) -> TextRange {
self.0.text_range()
}
}
};
}
macro_rules! define_expr {
($($variant:ident),+ $(,)?) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
$($variant($variant)),+
}
impl Expr {
pub fn cast(node: SyntaxNode) -> Option<Self> {
let kind = node.kind();
$(if $variant::can_cast(kind) { return Some(Expr::$variant($variant(node))); })+
None
}
pub fn as_cst(&self) -> &SyntaxNode {
match self { $(Expr::$variant(n) => n.as_cst()),+ }
}
pub fn text_range(&self) -> TextRange {
match self { $(Expr::$variant(n) => n.text_range()),+ }
}
}
};
}
impl Expr {
pub fn children(&self) -> Vec<Expr> {
match self {
Expr::NamedNode(n) => n.children().collect(),
Expr::SeqExpr(s) => s.children().collect(),
Expr::CapturedExpr(c) => c.inner().into_iter().collect(),
Expr::QuantifiedExpr(q) => q.inner().into_iter().collect(),
Expr::FieldExpr(f) => f.value().into_iter().collect(),
Expr::AltExpr(a) => a.branches().filter_map(|b| b.body()).collect(),
Expr::Ref(_) | Expr::AnonymousNode(_) => vec![],
}
}
}
ast_node!(Root, Root);
ast_node!(Def, Def);
ast_node!(NamedNode, Tree);
ast_node!(Ref, Ref);
ast_node!(AltExpr, Alt);
ast_node!(Branch, Branch);
ast_node!(SeqExpr, Seq);
ast_node!(CapturedExpr, Capture);
ast_node!(Type, Type);
ast_node!(QuantifiedExpr, Quantifier);
ast_node!(FieldExpr, Field);
ast_node!(NegatedField, NegatedField);
ast_node!(Anchor, Anchor);
ast_node!(NodePredicate, NodePredicate);
ast_node!(RegexLiteral, Regex);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SeqItem {
Expr(Expr),
Anchor(Anchor),
}
impl SeqItem {
pub fn cast(node: SyntaxNode) -> Option<Self> {
if let Some(expr) = Expr::cast(node.clone()) {
return Some(SeqItem::Expr(expr));
}
if let Some(anchor) = Anchor::cast(node) {
return Some(SeqItem::Anchor(anchor));
}
None
}
pub fn as_anchor(&self) -> Option<&Anchor> {
match self {
SeqItem::Anchor(a) => Some(a),
_ => None,
}
}
pub fn as_expr(&self) -> Option<&Expr> {
match self {
SeqItem::Expr(e) => Some(e),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AnonymousNode(SyntaxNode);
impl AnonymousNode {
pub fn cast(node: SyntaxNode) -> Option<Self> {
Self::can_cast(node.kind()).then(|| Self(node))
}
pub fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::Str | SyntaxKind::Wildcard)
}
pub fn as_cst(&self) -> &SyntaxNode {
&self.0
}
pub fn text_range(&self) -> TextRange {
self.0.text_range()
}
pub fn value(&self) -> Option<SyntaxToken> {
if self.0.kind() == SyntaxKind::Wildcard {
return None;
}
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::StrVal)
}
pub fn is_any(&self) -> bool {
self.0.kind() == SyntaxKind::Wildcard
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AltKind {
Tagged,
Untagged,
Mixed,
}
pub use plotnik_bytecode::PredicateOp;
pub fn predicate_op_from_syntax_kind(kind: SyntaxKind) -> Option<PredicateOp> {
match kind {
SyntaxKind::OpEq => Some(PredicateOp::Eq),
SyntaxKind::OpNe => Some(PredicateOp::Ne),
SyntaxKind::OpStartsWith => Some(PredicateOp::StartsWith),
SyntaxKind::OpEndsWith => Some(PredicateOp::EndsWith),
SyntaxKind::OpContains => Some(PredicateOp::Contains),
SyntaxKind::OpRegexMatch => Some(PredicateOp::RegexMatch),
SyntaxKind::OpRegexNoMatch => Some(PredicateOp::RegexNoMatch),
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PredicateValue<'q> {
String(&'q str),
Regex(&'q str),
}
define_expr!(
NamedNode,
Ref,
AnonymousNode,
AltExpr,
SeqExpr,
CapturedExpr,
QuantifiedExpr,
FieldExpr,
);
impl Root {
pub fn defs(&self) -> impl Iterator<Item = Def> + '_ {
self.0.children().filter_map(Def::cast)
}
pub fn exprs(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(Expr::cast)
}
}
impl Def {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
pub fn body(&self) -> Option<Expr> {
self.0.children().find_map(Expr::cast)
}
}
impl NamedNode {
pub fn node_type(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::Id
| SyntaxKind::Underscore
| SyntaxKind::KwError
| SyntaxKind::KwMissing
)
})
}
pub fn is_any(&self) -> bool {
self.node_type()
.map(|t| t.kind() == SyntaxKind::Underscore)
.unwrap_or(false)
}
pub fn is_missing(&self) -> bool {
self.node_type()
.map(|t| t.kind() == SyntaxKind::KwMissing)
.unwrap_or(false)
}
pub fn missing_constraint(&self) -> Option<SyntaxToken> {
if !self.is_missing() {
return None;
}
let mut found_missing = false;
for child in self.0.children_with_tokens() {
if let Some(token) = child.into_token() {
if token.kind() == SyntaxKind::KwMissing {
found_missing = true;
} else if found_missing
&& matches!(token.kind(), SyntaxKind::Id | SyntaxKind::StrVal)
{
return Some(token);
}
}
}
None
}
pub fn children(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(Expr::cast)
}
pub fn anchors(&self) -> impl Iterator<Item = Anchor> + '_ {
self.0.children().filter_map(Anchor::cast)
}
pub fn items(&self) -> impl Iterator<Item = SeqItem> + '_ {
self.0.children().filter_map(SeqItem::cast)
}
pub fn predicate(&self) -> Option<NodePredicate> {
self.0.children().find_map(NodePredicate::cast)
}
}
impl NodePredicate {
pub fn operator_token(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| predicate_op_from_syntax_kind(t.kind()).is_some())
}
pub fn operator(&self) -> Option<PredicateOp> {
self.operator_token()
.and_then(|t| predicate_op_from_syntax_kind(t.kind()))
}
pub fn string_value(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::StrVal)
}
pub fn regex(&self) -> Option<RegexLiteral> {
self.0.children().find_map(RegexLiteral::cast)
}
pub fn value<'q>(&self, source: &'q str) -> Option<PredicateValue<'q>> {
if let Some(str_token) = self.string_value() {
return Some(PredicateValue::String(token_src(&str_token, source)));
}
if let Some(regex) = self.regex() {
return Some(PredicateValue::Regex(regex.pattern(source)));
}
None
}
}
impl RegexLiteral {
pub fn pattern<'q>(&self, source: &'q str) -> &'q str {
let range = self.0.text_range();
let text = &source[usize::from(range.start())..usize::from(range.end())];
let Some(without_prefix) = text.strip_prefix('/') else {
return text;
};
without_prefix.strip_suffix('/').unwrap_or(without_prefix)
}
}
impl Ref {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
}
impl AltExpr {
pub fn kind(&self) -> AltKind {
let mut tagged = false;
let mut untagged = false;
for child in self.0.children().filter(|c| c.kind() == SyntaxKind::Branch) {
let has_label = child
.children_with_tokens()
.filter_map(|it| it.into_token())
.any(|t| t.kind() == SyntaxKind::Id);
if has_label {
tagged = true;
} else {
untagged = true;
}
}
match (tagged, untagged) {
(true, true) => AltKind::Mixed,
(true, false) => AltKind::Tagged,
_ => AltKind::Untagged,
}
}
pub fn branches(&self) -> impl Iterator<Item = Branch> + '_ {
self.0.children().filter_map(Branch::cast)
}
pub fn exprs(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(Expr::cast)
}
}
impl Branch {
pub fn label(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
pub fn body(&self) -> Option<Expr> {
self.0.children().find_map(Expr::cast)
}
}
impl SeqExpr {
pub fn children(&self) -> impl Iterator<Item = Expr> + '_ {
self.0.children().filter_map(Expr::cast)
}
pub fn anchors(&self) -> impl Iterator<Item = Anchor> + '_ {
self.0.children().filter_map(Anchor::cast)
}
pub fn items(&self) -> impl Iterator<Item = SeqItem> + '_ {
self.0.children().filter_map(SeqItem::cast)
}
}
impl CapturedExpr {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::CaptureToken | SyntaxKind::SuppressiveCapture
)
})
}
pub fn is_suppressive(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.any(|t| t.kind() == SyntaxKind::SuppressiveCapture)
}
pub fn inner(&self) -> Option<Expr> {
self.0.children().find_map(Expr::cast)
}
pub fn type_annotation(&self) -> Option<Type> {
self.0.children().find_map(Type::cast)
}
pub fn has_string_annotation(&self) -> bool {
self.type_annotation()
.is_some_and(|t| t.name().is_some_and(|n| n.text() == "string"))
}
}
impl Type {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
}
impl QuantifiedExpr {
pub fn inner(&self) -> Option<Expr> {
self.0.children().find_map(Expr::cast)
}
pub fn operator(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::Star
| SyntaxKind::Plus
| SyntaxKind::Question
| SyntaxKind::StarQuestion
| SyntaxKind::PlusQuestion
| SyntaxKind::QuestionQuestion
)
})
}
pub fn is_optional(&self) -> bool {
self.operator()
.map(|op| {
matches!(
op.kind(),
SyntaxKind::Question
| SyntaxKind::Star
| SyntaxKind::QuestionQuestion
| SyntaxKind::StarQuestion
)
})
.unwrap_or(false)
}
}
impl FieldExpr {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
pub fn value(&self) -> Option<Expr> {
self.0.children().find_map(Expr::cast)
}
}
impl NegatedField {
pub fn name(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|t| t.kind() == SyntaxKind::Id)
}
}
pub fn is_truly_empty_scope(inner: &Expr) -> bool {
match inner {
Expr::SeqExpr(seq) => seq.children().next().is_none(),
Expr::AltExpr(alt) => alt.branches().next().is_none(),
Expr::QuantifiedExpr(q) => q.inner().is_some_and(|i| is_truly_empty_scope(&i)),
_ => false,
}
}