openscad-parser 2.3.2__tar.gz → 2.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_parser
3
- Version: 2.3.2
3
+ Version: 2.3.4
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: genbadge[coverage]>=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.char) # Column number (1-indexed)
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
- - ``ListCompCStyleFor``: C-style for loops
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
- - ``ModularCLikeFor``: C-style for loops
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.file) # "example.scad"
641
- print(position.line) # 1 (1-indexed)
642
- print(position.char) # 1 (1-indexed, column number)
643
- print(position.position) # 0 (0-indexed character 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`` class provides lazy evaluation of line/column numbers from character positions.
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.char) # Column number (1-indexed)
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
- - ``ListCompCStyleFor``: C-style for loops
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
- - ``ModularCLikeFor``: C-style for loops
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.file) # "example.scad"
602
- print(position.line) # 1 (1-indexed)
603
- print(position.char) # 1 (1-indexed, column number)
604
- print(position.position) # 0 (0-indexed character 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`` class provides lazy evaluation of line/column numbers from character positions.
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.2"
7
+ version = "2.3.4"
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
+ "genbadge[coverage]>=1.1.0",
43
44
  ]
44
45
  yaml = [
45
46
  "PyYAML>=6.0",