Skip to content

Commit

Permalink
rewrite visitor architecture to support arbitrary visitor lifetime
Browse files Browse the repository at this point in the history
  • Loading branch information
rrevenantt committed Nov 8, 2020
1 parent fa24927 commit bc54601
Show file tree
Hide file tree
Showing 12 changed files with 49 additions and 66 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "antlr-rust"
version = "0.2.0-dev.1"
version = "0.2.0-dev.2"
authors = ["Konstantin Anisimov <[email protected]>"]
homepage = "https://github.com/rrevenantt/antlr4rust"
repository = "https://github.com/rrevenantt/antlr4rust"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# antlr4rust
[![docs](https://docs.rs/antlr-rust/badge.svg)](https://docs.rs/antlr-rust)
[![Crate](https://img.shields.io/crates/v/antlr_rust.svg)](https://crates.io/crates/antlr_rust)
[![Crate](https://flat.badgen.net/crates/v/antlr-rust)](https://crates.io/crates/antlr_rust)
[![docs](https://flat.badgen.net/badge/docs.rs/v0.2.0-dev.2)](https://docs.rs/antlr-rust/0.2.0-dev.2)

[ANTLR4](https://github.com/antlr/antlr4) runtime for Rust programming language.

Expand Down
2 changes: 2 additions & 0 deletions src/char_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::utils::Sealed;
pub trait CharStream<Data>: IntStream {
/// Returns underlying data piece, either slice or owned copy.
/// Panics if provided indexes are invalid
/// Called by parser only on token intervals.
/// This fact can be used by custom implementations
fn get_text(&self, a: isize, b: isize) -> Data;
fn get_text_from_interval(&self, i: &Interval) -> Data { self.get_text(i.a, i.b) }
}
Expand Down
20 changes: 0 additions & 20 deletions src/diagnostic_error_listener.rs

This file was deleted.

15 changes: 7 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#![warn(missing_docs)] // warn if there is missing docs
#![warn(missing_debug_implementations)]
#![warn(trivial_numeric_casts)]

//! # Antlr4 runtime
//!
//! **This is pre-release version.**
Expand Down Expand Up @@ -58,18 +59,16 @@
//! If you need to generate owned versions of parse tree or you want simpler usage,
//! you can opt out zero-copy by requiring `'input` to be static. In this case it is easier to also use
//! types that contains "owned" in their name or constructor function like `OwningTokenFactory`
//! or `InputStream::new_owned()`
//! or `InputStream::new_owned()`.
//!
//! ### Visitors and Listeners
//!
//! Currently visitors and listeners must outlive `'input`.
//! In practice this means that visitor has either `'static` or `'input` lifetime.
//! Thus you can retrieve references to parsed data from syntax tree to save in listener/visitor
//! (as example you can see visitor test). This should cover 99% of usecases.
//! Parse listeners must outlive 'input because they have to be stored inside of the parser.
//! It still allows to retrieve borrowed data from parse tree which should be enough to cover 99% use cases.
//!
//! `ParseTreeWalker` can accept listeners with arbitrary lifetime.
//!
//! You can try to give visitor outside references but in this case
//! if those references do not outlive `'input` you will get very confusing error messages,
//! so this is not recommended.
//! Visitors also can have arbitrary lifetime
//!
//! ### Downcasting
//!
Expand Down
8 changes: 5 additions & 3 deletions src/parser_rule_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::token::Token;
use crate::token_factory::{CommonTokenFactory, TokenFactory};
use crate::tree::{
ErrorNode, ParseTree, ParseTreeListener, ParseTreeVisitor, TerminalNode, Tree, Visitable,
VisitableDyn,
};
use better_any::{Tid, TidAble, TidExt};

Expand Down Expand Up @@ -131,7 +132,7 @@ pub trait RuleContextExt<'input>: ParserRuleContext<'input> {
fn accept_children<V>(&self, visitor: &mut V)
where
V: ParseTreeVisitor<'input, Self::Ctx> + ?Sized,
<Self::Ctx as ParserNodeType<'input>>::Type: Visitable<V>;
<Self::Ctx as ParserNodeType<'input>>::Type: VisitableDyn<V>;
}

impl<'input, T: ParserRuleContext<'input> + ?Sized + 'input> RuleContextExt<'input> for T {
Expand Down Expand Up @@ -177,9 +178,10 @@ impl<'input, T: ParserRuleContext<'input> + ?Sized + 'input> RuleContextExt<'inp
fn accept_children<V>(&self, visitor: &mut V)
where
V: ParseTreeVisitor<'input, Self::Ctx> + ?Sized,
<Self::Ctx as ParserNodeType<'input>>::Type: Visitable<V>,
<Self::Ctx as ParserNodeType<'input>>::Type: VisitableDyn<V>,
{
self.get_children().for_each(|child| child.accept(visitor))
self.get_children()
.for_each(|child| child.accept_dyn(visitor))
}
}

Expand Down
26 changes: 14 additions & 12 deletions src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,24 +221,23 @@ pub trait ParseTreeVisitor<'input, Node: ParserNodeType<'input>>:
fn visit_children(&mut self, node: &Node::Type) { self.visit_children_inner(node) }
}

/// How visitor is supposed to visit children
/// Workaround for default recursive children visiting
///
/// Default implementation visits children recursively
/// To override it you would need to enable specialization feature
///
/// It is already implemented for visitors that are bound by `'input`.
/// Already blanket implemented for all visitors.
/// To override it you would need to implement `ParseTreeVisitor::visit_children`
pub trait VisitChildren<'input, Node: ParserNodeType<'input>> {
fn visit_children_inner(&mut self, node: &Node::Type);
}

impl<'input, Node, T> VisitChildren<'input, Node> for T
where
Node: ParserNodeType<'input>,
T: ParseTreeVisitor<'input, Node>,
for<'a> &'a mut Self: CoerceUnsized<&'a mut Node::Visitor>,
Node::Type: Visitable<Node::Visitor>,
T: ParseTreeVisitor<'input, Node> + ?Sized,
// for<'a> &'a mut Self: CoerceUnsized<&'a mut Node::Visitor>,
Node::Type: VisitableDyn<T>,
{
default fn visit_children_inner(&mut self, node: &Node::Type) { node.accept_children(self) }
#[inline(always)]
fn visit_children_inner(&mut self, node: &Node::Type) { node.accept_children(self) }
}

pub trait Visitable<Vis: ?Sized> {
Expand All @@ -247,6 +246,12 @@ pub trait Visitable<Vis: ?Sized> {
}
}

pub trait VisitableDyn<Vis: ?Sized> {
fn accept_dyn(&self, visitor: &mut Vis) {
unreachable!("should have been properly implemented by generated context when reachable")
}
}

pub trait ParseTreeListener<'input, Node: ParserNodeType<'input>> {
fn visit_terminal(&mut self, _node: &TerminalNode<'input, Node>) {}
fn visit_error_node(&mut self, _node: &ErrorNode<'input, Node>) {}
Expand Down Expand Up @@ -279,9 +284,6 @@ where
T: ParseTreeListener<'input, Node> + 'a + ?Sized,
Node::Type: Listenable<T>,
{
// #[doc(hidden)]
// pub fn new() -> Self{ Self(PhantomData) }

pub fn walk<Listener, Ctx>(mut listener: Box<Listener>, t: &Ctx) -> Box<Listener>
where
for<'x> &'x mut Listener: CoerceUnsized<&'x mut T>,
Expand Down
14 changes: 10 additions & 4 deletions tests/gen/csvparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ use antlr_rust::token::{OwningToken, Token, TOKEN_EOF};
use antlr_rust::token_factory::{CommonTokenFactory, TokenAware, TokenFactory};
use antlr_rust::token_source::TokenSource;
use antlr_rust::token_stream::TokenStream;
use antlr_rust::tree::{
ErrorNode, LeafNode, Listenable, ParseTree, ParseTreeListener, ParseTreeVisitor,
ParseTreeWalker, TerminalNode, Visitable,
};
use antlr_rust::tree::*;
use antlr_rust::vocabulary::{Vocabulary, VocabularyImpl};
use antlr_rust::PredictionContextCache;

Expand Down Expand Up @@ -147,6 +144,15 @@ pub trait CSVParserContext<'input>:
{
}

impl<'input, 'x, T> VisitableDyn<T> for dyn CSVParserContext<'input> + 'input
where
T: CSVVisitor<'input> + 'x,
{
fn accept_dyn(&self, visitor: &mut T) {
self.accept(visitor as &mut (dyn CSVVisitor<'input> + 'x))
}
}

impl<'input> CSVParserContext<'input> for TerminalNode<'input, CSVParserContextType> {}
impl<'input> CSVParserContext<'input> for ErrorNode<'input, CSVParserContextType> {}

Expand Down
5 changes: 1 addition & 4 deletions tests/gen/labelsparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ use antlr_rust::token::{OwningToken, Token, TOKEN_EOF};
use antlr_rust::token_factory::{CommonTokenFactory, TokenAware, TokenFactory};
use antlr_rust::token_source::TokenSource;
use antlr_rust::token_stream::TokenStream;
use antlr_rust::tree::{
ErrorNode, LeafNode, Listenable, ParseTree, ParseTreeListener, ParseTreeVisitor,
ParseTreeWalker, TerminalNode, Visitable,
};
use antlr_rust::tree::*;
use antlr_rust::vocabulary::{Vocabulary, VocabularyImpl};
use antlr_rust::PredictionContextCache;
use antlr_rust::{TidAble, TidExt};
Expand Down
5 changes: 1 addition & 4 deletions tests/gen/referencetoatnparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ use antlr_rust::token::{OwningToken, Token, TOKEN_EOF};
use antlr_rust::token_factory::{CommonTokenFactory, TokenAware, TokenFactory};
use antlr_rust::token_source::TokenSource;
use antlr_rust::token_stream::TokenStream;
use antlr_rust::tree::{
ErrorNode, LeafNode, Listenable, ParseTree, ParseTreeListener, ParseTreeVisitor,
ParseTreeWalker, TerminalNode, Visitable,
};
use antlr_rust::tree::*;
use antlr_rust::vocabulary::{Vocabulary, VocabularyImpl};
use antlr_rust::PredictionContextCache;
use antlr_rust::{TidAble, TidExt};
Expand Down
5 changes: 1 addition & 4 deletions tests/gen/simplelrparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ use antlr_rust::token::{OwningToken, Token, TOKEN_EOF};
use antlr_rust::token_factory::{CommonTokenFactory, TokenAware, TokenFactory};
use antlr_rust::token_source::TokenSource;
use antlr_rust::token_stream::TokenStream;
use antlr_rust::tree::{
ErrorNode, LeafNode, Listenable, ParseTree, ParseTreeListener, ParseTreeVisitor,
ParseTreeWalker, TerminalNode, Visitable,
};
use antlr_rust::tree::*;
use antlr_rust::vocabulary::{Vocabulary, VocabularyImpl};
use antlr_rust::PredictionContextCache;
use antlr_rust::{TidAble, TidExt};
Expand Down
9 changes: 5 additions & 4 deletions tests/my_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,9 @@ if (x < x && a > 0) then duh
}
}

struct MyCSVVisitor<'i>(Vec<&'i str>);
struct MyCSVVisitor<'i, T>(Vec<&'i str>, T);

impl<'i> ParseTreeVisitor<'i, CSVParserContextType> for MyCSVVisitor<'i> {
impl<'i, T> ParseTreeVisitor<'i, CSVParserContextType> for MyCSVVisitor<'i, T> {
fn visit_terminal(&mut self, node: &TerminalNode<'i, CSVParserContextType>) {
if node.symbol.get_token_type() == csvparser::TEXT {
if let Cow::Borrowed(s) = node.symbol.text {
Expand All @@ -364,7 +364,7 @@ if (x < x && a > 0) then duh
use std::borrow::Cow;
use std::rc::Rc;

impl<'i> CSVVisitor<'i> for MyCSVVisitor<'i> {
impl<'i, T> CSVVisitor<'i> for MyCSVVisitor<'i, T> {
fn visit_hdr(&mut self, ctx: &HdrContext<'i>) {}

fn visit_row(&mut self, ctx: &RowContext<'i>) {
Expand All @@ -384,7 +384,8 @@ if (x < x && a > 0) then duh
let mut parser = CSVParser::new(token_source);
let result = parser.csvFile().expect("parsed unsuccessfully");

let mut visitor = MyCSVVisitor(Vec::new());
let mut test = 5;
let mut visitor = MyCSVVisitor(Vec::new(), &mut test);
result.accept(&mut visitor);
assert_eq!(visitor.0, vec!["d1", "d2"]);

Expand Down

0 comments on commit bc54601

Please sign in to comment.