openscad-parser 2.3.2__tar.gz → 2.3.3__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_parser-2.3.2 → openscad_parser-2.3.3}/PKG-INFO +68 -12
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/README.rst +66 -11
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/pyproject.toml +2 -1
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/__init__.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/__init__.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/builder.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/nodes.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/scope.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/serialization.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/ast/source_map.py +0 -0
- {openscad_parser-2.3.2 → openscad_parser-2.3.3}/src/openscad_parser/grammar.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscad_parser
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: A PEG parser to read OpenSCAD language source code, with optional AST tree generation.
|
|
5
5
|
Keywords: openscad,openscad parser,parser
|
|
6
6
|
Author: Revar Desmera
|
|
@@ -23,6 +23,7 @@ Requires-Dist: arpeggio>=2.0.3
|
|
|
23
23
|
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
24
24
|
Requires-Dist: pytest-cov>=7.1.0 ; extra == 'dev'
|
|
25
25
|
Requires-Dist: pyyaml>=6.0 ; extra == 'dev'
|
|
26
|
+
Requires-Dist: coverage-badge>=1.1.0 ; extra == 'dev'
|
|
26
27
|
Requires-Dist: pyyaml>=6.0 ; extra == 'yaml'
|
|
27
28
|
Maintainer: Revar Desmera
|
|
28
29
|
Maintainer-email: Revar Desmera <revarbat@gmail.com>
|
|
@@ -40,6 +41,9 @@ Description-Content-Type: text/x-rst
|
|
|
40
41
|
OpenSCAD Parser
|
|
41
42
|
===============
|
|
42
43
|
|
|
44
|
+
.. image:: https://raw.githubusercontent.com/BelfrySCAD/openscad_parser/main/coverage-badge.svg
|
|
45
|
+
:alt: Coverage
|
|
46
|
+
|
|
43
47
|
A PEG parser for the OpenSCAD language that can parse OpenSCAD source code and optionally generate an Abstract Syntax Tree (AST) for programmatic analysis and manipulation.
|
|
44
48
|
|
|
45
49
|
Features
|
|
@@ -232,7 +236,7 @@ All AST nodes inherit from ``ASTNode`` and have a ``position`` attribute for sou
|
|
|
232
236
|
|
|
233
237
|
# Access source position
|
|
234
238
|
print(assignment.position.line) # Line number (1-indexed)
|
|
235
|
-
print(assignment.position.
|
|
239
|
+
print(assignment.position.column) # Column number (1-indexed)
|
|
236
240
|
|
|
237
241
|
Examples
|
|
238
242
|
--------
|
|
@@ -409,7 +413,7 @@ List Comprehensions
|
|
|
409
413
|
|
|
410
414
|
- ``ListComprehension``: Vector/list literals
|
|
411
415
|
- ``ListCompFor``: for loops in list comprehensions
|
|
412
|
-
- ``
|
|
416
|
+
- ``ListCompCFor``: C-style for loops
|
|
413
417
|
- ``ListCompIf``, ``ListCompIfElse``: Conditionals
|
|
414
418
|
- ``ListCompLet``: let expressions
|
|
415
419
|
- ``ListCompEach``: each expressions
|
|
@@ -419,8 +423,9 @@ Module Instantiations
|
|
|
419
423
|
|
|
420
424
|
- ``ModularCall``: Module calls with arguments and children
|
|
421
425
|
- ``ModularFor``: for loops
|
|
422
|
-
- ``
|
|
426
|
+
- ``ModularCFor``: C-style for loops
|
|
423
427
|
- ``ModularIntersectionFor``: intersection_for loops
|
|
428
|
+
- ``ModularIntersectionCFor``: C-style intersection_for loops
|
|
424
429
|
- ``ModularLet``: let statements
|
|
425
430
|
- ``ModularEcho``: echo statements
|
|
426
431
|
- ``ModularAssert``: assert statements
|
|
@@ -471,10 +476,12 @@ Main Functions
|
|
|
471
476
|
:param debug: If True, enables debug output
|
|
472
477
|
:returns: ParserPython instance
|
|
473
478
|
|
|
474
|
-
``getASTfromString(code: str)``
|
|
479
|
+
``getASTfromString(code: str, include_comments: bool = False, origin: str = "<string>")``
|
|
475
480
|
Parse OpenSCAD code from a string and return its AST.
|
|
476
481
|
|
|
477
482
|
:param code: The OpenSCAD source code to be parsed
|
|
483
|
+
:param include_comments: If True, include comment nodes in the AST (default: False)
|
|
484
|
+
:param origin: Origin identifier used in source position tracking (default: "<string>")
|
|
478
485
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
479
486
|
:rtype: ASTNode | list[ASTNode] | None
|
|
480
487
|
|
|
@@ -515,12 +522,13 @@ Main Functions
|
|
|
515
522
|
|
|
516
523
|
**Note:** The ``process_includes`` parameter affects the AST structure (see ``getASTfromFile`` documentation).
|
|
517
524
|
|
|
518
|
-
``parse_ast(parser, code, file="")``
|
|
525
|
+
``parse_ast(parser, code, file="", source_map=None)``
|
|
519
526
|
Parse OpenSCAD code and generate an AST (lower-level API).
|
|
520
527
|
|
|
521
528
|
:param parser: Arpeggio parser instance from getOpenSCADParser()
|
|
522
529
|
:param code: OpenSCAD code string to parse
|
|
523
530
|
:param file: Optional file path for source location tracking
|
|
531
|
+
:param source_map: Optional ``SourceMap`` for multi-origin position tracking
|
|
524
532
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
525
533
|
|
|
526
534
|
``clear_ast_cache()``
|
|
@@ -529,6 +537,21 @@ Main Functions
|
|
|
529
537
|
|
|
530
538
|
This function removes all cached AST trees from memory.
|
|
531
539
|
|
|
540
|
+
``build_scopes(ast: list[ASTNode]) -> Scope``
|
|
541
|
+
Build a scope tree over an AST and attach a ``scope`` attribute to every node.
|
|
542
|
+
|
|
543
|
+
:param ast: A list of top-level AST nodes (as returned by the ``getAST*`` functions)
|
|
544
|
+
:returns: The root ``Scope`` object
|
|
545
|
+
|
|
546
|
+
``Scope``
|
|
547
|
+
Represents a lexical scope with three independent namespaces (variables, functions,
|
|
548
|
+
modules), mirroring OpenSCAD's scoping rules.
|
|
549
|
+
|
|
550
|
+
- ``scope.lookup_variable(name)`` — search this scope and its parents
|
|
551
|
+
- ``scope.lookup_function(name)`` — search this scope and its parents
|
|
552
|
+
- ``scope.lookup_module(name)`` — search this scope and its parents
|
|
553
|
+
- ``scope.parent`` — the enclosing scope (``None`` for root)
|
|
554
|
+
|
|
532
555
|
Serialization Functions
|
|
533
556
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
534
557
|
|
|
@@ -637,12 +660,15 @@ All AST nodes include source position information::
|
|
|
637
660
|
assignment = ast[0]
|
|
638
661
|
|
|
639
662
|
position = assignment.position
|
|
640
|
-
print(position.
|
|
641
|
-
print(position.line)
|
|
642
|
-
print(position.
|
|
643
|
-
print(position.
|
|
663
|
+
print(position.origin) # "example.scad" (origin identifier)
|
|
664
|
+
print(position.line) # 1 (1-indexed line number)
|
|
665
|
+
print(position.column) # 1 (1-indexed column number)
|
|
666
|
+
print(position.start_offset) # 0 (0-based byte offset of token start within origin)
|
|
667
|
+
print(position.end_offset) # N (0-based exclusive byte offset of token end)
|
|
644
668
|
|
|
645
|
-
The ``Position``
|
|
669
|
+
The ``Position`` dataclass carries both line/column coordinates and byte offsets relative
|
|
670
|
+
to the origin's content. For single-file parses these equal file byte offsets; for
|
|
671
|
+
multi-origin parses (e.g. after include expansion) they are relative to each included file.
|
|
646
672
|
|
|
647
673
|
Serialization
|
|
648
674
|
-------------
|
|
@@ -826,12 +852,42 @@ The AST is a tree structure that can be traversed recursively::
|
|
|
826
852
|
for node in ast:
|
|
827
853
|
visit_node(node)
|
|
828
854
|
|
|
855
|
+
Scope Tracking
|
|
856
|
+
--------------
|
|
857
|
+
|
|
858
|
+
The parser can build a scope tree over the AST, resolving variable, function, and module
|
|
859
|
+
names according to OpenSCAD's three-namespace scoping rules::
|
|
860
|
+
|
|
861
|
+
from openscad_parser.ast import getASTfromString, build_scopes
|
|
862
|
+
|
|
863
|
+
ast = getASTfromString("""
|
|
864
|
+
x = 10;
|
|
865
|
+
module box(size = x) { cube(size); }
|
|
866
|
+
box();
|
|
867
|
+
""")
|
|
868
|
+
|
|
869
|
+
root_scope = build_scopes(ast)
|
|
870
|
+
|
|
871
|
+
# Look up names in the root scope
|
|
872
|
+
print(root_scope.lookup_variable("x")) # Assignment node
|
|
873
|
+
print(root_scope.lookup_module("box")) # ModuleDeclaration node
|
|
874
|
+
|
|
875
|
+
# Each AST node has a .scope attribute pointing to its enclosing scope
|
|
876
|
+
box_decl = ast[1]
|
|
877
|
+
cube_call = box_decl.children[0]
|
|
878
|
+
print(cube_call.scope.lookup_variable("size")) # ParameterDeclaration node
|
|
879
|
+
|
|
880
|
+
``build_scopes(ast)`` returns the root ``Scope`` object and attaches a ``scope`` attribute
|
|
881
|
+
to every node in the tree. Scopes form a parent chain so lookups fall through to enclosing
|
|
882
|
+
scopes automatically. Declarations (variables, functions, modules) inside a block are
|
|
883
|
+
hoisted to the top of that block's scope before child nodes are visited.
|
|
884
|
+
|
|
829
885
|
Testing
|
|
830
886
|
-------
|
|
831
887
|
|
|
832
888
|
The project includes a comprehensive test suite. Run tests with::
|
|
833
889
|
|
|
834
|
-
pytest tests/
|
|
890
|
+
uv run pytest tests/
|
|
835
891
|
|
|
836
892
|
Development
|
|
837
893
|
-----------
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
OpenSCAD Parser
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
+
.. image:: https://raw.githubusercontent.com/BelfrySCAD/openscad_parser/main/coverage-badge.svg
|
|
5
|
+
:alt: Coverage
|
|
6
|
+
|
|
4
7
|
A PEG parser for the OpenSCAD language that can parse OpenSCAD source code and optionally generate an Abstract Syntax Tree (AST) for programmatic analysis and manipulation.
|
|
5
8
|
|
|
6
9
|
Features
|
|
@@ -193,7 +196,7 @@ All AST nodes inherit from ``ASTNode`` and have a ``position`` attribute for sou
|
|
|
193
196
|
|
|
194
197
|
# Access source position
|
|
195
198
|
print(assignment.position.line) # Line number (1-indexed)
|
|
196
|
-
print(assignment.position.
|
|
199
|
+
print(assignment.position.column) # Column number (1-indexed)
|
|
197
200
|
|
|
198
201
|
Examples
|
|
199
202
|
--------
|
|
@@ -370,7 +373,7 @@ List Comprehensions
|
|
|
370
373
|
|
|
371
374
|
- ``ListComprehension``: Vector/list literals
|
|
372
375
|
- ``ListCompFor``: for loops in list comprehensions
|
|
373
|
-
- ``
|
|
376
|
+
- ``ListCompCFor``: C-style for loops
|
|
374
377
|
- ``ListCompIf``, ``ListCompIfElse``: Conditionals
|
|
375
378
|
- ``ListCompLet``: let expressions
|
|
376
379
|
- ``ListCompEach``: each expressions
|
|
@@ -380,8 +383,9 @@ Module Instantiations
|
|
|
380
383
|
|
|
381
384
|
- ``ModularCall``: Module calls with arguments and children
|
|
382
385
|
- ``ModularFor``: for loops
|
|
383
|
-
- ``
|
|
386
|
+
- ``ModularCFor``: C-style for loops
|
|
384
387
|
- ``ModularIntersectionFor``: intersection_for loops
|
|
388
|
+
- ``ModularIntersectionCFor``: C-style intersection_for loops
|
|
385
389
|
- ``ModularLet``: let statements
|
|
386
390
|
- ``ModularEcho``: echo statements
|
|
387
391
|
- ``ModularAssert``: assert statements
|
|
@@ -432,10 +436,12 @@ Main Functions
|
|
|
432
436
|
:param debug: If True, enables debug output
|
|
433
437
|
:returns: ParserPython instance
|
|
434
438
|
|
|
435
|
-
``getASTfromString(code: str)``
|
|
439
|
+
``getASTfromString(code: str, include_comments: bool = False, origin: str = "<string>")``
|
|
436
440
|
Parse OpenSCAD code from a string and return its AST.
|
|
437
441
|
|
|
438
442
|
:param code: The OpenSCAD source code to be parsed
|
|
443
|
+
:param include_comments: If True, include comment nodes in the AST (default: False)
|
|
444
|
+
:param origin: Origin identifier used in source position tracking (default: "<string>")
|
|
439
445
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
440
446
|
:rtype: ASTNode | list[ASTNode] | None
|
|
441
447
|
|
|
@@ -476,12 +482,13 @@ Main Functions
|
|
|
476
482
|
|
|
477
483
|
**Note:** The ``process_includes`` parameter affects the AST structure (see ``getASTfromFile`` documentation).
|
|
478
484
|
|
|
479
|
-
``parse_ast(parser, code, file="")``
|
|
485
|
+
``parse_ast(parser, code, file="", source_map=None)``
|
|
480
486
|
Parse OpenSCAD code and generate an AST (lower-level API).
|
|
481
487
|
|
|
482
488
|
:param parser: Arpeggio parser instance from getOpenSCADParser()
|
|
483
489
|
:param code: OpenSCAD code string to parse
|
|
484
490
|
:param file: Optional file path for source location tracking
|
|
491
|
+
:param source_map: Optional ``SourceMap`` for multi-origin position tracking
|
|
485
492
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
486
493
|
|
|
487
494
|
``clear_ast_cache()``
|
|
@@ -490,6 +497,21 @@ Main Functions
|
|
|
490
497
|
|
|
491
498
|
This function removes all cached AST trees from memory.
|
|
492
499
|
|
|
500
|
+
``build_scopes(ast: list[ASTNode]) -> Scope``
|
|
501
|
+
Build a scope tree over an AST and attach a ``scope`` attribute to every node.
|
|
502
|
+
|
|
503
|
+
:param ast: A list of top-level AST nodes (as returned by the ``getAST*`` functions)
|
|
504
|
+
:returns: The root ``Scope`` object
|
|
505
|
+
|
|
506
|
+
``Scope``
|
|
507
|
+
Represents a lexical scope with three independent namespaces (variables, functions,
|
|
508
|
+
modules), mirroring OpenSCAD's scoping rules.
|
|
509
|
+
|
|
510
|
+
- ``scope.lookup_variable(name)`` — search this scope and its parents
|
|
511
|
+
- ``scope.lookup_function(name)`` — search this scope and its parents
|
|
512
|
+
- ``scope.lookup_module(name)`` — search this scope and its parents
|
|
513
|
+
- ``scope.parent`` — the enclosing scope (``None`` for root)
|
|
514
|
+
|
|
493
515
|
Serialization Functions
|
|
494
516
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
495
517
|
|
|
@@ -598,12 +620,15 @@ All AST nodes include source position information::
|
|
|
598
620
|
assignment = ast[0]
|
|
599
621
|
|
|
600
622
|
position = assignment.position
|
|
601
|
-
print(position.
|
|
602
|
-
print(position.line)
|
|
603
|
-
print(position.
|
|
604
|
-
print(position.
|
|
623
|
+
print(position.origin) # "example.scad" (origin identifier)
|
|
624
|
+
print(position.line) # 1 (1-indexed line number)
|
|
625
|
+
print(position.column) # 1 (1-indexed column number)
|
|
626
|
+
print(position.start_offset) # 0 (0-based byte offset of token start within origin)
|
|
627
|
+
print(position.end_offset) # N (0-based exclusive byte offset of token end)
|
|
605
628
|
|
|
606
|
-
The ``Position``
|
|
629
|
+
The ``Position`` dataclass carries both line/column coordinates and byte offsets relative
|
|
630
|
+
to the origin's content. For single-file parses these equal file byte offsets; for
|
|
631
|
+
multi-origin parses (e.g. after include expansion) they are relative to each included file.
|
|
607
632
|
|
|
608
633
|
Serialization
|
|
609
634
|
-------------
|
|
@@ -787,12 +812,42 @@ The AST is a tree structure that can be traversed recursively::
|
|
|
787
812
|
for node in ast:
|
|
788
813
|
visit_node(node)
|
|
789
814
|
|
|
815
|
+
Scope Tracking
|
|
816
|
+
--------------
|
|
817
|
+
|
|
818
|
+
The parser can build a scope tree over the AST, resolving variable, function, and module
|
|
819
|
+
names according to OpenSCAD's three-namespace scoping rules::
|
|
820
|
+
|
|
821
|
+
from openscad_parser.ast import getASTfromString, build_scopes
|
|
822
|
+
|
|
823
|
+
ast = getASTfromString("""
|
|
824
|
+
x = 10;
|
|
825
|
+
module box(size = x) { cube(size); }
|
|
826
|
+
box();
|
|
827
|
+
""")
|
|
828
|
+
|
|
829
|
+
root_scope = build_scopes(ast)
|
|
830
|
+
|
|
831
|
+
# Look up names in the root scope
|
|
832
|
+
print(root_scope.lookup_variable("x")) # Assignment node
|
|
833
|
+
print(root_scope.lookup_module("box")) # ModuleDeclaration node
|
|
834
|
+
|
|
835
|
+
# Each AST node has a .scope attribute pointing to its enclosing scope
|
|
836
|
+
box_decl = ast[1]
|
|
837
|
+
cube_call = box_decl.children[0]
|
|
838
|
+
print(cube_call.scope.lookup_variable("size")) # ParameterDeclaration node
|
|
839
|
+
|
|
840
|
+
``build_scopes(ast)`` returns the root ``Scope`` object and attaches a ``scope`` attribute
|
|
841
|
+
to every node in the tree. Scopes form a parent chain so lookups fall through to enclosing
|
|
842
|
+
scopes automatically. Declarations (variables, functions, modules) inside a block are
|
|
843
|
+
hoisted to the top of that block's scope before child nodes are visited.
|
|
844
|
+
|
|
790
845
|
Testing
|
|
791
846
|
-------
|
|
792
847
|
|
|
793
848
|
The project includes a comprehensive test suite. Run tests with::
|
|
794
849
|
|
|
795
|
-
pytest tests/
|
|
850
|
+
uv run pytest tests/
|
|
796
851
|
|
|
797
852
|
Development
|
|
798
853
|
-----------
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openscad_parser"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.3"
|
|
8
8
|
description = "A PEG parser to read OpenSCAD language source code, with optional AST tree generation."
|
|
9
9
|
readme = "README.rst"
|
|
10
10
|
authors = [
|
|
@@ -40,6 +40,7 @@ dev = [
|
|
|
40
40
|
"pytest>=7.0.0",
|
|
41
41
|
"pytest-cov>=7.1.0",
|
|
42
42
|
"PyYAML>=6.0",
|
|
43
|
+
"coverage-badge>=1.1.0",
|
|
43
44
|
]
|
|
44
45
|
yaml = [
|
|
45
46
|
"PyYAML>=6.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|