Skip to content

Commit 12ba1d9

Browse files
committed
support for visitors that return data
1 parent 6e7da59 commit 12ba1d9

20 files changed

+2395
-72
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ things to consider for next versions:
3838
- [ ] remove proc macro dependency
3939
- [ ] move to once_cell completely
4040
- [ ] rustfmt in antlr tool
41+
- [ ] make `Node` and associated type in visitors
42+
- [ ] bulk import for context extension traits
4143
- [ ] intradoc links
4244
- [ ] move benches to repository
4345
- [ ] LexerAtnSimulator position tracking customization
@@ -46,7 +48,7 @@ things to consider for next versions:
4648
- [ ] arena allocation for tree nodes
4749
- [ ] tree utils
4850
- [ ] features
49-
- [ ] statefull lexer test
51+
- [ ] stateful lexer test
5052
- [ ] better downcasting
5153
- [ ] options for customizing downcast bounds
5254
- [ ] token stream rewriter

build.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,24 @@ use std::process::Command;
99

1010
fn main() {
1111
let grammars = vec![
12+
"VisitorBasic",
13+
"VisitorCalc",
1214
"CSV",
1315
"ReferenceToATN",
1416
"XMLLexer",
1517
"SimpleLR",
1618
"Labels",
1719
"FHIRPath",
1820
];
19-
let additional_args = vec![Some("-visitor"), None, None, None, None];
21+
let additional_args = vec![
22+
Some("-visitor"),
23+
Some("-visitor"),
24+
Some("-visitor"),
25+
None,
26+
None,
27+
None,
28+
None,
29+
];
2030
let antlr_path = "/home/rrevenantt/dev/antlr4/tool/target/antlr4-4.8-2-SNAPSHOT-complete.jar";
2131

2232
for (grammar, arg) in grammars.into_iter().zip(additional_args) {

grammars/VisitorBasic.g4

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
grammar VisitorBasic;
2+
3+
s
4+
: 'A' EOF
5+
;
6+
7+
A : 'A';

grammars/VisitorCalc.g4

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
grammar VisitorCalc;
2+
3+
s
4+
: expr EOF
5+
;
6+
7+
expr
8+
: INT # number
9+
| expr (MUL | DIV) expr # multiply
10+
| expr (ADD | SUB) expr # add
11+
;
12+
13+
INT : [0-9]+;
14+
MUL : '*';
15+
DIV : '/';
16+
ADD : '+';
17+
SUB : '-';
18+
WS : [ \t]+ -> channel(HIDDEN);

src/tree.rs

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::token::Token;
2020
use crate::token_factory::TokenFactory;
2121
use crate::{interval_set, trees, CoerceTo};
2222
use better_any::{Tid, TidAble};
23+
use std::mem;
2324

2425
//todo try to make in more generic
2526
#[allow(missing_docs)]
@@ -220,25 +221,109 @@ impl<'input, Node: ParserNodeType<'input>, Visitor: ParseTreeVisitor<'input, Nod
220221
fn accept(&self, visitor: &mut Visitor) { visitor.visit_error_node(self) }
221222
}
222223

224+
pub trait ParseTreeVisitorCompat<'input>: VisitChildren<'input, Self::Node> {
225+
type Node: ParserNodeType<'input>;
226+
type Return: Default;
227+
228+
/// Temporary storage for `ParseTreeVisitor` blanket implementation to work
229+
fn temp_result(&mut self) -> &mut Self::Return;
230+
231+
fn visit(&mut self, node: &<Self::Node as ParserNodeType<'input>>::Type) -> Self::Return {
232+
self.visit_node(&node);
233+
mem::take(self.temp_result())
234+
}
235+
236+
/// Called on terminal(leaf) node
237+
fn visit_terminal(&mut self, _node: &TerminalNode<'input, Self::Node>) -> Self::Return {
238+
Self::Return::default()
239+
}
240+
/// Called on error node
241+
fn visit_error_node(&mut self, _node: &ErrorNode<'input, Self::Node>) -> Self::Return {
242+
Self::Return::default()
243+
}
244+
245+
fn visit_children(
246+
&mut self,
247+
node: &<Self::Node as ParserNodeType<'input>>::Type,
248+
) -> Self::Return {
249+
let mut result = Self::Return::default();
250+
for node in node.get_children() {
251+
if !self.should_visit_next_child(&node, &result) {
252+
break;
253+
}
254+
255+
let child_result = self.visit(&node);
256+
result = self.aggregate_results(result, child_result);
257+
}
258+
return result;
259+
}
260+
261+
fn aggregate_results(&self, aggregate: Self::Return, next: Self::Return) -> Self::Return {
262+
next
263+
}
264+
265+
fn should_visit_next_child(
266+
&self,
267+
node: &<Self::Node as ParserNodeType<'input>>::Type,
268+
current: &Self::Return,
269+
) -> bool {
270+
true
271+
}
272+
}
273+
274+
// struct VisitorAdapter<'input, T: ParseTreeVisitorCompat<'input>> {
275+
// visitor: T,
276+
// pub curr_value: T::Return,
277+
// _pd: PhantomData<&'input str>,
278+
// }
279+
280+
impl<'input, Node, T> ParseTreeVisitor<'input, Node> for T
281+
where
282+
Node: ParserNodeType<'input>,
283+
Node::Type: VisitableDyn<Self>,
284+
T: ParseTreeVisitorCompat<'input, Node = Node>,
285+
{
286+
fn visit_terminal(&mut self, node: &TerminalNode<'input, Node>) {
287+
let result = <Self as ParseTreeVisitorCompat>::visit_terminal(self, node);
288+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
289+
}
290+
291+
fn visit_error_node(&mut self, node: &ErrorNode<'input, Node>) {
292+
let result = <Self as ParseTreeVisitorCompat>::visit_error_node(self, node);
293+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
294+
}
295+
296+
fn visit_children(&mut self, node: &Node::Type) {
297+
let result = <Self as ParseTreeVisitorCompat>::visit_children(self, node);
298+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
299+
}
300+
}
301+
223302
/// Base interface for visiting over syntax tree
224303
pub trait ParseTreeVisitor<'input, Node: ParserNodeType<'input>>:
225304
VisitChildren<'input, Node>
226305
{
306+
/// Basically alias for `node.accept(self)` in visitor implementation
307+
/// just to make api closer to java
308+
227309
/// Called on terminal(leaf) node
228310
fn visit_terminal(&mut self, _node: &TerminalNode<'input, Node>) {}
229311
/// Called on error node
230312
fn visit_error_node(&mut self, _node: &ErrorNode<'input, Node>) {}
231313
/// Implement this only if you want to change children visiting algorithm
232-
fn visit_children(&mut self, node: &Node::Type) { self.visit_children_inner(node) }
314+
fn visit_children(&mut self, node: &Node::Type) {
315+
node.get_children()
316+
.for_each(|child| self.visit_node(&child))
317+
}
233318
}
234319

235320
/// Workaround for default recursive children visiting
236321
///
237322
/// Already blanket implemented for all visitors.
238323
/// To override it you would need to implement `ParseTreeVisitor::visit_children`
239324
pub trait VisitChildren<'input, Node: ParserNodeType<'input>> {
240-
#[doc(hidden)]
241-
fn visit_children_inner(&mut self, node: &Node::Type);
325+
// fn visit_children_inner(&mut self, node: &Node::Type);
326+
fn visit_node(&mut self, node: &Node::Type);
242327
}
243328

244329
impl<'input, Node, T> VisitChildren<'input, Node> for T
@@ -248,8 +333,13 @@ where
248333
// for<'a> &'a mut Self: CoerceUnsized<&'a mut Node::Visitor>,
249334
Node::Type: VisitableDyn<T>,
250335
{
251-
#[inline(always)]
252-
fn visit_children_inner(&mut self, node: &Node::Type) { node.accept_children(self) }
336+
// #[inline(always)]
337+
// fn visit_children_inner(&mut self, node: &Node::Type) {
338+
// // node.accept_children(self)
339+
//
340+
// }
341+
342+
fn visit_node(&mut self, node: &Node::Type) { node.accept_dyn(self) }
253343
}
254344

255345
/// Types that can accept particular visitor

templates/Rust.stg

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,7 @@ use antlr_rust::tree::ParseTreeListener;
100100
use super::<file.parserName; format="low">::*;
101101

102102
pub trait <file.grammarName>Listener\<'input> : ParseTreeListener\<'input,<file.parserName>ContextType>{
103-
104103
<file.listenerNames:{lname |
105-
106104
/**
107105
<if(file.listenerLabelRuleNames.(lname))>
108106
* Enter a parse tree produced by the {@code <lname>\}
@@ -128,6 +126,8 @@ fn exit_<lname>(&mut self, _ctx: &<lname; format="cap">Context\<'input>) { \}};
128126

129127
antlr_rust::coerce_from!{ 'input : <file.grammarName>Listener\<'input> }
130128

129+
130+
131131
>>
132132

133133
BaseListenerFile(file, header, namedActions) ::= <<>>
@@ -136,8 +136,9 @@ VisitorFile(file, header, namedActions) ::= <<
136136
#![allow(nonstandard_style)]
137137
<fileHeader(file.grammarFileName, file.ANTLRVersion)>
138138
<header>
139-
use antlr_rust::tree::{ParseTreeVisitor};
139+
use antlr_rust::tree::{ParseTreeVisitor,ParseTreeVisitorCompat};
140140
use super::<file.parserName; format="low">::*;
141+
use std::mem;
141142

142143
/**
143144
* This interface defines a complete generic visitor for a parse tree produced
@@ -156,7 +157,35 @@ pub trait <file.grammarName>Visitor\<'input>: ParseTreeVisitor\<'input,<file.par
156157
*/
157158
fn visit_<lname>(&mut self, ctx: &<lname; format="cap">Context\<'input>) { self.visit_children(ctx) \}
158159
}; separator="\n">
160+
}
159161

162+
pub trait <file.grammarName>VisitorCompat\<'input>:ParseTreeVisitorCompat\<'input, Node= <file.parserName>ContextType>{
163+
<file.visitorNames:{lname |
164+
/**
165+
<if(file.visitorLabelRuleNames.(lname))>
166+
* Visit a parse tree produced by the {@code <lname>\}
167+
* labeled alternative in {@link <file.parserName>#<file.visitorLabelRuleNames.(lname)>\}.
168+
<else>
169+
* Visit a parse tree produced by {@link <file.parserName>#<lname>\}.
170+
<endif>
171+
* @param ctx the parse tree
172+
*/
173+
fn visit_<lname>(&mut self, ctx: &<lname; format="cap">Context\<'input>) -> Self::Return {
174+
self.visit_children(ctx)
175+
\}
176+
}; separator="\n">
177+
}
178+
179+
impl\<'input,T> <file.grammarName>Visitor\<'input> for T
180+
where
181+
T: <file.grammarName>VisitorCompat\<'input>
182+
{
183+
<file.visitorNames:{lname |
184+
fn visit_<lname>(&mut self, ctx: &<lname; format="cap">Context\<'input>){
185+
let result = \<Self as <file.grammarName>VisitorCompat>::visit_<lname>(self, ctx);
186+
*\<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
187+
\}
188+
}; separator="\n">
160189
}
161190
>>
162191

tests/gen/csvlistener.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub trait CSVListener<'input>: ParseTreeListener<'input, CSVParserContextType> {
1414
* @param ctx the parse tree
1515
*/
1616
fn exit_csvFile(&mut self, _ctx: &CsvFileContext<'input>) {}
17-
1817
/**
1918
* Enter a parse tree produced by {@link CSVParser#hdr}.
2019
* @param ctx the parse tree
@@ -25,7 +24,6 @@ pub trait CSVListener<'input>: ParseTreeListener<'input, CSVParserContextType> {
2524
* @param ctx the parse tree
2625
*/
2726
fn exit_hdr(&mut self, _ctx: &HdrContext<'input>) {}
28-
2927
/**
3028
* Enter a parse tree produced by {@link CSVParser#row}.
3129
* @param ctx the parse tree
@@ -36,7 +34,6 @@ pub trait CSVListener<'input>: ParseTreeListener<'input, CSVParserContextType> {
3634
* @param ctx the parse tree
3735
*/
3836
fn exit_row(&mut self, _ctx: &RowContext<'input>) {}
39-
4037
/**
4138
* Enter a parse tree produced by {@link CSVParser#field}.
4239
* @param ctx the parse tree

tests/gen/csvvisitor.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#![allow(nonstandard_style)]
22
// Generated from CSV.g4 by ANTLR 4.8
33
use super::csvparser::*;
4-
use antlr_rust::tree::ParseTreeVisitor;
4+
use antlr_rust::tree::{ParseTreeVisitor, ParseTreeVisitorCompat};
5+
use std::mem;
56

67
/**
78
* This interface defines a complete generic visitor for a parse tree produced
@@ -32,3 +33,60 @@ pub trait CSVVisitor<'input>: ParseTreeVisitor<'input, CSVParserContextType> {
3233
*/
3334
fn visit_field(&mut self, ctx: &FieldContext<'input>) { self.visit_children(ctx) }
3435
}
36+
37+
pub trait CSVVisitorCompat<'input>:
38+
ParseTreeVisitorCompat<'input, Node = CSVParserContextType>
39+
{
40+
/**
41+
* Visit a parse tree produced by {@link CSVParser#csvFile}.
42+
* @param ctx the parse tree
43+
*/
44+
fn visit_csvFile(&mut self, ctx: &CsvFileContext<'input>) -> Self::Return {
45+
self.visit_children(ctx)
46+
}
47+
48+
/**
49+
* Visit a parse tree produced by {@link CSVParser#hdr}.
50+
* @param ctx the parse tree
51+
*/
52+
fn visit_hdr(&mut self, ctx: &HdrContext<'input>) -> Self::Return { self.visit_children(ctx) }
53+
54+
/**
55+
* Visit a parse tree produced by {@link CSVParser#row}.
56+
* @param ctx the parse tree
57+
*/
58+
fn visit_row(&mut self, ctx: &RowContext<'input>) -> Self::Return { self.visit_children(ctx) }
59+
60+
/**
61+
* Visit a parse tree produced by {@link CSVParser#field}.
62+
* @param ctx the parse tree
63+
*/
64+
fn visit_field(&mut self, ctx: &FieldContext<'input>) -> Self::Return {
65+
self.visit_children(ctx)
66+
}
67+
}
68+
69+
impl<'input, T> CSVVisitor<'input> for T
70+
where
71+
T: CSVVisitorCompat<'input>,
72+
{
73+
fn visit_csvFile(&mut self, ctx: &CsvFileContext<'input>) {
74+
let result = <Self as CSVVisitorCompat>::visit_csvFile(self, ctx);
75+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
76+
}
77+
78+
fn visit_hdr(&mut self, ctx: &HdrContext<'input>) {
79+
let result = <Self as CSVVisitorCompat>::visit_hdr(self, ctx);
80+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
81+
}
82+
83+
fn visit_row(&mut self, ctx: &RowContext<'input>) {
84+
let result = <Self as CSVVisitorCompat>::visit_row(self, ctx);
85+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
86+
}
87+
88+
fn visit_field(&mut self, ctx: &FieldContext<'input>) {
89+
let result = <Self as CSVVisitorCompat>::visit_field(self, ctx);
90+
*<Self as ParseTreeVisitorCompat>::temp_result(self) = result;
91+
}
92+
}

0 commit comments

Comments
 (0)