openscad-parser 2.4.4__tar.gz → 2.4.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_parser
3
- Version: 2.4.4
3
+ Version: 2.4.5
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
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_parser"
7
- version = "2.4.4"
7
+ version = "2.4.5"
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 = [
@@ -19,6 +19,7 @@ from .nodes import (
19
19
  ListComprehension,
20
20
  ListCompFor,
21
21
  ListCompCFor,
22
+ ListCompLet,
22
23
  )
23
24
 
24
25
 
@@ -76,6 +77,22 @@ def _fmt_list_elem(elem, indent: int, w: int) -> str:
76
77
  incrs = ", ".join(str(a) for a in elem.incrs)
77
78
  body = _fmt_list_elem(elem.body, indent + w, w)
78
79
  return f"for ({inits}; {elem.condition}; {incrs})\n{inner_pad}{body}"
80
+ if isinstance(elem, ListCompLet):
81
+ formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
82
+ body = _fmt_list_elem(elem.body, indent, w)
83
+ if len(formatted) > 1 or any("\n" in fa for fa in formatted):
84
+ assign_lines = (",\n" + inner_pad).join(formatted)
85
+ return f"let(\n{inner_pad}{assign_lines}\n{pad})\n{pad}{body}"
86
+ assigns = ", ".join(formatted)
87
+ return f"let({assigns}) {body}"
88
+ if isinstance(elem, LetOp):
89
+ formatted = [_fmt_assign(a, indent + w, w) for a in elem.assignments]
90
+ body = str(elem.body)
91
+ if len(formatted) > 1 or any("\n" in fa for fa in formatted):
92
+ assign_lines = (",\n" + inner_pad).join(formatted)
93
+ return f"let(\n{inner_pad}{assign_lines}\n{pad})\n{pad}{body}"
94
+ assigns = ", ".join(formatted)
95
+ return f"let({assigns}) {body}"
79
96
  return str(elem)
80
97
 
81
98
 
@@ -87,6 +104,11 @@ def _fmt_multiline_args(head: str, args: list, indent: int, w: int) -> str:
87
104
  return f"{head}(\n{inner_pad}{arg_lines}\n{pad})"
88
105
 
89
106
 
107
+ def _fmt_assign(assign, indent: int, w: int) -> str:
108
+ """Format an Assignment node, routing its expression through _fmt_expr."""
109
+ return f"{assign.name} = {_fmt_expr(assign.expr, indent, w)}"
110
+
111
+
90
112
  def _fmt_expr(expr, indent: int, w: int) -> str:
91
113
  """Format an expression with indent-aware layout for ternary, assert, and echo."""
92
114
  pad = " " * indent
@@ -105,25 +127,31 @@ def _fmt_expr(expr, indent: int, w: int) -> str:
105
127
  return f"echo({args})\n{pad}{_fmt_expr(expr.body, indent, w)}"
106
128
  if isinstance(expr, LetOp):
107
129
  inner_pad = " " * (indent + w)
108
- if len(expr.assignments) <= 1:
109
- assigns = ", ".join(str(a) for a in expr.assignments)
110
- return f"let({assigns})\n{pad}{_fmt_expr(expr.body, indent, w)}"
111
- else:
112
- assign_lines = (",\n" + inner_pad).join(str(a) for a in expr.assignments)
130
+ formatted = [_fmt_assign(a, indent + w, w) for a in expr.assignments]
131
+ if len(formatted) > 1 or any("\n" in fa for fa in formatted):
132
+ assign_lines = (",\n" + inner_pad).join(formatted)
113
133
  return (
114
134
  f"let(\n{inner_pad}{assign_lines}\n{pad})\n"
115
135
  f"{pad}{_fmt_expr(expr.body, indent, w)}"
116
136
  )
137
+ assigns = ", ".join(formatted)
138
+ return f"let({assigns})\n{pad}{_fmt_expr(expr.body, indent, w)}"
117
139
  if isinstance(expr, PrimaryCall):
118
140
  inline = str(expr)
119
141
  if len(inline) + indent > _MULTILINE_CHAR_LIMIT:
120
142
  return _fmt_multiline_args(str(expr.left), expr.arguments, indent, w)
121
143
  if isinstance(expr, ListComprehension):
122
- inline = str(expr)
123
- if len(inline) + indent > _MULTILINE_CHAR_LIMIT:
124
- inner_pad = " " * (indent + w)
125
- pad = " " * indent
126
- items = (",\n" + inner_pad).join(_fmt_list_elem(e, indent + w, w) for e in expr.elements)
144
+ inner_pad = " " * (indent + w)
145
+ let_multiline = any(
146
+ isinstance(e, (LetOp, ListCompLet)) and (
147
+ len(e.assignments) > 1 or
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]
154
+ items = (",\n" + inner_pad).join(formatted_elems)
127
155
  return f"[\n{inner_pad}{items}\n{pad}]"
128
156
  return str(expr)
129
157
 
@@ -227,7 +255,13 @@ def _fmt_inst(node: ModuleInstantiation, indent: int, w: int, prefix: str = "")
227
255
  return f"{pad}{prefix}intersection_for ({assigns})" + _fmt_child(node.body, indent, w)
228
256
 
229
257
  if isinstance(node, ModularLet):
230
- assigns = _join_str(_as_list(node.assignments))
258
+ inner_pad = " " * (indent + w)
259
+ formatted = [_fmt_assign(a, indent + w, w) for a in _as_list(node.assignments)]
260
+ if len(formatted) > 1 or any("\n" in fa for fa in formatted):
261
+ assign_lines = (",\n" + inner_pad).join(formatted)
262
+ tail = _fmt_child(node.children, indent, w)
263
+ return f"{pad}{prefix}let (\n{inner_pad}{assign_lines}\n{pad}){tail}"
264
+ assigns = ", ".join(formatted)
231
265
  return f"{pad}{prefix}let ({assigns})" + _fmt_child(node.children, indent, w)
232
266
 
233
267
  if isinstance(node, ModularEcho):