use crate::{Language, RedLeaf, RedNode, RedTree};
pub trait AstProcessor<'a, L: Language> {
fn traverse_pre_order(&self, root: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>);
fn traverse_post_order(&self, root: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>);
fn find_nodes_by_type(&self, root: RedTree<'a, L>, node_type: L::ElementType) -> Vec<RedNode<'a, L>>;
fn find_tokens_by_type(&self, root: RedTree<'a, L>, token_type: L::TokenType) -> Vec<RedLeaf<L>>;
fn find_node_at_offset(&self, root: RedTree<'a, L>, offset: usize) -> Option<RedTree<'a, L>>;
fn replace_node(&self, old_node: RedNode<'a, L>, new_node: RedNode<'a, L>) -> Result<RedTree<'a, L>, AstError>;
fn remove_node(&self, node: RedNode<'a, L>) -> Result<RedTree<'a, L>, AstError>;
fn add_child(&self, parent: RedNode<'a, L>, child: RedTree<'a, L>) -> Result<RedNode<'a, L>, AstError>;
}
pub trait Visitor<'a, L: Language> {
fn visit_node_pre(&mut self, node: RedNode<'a, L>) -> VisitResult;
fn visit_node_post(&mut self, node: RedNode<'a, L>) -> VisitResult;
fn visit_token(&mut self, token: RedLeaf<L>) -> VisitResult;
fn visit_node_pre_default(&mut self, _node: RedNode<'a, L>) -> VisitResult {
VisitResult::Continue
}
fn visit_node_post_default(&mut self, _node: RedNode<'a, L>) -> VisitResult {
VisitResult::Continue
}
fn visit_token_default(&mut self, _token: RedLeaf<L>) -> VisitResult {
VisitResult::Continue
}
}
pub enum VisitResult {
Continue,
SkipChildren,
Stop,
}
pub enum AstError {
NodeNotFound,
NodeNotModifiable,
InvalidOperation,
LanguageSpecific(String),
}
impl std::fmt::Debug for AstError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AstError::NodeNotFound => write!(f, "Node not found"),
AstError::NodeNotModifiable => write!(f, "Node not modifiable"),
AstError::InvalidOperation => write!(f, "Invalid operation"),
AstError::LanguageSpecific(msg) => write!(f, "Language-specific error: {}", msg),
}
}
}
impl std::fmt::Display for AstError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for AstError {}
pub struct DefaultAstProcessor;
impl<'a, L: Language> AstProcessor<'a, L> for DefaultAstProcessor {
fn traverse_pre_order(&self, root: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>) {
self.traverse_pre_order_impl(root, visitor)
}
fn traverse_post_order(&self, root: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>) {
self.traverse_post_order_impl(root, visitor)
}
fn find_nodes_by_type(&self, root: RedTree<'a, L>, node_type: L::ElementType) -> Vec<RedNode<'a, L>> {
let mut result = Vec::new();
let mut visitor = TypeFinderVisitor { node_type, result: &mut result };
self.traverse_pre_order(root, &mut visitor);
result
}
fn find_tokens_by_type(&self, root: RedTree<'a, L>, token_type: L::TokenType) -> Vec<RedLeaf<L>> {
let mut result = Vec::new();
let mut visitor = TokenTypeFinderVisitor { token_type, result: &mut result };
self.traverse_pre_order(root, &mut visitor);
result
}
fn find_node_at_offset(&self, root: RedTree<'a, L>, offset: usize) -> Option<RedTree<'a, L>> {
match root {
RedTree::Node(node) => {
if offset >= node.span().start && offset < node.span().end {
node.child_at_offset(offset)
}
else {
None
}
}
RedTree::Leaf(leaf) => {
if offset >= leaf.span.start && offset < leaf.span.end {
Some(RedTree::Leaf(leaf))
}
else {
None
}
}
}
}
fn replace_node(&self, _old_node: RedNode<'a, L>, _new_node: RedNode<'a, L>) -> Result<RedTree<'a, L>, AstError> {
Err(AstError::NodeNotModifiable)
}
fn remove_node(&self, _node: RedNode<'a, L>) -> Result<RedTree<'a, L>, AstError> {
Err(AstError::NodeNotModifiable)
}
fn add_child(&self, _parent: RedNode<'a, L>, _child: RedTree<'a, L>) -> Result<RedNode<'a, L>, AstError> {
Err(AstError::NodeNotModifiable)
}
}
impl DefaultAstProcessor {
pub fn new() -> Self {
Self
}
fn traverse_pre_order_impl<'a, L: Language>(&self, node: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>) {
match node {
RedTree::Node(n) => match visitor.visit_node_pre(n) {
VisitResult::Continue => {
for child in n.children() {
self.traverse_pre_order_impl(child, visitor);
}
visitor.visit_node_post(n);
}
VisitResult::SkipChildren => {
visitor.visit_node_post(n);
}
VisitResult::Stop => return,
},
RedTree::Leaf(t) => {
if let VisitResult::Stop = visitor.visit_token(t) {
return;
}
}
}
}
fn traverse_post_order_impl<'a, L: Language>(&self, node: RedTree<'a, L>, visitor: &mut impl Visitor<'a, L>) {
match node {
RedTree::Node(n) => {
if let VisitResult::Continue = visitor.visit_node_pre(n) {
for child in n.children() {
self.traverse_post_order_impl(child, visitor);
}
}
if let VisitResult::Stop = visitor.visit_node_post(n) {
return;
}
}
RedTree::Leaf(t) => {
if let VisitResult::Stop = visitor.visit_token(t) {
return;
}
}
}
}
}
struct TypeFinderVisitor<'b, 'a, L: Language> {
node_type: L::ElementType,
result: &'b mut Vec<RedNode<'a, L>>,
}
impl<'b, 'a, L: Language> Visitor<'a, L> for TypeFinderVisitor<'b, 'a, L> {
fn visit_node_pre(&mut self, node: RedNode<'a, L>) -> VisitResult {
if node.element_type() == self.node_type {
self.result.push(node);
}
VisitResult::Continue
}
fn visit_node_post(&mut self, _node: RedNode<'a, L>) -> VisitResult {
VisitResult::Continue
}
fn visit_token(&mut self, _token: RedLeaf<L>) -> VisitResult {
VisitResult::Continue
}
}
struct TokenTypeFinderVisitor<'b, L: Language> {
token_type: L::TokenType,
result: &'b mut Vec<RedLeaf<L>>,
}
impl<'b, 'a, L: Language> Visitor<'a, L> for TokenTypeFinderVisitor<'b, L> {
fn visit_node_pre(&mut self, _node: RedNode<'a, L>) -> VisitResult {
VisitResult::Continue
}
fn visit_node_post(&mut self, _node: RedNode<'a, L>) -> VisitResult {
VisitResult::Continue
}
fn visit_token(&mut self, token: RedLeaf<L>) -> VisitResult {
if token.kind == self.token_type {
self.result.push(token);
}
VisitResult::Continue
}
}
pub trait RedTreeExt<'a, L: Language> {
fn traverse_pre_order(&self, visitor: &mut impl Visitor<'a, L>);
fn traverse_post_order(&self, visitor: &mut impl Visitor<'a, L>);
fn find_nodes_by_type(&self, node_type: L::ElementType) -> Vec<RedNode<'a, L>>;
fn find_tokens_by_type(&self, token_type: L::TokenType) -> Vec<RedLeaf<L>>;
fn find_node_at_offset(&self, offset: usize) -> Option<RedTree<'a, L>>;
}
impl<'a, L: Language> RedTreeExt<'a, L> for RedTree<'a, L> {
fn traverse_pre_order(&self, visitor: &mut impl Visitor<'a, L>) {
let processor = DefaultAstProcessor::new();
processor.traverse_pre_order(*self, visitor);
}
fn traverse_post_order(&self, visitor: &mut impl Visitor<'a, L>) {
let processor = DefaultAstProcessor::new();
processor.traverse_post_order(*self, visitor);
}
fn find_nodes_by_type(&self, node_type: L::ElementType) -> Vec<RedNode<'a, L>> {
let processor = DefaultAstProcessor::new();
processor.find_nodes_by_type(*self, node_type)
}
fn find_tokens_by_type(&self, token_type: L::TokenType) -> Vec<RedLeaf<L>> {
let processor = DefaultAstProcessor::new();
processor.find_tokens_by_type(*self, token_type)
}
fn find_node_at_offset(&self, offset: usize) -> Option<RedTree<'a, L>> {
let processor = DefaultAstProcessor::new();
processor.find_node_at_offset(*self, offset)
}
}