openscad-lalr-parser 1.0.1__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.
- openscad_lalr_parser-1.0.1/PKG-INFO +297 -0
- openscad_lalr_parser-1.0.1/README.md +256 -0
- openscad_lalr_parser-1.0.1/pyproject.toml +66 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/__init__.py +841 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/cli.py +99 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/grammar.lark +222 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/nodes.py +1547 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/pretty_print.py +478 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/scope.py +121 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/serialization.py +309 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/source_map.py +398 -0
- openscad_lalr_parser-1.0.1/src/openscad_lalr_parser/transformer.py +557 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openscad_lalr_parser
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: A fast LALR(1) parser for the OpenSCAD language with AST generation, powered by Lark.
|
|
5
|
+
Keywords: openscad,openscad parser,parser,lalr
|
|
6
|
+
Author: Revar Desmera
|
|
7
|
+
Author-email: Revar Desmera <revarbat@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Manufacturing
|
|
13
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Artistic Software
|
|
21
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
22
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Dist: lark>=1.1.0
|
|
26
|
+
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=7.1.0 ; extra == 'dev'
|
|
28
|
+
Requires-Dist: genbadge[coverage]>=1.1.0 ; extra == 'dev'
|
|
29
|
+
Requires-Dist: pyyaml>=6.0 ; extra == 'dev'
|
|
30
|
+
Requires-Dist: pyyaml>=6.0 ; extra == 'yaml'
|
|
31
|
+
Maintainer: Revar Desmera
|
|
32
|
+
Maintainer-email: Revar Desmera <revarbat@gmail.com>
|
|
33
|
+
Requires-Python: >=3.11
|
|
34
|
+
Project-URL: Bug Tracker, https://github.com/BelfrySCAD/openscad_lalr_parser/issues
|
|
35
|
+
Project-URL: Homepage, https://github.com/BelfrySCAD/openscad_lalr_parser
|
|
36
|
+
Project-URL: Releases, https://github.com/BelfrySCAD/openscad_lalr_parser/releases
|
|
37
|
+
Project-URL: Repository, https://github.com/BelfrySCAD/openscad_lalr_parser
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Provides-Extra: yaml
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# openscad_lalr_parser
|
|
43
|
+
|
|
44
|
+
A fast LALR(1) parser for the OpenSCAD language with AST generation, powered by [Lark](https://github.com/lark-parser/lark).
|
|
45
|
+
|
|
46
|
+
[](https://github.com/BelfrySCAD/openscad_lalr_parser/actions/workflows/pytest.yml)
|
|
47
|
+
[](https://pypi.org/project/openscad-lalr-parser/)
|
|
48
|
+
[](https://opensource.org/licenses/MIT)
|
|
49
|
+
|
|
50
|
+
## Overview
|
|
51
|
+
|
|
52
|
+
`openscad_lalr_parser` parses OpenSCAD source code using a Lark-based LALR(1) parser and produces
|
|
53
|
+
an abstract syntax tree (AST). The AST node classes are identical to those in the
|
|
54
|
+
[openscad_parser](https://github.com/BelfrySCAD/openscad_parser) library, making it a drop-in
|
|
55
|
+
replacement with significantly better performance.
|
|
56
|
+
|
|
57
|
+
### Why LALR?
|
|
58
|
+
|
|
59
|
+
- **Speed**: LALR(1) parsing is O(n) in the input size — no PEG backtracking overhead.
|
|
60
|
+
- **Predictability**: Parse time is always proportional to input size.
|
|
61
|
+
- **Compatibility**: Produces the same AST node types as `openscad_parser`.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install openscad-lalr-parser
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For YAML output support:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install openscad-lalr-parser[yaml]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### From Source
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/BelfrySCAD/openscad_lalr_parser.git
|
|
79
|
+
cd openscad_lalr_parser
|
|
80
|
+
pip install -e ".[dev]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### Parse a String
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from openscad_lalr_parser import getASTfromString
|
|
89
|
+
|
|
90
|
+
ast = getASTfromString("cube([1, 2, 3]);")
|
|
91
|
+
for node in ast:
|
|
92
|
+
print(node)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Parse a File
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from openscad_lalr_parser import getASTfromFile
|
|
99
|
+
|
|
100
|
+
ast = getASTfromFile("model.scad")
|
|
101
|
+
for node in ast:
|
|
102
|
+
print(node)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Files are cached by modification time — repeated calls to `getASTfromFile()` with the same
|
|
106
|
+
unchanged file return instantly. A disk cache is also maintained for persistence across
|
|
107
|
+
interpreter sessions.
|
|
108
|
+
|
|
109
|
+
### Include Comments
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
ast = getASTfromString("""
|
|
113
|
+
// A comment
|
|
114
|
+
x = 42; /* inline */
|
|
115
|
+
""", include_comments=True)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Comments are returned as `CommentLine`, `CommentSpan`, and `BlankLine` nodes at the top level.
|
|
119
|
+
Inline comments adjacent to expressions are wrapped in `CommentedExpr` nodes with
|
|
120
|
+
`leading_comments` and `trailing_comments` fields.
|
|
121
|
+
|
|
122
|
+
### Scope Analysis
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from openscad_lalr_parser import getASTfromString, build_scopes
|
|
126
|
+
|
|
127
|
+
ast = getASTfromString('''
|
|
128
|
+
x = 10;
|
|
129
|
+
function double(n) = n * 2;
|
|
130
|
+
module box(size) { cube(size); }
|
|
131
|
+
''')
|
|
132
|
+
|
|
133
|
+
root_scope = build_scopes(ast)
|
|
134
|
+
print(root_scope.lookup_variable("x"))
|
|
135
|
+
print(root_scope.lookup_function("double"))
|
|
136
|
+
print(root_scope.lookup_module("box"))
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Serialization
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from openscad_lalr_parser import getASTfromString, ast_to_json, ast_from_json
|
|
143
|
+
|
|
144
|
+
ast = getASTfromString("cube(10);")
|
|
145
|
+
|
|
146
|
+
# Serialize to JSON
|
|
147
|
+
json_str = ast_to_json(ast, indent=2)
|
|
148
|
+
|
|
149
|
+
# Deserialize back to AST nodes
|
|
150
|
+
ast2 = ast_from_json(json_str)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
YAML serialization is available when PyYAML is installed:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from openscad_lalr_parser import ast_to_yaml, ast_from_yaml
|
|
157
|
+
|
|
158
|
+
yaml_str = ast_to_yaml(ast)
|
|
159
|
+
ast2 = ast_from_yaml(yaml_str)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Pretty-Printing
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from openscad_lalr_parser import getASTfromString, to_openscad
|
|
166
|
+
|
|
167
|
+
ast = getASTfromString("module box(w,h){cube([w,h,1]);}")
|
|
168
|
+
print(to_openscad(ast, indent_width=4))
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Output:
|
|
172
|
+
|
|
173
|
+
```openscad
|
|
174
|
+
module box(w, h) {
|
|
175
|
+
cube([w, h, 1]);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## CLI
|
|
180
|
+
|
|
181
|
+
The `openscad-lalr` command-line tool parses OpenSCAD files and outputs JSON, YAML, or
|
|
182
|
+
reformatted source.
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Parse to JSON (default)
|
|
186
|
+
openscad-lalr model.scad
|
|
187
|
+
|
|
188
|
+
# Pretty-print / reformat
|
|
189
|
+
openscad-lalr --format model.scad
|
|
190
|
+
|
|
191
|
+
# YAML output
|
|
192
|
+
openscad-lalr --yaml model.scad
|
|
193
|
+
|
|
194
|
+
# Read from stdin
|
|
195
|
+
echo 'cube(10);' | openscad-lalr -
|
|
196
|
+
|
|
197
|
+
# Include comments in output
|
|
198
|
+
openscad-lalr --with-comments model.scad
|
|
199
|
+
|
|
200
|
+
# Custom indentation
|
|
201
|
+
openscad-lalr --format --indent 2 model.scad
|
|
202
|
+
|
|
203
|
+
# Skip include resolution
|
|
204
|
+
openscad-lalr --no-includes model.scad
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## API Reference
|
|
208
|
+
|
|
209
|
+
### Parsing Functions
|
|
210
|
+
|
|
211
|
+
| Function | Description |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `getASTfromString(code, include_comments=False, origin="<string>")` | Parse OpenSCAD source code and return its AST. |
|
|
214
|
+
| `getASTfromFile(file, include_comments=False, process_includes=True)` | Parse a file with mtime-based caching and optional include resolution. |
|
|
215
|
+
| `getASTfromLibraryFile(currfile, libfile, ...)` | Find and parse a library file using OpenSCAD's search path rules. |
|
|
216
|
+
| `parse_ast(code, origin="<string>")` | Low-level parse returning AST nodes (no comment processing). |
|
|
217
|
+
| `findLibraryFile(currfile, libfile)` | Find a library file path without parsing it. |
|
|
218
|
+
| `clear_ast_cache()` | Clear the in-memory and on-disk AST caches. |
|
|
219
|
+
|
|
220
|
+
### Serialization Functions
|
|
221
|
+
|
|
222
|
+
| Function | Description |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `ast_to_json(ast, indent=None)` | Serialize AST nodes to a JSON string. |
|
|
225
|
+
| `ast_from_json(json_str)` | Deserialize AST nodes from a JSON string. |
|
|
226
|
+
| `ast_to_dict(ast)` | Convert AST nodes to plain dicts. |
|
|
227
|
+
| `ast_from_dict(data)` | Reconstruct AST nodes from dicts. |
|
|
228
|
+
| `ast_to_yaml(ast)` | Serialize AST nodes to YAML (requires PyYAML). |
|
|
229
|
+
| `ast_from_yaml(yaml_str)` | Deserialize AST nodes from YAML. |
|
|
230
|
+
| `to_openscad(ast, indent_width=4)` | Pretty-print AST back to OpenSCAD source. |
|
|
231
|
+
|
|
232
|
+
### Scope Analysis
|
|
233
|
+
|
|
234
|
+
| Function / Class | Description |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `build_scopes(ast)` | Build scope tree for top-level AST nodes. Returns root `Scope`. |
|
|
237
|
+
| `Scope` | Lexical scope with `lookup_variable()`, `lookup_function()`, `lookup_module()`. |
|
|
238
|
+
|
|
239
|
+
### AST Node Classes
|
|
240
|
+
|
|
241
|
+
All AST nodes inherit from `ASTNode`. The main categories are:
|
|
242
|
+
|
|
243
|
+
**Literals**: `Identifier`, `StringLiteral`, `NumberLiteral`, `BooleanLiteral`, `UndefinedLiteral`, `RangeLiteral`
|
|
244
|
+
|
|
245
|
+
**Operators**: `AdditionOp`, `SubtractionOp`, `MultiplicationOp`, `DivisionOp`, `ModuloOp`, `ExponentOp`, `UnaryMinusOp`, `LogicalAndOp`, `LogicalOrOp`, `LogicalNotOp`, `BitwiseAndOp`, `BitwiseOrOp`, `BitwiseNotOp`, `BitwiseShiftLeftOp`, `BitwiseShiftRightOp`, `EqualityOp`, `InequalityOp`, `GreaterThanOp`, `GreaterThanOrEqualOp`, `LessThanOp`, `LessThanOrEqualOp`, `TernaryOp`
|
|
246
|
+
|
|
247
|
+
**Expressions**: `PrimaryCall`, `PrimaryIndex`, `PrimaryMember`, `LetOp`, `EchoOp`, `AssertOp`, `FunctionLiteral`, `ListComprehension`
|
|
248
|
+
|
|
249
|
+
**List Comprehensions**: `ListCompFor`, `ListCompCFor`, `ListCompIf`, `ListCompIfElse`, `ListCompLet`, `ListCompEach`
|
|
250
|
+
|
|
251
|
+
**Declarations**: `Assignment`, `FunctionDeclaration`, `ModuleDeclaration`, `ParameterDeclaration`
|
|
252
|
+
|
|
253
|
+
**Module Instantiations**: `ModularCall`, `ModularFor`, `ModularIntersectionFor`, `ModularLet`, `ModularEcho`, `ModularAssert`, `ModularIf`, `ModularIfElse`
|
|
254
|
+
|
|
255
|
+
**Modifiers**: `ModularModifierShowOnly`, `ModularModifierHighlight`, `ModularModifierBackground`, `ModularModifierDisable`
|
|
256
|
+
|
|
257
|
+
**Imports**: `UseStatement`, `IncludeStatement`
|
|
258
|
+
|
|
259
|
+
**Arguments**: `PositionalArgument`, `NamedArgument`
|
|
260
|
+
|
|
261
|
+
**Comments**: `CommentLine`, `CommentSpan`, `CommentedExpr`, `BlankLine`
|
|
262
|
+
|
|
263
|
+
## Comparison with openscad_parser
|
|
264
|
+
|
|
265
|
+
| Feature | openscad_parser | openscad_lalr_parser |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| Parser type | PEG (Arpeggio) | LALR(1) (Lark) |
|
|
268
|
+
| AST nodes | ✓ | ✓ (identical) |
|
|
269
|
+
| Scope analysis | ✓ | ✓ |
|
|
270
|
+
| Comment preservation | ✓ | ✓ |
|
|
271
|
+
| Inline comment attachment | ✓ | ✓ |
|
|
272
|
+
| Serialization (JSON/YAML) | ✓ | ✓ |
|
|
273
|
+
| Pretty-printing | ✓ | ✓ |
|
|
274
|
+
| Source maps | — | ✓ |
|
|
275
|
+
| CLI tool | — | ✓ (`openscad-lalr`) |
|
|
276
|
+
| Disk caching | — | ✓ |
|
|
277
|
+
| Performance | Baseline | Faster (LALR, no backtracking) |
|
|
278
|
+
|
|
279
|
+
## Development
|
|
280
|
+
|
|
281
|
+
### Running Tests
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
pytest tests/ -v
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Running Tests with Coverage
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
pytest tests/ --cov=src/openscad_lalr_parser --cov-report=term-missing
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT License - Copyright (c) 2025 Belfry OpenSCAD Libraries
|
|
296
|
+
|
|
297
|
+
See [LICENSE](LICENSE) for full text.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# openscad_lalr_parser
|
|
2
|
+
|
|
3
|
+
A fast LALR(1) parser for the OpenSCAD language with AST generation, powered by [Lark](https://github.com/lark-parser/lark).
|
|
4
|
+
|
|
5
|
+
[](https://github.com/BelfrySCAD/openscad_lalr_parser/actions/workflows/pytest.yml)
|
|
6
|
+
[](https://pypi.org/project/openscad-lalr-parser/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
`openscad_lalr_parser` parses OpenSCAD source code using a Lark-based LALR(1) parser and produces
|
|
12
|
+
an abstract syntax tree (AST). The AST node classes are identical to those in the
|
|
13
|
+
[openscad_parser](https://github.com/BelfrySCAD/openscad_parser) library, making it a drop-in
|
|
14
|
+
replacement with significantly better performance.
|
|
15
|
+
|
|
16
|
+
### Why LALR?
|
|
17
|
+
|
|
18
|
+
- **Speed**: LALR(1) parsing is O(n) in the input size — no PEG backtracking overhead.
|
|
19
|
+
- **Predictability**: Parse time is always proportional to input size.
|
|
20
|
+
- **Compatibility**: Produces the same AST node types as `openscad_parser`.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install openscad-lalr-parser
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For YAML output support:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install openscad-lalr-parser[yaml]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### From Source
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/BelfrySCAD/openscad_lalr_parser.git
|
|
38
|
+
cd openscad_lalr_parser
|
|
39
|
+
pip install -e ".[dev]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Parse a String
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from openscad_lalr_parser import getASTfromString
|
|
48
|
+
|
|
49
|
+
ast = getASTfromString("cube([1, 2, 3]);")
|
|
50
|
+
for node in ast:
|
|
51
|
+
print(node)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Parse a File
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from openscad_lalr_parser import getASTfromFile
|
|
58
|
+
|
|
59
|
+
ast = getASTfromFile("model.scad")
|
|
60
|
+
for node in ast:
|
|
61
|
+
print(node)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Files are cached by modification time — repeated calls to `getASTfromFile()` with the same
|
|
65
|
+
unchanged file return instantly. A disk cache is also maintained for persistence across
|
|
66
|
+
interpreter sessions.
|
|
67
|
+
|
|
68
|
+
### Include Comments
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
ast = getASTfromString("""
|
|
72
|
+
// A comment
|
|
73
|
+
x = 42; /* inline */
|
|
74
|
+
""", include_comments=True)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Comments are returned as `CommentLine`, `CommentSpan`, and `BlankLine` nodes at the top level.
|
|
78
|
+
Inline comments adjacent to expressions are wrapped in `CommentedExpr` nodes with
|
|
79
|
+
`leading_comments` and `trailing_comments` fields.
|
|
80
|
+
|
|
81
|
+
### Scope Analysis
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from openscad_lalr_parser import getASTfromString, build_scopes
|
|
85
|
+
|
|
86
|
+
ast = getASTfromString('''
|
|
87
|
+
x = 10;
|
|
88
|
+
function double(n) = n * 2;
|
|
89
|
+
module box(size) { cube(size); }
|
|
90
|
+
''')
|
|
91
|
+
|
|
92
|
+
root_scope = build_scopes(ast)
|
|
93
|
+
print(root_scope.lookup_variable("x"))
|
|
94
|
+
print(root_scope.lookup_function("double"))
|
|
95
|
+
print(root_scope.lookup_module("box"))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Serialization
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from openscad_lalr_parser import getASTfromString, ast_to_json, ast_from_json
|
|
102
|
+
|
|
103
|
+
ast = getASTfromString("cube(10);")
|
|
104
|
+
|
|
105
|
+
# Serialize to JSON
|
|
106
|
+
json_str = ast_to_json(ast, indent=2)
|
|
107
|
+
|
|
108
|
+
# Deserialize back to AST nodes
|
|
109
|
+
ast2 = ast_from_json(json_str)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
YAML serialization is available when PyYAML is installed:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from openscad_lalr_parser import ast_to_yaml, ast_from_yaml
|
|
116
|
+
|
|
117
|
+
yaml_str = ast_to_yaml(ast)
|
|
118
|
+
ast2 = ast_from_yaml(yaml_str)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Pretty-Printing
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from openscad_lalr_parser import getASTfromString, to_openscad
|
|
125
|
+
|
|
126
|
+
ast = getASTfromString("module box(w,h){cube([w,h,1]);}")
|
|
127
|
+
print(to_openscad(ast, indent_width=4))
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Output:
|
|
131
|
+
|
|
132
|
+
```openscad
|
|
133
|
+
module box(w, h) {
|
|
134
|
+
cube([w, h, 1]);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## CLI
|
|
139
|
+
|
|
140
|
+
The `openscad-lalr` command-line tool parses OpenSCAD files and outputs JSON, YAML, or
|
|
141
|
+
reformatted source.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Parse to JSON (default)
|
|
145
|
+
openscad-lalr model.scad
|
|
146
|
+
|
|
147
|
+
# Pretty-print / reformat
|
|
148
|
+
openscad-lalr --format model.scad
|
|
149
|
+
|
|
150
|
+
# YAML output
|
|
151
|
+
openscad-lalr --yaml model.scad
|
|
152
|
+
|
|
153
|
+
# Read from stdin
|
|
154
|
+
echo 'cube(10);' | openscad-lalr -
|
|
155
|
+
|
|
156
|
+
# Include comments in output
|
|
157
|
+
openscad-lalr --with-comments model.scad
|
|
158
|
+
|
|
159
|
+
# Custom indentation
|
|
160
|
+
openscad-lalr --format --indent 2 model.scad
|
|
161
|
+
|
|
162
|
+
# Skip include resolution
|
|
163
|
+
openscad-lalr --no-includes model.scad
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## API Reference
|
|
167
|
+
|
|
168
|
+
### Parsing Functions
|
|
169
|
+
|
|
170
|
+
| Function | Description |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `getASTfromString(code, include_comments=False, origin="<string>")` | Parse OpenSCAD source code and return its AST. |
|
|
173
|
+
| `getASTfromFile(file, include_comments=False, process_includes=True)` | Parse a file with mtime-based caching and optional include resolution. |
|
|
174
|
+
| `getASTfromLibraryFile(currfile, libfile, ...)` | Find and parse a library file using OpenSCAD's search path rules. |
|
|
175
|
+
| `parse_ast(code, origin="<string>")` | Low-level parse returning AST nodes (no comment processing). |
|
|
176
|
+
| `findLibraryFile(currfile, libfile)` | Find a library file path without parsing it. |
|
|
177
|
+
| `clear_ast_cache()` | Clear the in-memory and on-disk AST caches. |
|
|
178
|
+
|
|
179
|
+
### Serialization Functions
|
|
180
|
+
|
|
181
|
+
| Function | Description |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `ast_to_json(ast, indent=None)` | Serialize AST nodes to a JSON string. |
|
|
184
|
+
| `ast_from_json(json_str)` | Deserialize AST nodes from a JSON string. |
|
|
185
|
+
| `ast_to_dict(ast)` | Convert AST nodes to plain dicts. |
|
|
186
|
+
| `ast_from_dict(data)` | Reconstruct AST nodes from dicts. |
|
|
187
|
+
| `ast_to_yaml(ast)` | Serialize AST nodes to YAML (requires PyYAML). |
|
|
188
|
+
| `ast_from_yaml(yaml_str)` | Deserialize AST nodes from YAML. |
|
|
189
|
+
| `to_openscad(ast, indent_width=4)` | Pretty-print AST back to OpenSCAD source. |
|
|
190
|
+
|
|
191
|
+
### Scope Analysis
|
|
192
|
+
|
|
193
|
+
| Function / Class | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `build_scopes(ast)` | Build scope tree for top-level AST nodes. Returns root `Scope`. |
|
|
196
|
+
| `Scope` | Lexical scope with `lookup_variable()`, `lookup_function()`, `lookup_module()`. |
|
|
197
|
+
|
|
198
|
+
### AST Node Classes
|
|
199
|
+
|
|
200
|
+
All AST nodes inherit from `ASTNode`. The main categories are:
|
|
201
|
+
|
|
202
|
+
**Literals**: `Identifier`, `StringLiteral`, `NumberLiteral`, `BooleanLiteral`, `UndefinedLiteral`, `RangeLiteral`
|
|
203
|
+
|
|
204
|
+
**Operators**: `AdditionOp`, `SubtractionOp`, `MultiplicationOp`, `DivisionOp`, `ModuloOp`, `ExponentOp`, `UnaryMinusOp`, `LogicalAndOp`, `LogicalOrOp`, `LogicalNotOp`, `BitwiseAndOp`, `BitwiseOrOp`, `BitwiseNotOp`, `BitwiseShiftLeftOp`, `BitwiseShiftRightOp`, `EqualityOp`, `InequalityOp`, `GreaterThanOp`, `GreaterThanOrEqualOp`, `LessThanOp`, `LessThanOrEqualOp`, `TernaryOp`
|
|
205
|
+
|
|
206
|
+
**Expressions**: `PrimaryCall`, `PrimaryIndex`, `PrimaryMember`, `LetOp`, `EchoOp`, `AssertOp`, `FunctionLiteral`, `ListComprehension`
|
|
207
|
+
|
|
208
|
+
**List Comprehensions**: `ListCompFor`, `ListCompCFor`, `ListCompIf`, `ListCompIfElse`, `ListCompLet`, `ListCompEach`
|
|
209
|
+
|
|
210
|
+
**Declarations**: `Assignment`, `FunctionDeclaration`, `ModuleDeclaration`, `ParameterDeclaration`
|
|
211
|
+
|
|
212
|
+
**Module Instantiations**: `ModularCall`, `ModularFor`, `ModularIntersectionFor`, `ModularLet`, `ModularEcho`, `ModularAssert`, `ModularIf`, `ModularIfElse`
|
|
213
|
+
|
|
214
|
+
**Modifiers**: `ModularModifierShowOnly`, `ModularModifierHighlight`, `ModularModifierBackground`, `ModularModifierDisable`
|
|
215
|
+
|
|
216
|
+
**Imports**: `UseStatement`, `IncludeStatement`
|
|
217
|
+
|
|
218
|
+
**Arguments**: `PositionalArgument`, `NamedArgument`
|
|
219
|
+
|
|
220
|
+
**Comments**: `CommentLine`, `CommentSpan`, `CommentedExpr`, `BlankLine`
|
|
221
|
+
|
|
222
|
+
## Comparison with openscad_parser
|
|
223
|
+
|
|
224
|
+
| Feature | openscad_parser | openscad_lalr_parser |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| Parser type | PEG (Arpeggio) | LALR(1) (Lark) |
|
|
227
|
+
| AST nodes | ✓ | ✓ (identical) |
|
|
228
|
+
| Scope analysis | ✓ | ✓ |
|
|
229
|
+
| Comment preservation | ✓ | ✓ |
|
|
230
|
+
| Inline comment attachment | ✓ | ✓ |
|
|
231
|
+
| Serialization (JSON/YAML) | ✓ | ✓ |
|
|
232
|
+
| Pretty-printing | ✓ | ✓ |
|
|
233
|
+
| Source maps | — | ✓ |
|
|
234
|
+
| CLI tool | — | ✓ (`openscad-lalr`) |
|
|
235
|
+
| Disk caching | — | ✓ |
|
|
236
|
+
| Performance | Baseline | Faster (LALR, no backtracking) |
|
|
237
|
+
|
|
238
|
+
## Development
|
|
239
|
+
|
|
240
|
+
### Running Tests
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
pytest tests/ -v
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Running Tests with Coverage
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pytest tests/ --cov=src/openscad_lalr_parser --cov-report=term-missing
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT License - Copyright (c) 2025 Belfry OpenSCAD Libraries
|
|
255
|
+
|
|
256
|
+
See [LICENSE](LICENSE) for full text.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.8.22,<=0.9.20"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openscad_lalr_parser"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "A fast LALR(1) parser for the OpenSCAD language with AST generation, powered by Lark."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name="Revar Desmera", email="revarbat@gmail.com" },
|
|
12
|
+
]
|
|
13
|
+
maintainers = [
|
|
14
|
+
{ name="Revar Desmera", email="revarbat@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
license = "MIT"
|
|
17
|
+
requires-python = ">=3.11"
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Manufacturing",
|
|
23
|
+
"Operating System :: MacOS :: MacOS X",
|
|
24
|
+
"Operating System :: Microsoft :: Windows",
|
|
25
|
+
"Operating System :: POSIX",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Topic :: Artistic Software",
|
|
31
|
+
"Topic :: Multimedia :: Graphics :: 3D Modeling",
|
|
32
|
+
"Topic :: Multimedia :: Graphics :: 3D Rendering",
|
|
33
|
+
"Topic :: Software Development :: Libraries",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
]
|
|
36
|
+
keywords = ["openscad", "openscad parser", "parser", "lalr"]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"lark>=1.1.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
yaml = [
|
|
43
|
+
"pyyaml>=6.0",
|
|
44
|
+
]
|
|
45
|
+
dev = [
|
|
46
|
+
"pytest>=7.0.0",
|
|
47
|
+
"pytest-cov>=7.1.0",
|
|
48
|
+
"genbadge[coverage]>=1.1.0",
|
|
49
|
+
"pyyaml>=6.0",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
openscad-lalr = "openscad_lalr_parser.cli:main"
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
"Homepage" = "https://github.com/BelfrySCAD/openscad_lalr_parser"
|
|
57
|
+
"Repository" = "https://github.com/BelfrySCAD/openscad_lalr_parser"
|
|
58
|
+
"Bug Tracker" = "https://github.com/BelfrySCAD/openscad_lalr_parser/issues"
|
|
59
|
+
"Releases" = "https://github.com/BelfrySCAD/openscad_lalr_parser/releases"
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
python_files = ["test_*.py"]
|
|
64
|
+
python_classes = ["Test*"]
|
|
65
|
+
python_functions = ["test_*"]
|
|
66
|
+
addopts = "-v"
|