openscad-parser 2.4.6__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.6 → openscad_parser-2.4.7}/PKG-INFO +1 -1
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/pyproject.toml +1 -1
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/builder.py +2 -2
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/nodes.py +8 -2
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/pretty_print.py +75 -7
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/README.rst +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/__init__.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/__init__.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/scope.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/serialization.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/ast/source_map.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/cli.py +0 -0
- {openscad_parser-2.4.6 → openscad_parser-2.4.7}/src/openscad_parser/grammar.py +0 -0
|
@@ -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
|
|
@@ -15,11 +15,15 @@ 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,
|
|
23
27
|
PositionalArgument,
|
|
24
28
|
NamedArgument,
|
|
25
29
|
)
|
|
@@ -105,14 +109,29 @@ def _fmt_list_elem(elem, indent: int, w: int) -> str:
|
|
|
105
109
|
return f"let({assigns})\n{pad}{body}"
|
|
106
110
|
if isinstance(elem, LetOp):
|
|
107
111
|
formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
|
|
108
|
-
body =
|
|
112
|
+
body = _fmt_expr(elem.body, indent, w)
|
|
109
113
|
if len(formatted) > 1 or any("\n" in fa for fa in formatted):
|
|
110
114
|
assign_lines = (",\n" + inner_pad).join(formatted)
|
|
111
115
|
return f"let(\n{inner_pad}{assign_lines}\n{pad})\n{pad}{body}"
|
|
112
116
|
assigns = ", ".join(formatted)
|
|
113
|
-
|
|
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
|
|
114
121
|
if isinstance(elem, ListComprehension):
|
|
115
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}"
|
|
116
135
|
return str(elem)
|
|
117
136
|
|
|
118
137
|
|
|
@@ -138,10 +157,43 @@ def _fmt_argument(arg, indent: int, w: int) -> str:
|
|
|
138
157
|
return str(arg)
|
|
139
158
|
|
|
140
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
|
+
|
|
141
190
|
def _fmt_expr(expr, indent: int, w: int) -> str:
|
|
142
191
|
"""Format an expression with indent-aware layout for ternary, assert, and echo."""
|
|
143
192
|
pad = " " * indent
|
|
144
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)
|
|
145
197
|
pad2 = " " * (indent + 2)
|
|
146
198
|
def _fmt_branch(branch):
|
|
147
199
|
# Nested ternary: 2 spaces per nesting level
|
|
@@ -157,9 +209,13 @@ def _fmt_expr(expr, indent: int, w: int) -> str:
|
|
|
157
209
|
)
|
|
158
210
|
if isinstance(expr, AssertOp):
|
|
159
211
|
args = ", ".join(str(a) for a in expr.arguments)
|
|
212
|
+
if isinstance(expr.body, UndefinedLiteral):
|
|
213
|
+
return f"assert({args})"
|
|
160
214
|
return f"assert({args})\n{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
161
215
|
if isinstance(expr, EchoOp):
|
|
162
216
|
args = ", ".join(str(a) for a in expr.arguments)
|
|
217
|
+
if isinstance(expr.body, UndefinedLiteral):
|
|
218
|
+
return f"echo({args})"
|
|
163
219
|
return f"echo({args})\n{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
164
220
|
if isinstance(expr, LetOp):
|
|
165
221
|
inner_pad = " " * (indent + w)
|
|
@@ -171,7 +227,7 @@ def _fmt_expr(expr, indent: int, w: int) -> str:
|
|
|
171
227
|
f"{pad}{_fmt_expr(expr.body, indent, w)}"
|
|
172
228
|
)
|
|
173
229
|
assigns = ", ".join(formatted)
|
|
174
|
-
return f"let({assigns})\n{
|
|
230
|
+
return f"let({assigns})\n{inner_pad}{_fmt_expr(expr.body, indent + w, w)}"
|
|
175
231
|
if isinstance(expr, PrimaryCall):
|
|
176
232
|
inline = str(expr)
|
|
177
233
|
if len(inline) + indent > _MULTILINE_CHAR_LIMIT:
|
|
@@ -301,12 +357,24 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
|
|
|
301
357
|
return f"{pad}{prefix}let ({assigns})" + _fmt_child(node.children, indent, w)
|
|
302
358
|
|
|
303
359
|
if isinstance(node, ModularEcho):
|
|
304
|
-
|
|
305
|
-
|
|
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)
|
|
306
368
|
|
|
307
369
|
if isinstance(node, ModularAssert):
|
|
308
|
-
|
|
309
|
-
|
|
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)
|
|
310
378
|
|
|
311
379
|
if isinstance(node, ModularIf):
|
|
312
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
|
|
File without changes
|