Skip to content

Commit 8e2d990

Browse files
committed
Provide mechanism to skip entire blocks
- Simplifies usage of visitor logic considerably - Add null_visitor to help with this
1 parent 56278ab commit 8e2d990

File tree

5 files changed

+390
-13
lines changed

5 files changed

+390
-13
lines changed

cxxheaderparser/parser.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
Value,
5959
Variable,
6060
)
61-
from .visitor import CxxVisitor
61+
from .visitor import CxxVisitor, null_visitor
6262

6363
LexTokenList = typing.List[LexToken]
6464
T = typing.TypeVar("T")
@@ -114,6 +114,7 @@ def debug_print(fmt: str, *args: typing.Any) -> None:
114114

115115
def _push_state(self, cls: typing.Type[ST], *args) -> ST:
116116
state = cls(self.state, *args)
117+
state._prior_visitor = self.visitor
117118
if isinstance(state, NamespaceBlockState):
118119
self.current_namespace = state.namespace
119120
self.state = state
@@ -122,6 +123,7 @@ def _push_state(self, cls: typing.Type[ST], *args) -> ST:
122123
def _pop_state(self) -> State:
123124
prev_state = self.state
124125
prev_state._finish(self.visitor)
126+
self.visitor = prev_state._prior_visitor
125127
state = prev_state.parent
126128
if state is None:
127129
raise CxxParseError("INTERNAL ERROR: unbalanced state")
@@ -454,7 +456,8 @@ def _parse_namespace(
454456
ns = NamespaceDecl(names, inline, doxygen)
455457
state = self._push_state(NamespaceBlockState, ns)
456458
state.location = location
457-
self.visitor.on_namespace_start(state)
459+
if self.visitor.on_namespace_start(state) is False:
460+
self.visitor = null_visitor
458461

459462
def _parse_extern(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
460463
etok = self.lex.token_if("STRING_LITERAL", "template")
@@ -463,7 +466,8 @@ def _parse_extern(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
463466
if self.lex.token_if("{"):
464467
state = self._push_state(ExternBlockState, etok.value)
465468
state.location = tok.location
466-
self.visitor.on_extern_block_start(state)
469+
if self.visitor.on_extern_block_start(state) is False:
470+
self.visitor = null_visitor
467471
return
468472

469473
# an extern variable/function with specific linkage
@@ -508,7 +512,8 @@ def _on_empty_block_start(
508512
self, tok: LexToken, doxygen: typing.Optional[str]
509513
) -> None:
510514
state = self._push_state(EmptyBlockState)
511-
self.visitor.on_empty_block_start(state)
515+
if self.visitor.on_empty_block_start(state) is False:
516+
self.visitor = null_visitor
512517

513518
def _on_block_end(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
514519
old_state = self._pop_state()
@@ -1143,7 +1148,8 @@ def _parse_class_decl(
11431148
ClassBlockState, clsdecl, default_access, typedef, mods
11441149
)
11451150
state.location = location
1146-
self.visitor.on_class_start(state)
1151+
if self.visitor.on_class_start(state) is False:
1152+
self.visitor = null_visitor
11471153

11481154
def _finish_class_decl(self, state: ClassBlockState) -> None:
11491155
self._finish_class_or_enum(

cxxheaderparser/parserstate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class State(typing.Generic[T, PT]):
4646
#: Approximate location that the parsed element was found at
4747
location: Location
4848

49+
#: internal detail used by parser
50+
_prior_visitor: "CxxVisitor"
51+
4952
def __init__(self, parent: typing.Optional["State[PT, typing.Any]"]) -> None:
5053
self.parent = parent
5154

cxxheaderparser/simple.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,22 +209,24 @@ def on_pragma(self, state: SState, content: Value) -> None:
209209
def on_include(self, state: SState, filename: str) -> None:
210210
self.data.includes.append(Include(filename))
211211

212-
def on_empty_block_start(self, state: SEmptyBlockState) -> None:
212+
def on_empty_block_start(self, state: SEmptyBlockState) -> typing.Optional[bool]:
213213
# this matters for some scope/resolving purposes, but you're
214214
# probably going to want to use clang if you care about that
215215
# level of detail
216216
state.user_data = state.parent.user_data
217+
return None
217218

218219
def on_empty_block_end(self, state: SEmptyBlockState) -> None:
219220
pass
220221

221-
def on_extern_block_start(self, state: SExternBlockState) -> None:
222+
def on_extern_block_start(self, state: SExternBlockState) -> typing.Optional[bool]:
222223
state.user_data = state.parent.user_data
224+
return None
223225

224226
def on_extern_block_end(self, state: SExternBlockState) -> None:
225227
pass
226228

227-
def on_namespace_start(self, state: SNamespaceBlockState) -> None:
229+
def on_namespace_start(self, state: SNamespaceBlockState) -> typing.Optional[bool]:
228230
parent_ns = state.parent.user_data
229231

230232
ns = None
@@ -247,6 +249,7 @@ def on_namespace_start(self, state: SNamespaceBlockState) -> None:
247249
ns.doxygen = state.namespace.doxygen
248250

249251
state.user_data = ns
252+
return None
250253

251254
def on_namespace_end(self, state: SNamespaceBlockState) -> None:
252255
pass
@@ -299,11 +302,12 @@ def on_enum(self, state: SState, enum: EnumDecl) -> None:
299302
# Class/union/struct
300303
#
301304

302-
def on_class_start(self, state: SClassBlockState) -> None:
305+
def on_class_start(self, state: SClassBlockState) -> typing.Optional[bool]:
303306
parent = state.parent.user_data
304307
block = ClassScope(state.class_decl)
305308
parent.classes.append(block)
306309
state.user_data = block
310+
return None
307311

308312
def on_class_field(self, state: SClassBlockState, f: Field) -> None:
309313
state.user_data.fields.append(f)

cxxheaderparser/visitor.py

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def on_include(self, state: State, filename: str) -> None:
5252
Called once for each ``#include`` directive encountered
5353
"""
5454

55-
def on_empty_block_start(self, state: EmptyBlockState) -> None:
55+
def on_empty_block_start(self, state: EmptyBlockState) -> typing.Optional[bool]:
5656
"""
5757
Called when a ``{`` is encountered that isn't associated with or
5858
consumed by other declarations.
@@ -62,31 +62,39 @@ def on_empty_block_start(self, state: EmptyBlockState) -> None:
6262
{
6363
// stuff
6464
}
65+
66+
If this function returns False, the visitor will not be called for any
67+
items inside this block (including on_empty_block_end)
6568
"""
6669

6770
def on_empty_block_end(self, state: EmptyBlockState) -> None:
6871
"""
6972
Called when an empty block ends
7073
"""
7174

72-
def on_extern_block_start(self, state: ExternBlockState) -> None:
75+
def on_extern_block_start(self, state: ExternBlockState) -> typing.Optional[bool]:
7376
"""
7477
.. code-block:: c++
7578
7679
extern "C" {
7780
7881
}
7982
83+
If this function returns False, the visitor will not be called for any
84+
items inside this block (including on_extern_block_end)
8085
"""
8186

8287
def on_extern_block_end(self, state: ExternBlockState) -> None:
8388
"""
8489
Called when an extern block ends
8590
"""
8691

87-
def on_namespace_start(self, state: NamespaceBlockState) -> None:
92+
def on_namespace_start(self, state: NamespaceBlockState) -> typing.Optional[bool]:
8893
"""
8994
Called when a ``namespace`` directive is encountered
95+
96+
If this function returns False, the visitor will not be called for any
97+
items inside this namespace (including on_namespace_end)
9098
"""
9199

92100
def on_namespace_end(self, state: NamespaceBlockState) -> None:
@@ -186,7 +194,7 @@ def on_enum(self, state: State, enum: EnumDecl) -> None:
186194
# Class/union/struct
187195
#
188196

189-
def on_class_start(self, state: ClassBlockState) -> None:
197+
def on_class_start(self, state: ClassBlockState) -> typing.Optional[bool]:
190198
"""
191199
Called when a class/struct/union is encountered
192200
@@ -199,6 +207,9 @@ def on_class_start(self, state: ClassBlockState) -> None:
199207
This is called first, followed by on_typedef for each typedef instance
200208
encountered. The compound type object is passed as the type to the
201209
typedef.
210+
211+
If this function returns False, the visitor will not be called for any
212+
items inside this class (including on_class_end)
202213
"""
203214

204215
def on_class_field(self, state: ClassBlockState, f: Field) -> None:
@@ -231,3 +242,87 @@ def on_class_end(self, state: ClassBlockState) -> None:
231242
Then ``on_class_start``, .. ``on_class_end`` are emitted, along with
232243
``on_variable`` for each instance declared.
233244
"""
245+
246+
247+
class NullVisitor:
248+
"""
249+
This visitor does nothing
250+
"""
251+
252+
def on_parse_start(self, state: NamespaceBlockState) -> None:
253+
return None
254+
255+
def on_pragma(self, state: State, content: Value) -> None:
256+
return None
257+
258+
def on_include(self, state: State, filename: str) -> None:
259+
return None
260+
261+
def on_empty_block_start(self, state: EmptyBlockState) -> typing.Optional[bool]:
262+
return None
263+
264+
def on_empty_block_end(self, state: EmptyBlockState) -> None:
265+
return None
266+
267+
def on_extern_block_start(self, state: ExternBlockState) -> typing.Optional[bool]:
268+
return None
269+
270+
def on_extern_block_end(self, state: ExternBlockState) -> None:
271+
return None
272+
273+
def on_namespace_start(self, state: NamespaceBlockState) -> typing.Optional[bool]:
274+
return None
275+
276+
def on_namespace_end(self, state: NamespaceBlockState) -> None:
277+
return None
278+
279+
def on_namespace_alias(self, state: State, alias: NamespaceAlias) -> None:
280+
return None
281+
282+
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
283+
return None
284+
285+
def on_template_inst(self, state: State, inst: TemplateInst) -> None:
286+
return None
287+
288+
def on_variable(self, state: State, v: Variable) -> None:
289+
return None
290+
291+
def on_function(self, state: State, fn: Function) -> None:
292+
return None
293+
294+
def on_method_impl(self, state: State, method: Method) -> None:
295+
return None
296+
297+
def on_typedef(self, state: State, typedef: Typedef) -> None:
298+
return None
299+
300+
def on_using_namespace(self, state: State, namespace: typing.List[str]) -> None:
301+
return None
302+
303+
def on_using_alias(self, state: State, using: UsingAlias) -> None:
304+
return None
305+
306+
def on_using_declaration(self, state: State, using: UsingDecl) -> None:
307+
return None
308+
309+
def on_enum(self, state: State, enum: EnumDecl) -> None:
310+
return None
311+
312+
def on_class_start(self, state: ClassBlockState) -> typing.Optional[bool]:
313+
return None
314+
315+
def on_class_field(self, state: ClassBlockState, f: Field) -> None:
316+
return None
317+
318+
def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
319+
return None
320+
321+
def on_class_method(self, state: ClassBlockState, method: Method) -> None:
322+
return None
323+
324+
def on_class_end(self, state: ClassBlockState) -> None:
325+
return None
326+
327+
328+
null_visitor = NullVisitor()

0 commit comments

Comments
 (0)