openscad-parser 2.4.5__tar.gz → 2.4.7__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.5 → openscad_parser-2.4.7}/PKG-INFO +11 -5
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/README.rst +10 -4
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/pyproject.toml +1 -1
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/builder.py +2 -2
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/nodes.py +10 -4
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/pretty_print.py +133 -29
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/__init__.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/__init__.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/scope.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/serialization.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/ast/source_map.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/src/openscad_parser/cli.py +0 -0
- {openscad_parser-2.4.5 → openscad_parser-2.4.7}/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.4.
|
|
3
|
+
Version: 2.4.7
|
|
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
|
|
@@ -65,7 +65,7 @@ Features
|
|
|
65
65
|
- AST tree can contain comment nodes (single-line and multi-line)
|
|
66
66
|
- AST tree uses dataclasses and can be pickled/unpickled for caching/serialization
|
|
67
67
|
- JSON and YAML serialization/deserialization of AST trees
|
|
68
|
-
- Pretty-printer that converts an AST back to formatted OpenSCAD source (``to_openscad()``)
|
|
68
|
+
- Pretty-printer that converts an AST back to formatted OpenSCAD source (``to_openscad()``) with correct operator-precedence parenthesization
|
|
69
69
|
- Command-line interface (``openscad-parser``) for JSON/YAML/formatted output
|
|
70
70
|
|
|
71
71
|
Installation
|
|
@@ -441,8 +441,8 @@ List Comprehensions
|
|
|
441
441
|
- ``ListComprehension(elements: list[VectorElement])``: Vector/list literals ``[elements]``
|
|
442
442
|
- ``ListCompFor(assignments: list[Assignment], body: VectorElement)``: for loops in list comprehensions ``for(assignments) body``
|
|
443
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``
|
|
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
446
|
- ``ListCompLet(assignments: list[Assignment], body: VectorElement)``: let expressions in list comprehensions ``let(assignments) body``
|
|
447
447
|
- ``ListCompEach(body: VectorElement)``: each expressions (flattens nested lists) ``each body``
|
|
448
448
|
|
|
@@ -805,7 +805,7 @@ The ``to_openscad()`` function converts an AST back to formatted OpenSCAD source
|
|
|
805
805
|
|
|
806
806
|
formatted = to_openscad(ast)
|
|
807
807
|
# module box(w, h) {
|
|
808
|
-
# cube([w, h, 1
|
|
808
|
+
# cube([w, h, 1]);
|
|
809
809
|
# }
|
|
810
810
|
print(formatted)
|
|
811
811
|
|
|
@@ -822,6 +822,12 @@ control structures, modifiers, list comprehensions, and comments.
|
|
|
822
822
|
|
|
823
823
|
- Blank lines are inserted before and after module/function declarations.
|
|
824
824
|
- Single-child module instantiations are formatted inline; multiple children use a block.
|
|
825
|
+
- Operator precedence is preserved — parentheses are re-inserted exactly where needed.
|
|
826
|
+
- Boolean literals are always written as ``true`` / ``false``.
|
|
827
|
+
- Ternary expressions are formatted across three lines (``condition``, ``? true``, ``: false``); block-formatted branches (``let``, ``assert``, ``echo``, list comprehensions, long calls) align their closing delimiter with their visual keyword column.
|
|
828
|
+
- ``let()``, ``assert()``, and ``echo()`` expressions place their body on the next line.
|
|
829
|
+
- List comprehensions containing ``for`` loops always expand to block form; ``if (condition)`` elements include parentheses around the condition.
|
|
830
|
+
- Long argument/parameter lists (> 100 characters) are formatted one argument per line, with each argument expression individually reformatted (ternaries, let, list comprehensions, nested long calls).
|
|
825
831
|
- Comments are preserved when the AST was parsed with ``include_comments=True``.
|
|
826
832
|
|
|
827
833
|
Controlling indentation::
|
|
@@ -22,7 +22,7 @@ Features
|
|
|
22
22
|
- AST tree can contain comment nodes (single-line and multi-line)
|
|
23
23
|
- AST tree uses dataclasses and can be pickled/unpickled for caching/serialization
|
|
24
24
|
- JSON and YAML serialization/deserialization of AST trees
|
|
25
|
-
- Pretty-printer that converts an AST back to formatted OpenSCAD source (``to_openscad()``)
|
|
25
|
+
- Pretty-printer that converts an AST back to formatted OpenSCAD source (``to_openscad()``) with correct operator-precedence parenthesization
|
|
26
26
|
- Command-line interface (``openscad-parser``) for JSON/YAML/formatted output
|
|
27
27
|
|
|
28
28
|
Installation
|
|
@@ -398,8 +398,8 @@ List Comprehensions
|
|
|
398
398
|
- ``ListComprehension(elements: list[VectorElement])``: Vector/list literals ``[elements]``
|
|
399
399
|
- ``ListCompFor(assignments: list[Assignment], body: VectorElement)``: for loops in list comprehensions ``for(assignments) body``
|
|
400
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``
|
|
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
403
|
- ``ListCompLet(assignments: list[Assignment], body: VectorElement)``: let expressions in list comprehensions ``let(assignments) body``
|
|
404
404
|
- ``ListCompEach(body: VectorElement)``: each expressions (flattens nested lists) ``each body``
|
|
405
405
|
|
|
@@ -762,7 +762,7 @@ The ``to_openscad()`` function converts an AST back to formatted OpenSCAD source
|
|
|
762
762
|
|
|
763
763
|
formatted = to_openscad(ast)
|
|
764
764
|
# module box(w, h) {
|
|
765
|
-
# cube([w, h, 1
|
|
765
|
+
# cube([w, h, 1]);
|
|
766
766
|
# }
|
|
767
767
|
print(formatted)
|
|
768
768
|
|
|
@@ -779,6 +779,12 @@ control structures, modifiers, list comprehensions, and comments.
|
|
|
779
779
|
|
|
780
780
|
- Blank lines are inserted before and after module/function declarations.
|
|
781
781
|
- Single-child module instantiations are formatted inline; multiple children use a block.
|
|
782
|
+
- Operator precedence is preserved — parentheses are re-inserted exactly where needed.
|
|
783
|
+
- Boolean literals are always written as ``true`` / ``false``.
|
|
784
|
+
- Ternary expressions are formatted across three lines (``condition``, ``? true``, ``: false``); block-formatted branches (``let``, ``assert``, ``echo``, list comprehensions, long calls) align their closing delimiter with their visual keyword column.
|
|
785
|
+
- ``let()``, ``assert()``, and ``echo()`` expressions place their body on the next line.
|
|
786
|
+
- List comprehensions containing ``for`` loops always expand to block form; ``if (condition)`` elements include parentheses around the condition.
|
|
787
|
+
- Long argument/parameter lists (> 100 characters) are formatted one argument per line, with each argument expression individually reformatted (ternaries, let, list comprehensions, nested long calls).
|
|
782
788
|
- Comments are preserved when the AST was parsed with ``include_comments=True``.
|
|
783
789
|
|
|
784
790
|
Controlling indentation::
|
|
@@ -624,7 +624,7 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
624
624
|
arguments = children[0] if len(children) > 0 else []
|
|
625
625
|
body = children[1] if len(children) > 1 else None
|
|
626
626
|
if body is None:
|
|
627
|
-
|
|
627
|
+
body = UndefinedLiteral(position=self._get_node_position(node))
|
|
628
628
|
return AssertOp(arguments=arguments, body=body, position=self._get_node_position(node))
|
|
629
629
|
|
|
630
630
|
def visit_echo_expr(self, node, children):
|
|
@@ -637,7 +637,7 @@ class ASTBuilderVisitor(PTNodeVisitor):
|
|
|
637
637
|
arguments = children[0] if len(children) > 0 else []
|
|
638
638
|
body = children[1] if len(children) > 1 else None
|
|
639
639
|
if body is None:
|
|
640
|
-
|
|
640
|
+
body = UndefinedLiteral(position=self._get_node_position(node))
|
|
641
641
|
return EchoOp(arguments=arguments, body=body, position=self._get_node_position(node))
|
|
642
642
|
|
|
643
643
|
def visit_ternary_expr(self, node, children):
|
|
@@ -430,7 +430,10 @@ class EchoOp(Expression):
|
|
|
430
430
|
body: Expression
|
|
431
431
|
|
|
432
432
|
def __str__(self):
|
|
433
|
-
|
|
433
|
+
args = ', '.join(str(arg) for arg in self.arguments)
|
|
434
|
+
if isinstance(self.body, UndefinedLiteral):
|
|
435
|
+
return f"echo({args})"
|
|
436
|
+
return f"echo({args}) {self.body}"
|
|
434
437
|
|
|
435
438
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
436
439
|
self.scope = parent_scope
|
|
@@ -458,7 +461,10 @@ class AssertOp(Expression):
|
|
|
458
461
|
body: Expression
|
|
459
462
|
|
|
460
463
|
def __str__(self):
|
|
461
|
-
|
|
464
|
+
args = ', '.join(str(arg) for arg in self.arguments)
|
|
465
|
+
if isinstance(self.body, UndefinedLiteral):
|
|
466
|
+
return f"assert({args})"
|
|
467
|
+
return f"assert({args}) {self.body}"
|
|
462
468
|
|
|
463
469
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
464
470
|
self.scope = parent_scope
|
|
@@ -1350,7 +1356,7 @@ class ListCompIf(VectorElement):
|
|
|
1350
1356
|
true_expr: VectorElement
|
|
1351
1357
|
|
|
1352
1358
|
def __str__(self):
|
|
1353
|
-
return f"if {self.condition} {self.true_expr}"
|
|
1359
|
+
return f"if ({self.condition}) {self.true_expr}"
|
|
1354
1360
|
|
|
1355
1361
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1356
1362
|
self.scope = parent_scope
|
|
@@ -1379,7 +1385,7 @@ class ListCompIfElse(VectorElement):
|
|
|
1379
1385
|
false_expr: VectorElement
|
|
1380
1386
|
|
|
1381
1387
|
def __str__(self):
|
|
1382
|
-
return f"if {self.condition} {self.true_expr} else {self.false_expr}"
|
|
1388
|
+
return f"if ({self.condition}) {self.true_expr} else {self.false_expr}"
|
|
1383
1389
|
|
|
1384
1390
|
def build_scope(self, parent_scope: "Scope") -> None:
|
|
1385
1391
|
self.scope = parent_scope
|
|
@@ -15,11 +15,17 @@ from .nodes import (
|
|
|
15
15
|
EchoOp,
|
|
16
16
|
AssertOp,
|
|
17
17
|
LetOp,
|
|
18
|
+
UndefinedLiteral,
|
|
18
19
|
PrimaryCall,
|
|
19
20
|
ListComprehension,
|
|
20
21
|
ListCompFor,
|
|
21
22
|
ListCompCFor,
|
|
22
23
|
ListCompLet,
|
|
24
|
+
ListCompIf,
|
|
25
|
+
ListCompIfElse,
|
|
26
|
+
ListCompEach,
|
|
27
|
+
PositionalArgument,
|
|
28
|
+
NamedArgument,
|
|
23
29
|
)
|
|
24
30
|
|
|
25
31
|
|
|
@@ -66,17 +72,33 @@ def _fmt_list_elem(elem, indent: int, w: int) -> str:
|
|
|
66
72
|
pad = " " * indent
|
|
67
73
|
inner_pad = " " * (indent + w)
|
|
68
74
|
if isinstance(elem, ListCompFor):
|
|
69
|
-
|
|
75
|
+
formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
|
|
70
76
|
body = _fmt_list_elem(elem.body, indent + w, w)
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
assigns_inline = ", ".join(formatted)
|
|
78
|
+
if any("\n" in fa for fa in formatted) or len(f"for ({assigns_inline})") + indent > _MULTILINE_CHAR_LIMIT:
|
|
79
|
+
assign_lines = (",\n" + inner_pad).join(formatted)
|
|
73
80
|
return f"for (\n{inner_pad}{assign_lines}\n{pad})\n{inner_pad}{body}"
|
|
74
81
|
return f"for ({assigns_inline})\n{inner_pad}{body}"
|
|
75
82
|
if isinstance(elem, ListCompCFor):
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
fmt_inits = [_fmt_assign(a, indent + w, w) for a in elem.inits]
|
|
84
|
+
fmt_incrs = [_fmt_assign(a, indent + w, w) for a in elem.incrs]
|
|
85
|
+
inits_str = ", ".join(fmt_inits)
|
|
86
|
+
incrs_str = ", ".join(fmt_incrs)
|
|
87
|
+
cond_str = str(elem.condition)
|
|
78
88
|
body = _fmt_list_elem(elem.body, indent + w, w)
|
|
79
|
-
|
|
89
|
+
header = f"for ({inits_str}; {cond_str}; {incrs_str})"
|
|
90
|
+
any_multiline = (
|
|
91
|
+
any("\n" in fa for fa in fmt_inits) or
|
|
92
|
+
any("\n" in fa for fa in fmt_incrs) or
|
|
93
|
+
"\n" in cond_str
|
|
94
|
+
)
|
|
95
|
+
if any_multiline or len(header) + indent > _MULTILINE_CHAR_LIMIT:
|
|
96
|
+
return (
|
|
97
|
+
f"for (\n{inner_pad}{inits_str};\n"
|
|
98
|
+
f"{inner_pad}{cond_str};\n"
|
|
99
|
+
f"{inner_pad}{incrs_str}\n{pad})\n{inner_pad}{body}"
|
|
100
|
+
)
|
|
101
|
+
return f"{header}\n{inner_pad}{body}"
|
|
80
102
|
if isinstance(elem, ListCompLet):
|
|
81
103
|
formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
|
|
82
104
|
body = _fmt_list_elem(elem.body, indent, w)
|
|
@@ -84,23 +106,40 @@ def _fmt_list_elem(elem, indent: int, w: int) -> str:
|
|
|
84
106
|
assign_lines = (",\n" + inner_pad).join(formatted)
|
|
85
107
|
return f"let(\n{inner_pad}{assign_lines}\n{pad})\n{pad}{body}"
|
|
86
108
|
assigns = ", ".join(formatted)
|
|
87
|
-
return f"let({assigns})
|
|
109
|
+
return f"let({assigns})\n{pad}{body}"
|
|
88
110
|
if isinstance(elem, LetOp):
|
|
89
111
|
formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
|
|
90
|
-
body =
|
|
112
|
+
body = _fmt_expr(elem.body, indent, w)
|
|
91
113
|
if len(formatted) > 1 or any("\n" in fa for fa in formatted):
|
|
92
114
|
assign_lines = (",\n" + inner_pad).join(formatted)
|
|
93
115
|
return f"let(\n{inner_pad}{assign_lines}\n{pad})\n{pad}{body}"
|
|
94
116
|
assigns = ", ".join(formatted)
|
|
95
|
-
|
|
117
|
+
inline = f"let({assigns}) {body}"
|
|
118
|
+
if "\n" in body or len(inline) + indent > _MULTILINE_CHAR_LIMIT:
|
|
119
|
+
return f"let({assigns})\n{pad}{body}"
|
|
120
|
+
return inline
|
|
121
|
+
if isinstance(elem, ListComprehension):
|
|
122
|
+
return _fmt_expr(elem, indent, w)
|
|
123
|
+
if isinstance(elem, ListCompIf):
|
|
124
|
+
cond = str(elem.condition)
|
|
125
|
+
body = _fmt_list_elem(elem.true_expr, indent + w, w)
|
|
126
|
+
return f"if ({cond})\n{inner_pad}{body}"
|
|
127
|
+
if isinstance(elem, ListCompIfElse):
|
|
128
|
+
cond = str(elem.condition)
|
|
129
|
+
true_body = _fmt_list_elem(elem.true_expr, indent + w, w)
|
|
130
|
+
false_body = _fmt_list_elem(elem.false_expr, indent + w, w)
|
|
131
|
+
return f"if ({cond})\n{inner_pad}{true_body}\n{pad}else\n{inner_pad}{false_body}"
|
|
132
|
+
if isinstance(elem, ListCompEach):
|
|
133
|
+
body = _fmt_list_elem(elem.body, indent, w)
|
|
134
|
+
return f"each {body}"
|
|
96
135
|
return str(elem)
|
|
97
136
|
|
|
98
137
|
|
|
99
|
-
def _fmt_multiline_args(head: str, args: list, indent: int, w: int) -> str:
|
|
138
|
+
def _fmt_multiline_args(head: str, args: list, indent: int, w: int, fmt_fn=str) -> str:
|
|
100
139
|
"""Format `head(arg1, arg2, ...)` with each arg on its own line."""
|
|
101
140
|
inner_pad = " " * (indent + w)
|
|
102
141
|
pad = " " * indent
|
|
103
|
-
arg_lines = (",\n" + inner_pad).join(
|
|
142
|
+
arg_lines = (",\n" + inner_pad).join(fmt_fn(a) for a in args)
|
|
104
143
|
return f"{head}(\n{inner_pad}{arg_lines}\n{pad})"
|
|
105
144
|
|
|
106
145
|
|
|
@@ -109,21 +148,74 @@ def _fmt_assign(assign, indent: int, w: int) -> str:
|
|
|
109
148
|
return f"{assign.name} = {_fmt_expr(assign.expr, indent, w)}"
|
|
110
149
|
|
|
111
150
|
|
|
151
|
+
def _fmt_argument(arg, indent: int, w: int) -> str:
|
|
152
|
+
"""Format a call argument, routing its expression through _fmt_expr."""
|
|
153
|
+
if isinstance(arg, PositionalArgument):
|
|
154
|
+
return _fmt_expr(arg.expr, indent, w)
|
|
155
|
+
if isinstance(arg, NamedArgument):
|
|
156
|
+
return f"{arg.name}={_fmt_expr(arg.expr, indent, w)}"
|
|
157
|
+
return str(arg)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _fmt_ternary_chain(expr: TernaryOp, indent: int, w: int) -> str:
|
|
161
|
+
"""Format a right-chain of ternaries with flat ? / : alignment.
|
|
162
|
+
|
|
163
|
+
All ': ' connectors stay at the same indent column as the conditions;
|
|
164
|
+
each true branch is on its own line at indent+w. The '?' moves to
|
|
165
|
+
the end of the condition line rather than the beginning of the true-branch.
|
|
166
|
+
|
|
167
|
+
cond1?
|
|
168
|
+
true1
|
|
169
|
+
: cond2?
|
|
170
|
+
true2
|
|
171
|
+
: final_else
|
|
172
|
+
"""
|
|
173
|
+
pad = " " * indent
|
|
174
|
+
inner_pad = " " * (indent + w)
|
|
175
|
+
parts = []
|
|
176
|
+
node: TernaryOp = expr
|
|
177
|
+
while isinstance(node, TernaryOp):
|
|
178
|
+
parts.append((node.condition, node.true_expr))
|
|
179
|
+
node = node.false_expr
|
|
180
|
+
final = node
|
|
181
|
+
lines = []
|
|
182
|
+
for i, (cond, true_expr) in enumerate(parts):
|
|
183
|
+
true_str = _fmt_expr(true_expr, indent + w, w)
|
|
184
|
+
prefix = "" if i == 0 else f"{pad}: "
|
|
185
|
+
lines.append(f"{prefix}{cond} ?\n{inner_pad}{true_str}")
|
|
186
|
+
lines.append(f"{pad}: {_fmt_expr(final, indent + w, w)}")
|
|
187
|
+
return "\n".join(lines)
|
|
188
|
+
|
|
189
|
+
|
|
112
190
|
def _fmt_expr(expr, indent: int, w: int) -> str:
|
|
113
191
|
"""Format an expression with indent-aware layout for ternary, assert, and echo."""
|
|
114
192
|
pad = " " * indent
|
|
115
193
|
if isinstance(expr, TernaryOp):
|
|
194
|
+
# Right-chain of ternaries → flat cascade format
|
|
195
|
+
if isinstance(expr.false_expr, TernaryOp):
|
|
196
|
+
return _fmt_ternary_chain(expr, indent, w)
|
|
116
197
|
pad2 = " " * (indent + 2)
|
|
198
|
+
def _fmt_branch(branch):
|
|
199
|
+
# Nested ternary: 2 spaces per nesting level
|
|
200
|
+
if isinstance(branch, TernaryOp):
|
|
201
|
+
return _fmt_expr(branch, indent + 2, w)
|
|
202
|
+
# All other expressions: their visual start is 2 chars into "? "/":", "
|
|
203
|
+
# so block content and closing delimiters align to indent + 4
|
|
204
|
+
return _fmt_expr(branch, indent + 4, w)
|
|
117
205
|
return (
|
|
118
206
|
f"{expr.condition}\n"
|
|
119
|
-
f"{pad2}? {
|
|
120
|
-
f"{pad2}: {
|
|
207
|
+
f"{pad2}? {_fmt_branch(expr.true_expr)}\n"
|
|
208
|
+
f"{pad2}: {_fmt_branch(expr.false_expr)}"
|
|
121
209
|
)
|
|
122
210
|
if isinstance(expr, AssertOp):
|
|
123
211
|
args = ", ".join(str(a) for a in expr.arguments)
|
|
212
|
+
if isinstance(expr.body, UndefinedLiteral):
|
|
213
|
+
return f"assert({args})"
|
|
124
214
|
return f"assert({args})\n{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
125
215
|
if isinstance(expr, EchoOp):
|
|
126
216
|
args = ", ".join(str(a) for a in expr.arguments)
|
|
217
|
+
if isinstance(expr.body, UndefinedLiteral):
|
|
218
|
+
return f"echo({args})"
|
|
127
219
|
return f"echo({args})\n{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
128
220
|
if isinstance(expr, LetOp):
|
|
129
221
|
inner_pad = " " * (indent + w)
|
|
@@ -135,22 +227,19 @@ def _fmt_expr(expr, indent: int, w: int) -> str:
|
|
|
135
227
|
f"{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
136
228
|
)
|
|
137
229
|
assigns = ", ".join(formatted)
|
|
138
|
-
return f"let({assigns})\n{
|
|
230
|
+
return f"let({assigns})\n{inner_pad}{_fmt_expr(expr.body, indent + w, w)}"
|
|
139
231
|
if isinstance(expr, PrimaryCall):
|
|
140
232
|
inline = str(expr)
|
|
141
233
|
if len(inline) + indent > _MULTILINE_CHAR_LIMIT:
|
|
142
|
-
return _fmt_multiline_args(
|
|
234
|
+
return _fmt_multiline_args(
|
|
235
|
+
str(expr.left), expr.arguments, indent, w,
|
|
236
|
+
fmt_fn=lambda a: _fmt_argument(a, indent + w, w),
|
|
237
|
+
)
|
|
143
238
|
if isinstance(expr, ListComprehension):
|
|
144
239
|
inner_pad = " " * (indent + w)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
any("\n" in _fmt_assign(a, indent + w + w, w) for a in e.assignments)
|
|
149
|
-
)
|
|
150
|
-
for e in expr.elements
|
|
151
|
-
)
|
|
152
|
-
if len(str(expr)) + indent > _MULTILINE_CHAR_LIMIT or let_multiline:
|
|
153
|
-
formatted_elems = [_fmt_list_elem(e, indent + w, w) for e in expr.elements]
|
|
240
|
+
formatted_elems = [_fmt_list_elem(e, indent + w, w) for e in expr.elements]
|
|
241
|
+
any_multiline = any("\n" in fe for fe in formatted_elems)
|
|
242
|
+
if len(str(expr)) + indent > _MULTILINE_CHAR_LIMIT or any_multiline:
|
|
154
243
|
items = (",\n" + inner_pad).join(formatted_elems)
|
|
155
244
|
return f"[\n{inner_pad}{items}\n{pad}]"
|
|
156
245
|
return str(expr)
|
|
@@ -241,7 +330,10 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
|
|
|
241
330
|
head = f"{pad}{prefix}{node.name}"
|
|
242
331
|
inline = f"{head}({_join_str(node.arguments)})"
|
|
243
332
|
if len(inline) > _MULTILINE_CHAR_LIMIT:
|
|
244
|
-
call = _fmt_multiline_args(
|
|
333
|
+
call = _fmt_multiline_args(
|
|
334
|
+
head, node.arguments, indent, w,
|
|
335
|
+
fmt_fn=lambda a: _fmt_argument(a, indent + w, w),
|
|
336
|
+
)
|
|
245
337
|
else:
|
|
246
338
|
call = inline
|
|
247
339
|
return call + _fmt_child(node.children, indent, w)
|
|
@@ -265,12 +357,24 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
|
|
|
265
357
|
return f"{pad}{prefix}let ({assigns})" + _fmt_child(node.children, indent, w)
|
|
266
358
|
|
|
267
359
|
if isinstance(node, ModularEcho):
|
|
268
|
-
|
|
269
|
-
|
|
360
|
+
head = f"{pad}{prefix}echo"
|
|
361
|
+
inline = f"{head}({_join_str(node.arguments)})"
|
|
362
|
+
if len(inline) > _MULTILINE_CHAR_LIMIT:
|
|
363
|
+
call = _fmt_multiline_args(head, node.arguments, indent, w,
|
|
364
|
+
fmt_fn=lambda a: _fmt_argument(a, indent + w, w))
|
|
365
|
+
else:
|
|
366
|
+
call = inline
|
|
367
|
+
return call + _fmt_child(node.children, indent, w)
|
|
270
368
|
|
|
271
369
|
if isinstance(node, ModularAssert):
|
|
272
|
-
|
|
273
|
-
|
|
370
|
+
head = f"{pad}{prefix}assert"
|
|
371
|
+
inline = f"{head}({_join_str(node.arguments)})"
|
|
372
|
+
if len(inline) > _MULTILINE_CHAR_LIMIT:
|
|
373
|
+
call = _fmt_multiline_args(head, node.arguments, indent, w,
|
|
374
|
+
fmt_fn=lambda a: _fmt_argument(a, indent + w, w))
|
|
375
|
+
else:
|
|
376
|
+
call = inline
|
|
377
|
+
return call + _fmt_child(node.children, indent, w)
|
|
274
378
|
|
|
275
379
|
if isinstance(node, ModularIf):
|
|
276
380
|
header = f"{pad}{prefix}if ({node.condition})"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|