openscad-parser 2.3.1__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.1 → openscad_parser-2.3.3}/PKG-INFO +69 -12
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/README.rst +66 -11
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/pyproject.toml +3 -1
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/builder.py +3 -27
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/source_map.py +0 -9
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/__init__.py +0 -0
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/__init__.py +0 -0
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/nodes.py +0 -0
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/scope.py +0 -0
- {openscad_parser-2.3.1 → openscad_parser-2.3.3}/src/openscad_parser/ast/serialization.py +0 -0
- {openscad_parser-2.3.1 → 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
|
|
@@ -22,6 +22,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
22
22
|
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
|
+
Requires-Dist: pyyaml>=6.0 ; extra == 'dev'
|
|
26
|
+
Requires-Dist: coverage-badge>=1.1.0 ; extra == 'dev'
|
|
25
27
|
Requires-Dist: pyyaml>=6.0 ; extra == 'yaml'
|
|
26
28
|
Maintainer: Revar Desmera
|
|
27
29
|
Maintainer-email: Revar Desmera <revarbat@gmail.com>
|
|
@@ -39,6 +41,9 @@ Description-Content-Type: text/x-rst
|
|
|
39
41
|
OpenSCAD Parser
|
|
40
42
|
===============
|
|
41
43
|
|
|
44
|
+
.. image:: https://raw.githubusercontent.com/BelfrySCAD/openscad_parser/main/coverage-badge.svg
|
|
45
|
+
:alt: Coverage
|
|
46
|
+
|
|
42
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.
|
|
43
48
|
|
|
44
49
|
Features
|
|
@@ -231,7 +236,7 @@ All AST nodes inherit from ``ASTNode`` and have a ``position`` attribute for sou
|
|
|
231
236
|
|
|
232
237
|
# Access source position
|
|
233
238
|
print(assignment.position.line) # Line number (1-indexed)
|
|
234
|
-
print(assignment.position.
|
|
239
|
+
print(assignment.position.column) # Column number (1-indexed)
|
|
235
240
|
|
|
236
241
|
Examples
|
|
237
242
|
--------
|
|
@@ -408,7 +413,7 @@ List Comprehensions
|
|
|
408
413
|
|
|
409
414
|
- ``ListComprehension``: Vector/list literals
|
|
410
415
|
- ``ListCompFor``: for loops in list comprehensions
|
|
411
|
-
- ``
|
|
416
|
+
- ``ListCompCFor``: C-style for loops
|
|
412
417
|
- ``ListCompIf``, ``ListCompIfElse``: Conditionals
|
|
413
418
|
- ``ListCompLet``: let expressions
|
|
414
419
|
- ``ListCompEach``: each expressions
|
|
@@ -418,8 +423,9 @@ Module Instantiations
|
|
|
418
423
|
|
|
419
424
|
- ``ModularCall``: Module calls with arguments and children
|
|
420
425
|
- ``ModularFor``: for loops
|
|
421
|
-
- ``
|
|
426
|
+
- ``ModularCFor``: C-style for loops
|
|
422
427
|
- ``ModularIntersectionFor``: intersection_for loops
|
|
428
|
+
- ``ModularIntersectionCFor``: C-style intersection_for loops
|
|
423
429
|
- ``ModularLet``: let statements
|
|
424
430
|
- ``ModularEcho``: echo statements
|
|
425
431
|
- ``ModularAssert``: assert statements
|
|
@@ -470,10 +476,12 @@ Main Functions
|
|
|
470
476
|
:param debug: If True, enables debug output
|
|
471
477
|
:returns: ParserPython instance
|
|
472
478
|
|
|
473
|
-
``getASTfromString(code: str)``
|
|
479
|
+
``getASTfromString(code: str, include_comments: bool = False, origin: str = "<string>")``
|
|
474
480
|
Parse OpenSCAD code from a string and return its AST.
|
|
475
481
|
|
|
476
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>")
|
|
477
485
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
478
486
|
:rtype: ASTNode | list[ASTNode] | None
|
|
479
487
|
|
|
@@ -514,12 +522,13 @@ Main Functions
|
|
|
514
522
|
|
|
515
523
|
**Note:** The ``process_includes`` parameter affects the AST structure (see ``getASTfromFile`` documentation).
|
|
516
524
|
|
|
517
|
-
``parse_ast(parser, code, file="")``
|
|
525
|
+
``parse_ast(parser, code, file="", source_map=None)``
|
|
518
526
|
Parse OpenSCAD code and generate an AST (lower-level API).
|
|
519
527
|
|
|
520
528
|
:param parser: Arpeggio parser instance from getOpenSCADParser()
|
|
521
529
|
:param code: OpenSCAD code string to parse
|
|
522
530
|
:param file: Optional file path for source location tracking
|
|
531
|
+
:param source_map: Optional ``SourceMap`` for multi-origin position tracking
|
|
523
532
|
:returns: AST node or list of AST nodes (for top-level statements)
|
|
524
533
|
|
|
525
534
|
``clear_ast_cache()``
|
|
@@ -528,6 +537,21 @@ Main Functions
|
|
|
528
537
|
|
|
529
538
|
This function removes all cached AST trees from memory.
|
|
530
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
|
+
|
|
531
555
|
Serialization Functions
|
|
532
556
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
533
557
|
|
|
@@ -636,12 +660,15 @@ All AST nodes include source position information::
|
|
|
636
660
|
assignment = ast[0]
|
|
637
661
|
|
|
638
662
|
position = assignment.position
|
|
639
|
-
print(position.
|
|
640
|
-
print(position.line)
|
|
641
|
-
print(position.
|
|
642
|
-
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)
|
|
643
668
|
|
|
644
|
-
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.
|
|
645
672
|
|
|
646
673
|
Serialization
|
|
647
674
|
-------------
|
|
@@ -825,12 +852,42 @@ The AST is a tree structure that can be traversed recursively::
|
|
|
825
852
|
for node in ast:
|
|
826
853
|
visit_node(node)
|
|
827
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
|
+
|
|
828
885
|
Testing
|
|
829
886
|
-------
|
|
830
887
|
|
|
831
888
|
The project includes a comprehensive test suite. Run tests with::
|
|
832
889
|
|
|
833
|
-
pytest tests/
|
|
890
|
+
uv run pytest tests/
|
|
834
891
|
|
|
835
892
|
Development
|
|
836
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 = [
|
|
@@ -39,6 +39,8 @@ dependencies = [
|
|
|
39
39
|
dev = [
|
|
40
40
|
"pytest>=7.0.0",
|
|
41
41
|
"pytest-cov>=7.1.0",
|
|
42
|
+
"PyYAML>=6.0",
|
|
43
|
+
"coverage-badge>=1.1.0",
|
|
42
44
|
]
|
|
43
45
|
yaml = [
|
|
44
46
|
"PyYAML>=6.0",
|
|
@@ -972,9 +972,9 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
972
972
|
for op in reversed(ops):
|
|
973
973
|
if op == '-':
|
|
974
974
|
result = UnaryMinusOp(expr=result, position=self._get_node_position(node))
|
|
975
|
-
elif op == '!':
|
|
975
|
+
elif op == '!': # pragma: no cover
|
|
976
976
|
result = LogicalNotOp(expr=result, position=self._get_node_position(node))
|
|
977
|
-
elif op == '~':
|
|
977
|
+
elif op == '~': # pragma: no cover
|
|
978
978
|
result = BitwiseNotOp(expr=result, position=self._get_node_position(node))
|
|
979
979
|
|
|
980
980
|
return result
|
|
@@ -1048,8 +1048,6 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1048
1048
|
|
|
1049
1049
|
def visit_vector_expr(self, node, children):
|
|
1050
1050
|
elements = children if children else []
|
|
1051
|
-
if not isinstance(elements, list):
|
|
1052
|
-
elements = [elements]
|
|
1053
1051
|
return ListComprehension(elements=elements, position=self._get_node_position(node))
|
|
1054
1052
|
|
|
1055
1053
|
def visit_funclit_def(self, node, children):
|
|
@@ -1065,7 +1063,7 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1065
1063
|
return children[0]
|
|
1066
1064
|
|
|
1067
1065
|
def visit_listcomp_paren_expr(self, node, children):
|
|
1068
|
-
return children[0]
|
|
1066
|
+
return children[0]
|
|
1069
1067
|
|
|
1070
1068
|
def visit_listcomp_let(self, node, children):
|
|
1071
1069
|
return ListCompLet(assignments=children[0], body=children[1], position=self._get_node_position(node))
|
|
@@ -1118,8 +1116,6 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1118
1116
|
mods = children.get_rule("child_statement") if hasattr(children, "get_rule") else (children[2] if len(children) > 2 else [])
|
|
1119
1117
|
if mods is None: # pragma: no cover
|
|
1120
1118
|
mods = []
|
|
1121
|
-
if not isinstance(mods, list):
|
|
1122
|
-
mods = [mods]
|
|
1123
1119
|
return ModularCall(
|
|
1124
1120
|
name=name,
|
|
1125
1121
|
arguments=arguments,
|
|
@@ -1131,8 +1127,6 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1131
1127
|
initial = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1132
1128
|
increment = children[2] if isinstance(children[2], list) else [children[2]]
|
|
1133
1129
|
body = children.get_rule('child_statement')
|
|
1134
|
-
if not isinstance(body, list):
|
|
1135
|
-
body = [body]
|
|
1136
1130
|
return ModularCFor(
|
|
1137
1131
|
initial=initial,
|
|
1138
1132
|
condition=children[1],
|
|
@@ -1144,8 +1138,6 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1144
1138
|
def visit_modular_for(self, node, children):
|
|
1145
1139
|
assignments = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1146
1140
|
body = children.get_rule('child_statement')
|
|
1147
|
-
if not isinstance(body, list):
|
|
1148
|
-
body = [body]
|
|
1149
1141
|
return ModularFor(
|
|
1150
1142
|
assignments=assignments,
|
|
1151
1143
|
body=body,
|
|
@@ -1156,8 +1148,6 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1156
1148
|
initial = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1157
1149
|
increment = children[2] if isinstance(children[2], list) else [children[2]]
|
|
1158
1150
|
body = children.get_rule('child_statement')
|
|
1159
|
-
if not isinstance(body, list):
|
|
1160
|
-
body = [body]
|
|
1161
1151
|
return ModularIntersectionCFor(
|
|
1162
1152
|
initial=initial,
|
|
1163
1153
|
condition=children[1],
|
|
@@ -1169,46 +1159,32 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1169
1159
|
def visit_modular_intersection_for(self, node, children):
|
|
1170
1160
|
assignments = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1171
1161
|
body = children.get_rule('child_statement')
|
|
1172
|
-
if not isinstance(body, list):
|
|
1173
|
-
body = [body]
|
|
1174
1162
|
return ModularIntersectionFor(assignments=assignments, body=body, position=self._get_node_position(node))
|
|
1175
1163
|
|
|
1176
1164
|
def visit_modular_let(self, node, children):
|
|
1177
1165
|
assignments = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1178
1166
|
mods = children.get_rule('child_statement')
|
|
1179
|
-
if not isinstance(mods, list):
|
|
1180
|
-
mods = [mods]
|
|
1181
1167
|
return ModularLet(assignments=assignments, children=mods, position=self._get_node_position(node))
|
|
1182
1168
|
|
|
1183
1169
|
def visit_modular_echo(self, node, children):
|
|
1184
1170
|
arguments = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1185
1171
|
mods = children.get_rule('child_statement')
|
|
1186
|
-
if not isinstance(mods, list):
|
|
1187
|
-
mods = [mods]
|
|
1188
1172
|
return ModularEcho(arguments=arguments, children=mods, position=self._get_node_position(node))
|
|
1189
1173
|
|
|
1190
1174
|
def visit_modular_assert(self, node, children):
|
|
1191
1175
|
arguments = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1192
1176
|
mods = children.get_rule('child_statement')
|
|
1193
|
-
if not isinstance(mods, list):
|
|
1194
|
-
mods = [mods]
|
|
1195
1177
|
return ModularAssert(arguments=arguments, children=mods, position=self._get_node_position(node))
|
|
1196
1178
|
|
|
1197
1179
|
def visit_if_statement(self, node, children):
|
|
1198
1180
|
condition = children[0]
|
|
1199
1181
|
true_branch = children.get_rule('child_statement')
|
|
1200
|
-
if not isinstance(true_branch, list):
|
|
1201
|
-
true_branch = [true_branch]
|
|
1202
1182
|
return ModularIf(condition=condition, true_branch=true_branch, position=self._get_node_position(node))
|
|
1203
1183
|
|
|
1204
1184
|
def visit_ifelse_statement(self, node, children):
|
|
1205
1185
|
condition = children[0]
|
|
1206
1186
|
true_branch = children.get_rule('child_statement')
|
|
1207
1187
|
false_branch = children.get_rule('child_statement', index=1)
|
|
1208
|
-
if not isinstance(true_branch, list):
|
|
1209
|
-
true_branch = [true_branch]
|
|
1210
|
-
if not isinstance(false_branch, list):
|
|
1211
|
-
false_branch = [false_branch]
|
|
1212
1188
|
return ModularIfElse(condition=condition, true_branch=true_branch, false_branch=false_branch, position=self._get_node_position(node))
|
|
1213
1189
|
|
|
1214
1190
|
def visit_modifier_show_only(self, node, children):
|
|
@@ -118,9 +118,6 @@ class SourceMap:
|
|
|
118
118
|
start_pos: Starting position in the combined string (0-indexed)
|
|
119
119
|
length: Number of characters to replace
|
|
120
120
|
"""
|
|
121
|
-
if length <= 0:
|
|
122
|
-
return
|
|
123
|
-
|
|
124
121
|
end_pos = start_pos + length
|
|
125
122
|
|
|
126
123
|
# Collect segments to modify and new segments to add (for splits)
|
|
@@ -330,12 +327,6 @@ class SourceMap:
|
|
|
330
327
|
else:
|
|
331
328
|
left = mid + 1
|
|
332
329
|
|
|
333
|
-
# Check if position is in the last segment
|
|
334
|
-
if self._segments:
|
|
335
|
-
last_segment = self._segments[-1]
|
|
336
|
-
if last_segment.combined_start <= position < last_segment.combined_start + len(last_segment.content):
|
|
337
|
-
return last_segment
|
|
338
|
-
|
|
339
330
|
return None
|
|
340
331
|
|
|
341
332
|
def _calculate_location_in_segment(self, segment: SourceSegment, offset: int,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|