dbrownell-parserlib 0.10.2__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/PKG-INFO +12 -10
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/README.md +11 -9
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/pyproject.toml +1 -1
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/antlr/antlr_parser.py +25 -0
- dbrownell_parserlib-0.11.0/src/dbrownell_ParserLib/antlr/antlr_visitor_mixin.py +150 -0
- dbrownell_parserlib-0.10.2/src/dbrownell_ParserLib/antlr/antlr_visitor_mixins.py +0 -169
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/__init__.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/antlr/__init__.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/__init__.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/antlr-4.13.2-complete.jar +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/errors.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/expression.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/location.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/multiline_strings.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/py.typed +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/region.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/terminal_expression.py +0 -0
- {dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/visitors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dbrownell-parserlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Functionality useful when creating parsers.
|
|
5
5
|
Author: Dave Brownell
|
|
6
6
|
Author-email: Dave Brownell <github@DavidBrownell.com>
|
|
@@ -56,8 +56,9 @@ Description-Content-Type: text/markdown
|
|
|
56
56
|
- **Visitor Pattern**: Flexible visitor pattern implementation with fine-grained traversal control
|
|
57
57
|
- **Error Handling**: Rich error objects that associate messages with source locations and extract Python exception tracebacks
|
|
58
58
|
- **Multi-threaded Parsing**: Built-in support for parsing multiple files concurrently
|
|
59
|
-
- **Workspace Support**: Parse
|
|
60
|
-
- **Whitespace Handling**:
|
|
59
|
+
- **Workspace Support**: Parse translation units organized across multiple workspace roots
|
|
60
|
+
- **Whitespace Handling**: A single visitor mixin supports both insignificant and significant whitespace grammars (e.g., Python-like indentation); grammars that define `INDENT`/`DEDENT` tokens are detected automatically
|
|
61
|
+
- **Multiline String Extraction**: Extract multiline string content that must be vertically aligned with its opening token, with detailed errors on misalignment
|
|
61
62
|
|
|
62
63
|
### How to use `dbrownell_ParserLib`
|
|
63
64
|
|
|
@@ -101,7 +102,7 @@ Implement a visitor that converts ANTLR parse trees to Expression objects:
|
|
|
101
102
|
```python
|
|
102
103
|
from dataclasses import dataclass
|
|
103
104
|
|
|
104
|
-
from dbrownell_ParserLib.antlr.
|
|
105
|
+
from dbrownell_ParserLib.antlr.antlr_visitor_mixin import AntlrVisitorMixin
|
|
105
106
|
from dbrownell_ParserLib.expression import Expression
|
|
106
107
|
from dbrownell_ParserLib.terminal_expression import TerminalExpression
|
|
107
108
|
|
|
@@ -120,8 +121,10 @@ class BinaryExpression(Expression):
|
|
|
120
121
|
yield "right", self.right
|
|
121
122
|
|
|
122
123
|
|
|
123
|
-
class CalculatorVisitor(
|
|
124
|
-
def
|
|
124
|
+
class CalculatorVisitor(AntlrVisitorMixin, GeneratedVisitor):
|
|
125
|
+
def visitBinaryOp(self, ctx):
|
|
126
|
+
operator = TerminalExpression[str](self.CreateRegion(ctx.children[1]), ctx.children[1].getText())
|
|
127
|
+
|
|
125
128
|
children = self.GetChildren(ctx)
|
|
126
129
|
assert len(children) == 2, children
|
|
127
130
|
|
|
@@ -129,20 +132,19 @@ class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisit
|
|
|
129
132
|
BinaryExpression(
|
|
130
133
|
self.CreateRegion(ctx),
|
|
131
134
|
children[0],
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
operator,
|
|
134
136
|
children[1],
|
|
135
137
|
)
|
|
136
138
|
)
|
|
137
139
|
|
|
138
140
|
def visitNumber(self, ctx):
|
|
139
141
|
self._stack.append(
|
|
140
|
-
TerminalExpression(self.CreateRegion(ctx), int(ctx.getText()))
|
|
142
|
+
TerminalExpression[int](self.CreateRegion(ctx), int(ctx.getText()))
|
|
141
143
|
)
|
|
142
144
|
```
|
|
143
145
|
|
|
144
146
|
#### 4. Create Parser Instance
|
|
145
|
-
Use `CreateAntlrParser` to build a callable parser
|
|
147
|
+
Use `CreateAntlrParser` to build a callable parser. Grammars that define `INDENT` and `DEDENT` tokens (significant whitespace) are detected automatically; no additional configuration is required.
|
|
146
148
|
|
|
147
149
|
```python
|
|
148
150
|
from dbrownell_ParserLib.antlr.antlr_parser import CreateAntlrParser
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
- **Visitor Pattern**: Flexible visitor pattern implementation with fine-grained traversal control
|
|
35
35
|
- **Error Handling**: Rich error objects that associate messages with source locations and extract Python exception tracebacks
|
|
36
36
|
- **Multi-threaded Parsing**: Built-in support for parsing multiple files concurrently
|
|
37
|
-
- **Workspace Support**: Parse
|
|
38
|
-
- **Whitespace Handling**:
|
|
37
|
+
- **Workspace Support**: Parse translation units organized across multiple workspace roots
|
|
38
|
+
- **Whitespace Handling**: A single visitor mixin supports both insignificant and significant whitespace grammars (e.g., Python-like indentation); grammars that define `INDENT`/`DEDENT` tokens are detected automatically
|
|
39
|
+
- **Multiline String Extraction**: Extract multiline string content that must be vertically aligned with its opening token, with detailed errors on misalignment
|
|
39
40
|
|
|
40
41
|
### How to use `dbrownell_ParserLib`
|
|
41
42
|
|
|
@@ -79,7 +80,7 @@ Implement a visitor that converts ANTLR parse trees to Expression objects:
|
|
|
79
80
|
```python
|
|
80
81
|
from dataclasses import dataclass
|
|
81
82
|
|
|
82
|
-
from dbrownell_ParserLib.antlr.
|
|
83
|
+
from dbrownell_ParserLib.antlr.antlr_visitor_mixin import AntlrVisitorMixin
|
|
83
84
|
from dbrownell_ParserLib.expression import Expression
|
|
84
85
|
from dbrownell_ParserLib.terminal_expression import TerminalExpression
|
|
85
86
|
|
|
@@ -98,8 +99,10 @@ class BinaryExpression(Expression):
|
|
|
98
99
|
yield "right", self.right
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
class CalculatorVisitor(
|
|
102
|
-
def
|
|
102
|
+
class CalculatorVisitor(AntlrVisitorMixin, GeneratedVisitor):
|
|
103
|
+
def visitBinaryOp(self, ctx):
|
|
104
|
+
operator = TerminalExpression[str](self.CreateRegion(ctx.children[1]), ctx.children[1].getText())
|
|
105
|
+
|
|
103
106
|
children = self.GetChildren(ctx)
|
|
104
107
|
assert len(children) == 2, children
|
|
105
108
|
|
|
@@ -107,20 +110,19 @@ class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisit
|
|
|
107
110
|
BinaryExpression(
|
|
108
111
|
self.CreateRegion(ctx),
|
|
109
112
|
children[0],
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
operator,
|
|
112
114
|
children[1],
|
|
113
115
|
)
|
|
114
116
|
)
|
|
115
117
|
|
|
116
118
|
def visitNumber(self, ctx):
|
|
117
119
|
self._stack.append(
|
|
118
|
-
TerminalExpression(self.CreateRegion(ctx), int(ctx.getText()))
|
|
120
|
+
TerminalExpression[int](self.CreateRegion(ctx), int(ctx.getText()))
|
|
119
121
|
)
|
|
120
122
|
```
|
|
121
123
|
|
|
122
124
|
#### 4. Create Parser Instance
|
|
123
|
-
Use `CreateAntlrParser` to build a callable parser
|
|
125
|
+
Use `CreateAntlrParser` to build a callable parser. Grammars that define `INDENT` and `DEDENT` tokens (significant whitespace) are detected automatically; no additional configuration is required.
|
|
124
126
|
|
|
125
127
|
```python
|
|
126
128
|
from dbrownell_ParserLib.antlr.antlr_parser import CreateAntlrParser
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dbrownell-parserlib"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0"
|
|
4
4
|
# ^^^^^
|
|
5
5
|
# Wheel names will be generated according to this value. Do not manually modify this value; instead
|
|
6
6
|
# update it according to committed changes by running this command from the root of the repository:
|
|
@@ -102,6 +102,9 @@ class CreateVisitorFuncType(Protocol):
|
|
|
102
102
|
on_progress_func: Callable[[int], None],
|
|
103
103
|
*,
|
|
104
104
|
is_included_file: bool,
|
|
105
|
+
parser_newline_string: str | None,
|
|
106
|
+
indent_token_type: int | None,
|
|
107
|
+
dedent_token_type: int | None,
|
|
105
108
|
) -> antlr4.ParseTreeVisitor: ...
|
|
106
109
|
|
|
107
110
|
|
|
@@ -117,9 +120,18 @@ def CreateAntlrParser(
|
|
|
117
120
|
get_ast_func: Callable[[antlr4.Parser], antlr4.ParserRuleContext],
|
|
118
121
|
custom_lexer_init_func: Callable[[antlr4.Lexer], None] | None = None,
|
|
119
122
|
custom_parser_init_func: Callable[[antlr4.Parser], None] | None = None,
|
|
123
|
+
parser_newline_string: str | bool | None = None, # noqa: FBT001
|
|
120
124
|
) -> AntlrParser:
|
|
121
125
|
"""Create an `AntlrParser` that uses the given code generated by ANTLR."""
|
|
122
126
|
|
|
127
|
+
indent_token_type = getattr(antlr_parser, "INDENT", None)
|
|
128
|
+
dedent_token_type = getattr(antlr_parser, "DEDENT", None)
|
|
129
|
+
|
|
130
|
+
if parser_newline_string is None and indent_token_type is not None and dedent_token_type is not None:
|
|
131
|
+
parser_newline_string = "newLine"
|
|
132
|
+
elif isinstance(parser_newline_string, bool):
|
|
133
|
+
parser_newline_string = None
|
|
134
|
+
|
|
123
135
|
# ----------------------------------------------------------------------
|
|
124
136
|
def CreateLexer(input_stream: antlr4.InputStream) -> antlr4.Lexer:
|
|
125
137
|
lexer = antlr_lexer(input_stream)
|
|
@@ -145,6 +157,9 @@ def CreateAntlrParser(
|
|
|
145
157
|
CreateParser,
|
|
146
158
|
create_visitor_func,
|
|
147
159
|
get_ast_func,
|
|
160
|
+
indent_token_type,
|
|
161
|
+
dedent_token_type,
|
|
162
|
+
parser_newline_string,
|
|
148
163
|
)
|
|
149
164
|
|
|
150
165
|
|
|
@@ -162,12 +177,19 @@ class _AntlrParser(AntlrParser):
|
|
|
162
177
|
create_parser_func: Callable[[antlr4.CommonTokenStream], antlr4.Parser],
|
|
163
178
|
create_visitor_func: CreateVisitorFuncType,
|
|
164
179
|
get_ast_func: Callable[[antlr4.Parser], antlr4.ParserRuleContext],
|
|
180
|
+
indent_token_type: int | None,
|
|
181
|
+
dedent_token_type: int | None,
|
|
182
|
+
parser_newline_string: str | None,
|
|
165
183
|
) -> None:
|
|
166
184
|
self._create_lexer_func = create_lexer_func
|
|
167
185
|
self._create_parser_func = create_parser_func
|
|
168
186
|
self._create_visitor_func = create_visitor_func
|
|
169
187
|
self._get_ast_func = get_ast_func
|
|
170
188
|
|
|
189
|
+
self._indent_token_type = indent_token_type
|
|
190
|
+
self._dedent_token_type = dedent_token_type
|
|
191
|
+
self._parser_newline_string = parser_newline_string
|
|
192
|
+
|
|
171
193
|
self._execute_state: _AntlrParser._ExecuteState | None = None
|
|
172
194
|
|
|
173
195
|
# ----------------------------------------------------------------------
|
|
@@ -433,6 +455,9 @@ class _AntlrParser(AntlrParser):
|
|
|
433
455
|
fullpath,
|
|
434
456
|
lambda line: cast(None, status.OnProgress(line, None)),
|
|
435
457
|
is_included_file=is_included_file,
|
|
458
|
+
indent_token_type=self._indent_token_type,
|
|
459
|
+
dedent_token_type=self._dedent_token_type,
|
|
460
|
+
parser_newline_string=self._parser_newline_string,
|
|
436
461
|
)
|
|
437
462
|
|
|
438
463
|
ast = self._get_ast_func(parser)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from typing import cast, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import antlr4
|
|
7
|
+
|
|
8
|
+
from dbrownell_ParserLib.region import Location, Region
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ----------------------------------------------------------------------
|
|
16
|
+
class AntlrVisitorMixin:
|
|
17
|
+
"""Mixin that provides common visitation functionality; it is used along side ANTLR-generated visitors."""
|
|
18
|
+
|
|
19
|
+
# ----------------------------------------------------------------------
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
filename: Path,
|
|
23
|
+
on_progress_func: Callable[[int], None],
|
|
24
|
+
*,
|
|
25
|
+
is_included_file: bool,
|
|
26
|
+
parser_newline_string: str | None,
|
|
27
|
+
indent_token_type: int | None,
|
|
28
|
+
dedent_token_type: int | None,
|
|
29
|
+
) -> None:
|
|
30
|
+
self.filename = filename
|
|
31
|
+
self.is_included_file = is_included_file
|
|
32
|
+
|
|
33
|
+
# Protected data
|
|
34
|
+
self._on_progress_func = on_progress_func
|
|
35
|
+
self._parser_newline_string = parser_newline_string
|
|
36
|
+
self._indent_token_type = indent_token_type
|
|
37
|
+
self._dedent_token_type = dedent_token_type
|
|
38
|
+
self._stack: list[object] = []
|
|
39
|
+
|
|
40
|
+
# Private data
|
|
41
|
+
self._current_line: int = 0
|
|
42
|
+
self._stop_token_regex = re.compile(r"^\r?\n[ \t]*$")
|
|
43
|
+
|
|
44
|
+
# ----------------------------------------------------------------------
|
|
45
|
+
def CreateRegion(
|
|
46
|
+
self,
|
|
47
|
+
ctx: antlr4.TerminalNode | antlr4.ParserRuleContext,
|
|
48
|
+
) -> Region:
|
|
49
|
+
"""Create a `Region` from the given context."""
|
|
50
|
+
|
|
51
|
+
if isinstance(ctx, antlr4.TerminalNode):
|
|
52
|
+
start_token = cast(antlr4.Token, ctx.symbol) # ty: ignore[unresolved-attribute]
|
|
53
|
+
end_location = self._CreateEndLocation(start_token)
|
|
54
|
+
else:
|
|
55
|
+
start_token = ctx.start
|
|
56
|
+
stop_token = ctx.stop
|
|
57
|
+
|
|
58
|
+
assert ctx.children is not None
|
|
59
|
+
assert start_token is not None
|
|
60
|
+
assert stop_token is not None
|
|
61
|
+
|
|
62
|
+
end_location: Location | None = None
|
|
63
|
+
|
|
64
|
+
if self._parser_newline_string is not None:
|
|
65
|
+
if self._IsStopToken(stop_token):
|
|
66
|
+
assert len(ctx.children) == 2, ctx.children # noqa: PLR2004
|
|
67
|
+
end_location = self._CreateEndLocation(start_token)
|
|
68
|
+
elif stop_token.type == self._dedent_token_type:
|
|
69
|
+
end_location = Location(stop_token.line, stop_token.column + 1)
|
|
70
|
+
|
|
71
|
+
if end_location is None:
|
|
72
|
+
end_location = self._CreateEndLocation(stop_token)
|
|
73
|
+
|
|
74
|
+
assert start_token.line is not None
|
|
75
|
+
assert start_token.column is not None
|
|
76
|
+
|
|
77
|
+
region = Region(
|
|
78
|
+
self.filename,
|
|
79
|
+
Location(start_token.line, start_token.column + 1),
|
|
80
|
+
end_location,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
self._OnProgress(region.end.line)
|
|
84
|
+
|
|
85
|
+
return region
|
|
86
|
+
|
|
87
|
+
# ----------------------------------------------------------------------
|
|
88
|
+
def GetChildren(self, ctx: antlr4.ParserRuleContext) -> list[object]:
|
|
89
|
+
"""Return the results of visiting the children of the given context."""
|
|
90
|
+
|
|
91
|
+
prev_num_stack_items = len(self._stack)
|
|
92
|
+
|
|
93
|
+
cast(antlr4.ParseTreeVisitor, self).visitChildren(ctx)
|
|
94
|
+
|
|
95
|
+
results = self._stack[prev_num_stack_items:]
|
|
96
|
+
del self._stack[prev_num_stack_items:]
|
|
97
|
+
|
|
98
|
+
return results
|
|
99
|
+
|
|
100
|
+
# ----------------------------------------------------------------------
|
|
101
|
+
# | Protected Methods
|
|
102
|
+
def _OnProgress(self, end_line: int) -> None:
|
|
103
|
+
if end_line > self._current_line:
|
|
104
|
+
self._current_line = end_line
|
|
105
|
+
self._on_progress_func(end_line)
|
|
106
|
+
|
|
107
|
+
# ----------------------------------------------------------------------
|
|
108
|
+
# ----------------------------------------------------------------------
|
|
109
|
+
# ----------------------------------------------------------------------
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _CreateEndLocation(token: antlr4.Token) -> Location:
|
|
112
|
+
assert token.text is not None
|
|
113
|
+
assert token.line is not None
|
|
114
|
+
assert token.column is not None
|
|
115
|
+
|
|
116
|
+
return CreateEndLocation(token.text, token.line, token.column + 1)
|
|
117
|
+
|
|
118
|
+
# ----------------------------------------------------------------------
|
|
119
|
+
def _IsStopToken(self, token: antlr4.Token) -> bool:
|
|
120
|
+
assert token.text is not None
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
token.text == self._parser_newline_string or self._stop_token_regex.match(token.text) is not None
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ----------------------------------------------------------------------
|
|
128
|
+
def CreateEndLocation(text: str, line: int, column: int) -> Location:
|
|
129
|
+
"""Create a `Location` based on the text and current line and column."""
|
|
130
|
+
|
|
131
|
+
# Note that this function will not be invoked outside of this file; it is implemented as a
|
|
132
|
+
# function so that it can be exercised by automated tests.
|
|
133
|
+
|
|
134
|
+
assert line > 0, line
|
|
135
|
+
assert column > 0, column
|
|
136
|
+
|
|
137
|
+
lines = text.splitlines()
|
|
138
|
+
|
|
139
|
+
last_nonblank_line = None
|
|
140
|
+
|
|
141
|
+
for line_index, line_text in enumerate(lines):
|
|
142
|
+
if line_text.strip():
|
|
143
|
+
last_nonblank_line = line_index
|
|
144
|
+
|
|
145
|
+
assert last_nonblank_line is not None
|
|
146
|
+
|
|
147
|
+
if last_nonblank_line == 0:
|
|
148
|
+
return Location(line, column + len(lines[0]))
|
|
149
|
+
|
|
150
|
+
return Location(line + last_nonblank_line, len(lines[last_nonblank_line]) + 1)
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
"""Various ANTLR visitor mixins that can be used to add functionality to the generated visitor classes."""
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
|
-
from typing import cast, override, TYPE_CHECKING
|
|
7
|
-
|
|
8
|
-
import antlr4
|
|
9
|
-
|
|
10
|
-
from dbrownell_ParserLib.region import Location, Region
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from collections.abc import Callable
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# ----------------------------------------------------------------------
|
|
18
|
-
class AntlrVisitorMixinBase(ABC):
|
|
19
|
-
"""Base class for visitor mixins. This class is not meant to be used directly, but rather to be inherited by other mixin classes."""
|
|
20
|
-
|
|
21
|
-
# ----------------------------------------------------------------------
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
filename: Path,
|
|
25
|
-
on_progress_func: Callable[[int], None],
|
|
26
|
-
*,
|
|
27
|
-
is_included_file: bool,
|
|
28
|
-
) -> None:
|
|
29
|
-
self.filename = filename
|
|
30
|
-
self.is_included_file = is_included_file
|
|
31
|
-
|
|
32
|
-
# Protected data
|
|
33
|
-
self._on_progress_func = on_progress_func
|
|
34
|
-
self._stack: list[object] = []
|
|
35
|
-
|
|
36
|
-
# Private data
|
|
37
|
-
self._current_line: int = 0
|
|
38
|
-
|
|
39
|
-
# ----------------------------------------------------------------------
|
|
40
|
-
def CreateRegion(
|
|
41
|
-
self,
|
|
42
|
-
ctx: antlr4.TerminalNode | antlr4.ParserRuleContext,
|
|
43
|
-
) -> Region:
|
|
44
|
-
"""Create a `Region` from the given context."""
|
|
45
|
-
|
|
46
|
-
if isinstance(ctx, antlr4.TerminalNode):
|
|
47
|
-
token = cast(antlr4.Token, ctx.symbol) # ty: ignore[unresolved-attribute]
|
|
48
|
-
|
|
49
|
-
assert token.column is not None
|
|
50
|
-
|
|
51
|
-
start_line = token.line
|
|
52
|
-
start_col = token.column + 1
|
|
53
|
-
end_line = token.line
|
|
54
|
-
end_col = token.column + 1 + len(token.text)
|
|
55
|
-
|
|
56
|
-
assert start_line is not None
|
|
57
|
-
assert end_line is not None
|
|
58
|
-
|
|
59
|
-
region = Region(
|
|
60
|
-
self.filename,
|
|
61
|
-
Location(start_line, start_col),
|
|
62
|
-
Location(end_line, end_col),
|
|
63
|
-
)
|
|
64
|
-
else:
|
|
65
|
-
region = self._CreateRegionFromRule(ctx)
|
|
66
|
-
|
|
67
|
-
self._OnProgress(region.end.line)
|
|
68
|
-
|
|
69
|
-
return region
|
|
70
|
-
|
|
71
|
-
# ----------------------------------------------------------------------
|
|
72
|
-
def GetChildren(self, ctx: antlr4.ParserRuleContext) -> list[object]:
|
|
73
|
-
"""Return the results of visiting the children of the given context."""
|
|
74
|
-
|
|
75
|
-
prev_num_stack_items = len(self._stack)
|
|
76
|
-
|
|
77
|
-
cast(antlr4.ParseTreeVisitor, self).visitChildren(ctx)
|
|
78
|
-
|
|
79
|
-
results = self._stack[prev_num_stack_items:]
|
|
80
|
-
del self._stack[prev_num_stack_items:]
|
|
81
|
-
|
|
82
|
-
return results
|
|
83
|
-
|
|
84
|
-
# ----------------------------------------------------------------------
|
|
85
|
-
# | Protected Methods
|
|
86
|
-
def _OnProgress(self, end_line: int) -> None:
|
|
87
|
-
if end_line > self._current_line:
|
|
88
|
-
self._current_line = end_line
|
|
89
|
-
self._on_progress_func(end_line)
|
|
90
|
-
|
|
91
|
-
# ----------------------------------------------------------------------
|
|
92
|
-
@abstractmethod
|
|
93
|
-
def _CreateRegionFromRule(self, ctx: antlr4.ParserRuleContext) -> Region: ...
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# ----------------------------------------------------------------------
|
|
97
|
-
class InsignificantWhitespaceAntlrVisitorMixin(AntlrVisitorMixinBase):
|
|
98
|
-
"""Mixin that adds functionality used for grammars that ignore insignificant whitespace."""
|
|
99
|
-
|
|
100
|
-
# ----------------------------------------------------------------------
|
|
101
|
-
def _CreateRegionFromRule(self, ctx: antlr4.ParserRuleContext) -> Region:
|
|
102
|
-
start_token = ctx.start
|
|
103
|
-
stop_token = ctx.stop
|
|
104
|
-
|
|
105
|
-
if start_token is None or stop_token is None:
|
|
106
|
-
msg = "Context does not have start or stop token"
|
|
107
|
-
raise ValueError(msg)
|
|
108
|
-
|
|
109
|
-
start_line = start_token.line
|
|
110
|
-
start_col = start_token.column + 1
|
|
111
|
-
end_line = stop_token.line
|
|
112
|
-
end_col = stop_token.column + 1 + len(stop_token.text)
|
|
113
|
-
|
|
114
|
-
assert start_line is not None
|
|
115
|
-
assert end_line is not None
|
|
116
|
-
|
|
117
|
-
return Region(
|
|
118
|
-
self.filename,
|
|
119
|
-
Location(start_line, start_col),
|
|
120
|
-
Location(end_line, end_col),
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# ----------------------------------------------------------------------
|
|
125
|
-
class SignificantWhitespaceAntlrVisitorMixin(AntlrVisitorMixinBase):
|
|
126
|
-
"""Mixin that adds functionality used for grammars that treat whitespace as significant."""
|
|
127
|
-
|
|
128
|
-
# ----------------------------------------------------------------------
|
|
129
|
-
def __init__(
|
|
130
|
-
self,
|
|
131
|
-
dedent_token: int,
|
|
132
|
-
newline_token: int,
|
|
133
|
-
newline_token_string: str,
|
|
134
|
-
) -> None:
|
|
135
|
-
self._dedent_token = dedent_token
|
|
136
|
-
self._newline_token = newline_token
|
|
137
|
-
self._newline_token_string = newline_token_string
|
|
138
|
-
|
|
139
|
-
self._newline_indent_regex = re.compile(r"^\r?\n[ \t]*$")
|
|
140
|
-
|
|
141
|
-
# ----------------------------------------------------------------------
|
|
142
|
-
@override
|
|
143
|
-
def _CreateRegionFromRule(
|
|
144
|
-
self,
|
|
145
|
-
ctx: antlr4.ParserRuleContext,
|
|
146
|
-
) -> Region:
|
|
147
|
-
assert isinstance(ctx.start, antlr4.Token), ctx.start
|
|
148
|
-
assert isinstance(ctx.stop, antlr4.Token), ctx.stop
|
|
149
|
-
|
|
150
|
-
if ctx.stop.text == self._newline_token_string or self._newline_indent_regex.match(ctx.stop.text):
|
|
151
|
-
assert ctx.stop.line == ctx.start.line or ctx.stop.line - 1 == ctx.start.line, (
|
|
152
|
-
ctx.start.line,
|
|
153
|
-
ctx.stop.line,
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
stop_line = ctx.start.line
|
|
157
|
-
stop_column = ctx.start.column + 1 + len(ctx.start.text)
|
|
158
|
-
|
|
159
|
-
return Region(
|
|
160
|
-
self.filename,
|
|
161
|
-
Location(ctx.start.line, ctx.start.column + 1),
|
|
162
|
-
Location(stop_line, stop_column),
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
return Region(
|
|
166
|
-
self.filename,
|
|
167
|
-
Location(ctx.start.line, ctx.start.column + 1),
|
|
168
|
-
Location(ctx.stop.line, ctx.stop.column + 1),
|
|
169
|
-
)
|
{dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/__init__.py
RENAMED
|
File without changes
|
{dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/antlr/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/expression.py
RENAMED
|
File without changes
|
{dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/location.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dbrownell_parserlib-0.10.2 → dbrownell_parserlib-0.11.0}/src/dbrownell_ParserLib/visitors.py
RENAMED
|
File without changes
|