dbrownell-parserlib 0.8.2__tar.gz → 0.8.4__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.8.2 → dbrownell_parserlib-0.8.4}/PKG-INFO +151 -3
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/README.md +150 -2
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/pyproject.toml +1 -1
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/__init__.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/__init__.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/antlr_parser.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/antlr_visitor_mixins.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/__init__.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/antlr-4.13.2-complete.jar +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/errors.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/expression.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/location.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/py.typed +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/region.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/terminal_expression.py +0 -0
- {dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/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.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Functionality useful when creating parsers.
|
|
5
5
|
Author: Dave Brownell
|
|
6
6
|
Author-email: Dave Brownell <github@DavidBrownell.com>
|
|
@@ -47,10 +47,158 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
- [License](#license)
|
|
48
48
|
|
|
49
49
|
## Overview
|
|
50
|
-
|
|
50
|
+
`dbrownell_ParserLib` is a Python library that provides utility functionality for creating parsers, with a primary focus on [ANTLR](https://antlr.org) (Another Tool for Language Recognition) integration. The library simplifies the process of building parsers that produce strongly-typed abstract syntax trees (ASTs) with comprehensive source location tracking.
|
|
51
|
+
|
|
52
|
+
### Key Features
|
|
53
|
+
- **ANTLR Integration**: Streamlined wrapper around ANTLR for Python, handling grammar compilation and parser generation
|
|
54
|
+
- **AST Representation**: Expression-based AST nodes with automatic parent-child relationship management
|
|
55
|
+
- **Source Location Tracking**: Every AST node tracks its source file location (filename, line, column ranges) for detailed error reporting
|
|
56
|
+
- **Visitor Pattern**: Flexible visitor pattern implementation with fine-grained traversal control
|
|
57
|
+
- **Error Handling**: Rich error objects that associate messages with source locations and extract Python exception tracebacks
|
|
58
|
+
- **Multi-threaded Parsing**: Built-in support for parsing multiple files concurrently
|
|
59
|
+
- **Workspace Support**: Parse entire directory trees with customizable file filtering
|
|
60
|
+
- **Whitespace Handling**: Mixins for both insignificant and significant whitespace grammars (e.g., Python-like indentation)
|
|
51
61
|
|
|
52
62
|
### How to use `dbrownell_ParserLib`
|
|
53
|
-
|
|
63
|
+
|
|
64
|
+
The typical workflow for creating a parser with this library involves the following steps. For detailed examples, see the test files in the repository, particularly the end-to-end tests that demonstrate complete parsing workflows.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
#### 1. Define an ANTLR Grammar
|
|
68
|
+
Create a `.g4` grammar file defining your language syntax:
|
|
69
|
+
|
|
70
|
+
```antlr
|
|
71
|
+
grammar Calculator;
|
|
72
|
+
|
|
73
|
+
expr : expr ('*'|'/') expr # BinaryOp
|
|
74
|
+
| expr ('+'|'-') expr # BinaryOp
|
|
75
|
+
| INT # Number
|
|
76
|
+
;
|
|
77
|
+
|
|
78
|
+
INT : [0-9]+ ;
|
|
79
|
+
WS : [ \t\n\r]+ -> skip ;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### 2. Build the Grammar
|
|
83
|
+
Use `BuildAntlrGrammar` to generate the lexer and parser:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from pathlib import Path
|
|
87
|
+
|
|
88
|
+
from dbrownell_ParserLib import BuildAntlrGrammar
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
BuildAntlrGrammar(
|
|
92
|
+
dm, # DoneManager instance
|
|
93
|
+
Path("Calculator.g4"),
|
|
94
|
+
Path("output_dir"),
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### 3. Create Custom Visitor
|
|
99
|
+
Implement a visitor that converts ANTLR parse trees to Expression objects:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from dataclasses import dataclass
|
|
103
|
+
|
|
104
|
+
from dbrownell_ParserLib import (
|
|
105
|
+
Expression,
|
|
106
|
+
InsignificantWhitespaceAntlrVisitorMixin,
|
|
107
|
+
TerminalExpression,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
from CalculatorVisitor import CalculatorVisitor as GeneratedVisitor
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(eq=False)
|
|
114
|
+
class BinaryExpression(Expression):
|
|
115
|
+
left: Expression
|
|
116
|
+
operator: TerminalExpression[str]
|
|
117
|
+
right: Expression
|
|
118
|
+
|
|
119
|
+
def _GenerateAcceptDetails(self):
|
|
120
|
+
yield "left", self.left
|
|
121
|
+
yield "operator", self.operator
|
|
122
|
+
yield "right", self.right
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisitor):
|
|
126
|
+
def visitExpr(self, ctx):
|
|
127
|
+
children = self.GetChildren(ctx)
|
|
128
|
+
assert len(children) == 2, children
|
|
129
|
+
|
|
130
|
+
self._stack.append(
|
|
131
|
+
BinaryExpression(
|
|
132
|
+
self.CreateRegion(ctx),
|
|
133
|
+
children[0],
|
|
134
|
+
TerminalExpression[str](self.CreateRegion(ctx.children[1]), ctx.children[1].getText()),
|
|
135
|
+
|
|
136
|
+
children[1],
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def visitNumber(self, ctx):
|
|
141
|
+
self._stack.append(
|
|
142
|
+
TerminalExpression(self.CreateRegion(ctx), int(ctx.getText()))
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### 4. Create Parser Instance
|
|
147
|
+
Use `CreateAntlrParser` to build a callable parser:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from dbrownell_ParserLib import CreateAntlrParser
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
parser = CreateAntlrParser(
|
|
154
|
+
CalculatorLexer,
|
|
155
|
+
CalculatorParser,
|
|
156
|
+
CalculatorVisitor,
|
|
157
|
+
lambda p: p.expr(), # Entry point rule
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### 5. Parse Files
|
|
162
|
+
Parse single files, multiple files, or entire workspaces:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from dbrownell_ParserLib import Error
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Parse a single file
|
|
169
|
+
results = parser(dm, Path("input.txt"), None)
|
|
170
|
+
|
|
171
|
+
# Parse multiple files
|
|
172
|
+
results = parser(dm, [Path("file1.txt"), Path("file2.txt")], None)
|
|
173
|
+
|
|
174
|
+
# Check results
|
|
175
|
+
for filepath, result in results.items():
|
|
176
|
+
if isinstance(result, Error):
|
|
177
|
+
print(f"Parse error in {filepath}: {result}")
|
|
178
|
+
else:
|
|
179
|
+
# result is the visitor containing the parsed AST
|
|
180
|
+
ast = result._stack[0]
|
|
181
|
+
# Process the AST
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### 6. Traverse the AST
|
|
185
|
+
Use the visitor pattern to process your AST:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from contextlib import contextmanager
|
|
189
|
+
|
|
190
|
+
from dbrownell_ParserLib import ExpressionVisitorHelper, VisitResult
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class EvaluatorVisitor(ExpressionVisitorHelper):
|
|
194
|
+
@contextmanager
|
|
195
|
+
def OnBinaryExpression(self, expression):
|
|
196
|
+
# Process binary expressions
|
|
197
|
+
yield VisitResult.Continue
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
ast.Accept(EvaluatorVisitor())
|
|
201
|
+
```
|
|
54
202
|
|
|
55
203
|
<!-- Content below this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
|
|
56
204
|
|
|
@@ -25,10 +25,158 @@
|
|
|
25
25
|
- [License](#license)
|
|
26
26
|
|
|
27
27
|
## Overview
|
|
28
|
-
|
|
28
|
+
`dbrownell_ParserLib` is a Python library that provides utility functionality for creating parsers, with a primary focus on [ANTLR](https://antlr.org) (Another Tool for Language Recognition) integration. The library simplifies the process of building parsers that produce strongly-typed abstract syntax trees (ASTs) with comprehensive source location tracking.
|
|
29
|
+
|
|
30
|
+
### Key Features
|
|
31
|
+
- **ANTLR Integration**: Streamlined wrapper around ANTLR for Python, handling grammar compilation and parser generation
|
|
32
|
+
- **AST Representation**: Expression-based AST nodes with automatic parent-child relationship management
|
|
33
|
+
- **Source Location Tracking**: Every AST node tracks its source file location (filename, line, column ranges) for detailed error reporting
|
|
34
|
+
- **Visitor Pattern**: Flexible visitor pattern implementation with fine-grained traversal control
|
|
35
|
+
- **Error Handling**: Rich error objects that associate messages with source locations and extract Python exception tracebacks
|
|
36
|
+
- **Multi-threaded Parsing**: Built-in support for parsing multiple files concurrently
|
|
37
|
+
- **Workspace Support**: Parse entire directory trees with customizable file filtering
|
|
38
|
+
- **Whitespace Handling**: Mixins for both insignificant and significant whitespace grammars (e.g., Python-like indentation)
|
|
29
39
|
|
|
30
40
|
### How to use `dbrownell_ParserLib`
|
|
31
|
-
|
|
41
|
+
|
|
42
|
+
The typical workflow for creating a parser with this library involves the following steps. For detailed examples, see the test files in the repository, particularly the end-to-end tests that demonstrate complete parsing workflows.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#### 1. Define an ANTLR Grammar
|
|
46
|
+
Create a `.g4` grammar file defining your language syntax:
|
|
47
|
+
|
|
48
|
+
```antlr
|
|
49
|
+
grammar Calculator;
|
|
50
|
+
|
|
51
|
+
expr : expr ('*'|'/') expr # BinaryOp
|
|
52
|
+
| expr ('+'|'-') expr # BinaryOp
|
|
53
|
+
| INT # Number
|
|
54
|
+
;
|
|
55
|
+
|
|
56
|
+
INT : [0-9]+ ;
|
|
57
|
+
WS : [ \t\n\r]+ -> skip ;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### 2. Build the Grammar
|
|
61
|
+
Use `BuildAntlrGrammar` to generate the lexer and parser:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pathlib import Path
|
|
65
|
+
|
|
66
|
+
from dbrownell_ParserLib import BuildAntlrGrammar
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
BuildAntlrGrammar(
|
|
70
|
+
dm, # DoneManager instance
|
|
71
|
+
Path("Calculator.g4"),
|
|
72
|
+
Path("output_dir"),
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### 3. Create Custom Visitor
|
|
77
|
+
Implement a visitor that converts ANTLR parse trees to Expression objects:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from dataclasses import dataclass
|
|
81
|
+
|
|
82
|
+
from dbrownell_ParserLib import (
|
|
83
|
+
Expression,
|
|
84
|
+
InsignificantWhitespaceAntlrVisitorMixin,
|
|
85
|
+
TerminalExpression,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
from CalculatorVisitor import CalculatorVisitor as GeneratedVisitor
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(eq=False)
|
|
92
|
+
class BinaryExpression(Expression):
|
|
93
|
+
left: Expression
|
|
94
|
+
operator: TerminalExpression[str]
|
|
95
|
+
right: Expression
|
|
96
|
+
|
|
97
|
+
def _GenerateAcceptDetails(self):
|
|
98
|
+
yield "left", self.left
|
|
99
|
+
yield "operator", self.operator
|
|
100
|
+
yield "right", self.right
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisitor):
|
|
104
|
+
def visitExpr(self, ctx):
|
|
105
|
+
children = self.GetChildren(ctx)
|
|
106
|
+
assert len(children) == 2, children
|
|
107
|
+
|
|
108
|
+
self._stack.append(
|
|
109
|
+
BinaryExpression(
|
|
110
|
+
self.CreateRegion(ctx),
|
|
111
|
+
children[0],
|
|
112
|
+
TerminalExpression[str](self.CreateRegion(ctx.children[1]), ctx.children[1].getText()),
|
|
113
|
+
|
|
114
|
+
children[1],
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def visitNumber(self, ctx):
|
|
119
|
+
self._stack.append(
|
|
120
|
+
TerminalExpression(self.CreateRegion(ctx), int(ctx.getText()))
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### 4. Create Parser Instance
|
|
125
|
+
Use `CreateAntlrParser` to build a callable parser:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from dbrownell_ParserLib import CreateAntlrParser
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
parser = CreateAntlrParser(
|
|
132
|
+
CalculatorLexer,
|
|
133
|
+
CalculatorParser,
|
|
134
|
+
CalculatorVisitor,
|
|
135
|
+
lambda p: p.expr(), # Entry point rule
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### 5. Parse Files
|
|
140
|
+
Parse single files, multiple files, or entire workspaces:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from dbrownell_ParserLib import Error
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Parse a single file
|
|
147
|
+
results = parser(dm, Path("input.txt"), None)
|
|
148
|
+
|
|
149
|
+
# Parse multiple files
|
|
150
|
+
results = parser(dm, [Path("file1.txt"), Path("file2.txt")], None)
|
|
151
|
+
|
|
152
|
+
# Check results
|
|
153
|
+
for filepath, result in results.items():
|
|
154
|
+
if isinstance(result, Error):
|
|
155
|
+
print(f"Parse error in {filepath}: {result}")
|
|
156
|
+
else:
|
|
157
|
+
# result is the visitor containing the parsed AST
|
|
158
|
+
ast = result._stack[0]
|
|
159
|
+
# Process the AST
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### 6. Traverse the AST
|
|
163
|
+
Use the visitor pattern to process your AST:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from contextlib import contextmanager
|
|
167
|
+
|
|
168
|
+
from dbrownell_ParserLib import ExpressionVisitorHelper, VisitResult
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class EvaluatorVisitor(ExpressionVisitorHelper):
|
|
172
|
+
@contextmanager
|
|
173
|
+
def OnBinaryExpression(self, expression):
|
|
174
|
+
# Process binary expressions
|
|
175
|
+
yield VisitResult.Continue
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
ast.Accept(EvaluatorVisitor())
|
|
179
|
+
```
|
|
32
180
|
|
|
33
181
|
<!-- Content below this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
|
|
34
182
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dbrownell-parserlib"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.4"
|
|
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:
|
|
File without changes
|
{dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/antlr/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dbrownell_parserlib-0.8.2 → dbrownell_parserlib-0.8.4}/src/dbrownell_ParserLib/expression.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|