openscad-parser 2.4.0__tar.gz → 2.4.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_parser-2.4.0 → openscad_parser-2.4.1}/PKG-INFO +77 -55
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/README.rst +76 -54
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/pyproject.toml +1 -1
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/__init__.py +0 -2
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/builder.py +60 -41
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/nodes.py +20 -126
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/pretty_print.py +4 -14
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/serialization.py +0 -4
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/cli.py +2 -2
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/grammar.py +7 -29
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/__init__.py +0 -0
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/scope.py +0 -0
- {openscad_parser-2.4.0 → openscad_parser-2.4.1}/src/openscad_parser/ast/source_map.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscad_parser
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
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
|
|
@@ -360,107 +360,129 @@ Parsing Library Files
|
|
|
360
360
|
AST Node Types
|
|
361
361
|
--------------
|
|
362
362
|
|
|
363
|
-
The AST includes comprehensive node types for all OpenSCAD language constructs
|
|
363
|
+
The AST includes comprehensive node types for all OpenSCAD language constructs.
|
|
364
|
+
All nodes inherit from ``ASTNode`` and carry ``position: Position`` and ``scope: Scope | None`` attributes.
|
|
364
365
|
|
|
365
366
|
Base Classes
|
|
366
367
|
~~~~~~~~~~~~
|
|
367
368
|
|
|
368
|
-
- ``ASTNode``: Base class for all AST nodes
|
|
369
|
+
- ``ASTNode(position: Position, scope: Scope | None)``: Base class for all AST nodes
|
|
369
370
|
- ``Expression``: Base class for all expression nodes
|
|
370
|
-
- ``Primary``: Base class for atomic value types
|
|
371
|
+
- ``Primary``: Base class for atomic value types (extends ``Expression``)
|
|
371
372
|
- ``ModuleInstantiation``: Base class for module-related statements
|
|
373
|
+
- ``VectorElement``: Base class for list comprehension elements
|
|
372
374
|
|
|
373
375
|
Literals
|
|
374
376
|
~~~~~~~~
|
|
375
377
|
|
|
376
|
-
- ``Identifier``: Variable, function, or module names
|
|
377
|
-
- ``StringLiteral``: String values
|
|
378
|
-
- ``NumberLiteral``: Numeric values
|
|
379
|
-
- ``BooleanLiteral``: true/false values
|
|
380
|
-
- ``UndefinedLiteral``: undef value
|
|
381
|
-
- ``RangeLiteral``: Range expressions [start:end
|
|
378
|
+
- ``Identifier(name: str)``: Variable, function, or module names
|
|
379
|
+
- ``StringLiteral(val: str)``: String values
|
|
380
|
+
- ``NumberLiteral(val: float)``: Numeric values
|
|
381
|
+
- ``BooleanLiteral(val: bool)``: true/false values
|
|
382
|
+
- ``UndefinedLiteral``: The ``undef`` value (no additional fields)
|
|
383
|
+
- ``RangeLiteral(start: Expression, end: Expression, step: Expression)``: Range expressions ``[start:step:end]``
|
|
382
384
|
|
|
383
385
|
Operators
|
|
384
386
|
~~~~~~~~~
|
|
385
387
|
|
|
388
|
+
All operators inherit from ``Expression`` and represent their respective operations with typed fields for operands. The AST preserves operator precedence and associativity as defined in OpenSCAD.
|
|
389
|
+
|
|
386
390
|
Arithmetic:
|
|
387
|
-
|
|
388
|
-
- ``
|
|
391
|
+
|
|
392
|
+
- ``AdditionOp(left: Expression, right: Expression)``: represents ``left + right``
|
|
393
|
+
- ``SubtractionOp(left: Expression, right: Expression)``: represents ``left - right``
|
|
394
|
+
- ``MultiplicationOp(left: Expression, right: Expression)``: represents ``left * right``
|
|
395
|
+
- ``DivisionOp(left: Expression, right: Expression)``: represents ``left / right``
|
|
396
|
+
- ``ModuloOp(left: Expression, right: Expression)``: represents ``left % right``
|
|
397
|
+
- ``ExponentOp(left: Expression, right: Expression)``: represents ``left ^ right``
|
|
398
|
+
- ``UnaryMinusOp(expr: Expression)``: represents ``-expr``
|
|
389
399
|
|
|
390
400
|
Logical:
|
|
391
|
-
|
|
401
|
+
|
|
402
|
+
- ``LogicalAndOp(left: Expression, right: Expression)``: represents ``left && right``
|
|
403
|
+
- ``LogicalOrOp(left: Expression, right: Expression)``: represents ``left || right``
|
|
404
|
+
- ``LogicalNotOp(expr: Expression)``: represents ``!expr``
|
|
392
405
|
|
|
393
406
|
Comparison:
|
|
394
|
-
|
|
395
|
-
- ``
|
|
396
|
-
- ``
|
|
407
|
+
|
|
408
|
+
- ``EqualityOp(left: Expression, right: Expression)``: represents ``left == right``
|
|
409
|
+
- ``InequalityOp(left: Expression, right: Expression)``: represents ``left != right``
|
|
410
|
+
- ``GreaterThanOp(left: Expression, right: Expression)``: represents ``left > right``
|
|
411
|
+
- ``GreaterThanOrEqualOp(left: Expression, right: Expression)``: represents ``left >= right``
|
|
412
|
+
- ``LessThanOp(left: Expression, right: Expression)``: represents ``left < right``
|
|
413
|
+
- ``LessThanOrEqualOp(left: Expression, right: Expression)``: represents ``left <= right``
|
|
397
414
|
|
|
398
415
|
Bitwise:
|
|
399
|
-
|
|
400
|
-
- ``
|
|
416
|
+
|
|
417
|
+
- ``BitwiseAndOp(left: Expression, right: Expression)``: represents ``left & right``
|
|
418
|
+
- ``BitwiseOrOp(left: Expression, right: Expression)``: represents ``left | right``
|
|
419
|
+
- ``BitwiseShiftLeftOp(left: Expression, right: Expression)``: represents ``left << right``
|
|
420
|
+
- ``BitwiseShiftRightOp(left: Expression, right: Expression)``: represents ``left >> right``
|
|
421
|
+
- ``BitwiseNotOp(expr: Expression)``: represents ``~expr``
|
|
401
422
|
|
|
402
423
|
Other:
|
|
403
|
-
|
|
424
|
+
|
|
425
|
+
- ``TernaryOp(condition: Expression, true_expr: Expression, false_expr: Expression)``: Represents ``condition ? true_expr : false_expr``
|
|
404
426
|
|
|
405
427
|
Expressions
|
|
406
428
|
~~~~~~~~~~~
|
|
407
429
|
|
|
408
|
-
- ``LetOp``: let(assignments) body
|
|
409
|
-
- ``EchoOp``: echo(arguments) body
|
|
410
|
-
- ``AssertOp``: assert(arguments) body
|
|
411
|
-
- ``FunctionLiteral``: function(parameters) body
|
|
412
|
-
- ``PrimaryCall``:
|
|
413
|
-
- ``PrimaryIndex``:
|
|
414
|
-
- ``PrimaryMember
|
|
430
|
+
- ``LetOp(assignments: list[Assignment], body: Expression)``: let clause ``let(assignments) body``
|
|
431
|
+
- ``EchoOp(arguments: list[Argument], body: Expression)``: echo clause ``echo(arguments) body``
|
|
432
|
+
- ``AssertOp(arguments: list[Argument], body: Expression)``: assert clause ``assert(arguments) body``
|
|
433
|
+
- ``FunctionLiteral(parameters: list[ParameterDeclaration], body: Expression)``: Anonymous function expression ``function(parameters) body``
|
|
434
|
+
- ``PrimaryCall(left: Expression, arguments: list[Argument])``: Function calls ``left(arguments)``
|
|
435
|
+
- ``PrimaryIndex(left: Expression, index: Expression)``: Array indexing ``left[index]``
|
|
436
|
+
- ``PrimaryMember(left: Expression, member: Identifier)``: Member access ``left.member``
|
|
415
437
|
|
|
416
438
|
List Comprehensions
|
|
417
439
|
~~~~~~~~~~~~~~~~~~~
|
|
418
440
|
|
|
419
|
-
- ``ListComprehension``: Vector/list literals
|
|
420
|
-
- ``ListCompFor``: for loops in list comprehensions
|
|
421
|
-
- ``ListCompCFor``: C-style for loops
|
|
422
|
-
- ``ListCompIf
|
|
423
|
-
- ``
|
|
424
|
-
- ``
|
|
441
|
+
- ``ListComprehension(elements: list[VectorElement])``: Vector/list literals ``[elements]``
|
|
442
|
+
- ``ListCompFor(assignments: list[Assignment], body: VectorElement)``: for loops in list comprehensions ``for(assignments) body``
|
|
443
|
+
- ``ListCompCFor(inits: list[Assignment], condition: Expression, incrs: list[Assignment], body: VectorElement)``: C-style for loops in list comprehensions ``for(inits; condition; incrs) body``
|
|
444
|
+
- ``ListCompIf(condition: Expression, true_expr: VectorElement)``: Conditional inclusion without else ``if(condition) true_expr``
|
|
445
|
+
- ``ListCompIfElse(condition: Expression, true_expr: VectorElement, false_expr: VectorElement)``: Conditional inclusion with else ``if(condition) true_expr else false_expr``
|
|
446
|
+
- ``ListCompLet(assignments: list[Assignment], body: VectorElement)``: let expressions in list comprehensions ``let(assignments) body``
|
|
447
|
+
- ``ListCompEach(body: VectorElement)``: each expressions (flattens nested lists) ``each body``
|
|
425
448
|
|
|
426
449
|
Module Instantiations
|
|
427
450
|
~~~~~~~~~~~~~~~~~~~~~
|
|
428
451
|
|
|
429
|
-
- ``ModularCall``: Module calls
|
|
430
|
-
- ``ModularFor``: for loops
|
|
431
|
-
- ``
|
|
432
|
-
- ``
|
|
433
|
-
- ``
|
|
434
|
-
- ``
|
|
435
|
-
- ``
|
|
436
|
-
- ``
|
|
437
|
-
- ``
|
|
438
|
-
- ``
|
|
439
|
-
- ``
|
|
440
|
-
- ``
|
|
441
|
-
- ``ModularModifierDisable``: ``*`` modifier
|
|
452
|
+
- ``ModularCall(name: Identifier, arguments: list[Argument], children: list[ModuleInstantiation])``: Module calls ``name(arguments) { children }``
|
|
453
|
+
- ``ModularFor(assignments: list[Assignment], body: ModuleInstantiation)``: for loops in module bodies ``for(assignments) body``
|
|
454
|
+
- ``ModularIntersectionFor(assignments: list[Assignment], body: ModuleInstantiation)``: intersection_for loops ``intersection_for(assignments) body``
|
|
455
|
+
- ``ModularLet(assignments: list[Assignment], children: list[ModuleInstantiation])``: let statements in module bodies ``let(assignments) { children }``
|
|
456
|
+
- ``ModularEcho(arguments: list[Argument], children: list[ModuleInstantiation])``: echo statements in module bodies ``echo(arguments) { children }``
|
|
457
|
+
- ``ModularAssert(arguments: list[Argument], children: list[ModuleInstantiation])``: assert statements in module bodies ``assert(arguments) { children }``
|
|
458
|
+
- ``ModularIf(condition: Expression, true_branch: ModuleInstantiation)``: if statements in module bodies, with no else ``if(condition) true_branch``
|
|
459
|
+
- ``ModularIfElse(condition: Expression, true_branch: ModuleInstantiation, false_branch: ModuleInstantiation)``: if/else statements in module bodies ``if(condition) true_branch else false_branch``
|
|
460
|
+
- ``ModularModifierShowOnly(child: ModuleInstantiation)``: Show-Only modifier ``!child``
|
|
461
|
+
- ``ModularModifierHighlight(child: ModuleInstantiation)``: Highlight modifier ``#child``
|
|
462
|
+
- ``ModularModifierBackground(child: ModuleInstantiation)``: Background modifier ``%child``
|
|
463
|
+
- ``ModularModifierDisable(child: ModuleInstantiation)``: Disabler modifier ``*child``
|
|
442
464
|
|
|
443
465
|
Declarations
|
|
444
466
|
~~~~~~~~~~~~
|
|
445
467
|
|
|
446
|
-
- ``ModuleDeclaration``: module
|
|
447
|
-
- ``FunctionDeclaration``: function
|
|
448
|
-
- ``ParameterDeclaration``:
|
|
449
|
-
- ``Assignment``:
|
|
468
|
+
- ``ModuleDeclaration(name: Identifier, parameters: list[ParameterDeclaration], children: list[ModuleInstantiation | Assignment | FunctionDeclaration | ModuleDeclaration])``: Module definitions ``module name(parameters) { children }``
|
|
469
|
+
- ``FunctionDeclaration(name: Identifier, parameters: list[ParameterDeclaration], expr: Expression)``: Function definitions ``function name(parameters) = expr;``
|
|
470
|
+
- ``ParameterDeclaration(name: Identifier, default: Expression | None)``: Function/module parameter with optional default value ``name=default`` or ``name``
|
|
471
|
+
- ``Assignment(name: Identifier, expr: Expression)``: Variable assignments ``name = expr;``
|
|
450
472
|
|
|
451
473
|
Statements
|
|
452
474
|
~~~~~~~~~~
|
|
453
475
|
|
|
454
|
-
- ``UseStatement``: use <filepath
|
|
455
|
-
- ``IncludeStatement``: include <filepath
|
|
456
|
-
- ``PositionalArgument``: Function call positional arguments
|
|
457
|
-
- ``NamedArgument``: Function call named arguments
|
|
476
|
+
- ``UseStatement(filepath: StringLiteral)``: Represents ``use <filepath>``
|
|
477
|
+
- ``IncludeStatement(filepath: StringLiteral)``: Represents ``include <filepath>``
|
|
478
|
+
- ``PositionalArgument(expr: Expression)``: Function call positional arguments ``expr``
|
|
479
|
+
- ``NamedArgument(name: Identifier, expr: Expression)``: Function call named arguments ``name=expr``
|
|
458
480
|
|
|
459
481
|
Comments
|
|
460
482
|
~~~~~~~~
|
|
461
483
|
|
|
462
|
-
- ``CommentLine``: Single-line comments
|
|
463
|
-
- ``CommentSpan``: Multi-line comments ``/* */``
|
|
484
|
+
- ``CommentLine(text: str)``: Single-line comments ``// str``
|
|
485
|
+
- ``CommentSpan(text: str)``: Multi-line comments ``/* str */``
|
|
464
486
|
|
|
465
487
|
All AST node classes are fully documented with docstrings that include:
|
|
466
488
|
- Description of what the node represents
|
|
@@ -317,107 +317,129 @@ Parsing Library Files
|
|
|
317
317
|
AST Node Types
|
|
318
318
|
--------------
|
|
319
319
|
|
|
320
|
-
The AST includes comprehensive node types for all OpenSCAD language constructs
|
|
320
|
+
The AST includes comprehensive node types for all OpenSCAD language constructs.
|
|
321
|
+
All nodes inherit from ``ASTNode`` and carry ``position: Position`` and ``scope: Scope | None`` attributes.
|
|
321
322
|
|
|
322
323
|
Base Classes
|
|
323
324
|
~~~~~~~~~~~~
|
|
324
325
|
|
|
325
|
-
- ``ASTNode``: Base class for all AST nodes
|
|
326
|
+
- ``ASTNode(position: Position, scope: Scope | None)``: Base class for all AST nodes
|
|
326
327
|
- ``Expression``: Base class for all expression nodes
|
|
327
|
-
- ``Primary``: Base class for atomic value types
|
|
328
|
+
- ``Primary``: Base class for atomic value types (extends ``Expression``)
|
|
328
329
|
- ``ModuleInstantiation``: Base class for module-related statements
|
|
330
|
+
- ``VectorElement``: Base class for list comprehension elements
|
|
329
331
|
|
|
330
332
|
Literals
|
|
331
333
|
~~~~~~~~
|
|
332
334
|
|
|
333
|
-
- ``Identifier``: Variable, function, or module names
|
|
334
|
-
- ``StringLiteral``: String values
|
|
335
|
-
- ``NumberLiteral``: Numeric values
|
|
336
|
-
- ``BooleanLiteral``: true/false values
|
|
337
|
-
- ``UndefinedLiteral``: undef value
|
|
338
|
-
- ``RangeLiteral``: Range expressions [start:end
|
|
335
|
+
- ``Identifier(name: str)``: Variable, function, or module names
|
|
336
|
+
- ``StringLiteral(val: str)``: String values
|
|
337
|
+
- ``NumberLiteral(val: float)``: Numeric values
|
|
338
|
+
- ``BooleanLiteral(val: bool)``: true/false values
|
|
339
|
+
- ``UndefinedLiteral``: The ``undef`` value (no additional fields)
|
|
340
|
+
- ``RangeLiteral(start: Expression, end: Expression, step: Expression)``: Range expressions ``[start:step:end]``
|
|
339
341
|
|
|
340
342
|
Operators
|
|
341
343
|
~~~~~~~~~
|
|
342
344
|
|
|
345
|
+
All operators inherit from ``Expression`` and represent their respective operations with typed fields for operands. The AST preserves operator precedence and associativity as defined in OpenSCAD.
|
|
346
|
+
|
|
343
347
|
Arithmetic:
|
|
344
|
-
|
|
345
|
-
- ``
|
|
348
|
+
|
|
349
|
+
- ``AdditionOp(left: Expression, right: Expression)``: represents ``left + right``
|
|
350
|
+
- ``SubtractionOp(left: Expression, right: Expression)``: represents ``left - right``
|
|
351
|
+
- ``MultiplicationOp(left: Expression, right: Expression)``: represents ``left * right``
|
|
352
|
+
- ``DivisionOp(left: Expression, right: Expression)``: represents ``left / right``
|
|
353
|
+
- ``ModuloOp(left: Expression, right: Expression)``: represents ``left % right``
|
|
354
|
+
- ``ExponentOp(left: Expression, right: Expression)``: represents ``left ^ right``
|
|
355
|
+
- ``UnaryMinusOp(expr: Expression)``: represents ``-expr``
|
|
346
356
|
|
|
347
357
|
Logical:
|
|
348
|
-
|
|
358
|
+
|
|
359
|
+
- ``LogicalAndOp(left: Expression, right: Expression)``: represents ``left && right``
|
|
360
|
+
- ``LogicalOrOp(left: Expression, right: Expression)``: represents ``left || right``
|
|
361
|
+
- ``LogicalNotOp(expr: Expression)``: represents ``!expr``
|
|
349
362
|
|
|
350
363
|
Comparison:
|
|
351
|
-
|
|
352
|
-
- ``
|
|
353
|
-
- ``
|
|
364
|
+
|
|
365
|
+
- ``EqualityOp(left: Expression, right: Expression)``: represents ``left == right``
|
|
366
|
+
- ``InequalityOp(left: Expression, right: Expression)``: represents ``left != right``
|
|
367
|
+
- ``GreaterThanOp(left: Expression, right: Expression)``: represents ``left > right``
|
|
368
|
+
- ``GreaterThanOrEqualOp(left: Expression, right: Expression)``: represents ``left >= right``
|
|
369
|
+
- ``LessThanOp(left: Expression, right: Expression)``: represents ``left < right``
|
|
370
|
+
- ``LessThanOrEqualOp(left: Expression, right: Expression)``: represents ``left <= right``
|
|
354
371
|
|
|
355
372
|
Bitwise:
|
|
356
|
-
|
|
357
|
-
- ``
|
|
373
|
+
|
|
374
|
+
- ``BitwiseAndOp(left: Expression, right: Expression)``: represents ``left & right``
|
|
375
|
+
- ``BitwiseOrOp(left: Expression, right: Expression)``: represents ``left | right``
|
|
376
|
+
- ``BitwiseShiftLeftOp(left: Expression, right: Expression)``: represents ``left << right``
|
|
377
|
+
- ``BitwiseShiftRightOp(left: Expression, right: Expression)``: represents ``left >> right``
|
|
378
|
+
- ``BitwiseNotOp(expr: Expression)``: represents ``~expr``
|
|
358
379
|
|
|
359
380
|
Other:
|
|
360
|
-
|
|
381
|
+
|
|
382
|
+
- ``TernaryOp(condition: Expression, true_expr: Expression, false_expr: Expression)``: Represents ``condition ? true_expr : false_expr``
|
|
361
383
|
|
|
362
384
|
Expressions
|
|
363
385
|
~~~~~~~~~~~
|
|
364
386
|
|
|
365
|
-
- ``LetOp``: let(assignments) body
|
|
366
|
-
- ``EchoOp``: echo(arguments) body
|
|
367
|
-
- ``AssertOp``: assert(arguments) body
|
|
368
|
-
- ``FunctionLiteral``: function(parameters) body
|
|
369
|
-
- ``PrimaryCall``:
|
|
370
|
-
- ``PrimaryIndex``:
|
|
371
|
-
- ``PrimaryMember
|
|
387
|
+
- ``LetOp(assignments: list[Assignment], body: Expression)``: let clause ``let(assignments) body``
|
|
388
|
+
- ``EchoOp(arguments: list[Argument], body: Expression)``: echo clause ``echo(arguments) body``
|
|
389
|
+
- ``AssertOp(arguments: list[Argument], body: Expression)``: assert clause ``assert(arguments) body``
|
|
390
|
+
- ``FunctionLiteral(parameters: list[ParameterDeclaration], body: Expression)``: Anonymous function expression ``function(parameters) body``
|
|
391
|
+
- ``PrimaryCall(left: Expression, arguments: list[Argument])``: Function calls ``left(arguments)``
|
|
392
|
+
- ``PrimaryIndex(left: Expression, index: Expression)``: Array indexing ``left[index]``
|
|
393
|
+
- ``PrimaryMember(left: Expression, member: Identifier)``: Member access ``left.member``
|
|
372
394
|
|
|
373
395
|
List Comprehensions
|
|
374
396
|
~~~~~~~~~~~~~~~~~~~
|
|
375
397
|
|
|
376
|
-
- ``ListComprehension``: Vector/list literals
|
|
377
|
-
- ``ListCompFor``: for loops in list comprehensions
|
|
378
|
-
- ``ListCompCFor``: C-style for loops
|
|
379
|
-
- ``ListCompIf
|
|
380
|
-
- ``
|
|
381
|
-
- ``
|
|
398
|
+
- ``ListComprehension(elements: list[VectorElement])``: Vector/list literals ``[elements]``
|
|
399
|
+
- ``ListCompFor(assignments: list[Assignment], body: VectorElement)``: for loops in list comprehensions ``for(assignments) body``
|
|
400
|
+
- ``ListCompCFor(inits: list[Assignment], condition: Expression, incrs: list[Assignment], body: VectorElement)``: C-style for loops in list comprehensions ``for(inits; condition; incrs) body``
|
|
401
|
+
- ``ListCompIf(condition: Expression, true_expr: VectorElement)``: Conditional inclusion without else ``if(condition) true_expr``
|
|
402
|
+
- ``ListCompIfElse(condition: Expression, true_expr: VectorElement, false_expr: VectorElement)``: Conditional inclusion with else ``if(condition) true_expr else false_expr``
|
|
403
|
+
- ``ListCompLet(assignments: list[Assignment], body: VectorElement)``: let expressions in list comprehensions ``let(assignments) body``
|
|
404
|
+
- ``ListCompEach(body: VectorElement)``: each expressions (flattens nested lists) ``each body``
|
|
382
405
|
|
|
383
406
|
Module Instantiations
|
|
384
407
|
~~~~~~~~~~~~~~~~~~~~~
|
|
385
408
|
|
|
386
|
-
- ``ModularCall``: Module calls
|
|
387
|
-
- ``ModularFor``: for loops
|
|
388
|
-
- ``
|
|
389
|
-
- ``
|
|
390
|
-
- ``
|
|
391
|
-
- ``
|
|
392
|
-
- ``
|
|
393
|
-
- ``
|
|
394
|
-
- ``
|
|
395
|
-
- ``
|
|
396
|
-
- ``
|
|
397
|
-
- ``
|
|
398
|
-
- ``ModularModifierDisable``: ``*`` modifier
|
|
409
|
+
- ``ModularCall(name: Identifier, arguments: list[Argument], children: list[ModuleInstantiation])``: Module calls ``name(arguments) { children }``
|
|
410
|
+
- ``ModularFor(assignments: list[Assignment], body: ModuleInstantiation)``: for loops in module bodies ``for(assignments) body``
|
|
411
|
+
- ``ModularIntersectionFor(assignments: list[Assignment], body: ModuleInstantiation)``: intersection_for loops ``intersection_for(assignments) body``
|
|
412
|
+
- ``ModularLet(assignments: list[Assignment], children: list[ModuleInstantiation])``: let statements in module bodies ``let(assignments) { children }``
|
|
413
|
+
- ``ModularEcho(arguments: list[Argument], children: list[ModuleInstantiation])``: echo statements in module bodies ``echo(arguments) { children }``
|
|
414
|
+
- ``ModularAssert(arguments: list[Argument], children: list[ModuleInstantiation])``: assert statements in module bodies ``assert(arguments) { children }``
|
|
415
|
+
- ``ModularIf(condition: Expression, true_branch: ModuleInstantiation)``: if statements in module bodies, with no else ``if(condition) true_branch``
|
|
416
|
+
- ``ModularIfElse(condition: Expression, true_branch: ModuleInstantiation, false_branch: ModuleInstantiation)``: if/else statements in module bodies ``if(condition) true_branch else false_branch``
|
|
417
|
+
- ``ModularModifierShowOnly(child: ModuleInstantiation)``: Show-Only modifier ``!child``
|
|
418
|
+
- ``ModularModifierHighlight(child: ModuleInstantiation)``: Highlight modifier ``#child``
|
|
419
|
+
- ``ModularModifierBackground(child: ModuleInstantiation)``: Background modifier ``%child``
|
|
420
|
+
- ``ModularModifierDisable(child: ModuleInstantiation)``: Disabler modifier ``*child``
|
|
399
421
|
|
|
400
422
|
Declarations
|
|
401
423
|
~~~~~~~~~~~~
|
|
402
424
|
|
|
403
|
-
- ``ModuleDeclaration``: module
|
|
404
|
-
- ``FunctionDeclaration``: function
|
|
405
|
-
- ``ParameterDeclaration``:
|
|
406
|
-
- ``Assignment``:
|
|
425
|
+
- ``ModuleDeclaration(name: Identifier, parameters: list[ParameterDeclaration], children: list[ModuleInstantiation | Assignment | FunctionDeclaration | ModuleDeclaration])``: Module definitions ``module name(parameters) { children }``
|
|
426
|
+
- ``FunctionDeclaration(name: Identifier, parameters: list[ParameterDeclaration], expr: Expression)``: Function definitions ``function name(parameters) = expr;``
|
|
427
|
+
- ``ParameterDeclaration(name: Identifier, default: Expression | None)``: Function/module parameter with optional default value ``name=default`` or ``name``
|
|
428
|
+
- ``Assignment(name: Identifier, expr: Expression)``: Variable assignments ``name = expr;``
|
|
407
429
|
|
|
408
430
|
Statements
|
|
409
431
|
~~~~~~~~~~
|
|
410
432
|
|
|
411
|
-
- ``UseStatement``: use <filepath
|
|
412
|
-
- ``IncludeStatement``: include <filepath
|
|
413
|
-
- ``PositionalArgument``: Function call positional arguments
|
|
414
|
-
- ``NamedArgument``: Function call named arguments
|
|
433
|
+
- ``UseStatement(filepath: StringLiteral)``: Represents ``use <filepath>``
|
|
434
|
+
- ``IncludeStatement(filepath: StringLiteral)``: Represents ``include <filepath>``
|
|
435
|
+
- ``PositionalArgument(expr: Expression)``: Function call positional arguments ``expr``
|
|
436
|
+
- ``NamedArgument(name: Identifier, expr: Expression)``: Function call named arguments ``name=expr``
|
|
415
437
|
|
|
416
438
|
Comments
|
|
417
439
|
~~~~~~~~
|
|
418
440
|
|
|
419
|
-
- ``CommentLine``: Single-line comments
|
|
420
|
-
- ``CommentSpan``: Multi-line comments ``/* */``
|
|
441
|
+
- ``CommentLine(text: str)``: Single-line comments ``// str``
|
|
442
|
+
- ``CommentSpan(text: str)``: Multi-line comments ``/* str */``
|
|
421
443
|
|
|
422
444
|
All AST node classes are fully documented with docstrings that include:
|
|
423
445
|
- Description of what the node represents
|
|
@@ -6,6 +6,28 @@ from .source_map import SourceMap
|
|
|
6
6
|
from .nodes import *
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
class _CForParts:
|
|
10
|
+
"""Wrapper returned by c_for_inits/c_for_incrs visitors.
|
|
11
|
+
|
|
12
|
+
Always truthy so Arpeggio does not drop it from parent children when
|
|
13
|
+
there are zero assignments, and not a list so Arpeggio does not flatten
|
|
14
|
+
it into the parent's children list.
|
|
15
|
+
"""
|
|
16
|
+
__slots__ = ('_items',)
|
|
17
|
+
|
|
18
|
+
def __init__(self, items: list) -> None:
|
|
19
|
+
self._items = items
|
|
20
|
+
|
|
21
|
+
def __iter__(self):
|
|
22
|
+
return iter(self._items)
|
|
23
|
+
|
|
24
|
+
def __len__(self) -> int:
|
|
25
|
+
return len(self._items)
|
|
26
|
+
|
|
27
|
+
def __bool__(self) -> bool: # pragma: no cover
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
|
|
9
31
|
@dataclass
|
|
10
32
|
class Position:
|
|
11
33
|
"""Represents a location in a source origin.
|
|
@@ -1051,7 +1073,9 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1051
1073
|
return ListComprehension(elements=elements, position=self._get_node_position(node))
|
|
1052
1074
|
|
|
1053
1075
|
def visit_funclit_def(self, node, children):
|
|
1054
|
-
|
|
1076
|
+
parameters = [c for c in children if isinstance(c, ParameterDeclaration)]
|
|
1077
|
+
body = next(c for c in children if not isinstance(c, ParameterDeclaration))
|
|
1078
|
+
return FunctionLiteral(parameters=parameters, body=body, position=self._get_node_position(node))
|
|
1055
1079
|
|
|
1056
1080
|
def visit_vector_elements(self, node, children):
|
|
1057
1081
|
return list(children) if children else []
|
|
@@ -1066,24 +1090,43 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1066
1090
|
return children[0]
|
|
1067
1091
|
|
|
1068
1092
|
def visit_listcomp_let(self, node, children):
|
|
1069
|
-
|
|
1070
|
-
|
|
1093
|
+
body = children[-1]
|
|
1094
|
+
assignments = [c for c in children[:-1] if isinstance(c, Assignment)]
|
|
1095
|
+
return ListCompLet(assignments=assignments, body=body, position=self._get_node_position(node))
|
|
1096
|
+
|
|
1071
1097
|
def visit_listcomp_each(self, node, children):
|
|
1072
1098
|
return ListCompEach(body=children[0], position=self._get_node_position(node))
|
|
1073
|
-
|
|
1099
|
+
|
|
1074
1100
|
def visit_listcomp_for(self, node, children):
|
|
1101
|
+
body = children[-1]
|
|
1102
|
+
assignments = [c for c in children[:-1] if isinstance(c, Assignment)]
|
|
1075
1103
|
return ListCompFor(
|
|
1076
|
-
assignments=
|
|
1077
|
-
body=
|
|
1104
|
+
assignments=assignments,
|
|
1105
|
+
body=body,
|
|
1078
1106
|
position=self._get_node_position(node)
|
|
1079
1107
|
)
|
|
1080
1108
|
|
|
1109
|
+
def visit_c_for_inits(self, node, children):
|
|
1110
|
+
return _CForParts([a for a in children if isinstance(a, Assignment)])
|
|
1111
|
+
|
|
1112
|
+
def visit_c_for_incrs(self, node, children):
|
|
1113
|
+
return _CForParts([a for a in children if isinstance(a, Assignment)])
|
|
1114
|
+
|
|
1081
1115
|
def visit_listcomp_c_for(self, node, children):
|
|
1116
|
+
idx = 0
|
|
1117
|
+
inits = list(children[idx]) if isinstance(children[idx], _CForParts) else []
|
|
1118
|
+
if isinstance(children[idx], _CForParts):
|
|
1119
|
+
idx += 1
|
|
1120
|
+
condition = children[idx]; idx += 1
|
|
1121
|
+
incrs = list(children[idx]) if idx < len(children) and isinstance(children[idx], _CForParts) else []
|
|
1122
|
+
if idx < len(children) and isinstance(children[idx], _CForParts):
|
|
1123
|
+
idx += 1
|
|
1124
|
+
body = children[idx]
|
|
1082
1125
|
return ListCompCFor(
|
|
1083
|
-
|
|
1084
|
-
condition=
|
|
1085
|
-
|
|
1086
|
-
body=
|
|
1126
|
+
inits=inits,
|
|
1127
|
+
condition=condition,
|
|
1128
|
+
incrs=incrs,
|
|
1129
|
+
body=body,
|
|
1087
1130
|
position=self._get_node_position(node)
|
|
1088
1131
|
)
|
|
1089
1132
|
|
|
@@ -1123,56 +1166,32 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
1123
1166
|
position=self._get_node_position(node)
|
|
1124
1167
|
)
|
|
1125
1168
|
|
|
1126
|
-
def visit_modular_c_for(self, node, children):
|
|
1127
|
-
initial = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1128
|
-
increment = children[2] if isinstance(children[2], list) else [children[2]]
|
|
1129
|
-
body = children.get_rule('child_statement')
|
|
1130
|
-
return ModularCFor(
|
|
1131
|
-
initial=initial,
|
|
1132
|
-
condition=children[1],
|
|
1133
|
-
increment=increment,
|
|
1134
|
-
body=body,
|
|
1135
|
-
position=self._get_node_position(node)
|
|
1136
|
-
)
|
|
1137
|
-
|
|
1138
1169
|
def visit_modular_for(self, node, children):
|
|
1139
|
-
assignments =
|
|
1170
|
+
assignments = [c for c in children if isinstance(c, Assignment)]
|
|
1140
1171
|
body = children.get_rule('child_statement')
|
|
1141
1172
|
return ModularFor(
|
|
1142
1173
|
assignments=assignments,
|
|
1143
1174
|
body=body,
|
|
1144
1175
|
position=self._get_node_position(node)
|
|
1145
1176
|
)
|
|
1146
|
-
|
|
1147
|
-
def visit_modular_intersection_c_for(self, node, children):
|
|
1148
|
-
initial = children[0] if isinstance(children[0], list) else [children[0]]
|
|
1149
|
-
increment = children[2] if isinstance(children[2], list) else [children[2]]
|
|
1150
|
-
body = children.get_rule('child_statement')
|
|
1151
|
-
return ModularIntersectionCFor(
|
|
1152
|
-
initial=initial,
|
|
1153
|
-
condition=children[1],
|
|
1154
|
-
increment=increment,
|
|
1155
|
-
body=body,
|
|
1156
|
-
position=self._get_node_position(node)
|
|
1157
|
-
)
|
|
1158
1177
|
|
|
1159
1178
|
def visit_modular_intersection_for(self, node, children):
|
|
1160
|
-
assignments =
|
|
1179
|
+
assignments = [c for c in children if isinstance(c, Assignment)]
|
|
1161
1180
|
body = children.get_rule('child_statement')
|
|
1162
1181
|
return ModularIntersectionFor(assignments=assignments, body=body, position=self._get_node_position(node))
|
|
1163
|
-
|
|
1182
|
+
|
|
1164
1183
|
def visit_modular_let(self, node, children):
|
|
1165
|
-
assignments =
|
|
1184
|
+
assignments = [c for c in children if isinstance(c, Assignment)]
|
|
1166
1185
|
mods = children.get_rule('child_statement')
|
|
1167
1186
|
return ModularLet(assignments=assignments, children=mods, position=self._get_node_position(node))
|
|
1168
1187
|
|
|
1169
1188
|
def visit_modular_echo(self, node, children):
|
|
1170
|
-
arguments =
|
|
1189
|
+
arguments = [c for c in children if isinstance(c, Argument)]
|
|
1171
1190
|
mods = children.get_rule('child_statement')
|
|
1172
1191
|
return ModularEcho(arguments=arguments, children=mods, position=self._get_node_position(node))
|
|
1173
|
-
|
|
1192
|
+
|
|
1174
1193
|
def visit_modular_assert(self, node, children):
|
|
1175
|
-
arguments =
|
|
1194
|
+
arguments = [c for c in children if isinstance(c, Argument)]
|
|
1176
1195
|
mods = children.get_rule('child_statement')
|
|
1177
1196
|
return ModularAssert(arguments=arguments, children=mods, position=self._get_node_position(node))
|
|
1178
1197
|
|
|
@@ -1061,35 +1061,30 @@ class LessThanOrEqualOp(Expression):
|
|
|
1061
1061
|
@dataclass
|
|
1062
1062
|
class FunctionLiteral(Expression):
|
|
1063
1063
|
"""Represents an OpenSCAD function literal (anonymous function).
|
|
1064
|
-
|
|
1064
|
+
|
|
1065
1065
|
Function literals are anonymous functions that can be assigned to variables
|
|
1066
1066
|
or used directly in expressions. They are defined using the 'function' keyword.
|
|
1067
|
-
|
|
1067
|
+
|
|
1068
1068
|
Examples:
|
|
1069
1069
|
x = function(x) x * 2; // Anonymous function assigned to x
|
|
1070
1070
|
function(a, b) a + b // Function literal in expression
|
|
1071
|
-
|
|
1071
|
+
|
|
1072
1072
|
Attributes:
|
|
1073
|
-
|
|
1073
|
+
parameters: List of parameter declarations.
|
|
1074
1074
|
body: The expression body of the function.
|
|
1075
1075
|
"""
|
|
1076
|
-
|
|
1076
|
+
parameters: list[ParameterDeclaration]
|
|
1077
1077
|
body: Expression
|
|
1078
1078
|
|
|
1079
1079
|
def __str__(self):
|
|
1080
|
-
return f"function({', '.join(str(
|
|
1080
|
+
return f"function({', '.join(str(p) for p in self.parameters)}) {self.body}"
|
|
1081
1081
|
|
|
1082
1082
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1083
1083
|
self.scope = parent_scope
|
|
1084
1084
|
func_scope = parent_scope.child_scope()
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
arguments = [arguments] if arguments is not None else []
|
|
1089
|
-
for arg in arguments:
|
|
1090
|
-
if isinstance(arg, ParameterDeclaration):
|
|
1091
|
-
func_scope.define_variable(arg.name.name, arg)
|
|
1092
|
-
arg.build_scope(func_scope)
|
|
1085
|
+
for param in self.parameters:
|
|
1086
|
+
func_scope.define_variable(param.name.name, param)
|
|
1087
|
+
param.build_scope(func_scope)
|
|
1093
1088
|
self.body.build_scope(func_scope)
|
|
1094
1089
|
|
|
1095
1090
|
|
|
@@ -1202,10 +1197,10 @@ class ListCompLet(VectorElement):
|
|
|
1202
1197
|
|
|
1203
1198
|
Attributes:
|
|
1204
1199
|
assignments: List of local variable assignments.
|
|
1205
|
-
body: The
|
|
1200
|
+
body: The vector element body that uses the assigned variables.
|
|
1206
1201
|
"""
|
|
1207
1202
|
assignments: list[Assignment]
|
|
1208
|
-
body:
|
|
1203
|
+
body: VectorElement
|
|
1209
1204
|
|
|
1210
1205
|
def __str__(self):
|
|
1211
1206
|
return f"let ({', '.join(str(a) for a in self.assignments)}) {self.body}"
|
|
@@ -1213,11 +1208,7 @@ class ListCompLet(VectorElement):
|
|
|
1213
1208
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1214
1209
|
self.scope = parent_scope
|
|
1215
1210
|
let_scope = parent_scope.child_scope()
|
|
1216
|
-
|
|
1217
|
-
assignments = self.assignments
|
|
1218
|
-
if not isinstance(assignments, (list, tuple)):
|
|
1219
|
-
assignments = [assignments] if assignments is not None else []
|
|
1220
|
-
for assignment in assignments:
|
|
1211
|
+
for assignment in self.assignments:
|
|
1221
1212
|
let_scope.define_variable(assignment.name.name, assignment)
|
|
1222
1213
|
assignment.build_scope(let_scope)
|
|
1223
1214
|
self.body.build_scope(let_scope)
|
|
@@ -1272,11 +1263,7 @@ class ListCompFor(VectorElement):
|
|
|
1272
1263
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1273
1264
|
self.scope = parent_scope
|
|
1274
1265
|
for_scope = parent_scope.child_scope()
|
|
1275
|
-
|
|
1276
|
-
assignments = self.assignments
|
|
1277
|
-
if not isinstance(assignments, (list, tuple)):
|
|
1278
|
-
assignments = [assignments] if assignments is not None else []
|
|
1279
|
-
for assignment in assignments:
|
|
1266
|
+
for assignment in self.assignments:
|
|
1280
1267
|
assignment.scope = for_scope
|
|
1281
1268
|
for_scope.define_variable(assignment.name.name, assignment)
|
|
1282
1269
|
assignment.name.build_scope(for_scope)
|
|
@@ -1297,34 +1284,27 @@ class ListCompCFor(VectorElement):
|
|
|
1297
1284
|
[i*2 for (i=0; i<10; i=i+2)] // With increment step
|
|
1298
1285
|
|
|
1299
1286
|
Attributes:
|
|
1300
|
-
|
|
1287
|
+
inits: List of initialization assignments.
|
|
1301
1288
|
condition: The loop continuation condition.
|
|
1302
|
-
|
|
1289
|
+
incrs: List of increment assignments.
|
|
1303
1290
|
body: The expression to evaluate for each iteration.
|
|
1304
1291
|
"""
|
|
1305
|
-
|
|
1292
|
+
inits: list[Assignment]
|
|
1306
1293
|
condition: Expression
|
|
1307
|
-
|
|
1294
|
+
incrs: list[Assignment]
|
|
1308
1295
|
body: VectorElement
|
|
1309
1296
|
|
|
1310
1297
|
def __str__(self):
|
|
1311
|
-
return f"for ({', '.join(str(a) for a in self.
|
|
1298
|
+
return f"for ({', '.join(str(a) for a in self.inits)}; {self.condition}; {', '.join(str(a) for a in self.incrs)}) {self.body}"
|
|
1312
1299
|
|
|
1313
1300
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1314
1301
|
self.scope = parent_scope
|
|
1315
1302
|
for_scope = parent_scope.child_scope()
|
|
1316
|
-
|
|
1317
|
-
initial = self.initial
|
|
1318
|
-
if not isinstance(initial, (list, tuple)):
|
|
1319
|
-
initial = [initial] if initial is not None else []
|
|
1320
|
-
increment = self.increment
|
|
1321
|
-
if not isinstance(increment, (list, tuple)):
|
|
1322
|
-
increment = [increment] if increment is not None else []
|
|
1323
|
-
for assignment in initial:
|
|
1303
|
+
for assignment in self.inits:
|
|
1324
1304
|
for_scope.define_variable(assignment.name.name, assignment)
|
|
1325
1305
|
assignment.build_scope(for_scope)
|
|
1326
1306
|
self.condition.build_scope(for_scope)
|
|
1327
|
-
for assignment in
|
|
1307
|
+
for assignment in self.incrs:
|
|
1328
1308
|
assignment.build_scope(for_scope)
|
|
1329
1309
|
self.body.build_scope(for_scope)
|
|
1330
1310
|
|
|
@@ -1496,46 +1476,6 @@ class ModularFor(ModuleInstantiation):
|
|
|
1496
1476
|
node.build_scope(for_scope)
|
|
1497
1477
|
|
|
1498
1478
|
|
|
1499
|
-
@dataclass
|
|
1500
|
-
class ModularCFor(ModuleInstantiation):
|
|
1501
|
-
"""Represents a C-style for loop module instantiation.
|
|
1502
|
-
|
|
1503
|
-
C-style for loops have three parts: initialization, condition, and increment.
|
|
1504
|
-
Similar to C/Java for loops: for (init; condition; increment) body
|
|
1505
|
-
|
|
1506
|
-
Examples:
|
|
1507
|
-
for (i=0; i<5; i=i+1) translate([i, 0, 0]) cube(1);
|
|
1508
|
-
for (i=0; i<10; i=i+2) rotate([0, 0, i]) cube(1);
|
|
1509
|
-
|
|
1510
|
-
Attributes:
|
|
1511
|
-
initial: List of initialization assignments.
|
|
1512
|
-
condition: The loop continuation condition.
|
|
1513
|
-
increment: List of increment assignments.
|
|
1514
|
-
body: The module instantiation to execute for each iteration.
|
|
1515
|
-
"""
|
|
1516
|
-
initial: list[Assignment]
|
|
1517
|
-
condition: Expression
|
|
1518
|
-
increment: list[Assignment]
|
|
1519
|
-
body: ModuleInstantiation
|
|
1520
|
-
|
|
1521
|
-
def __str__(self):
|
|
1522
|
-
return f"for ({'; '.join(str(a) for a in self.initial)}; {self.condition}; {', '.join(str(a) for a in self.increment)}) {self.body}"
|
|
1523
|
-
|
|
1524
|
-
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1525
|
-
self.scope = parent_scope
|
|
1526
|
-
for_scope = parent_scope.child_scope()
|
|
1527
|
-
for assignment in self.initial:
|
|
1528
|
-
for_scope.define_variable(assignment.name.name, assignment)
|
|
1529
|
-
assignment.build_scope(for_scope)
|
|
1530
|
-
self.condition.build_scope(for_scope)
|
|
1531
|
-
for assignment in self.increment:
|
|
1532
|
-
assignment.build_scope(for_scope)
|
|
1533
|
-
body = self.body if isinstance(self.body, list) else [self.body]
|
|
1534
|
-
_collect_hoisted_declarations(body, for_scope)
|
|
1535
|
-
for node in body:
|
|
1536
|
-
node.build_scope(for_scope)
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
1479
|
@dataclass
|
|
1540
1480
|
class ModularIntersectionFor(ModuleInstantiation):
|
|
1541
1481
|
"""Represents an intersection_for loop module instantiation.
|
|
@@ -1570,52 +1510,6 @@ class ModularIntersectionFor(ModuleInstantiation):
|
|
|
1570
1510
|
node.build_scope(for_scope)
|
|
1571
1511
|
|
|
1572
1512
|
|
|
1573
|
-
@dataclass
|
|
1574
|
-
class ModularIntersectionCFor(ModuleInstantiation):
|
|
1575
|
-
"""Represents an intersection_for C-style loop module instantiation.
|
|
1576
|
-
|
|
1577
|
-
Similar to a C-style for loop, but computes the intersection of all
|
|
1578
|
-
iterations rather than the union. Used for creating complex intersections
|
|
1579
|
-
that use explicit initialization, condition, and increment.
|
|
1580
|
-
|
|
1581
|
-
Examples:
|
|
1582
|
-
intersection_for(i = 0; i < 3; i = i + 1) rotate([0,0,i*90]) cube(10);
|
|
1583
|
-
|
|
1584
|
-
Attributes:
|
|
1585
|
-
initial: List of initialization assignments.
|
|
1586
|
-
condition: The loop continuation condition.
|
|
1587
|
-
increment: List of increment assignments.
|
|
1588
|
-
body: The module instantiation to execute for each iteration.
|
|
1589
|
-
"""
|
|
1590
|
-
initial: list[Assignment]
|
|
1591
|
-
condition: Expression
|
|
1592
|
-
increment: list[Assignment]
|
|
1593
|
-
body: ModuleInstantiation
|
|
1594
|
-
|
|
1595
|
-
def __str__(self):
|
|
1596
|
-
return (
|
|
1597
|
-
f"intersection_for ("
|
|
1598
|
-
f"{'; '.join(str(a) for a in self.initial)}; "
|
|
1599
|
-
f"{self.condition}; "
|
|
1600
|
-
f"{', '.join(str(a) for a in self.increment)}"
|
|
1601
|
-
f") {self.body}"
|
|
1602
|
-
)
|
|
1603
|
-
|
|
1604
|
-
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1605
|
-
self.scope = parent_scope
|
|
1606
|
-
for_scope = parent_scope.child_scope()
|
|
1607
|
-
for assignment in self.initial:
|
|
1608
|
-
for_scope.define_variable(assignment.name.name, assignment)
|
|
1609
|
-
assignment.build_scope(for_scope)
|
|
1610
|
-
self.condition.build_scope(for_scope)
|
|
1611
|
-
for assignment in self.increment:
|
|
1612
|
-
assignment.build_scope(for_scope)
|
|
1613
|
-
body = self.body if isinstance(self.body, list) else [self.body]
|
|
1614
|
-
_collect_hoisted_declarations(body, for_scope)
|
|
1615
|
-
for node in body:
|
|
1616
|
-
node.build_scope(for_scope)
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
1513
|
@dataclass
|
|
1620
1514
|
class ModularLet(ModuleInstantiation):
|
|
1621
1515
|
"""Represents a let statement for module instantiations.
|
|
@@ -4,8 +4,8 @@ from .nodes import (
|
|
|
4
4
|
ASTNode, Assignment, FunctionDeclaration, ModuleDeclaration,
|
|
5
5
|
UseStatement, IncludeStatement,
|
|
6
6
|
ModuleInstantiation,
|
|
7
|
-
ModularCall, ModularFor,
|
|
8
|
-
ModularIntersectionFor,
|
|
7
|
+
ModularCall, ModularFor,
|
|
8
|
+
ModularIntersectionFor,
|
|
9
9
|
ModularLet, ModularEcho, ModularAssert,
|
|
10
10
|
ModularIf, ModularIfElse,
|
|
11
11
|
ModularModifierShowOnly, ModularModifierHighlight,
|
|
@@ -72,7 +72,7 @@ def _fmt_node(node: ASTNode, indent: int, w: int) -> str:
|
|
|
72
72
|
return f"{pad}module {node.name}({params}) {block}"
|
|
73
73
|
if isinstance(node, ModuleInstantiation):
|
|
74
74
|
return _fmt_inst(node, indent, w)
|
|
75
|
-
return f"{pad}{node}"
|
|
75
|
+
return f"{pad}{node}" # pragma: no cover
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
def _fmt_block(nodes: list, indent: int, w: int) -> str:
|
|
@@ -129,20 +129,10 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
|
|
|
129
129
|
assigns = _join_str(_as_list(node.assignments))
|
|
130
130
|
return f"{pad}{prefix}for ({assigns})" + _fmt_child(node.body, indent, w)
|
|
131
131
|
|
|
132
|
-
if isinstance(node, ModularCFor):
|
|
133
|
-
init = _join_str(_as_list(node.initial))
|
|
134
|
-
inc = _join_str(_as_list(node.increment))
|
|
135
|
-
return f"{pad}{prefix}for ({init}; {node.condition}; {inc})" + _fmt_child(node.body, indent, w)
|
|
136
|
-
|
|
137
132
|
if isinstance(node, ModularIntersectionFor):
|
|
138
133
|
assigns = _join_str(_as_list(node.assignments))
|
|
139
134
|
return f"{pad}{prefix}intersection_for ({assigns})" + _fmt_child(node.body, indent, w)
|
|
140
135
|
|
|
141
|
-
if isinstance(node, ModularIntersectionCFor):
|
|
142
|
-
init = _join_str(_as_list(node.initial))
|
|
143
|
-
inc = _join_str(_as_list(node.increment))
|
|
144
|
-
return f"{pad}{prefix}intersection_for ({init}; {node.condition}; {inc})" + _fmt_child(node.body, indent, w)
|
|
145
|
-
|
|
146
136
|
if isinstance(node, ModularLet):
|
|
147
137
|
assigns = _join_str(_as_list(node.assignments))
|
|
148
138
|
return f"{pad}{prefix}let ({assigns})" + _fmt_child(node.children, indent, w)
|
|
@@ -166,4 +156,4 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
|
|
|
166
156
|
connector = " else" if true_tail.startswith(" {") else f"\n{pad}else"
|
|
167
157
|
return header + true_tail + connector + false_tail
|
|
168
158
|
|
|
169
|
-
return f"{pad}{prefix}{node};"
|
|
159
|
+
return f"{pad}{prefix}{node};" # pragma: no cover
|
|
@@ -76,9 +76,7 @@ from .nodes import (
|
|
|
76
76
|
ModuleInstantiation,
|
|
77
77
|
ModularCall,
|
|
78
78
|
ModularFor,
|
|
79
|
-
ModularCFor,
|
|
80
79
|
ModularIntersectionFor,
|
|
81
|
-
ModularIntersectionCFor,
|
|
82
80
|
ModularLet,
|
|
83
81
|
ModularEcho,
|
|
84
82
|
ModularAssert,
|
|
@@ -157,9 +155,7 @@ _NODE_REGISTRY: dict[str, type[ASTNode]] = {
|
|
|
157
155
|
# Module instantiations
|
|
158
156
|
ModularCall,
|
|
159
157
|
ModularFor,
|
|
160
|
-
ModularCFor,
|
|
161
158
|
ModularIntersectionFor,
|
|
162
|
-
ModularIntersectionCFor,
|
|
163
159
|
ModularLet,
|
|
164
160
|
ModularEcho,
|
|
165
161
|
ModularAssert,
|
|
@@ -72,7 +72,7 @@ def main():
|
|
|
72
72
|
elif args.yaml:
|
|
73
73
|
try:
|
|
74
74
|
from openscad_parser.ast import ast_to_yaml
|
|
75
|
-
except ImportError:
|
|
75
|
+
except ImportError: # pragma: no cover
|
|
76
76
|
print("openscad-parser: --yaml requires PyYAML (pip install openscad_parser[yaml])", file=sys.stderr)
|
|
77
77
|
sys.exit(1)
|
|
78
78
|
print(ast_to_yaml(ast))
|
|
@@ -80,5 +80,5 @@ def main():
|
|
|
80
80
|
print(ast_to_json(ast, indent=args.indent))
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
if __name__ == "__main__":
|
|
83
|
+
if __name__ == "__main__": # pragma: no cover
|
|
84
84
|
main()
|
|
@@ -384,9 +384,7 @@ def ifelse_statement():
|
|
|
384
384
|
|
|
385
385
|
def single_module_instantiation():
|
|
386
386
|
return [
|
|
387
|
-
modular_c_for,
|
|
388
387
|
modular_for,
|
|
389
|
-
modular_intersection_c_for,
|
|
390
388
|
modular_intersection_for,
|
|
391
389
|
modular_let,
|
|
392
390
|
modular_assert,
|
|
@@ -409,36 +407,16 @@ def modular_for():
|
|
|
409
407
|
return (KWD_FOR, TOK_PAREN, assignments_expr, TOK_ENDPAREN, child_statement)
|
|
410
408
|
|
|
411
409
|
|
|
412
|
-
def
|
|
413
|
-
return
|
|
414
|
-
KWD_FOR,
|
|
415
|
-
TOK_PAREN,
|
|
416
|
-
assignments_expr,
|
|
417
|
-
TOK_SEMICOLON,
|
|
418
|
-
expr,
|
|
419
|
-
TOK_SEMICOLON,
|
|
420
|
-
assignments_expr,
|
|
421
|
-
TOK_ENDPAREN,
|
|
422
|
-
child_statement
|
|
423
|
-
)
|
|
410
|
+
def c_for_inits():
|
|
411
|
+
return assignments_expr
|
|
424
412
|
|
|
425
413
|
|
|
426
|
-
def
|
|
427
|
-
return
|
|
414
|
+
def c_for_incrs():
|
|
415
|
+
return assignments_expr
|
|
428
416
|
|
|
429
417
|
|
|
430
|
-
def
|
|
431
|
-
return (
|
|
432
|
-
KWD_INTERSECTION_FOR,
|
|
433
|
-
TOK_PAREN,
|
|
434
|
-
assignments_expr,
|
|
435
|
-
TOK_SEMICOLON,
|
|
436
|
-
expr,
|
|
437
|
-
TOK_SEMICOLON,
|
|
438
|
-
assignments_expr,
|
|
439
|
-
TOK_ENDPAREN,
|
|
440
|
-
child_statement
|
|
441
|
-
)
|
|
418
|
+
def modular_intersection_for():
|
|
419
|
+
return (KWD_INTERSECTION_FOR, TOK_PAREN, assignments_expr, TOK_ENDPAREN, child_statement)
|
|
442
420
|
|
|
443
421
|
|
|
444
422
|
def modular_let():
|
|
@@ -686,7 +664,7 @@ def listcomp_for():
|
|
|
686
664
|
|
|
687
665
|
|
|
688
666
|
def listcomp_c_for():
|
|
689
|
-
return (KWD_FOR, TOK_PAREN,
|
|
667
|
+
return (KWD_FOR, TOK_PAREN, c_for_inits, TOK_SEMICOLON, expr, TOK_SEMICOLON, c_for_incrs, TOK_ENDPAREN, vector_element)
|
|
690
668
|
|
|
691
669
|
|
|
692
670
|
def listcomp_ifonly():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|