jaclang 0.8.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

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.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (77) hide show
  1. jaclang/cli/cli.py +11 -9
  2. jaclang/compiler/jac.lark +2 -12
  3. jaclang/compiler/larkparse/jac_parser.py +1 -1
  4. jaclang/compiler/parser.py +360 -521
  5. jaclang/compiler/passes/main/cfg_build_pass.py +2 -2
  6. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  7. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  8. jaclang/compiler/passes/main/import_pass.py +3 -3
  9. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +196 -218
  11. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  13. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  14. jaclang/compiler/passes/main/sym_tab_link_pass.py +4 -4
  15. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  16. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  17. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +4 -2
  18. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +197 -120
  19. jaclang/compiler/program.py +2 -7
  20. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  21. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  22. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  23. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  24. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  25. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +11 -0
  26. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +7 -0
  27. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  28. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  29. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  30. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  31. jaclang/compiler/tests/test_importer.py +20 -0
  32. jaclang/compiler/tests/test_parser.py +1 -0
  33. jaclang/compiler/unitree.py +456 -304
  34. jaclang/langserve/engine.jac +498 -0
  35. jaclang/langserve/sem_manager.jac +309 -0
  36. jaclang/langserve/server.jac +186 -0
  37. jaclang/langserve/tests/server_test/test_lang_serve.py +6 -7
  38. jaclang/langserve/tests/server_test/utils.py +4 -1
  39. jaclang/langserve/tests/session.jac +294 -0
  40. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  41. jaclang/langserve/tests/test_server.py +12 -7
  42. jaclang/langserve/utils.jac +51 -30
  43. jaclang/runtimelib/archetype.py +1 -1
  44. jaclang/runtimelib/builtin.py +17 -14
  45. jaclang/runtimelib/importer.py +26 -8
  46. jaclang/runtimelib/machine.py +96 -55
  47. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  48. jaclang/runtimelib/utils.py +3 -3
  49. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  50. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  51. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  52. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  53. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  54. jaclang/tests/fixtures/concurrency.jac +1 -1
  55. jaclang/tests/fixtures/edge_ability.jac +49 -0
  56. jaclang/tests/fixtures/guess_game.jac +1 -1
  57. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  58. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  59. jaclang/tests/fixtures/node_del.jac +30 -36
  60. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  61. jaclang/tests/test_cli.py +12 -7
  62. jaclang/tests/test_language.py +91 -16
  63. jaclang/utils/helpers.py +14 -6
  64. jaclang/utils/lang_tools.py +2 -3
  65. jaclang/utils/tests/test_lang_tools.py +2 -1
  66. jaclang/utils/treeprinter.py +3 -4
  67. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/METADATA +4 -3
  68. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/RECORD +71 -55
  69. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/WHEEL +1 -1
  70. jaclang/langserve/engine.py +0 -553
  71. jaclang/langserve/sem_manager.py +0 -383
  72. jaclang/langserve/server.py +0 -167
  73. jaclang/langserve/tests/session.py +0 -255
  74. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  75. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  76. /jaclang/langserve/{__init__.py → __init__.jac} +0 -0
  77. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/entry_points.txt +0 -0
@@ -14,6 +14,7 @@ relationships between the two AST representations throughout the compilation pro
14
14
  """
15
15
 
16
16
  import ast as ast3
17
+ from typing import Sequence
17
18
 
18
19
  import jaclang.compiler.unitree as uni
19
20
  from jaclang.compiler.passes import UniPass
@@ -50,7 +51,7 @@ class PyJacAstLinkPass(UniPass):
50
51
  self.link_jac_py_nodes(jac_node=node.body, py_nodes=node.gen.py_ast)
51
52
 
52
53
  def exit_impl_def(self, node: uni.ImplDef) -> None:
53
- for i in node.target.items:
54
+ for i in node.target:
54
55
  if i.name_spec.name_of.sym:
55
56
  self.link_jac_py_nodes(
56
57
  jac_node=i, py_nodes=i.name_spec.name_of.sym.decl.gen.py_ast
@@ -62,12 +63,12 @@ class PyJacAstLinkPass(UniPass):
62
63
 
63
64
  if isinstance(node.decl_link, uni.Ability) and node.decl_link.signature:
64
65
  if isinstance(node.spec, uni.FuncSignature) and node.spec.params:
65
- for src_prm in node.spec.params.items:
66
+ for src_prm in node.spec.params:
66
67
  if (
67
68
  isinstance(node.decl_link.signature, uni.FuncSignature)
68
69
  and node.decl_link.signature.params
69
70
  ):
70
- for trg_prm in node.decl_link.signature.params.items:
71
+ for trg_prm in node.decl_link.signature.params:
71
72
  if src_prm.name.sym_name == trg_prm.name.sym_name:
72
73
  self.link_jac_py_nodes(
73
74
  jac_node=src_prm, py_nodes=trg_prm.gen.py_ast
@@ -88,9 +89,9 @@ class PyJacAstLinkPass(UniPass):
88
89
  )
89
90
 
90
91
  if isinstance(node.decl_link, uni.Ability) and isinstance(
91
- node.target, uni.SubNodeList
92
+ node.target, Sequence
92
93
  ):
93
- for arch in node.target.items:
94
+ for arch in node.target:
94
95
  if arch.name_spec.name_of.sym:
95
96
  arch.name_spec.name_of.sym.add_use(arch.name_spec)
96
97
 
@@ -107,11 +108,11 @@ class PyJacAstLinkPass(UniPass):
107
108
  self.link_jac_py_nodes(jac_node=node.alias, py_nodes=node.gen.py_ast)
108
109
 
109
110
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
110
- for x, y in enumerate(node.target.items):
111
+ for x, y in enumerate(node.target):
111
112
  self.link_jac_py_nodes(jac_node=y, py_nodes=[node.gen.py_ast[x]])
112
113
 
113
114
  def exit_non_local_stmt(self, node: uni.NonLocalStmt) -> None:
114
- for x, y in enumerate(node.target.items):
115
+ for x, y in enumerate(node.target):
115
116
  self.link_jac_py_nodes(jac_node=y, py_nodes=[node.gen.py_ast[x]])
116
117
 
117
118
  def exit_k_w_pair(self, node: uni.KWPair) -> None:
@@ -54,7 +54,7 @@ class SymTabBuildPass(UniPass):
54
54
 
55
55
  def exit_global_vars(self, node: uni.GlobalVars) -> None:
56
56
  for i in self.get_all_sub_nodes(node, uni.Assignment):
57
- for j in i.target.items:
57
+ for j in i.target:
58
58
  if isinstance(j, uni.AstSymbolNode):
59
59
  j.sym_tab.def_insert(j, access_spec=node, single_decl="global var")
60
60
  else:
@@ -75,12 +75,12 @@ class SymTabBuildPass(UniPass):
75
75
  def exit_module_path(self, node: uni.ModulePath) -> None:
76
76
  if node.alias:
77
77
  node.alias.sym_tab.def_insert(node.alias, single_decl="import")
78
- elif node.path and isinstance(node.path.items[0], uni.Name):
78
+ elif node.path and isinstance(node.path[0], uni.Name):
79
79
  if node.parent_of_type(uni.Import) and not (
80
80
  node.parent_of_type(uni.Import).from_loc
81
81
  and node.parent_of_type(uni.Import).is_jac
82
82
  ):
83
- node.path.items[0].sym_tab.def_insert(node.path.items[0])
83
+ node.path[0].sym_tab.def_insert(node.path[0])
84
84
  else:
85
85
  pass # Need to support pythonic import symbols with dots in it
86
86
 
@@ -97,15 +97,15 @@ class SymTabLinkPass(Transform[uni.Module, uni.Module]):
97
97
 
98
98
  def _is_all_import(self, imp_node: uni.Import, node: uni.ModulePath) -> bool:
99
99
  """Determine if this is an all import (import everything from module)."""
100
- return (
101
- imp_node.is_jac and node.parent and isinstance(node.parent, uni.SubNodeList)
102
- ) or (imp_node.is_py and imp_node.from_loc is None and not imp_node.is_absorb)
100
+ return (imp_node.is_jac and node in imp_node.items) or (
101
+ imp_node.is_py and imp_node.from_loc is None and not imp_node.is_absorb
102
+ )
103
103
 
104
104
  def _get_imported_symbols(self, node: uni.ModulePath) -> list[str]:
105
105
  """Get list of specific symbols being imported."""
106
106
  symbols = []
107
107
  if node.parent and isinstance(node.parent, uni.Import):
108
- for mod_items in node.parent.items.items:
108
+ for mod_items in node.parent.items:
109
109
  if isinstance(mod_items, uni.ModuleItem):
110
110
  symbols.append(mod_items.name.value)
111
111
  return symbols
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  node LLM{
4
2
 
5
3
  can infer with AgentNode entry{
@@ -9,15 +7,13 @@ node LLM{
9
7
  def testing_infer {
10
8
  return "LLM's response";
11
9
  }
12
-
13
10
  }
14
11
 
15
12
 
16
- obj AgentNode{
13
+ walker Agent{
17
14
  has describ :str = "AgentNode";
18
15
 
19
16
  can connect with LLM entry{
20
17
  return "AgentNode's response";
21
-
22
18
  }
23
19
  }
@@ -1,14 +1,7 @@
1
-
2
-
3
-
4
1
  import from action {actions}
5
2
 
6
-
7
3
  with entry{
8
-
9
4
  openai = actions.LLM();
10
- agent = actions.AgentNode();
5
+ agent = actions.Agent();
11
6
  print("openai: response: ", openai.testing_infer());
12
-
13
-
14
7
  }
@@ -3,8 +3,10 @@
3
3
  from jaclang.compiler.passes.main import CompilerMode as CMode
4
4
  from jaclang.compiler.program import JacProgram
5
5
  from jaclang.utils.test import TestCase
6
+ import unittest
6
7
 
7
8
 
9
+ @unittest.skip("Skipping CFG build pass tests")
8
10
  class TestCFGBuildPass(TestCase):
9
11
  """Test FuseTypeInfoPass module."""
10
12
 
@@ -30,7 +32,7 @@ class TestCFGBuildPass(TestCase):
30
32
  prog=prog,
31
33
  )
32
34
 
33
- dot = cfg_pass.dotgen_cfg()
35
+ dot = cfg_pass.printgraph_cfg()
34
36
 
35
37
  expected_dot = (
36
38
  "digraph G {\n"
@@ -78,7 +80,7 @@ class TestCFGBuildPass(TestCase):
78
80
  prog=prog,
79
81
  )
80
82
 
81
- dot = cfg_pass.dotgen_cfg()
83
+ dot = cfg_pass.printgraph_cfg()
82
84
 
83
85
  expected_dot = (
84
86
  "digraph G {\n"
@@ -3,7 +3,7 @@
3
3
  This is a pass for generating DocIr for Jac code.
4
4
  """
5
5
 
6
- from typing import List, Optional
6
+ from typing import List, Optional, Sequence
7
7
 
8
8
  import jaclang.compiler.passes.tool.doc_ir as doc
9
9
  import jaclang.compiler.unitree as uni
@@ -139,12 +139,7 @@ class DocIRGenPass(UniPass):
139
139
  """Exit import node."""
140
140
  parts: list[doc.DocType] = []
141
141
  for i in node.kid:
142
- if isinstance(i, uni.SubNodeList) and i.items:
143
- parts.append(
144
- self.indent(self.concat([self.tight_line(), i.gen.doc_ir]))
145
- )
146
- parts.append(self.line())
147
- elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
142
+ if isinstance(i, uni.Token) and i.name == Tok.SEMI:
148
143
  parts.pop()
149
144
  parts.append(i.gen.doc_ir)
150
145
  else:
@@ -179,13 +174,38 @@ class DocIRGenPass(UniPass):
179
174
  def exit_archetype(self, node: uni.Archetype) -> None:
180
175
  """Generate DocIR for archetypes."""
181
176
  parts: list[doc.DocType] = []
177
+ body_parts: list[doc.DocType] = []
178
+ prev_item = None
179
+ in_body = False
182
180
  for i in node.kid:
183
- if i in [node.doc, node.decorators]:
181
+ if (node.doc and i is node.doc) or (
182
+ node.decorators and i in node.decorators
183
+ ):
184
184
  parts.append(i.gen.doc_ir)
185
185
  parts.append(self.hard_line())
186
186
  elif i == node.name:
187
187
  parts.append(i.gen.doc_ir)
188
188
  parts.append(self.space())
189
+ elif isinstance(i, uni.Token) and i.name == Tok.LBRACE:
190
+ parts.append(i.gen.doc_ir)
191
+ elif isinstance(node.body, Sequence) and i in node.body:
192
+ if not in_body:
193
+ body_parts.append(self.hard_line())
194
+ if (prev_item and type(prev_item) is not type(i)) or (
195
+ prev_item and not self.is_one_line(prev_item)
196
+ ):
197
+ body_parts.append(self.hard_line())
198
+ body_parts.append(i.gen.doc_ir)
199
+ body_parts.append(self.hard_line())
200
+ prev_item = i
201
+ in_body = True
202
+ elif in_body:
203
+ in_body = False
204
+ body_parts.pop()
205
+ parts.append(self.indent(self.concat(body_parts)))
206
+ parts.append(self.hard_line())
207
+ parts.append(i.gen.doc_ir)
208
+ parts.append(self.space())
189
209
  elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
190
210
  parts.pop()
191
211
  parts.append(i.gen.doc_ir)
@@ -193,39 +213,72 @@ class DocIRGenPass(UniPass):
193
213
  else:
194
214
  parts.append(i.gen.doc_ir)
195
215
  parts.append(self.space())
216
+
196
217
  node.gen.doc_ir = self.finalize(parts)
197
218
 
198
219
  def exit_ability(self, node: uni.Ability) -> None:
199
220
  """Generate DocIR for abilities."""
200
221
  parts: list[doc.DocType] = []
222
+ body_parts: list[doc.DocType] = []
223
+ in_body = False
201
224
  for i in node.kid:
202
- if i in [node.doc, node.decorators]:
225
+ if i == node.doc or (node.decorators and i in node.decorators):
203
226
  parts.append(i.gen.doc_ir)
204
227
  parts.append(self.hard_line())
205
228
  elif i == node.name_ref:
206
229
  parts.append(i.gen.doc_ir)
207
230
  if not isinstance(node.signature, uni.FuncSignature):
208
231
  parts.append(self.space())
232
+ elif isinstance(node.body, Sequence) and i in node.body:
233
+ if not in_body:
234
+ parts.pop()
235
+ body_parts.append(self.hard_line())
236
+ body_parts.append(i.gen.doc_ir)
237
+ body_parts.append(self.hard_line())
238
+ in_body = True
239
+ elif in_body:
240
+ in_body = False
241
+ body_parts.pop()
242
+ parts.append(self.indent(self.concat(body_parts)))
243
+ parts.append(self.hard_line())
244
+ parts.append(i.gen.doc_ir)
245
+ parts.append(self.space())
209
246
  elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
210
247
  parts.pop()
211
248
  parts.append(i.gen.doc_ir)
249
+ parts.append(self.space())
212
250
  else:
213
251
  parts.append(i.gen.doc_ir)
214
- if not isinstance(i, uni.CommentToken):
215
- parts.append(self.space())
252
+ parts.append(self.space())
216
253
  node.gen.doc_ir = self.finalize(parts)
217
254
 
218
255
  def exit_func_signature(self, node: uni.FuncSignature) -> None:
219
256
  """Generate DocIR for function signatures."""
220
257
  parts: list[doc.DocType] = []
258
+ indent_parts: list[doc.DocType] = []
259
+ in_params = False
221
260
  for i in node.kid:
222
- if isinstance(i, uni.Token) and i.name == Tok.LPAREN:
261
+ if isinstance(i, uni.Token) and i.name == Tok.LPAREN and node.params:
262
+ in_params = True
223
263
  parts.append(i.gen.doc_ir)
224
- elif i == node.params:
264
+ elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
265
+ in_params = False
225
266
  parts.append(
226
- self.indent(self.concat([self.tight_line(), i.gen.doc_ir]))
267
+ self.indent(self.concat([self.tight_line(), *indent_parts]))
227
268
  )
228
269
  parts.append(self.tight_line())
270
+ parts.append(i.gen.doc_ir)
271
+ parts.append(self.space())
272
+ elif isinstance(i, uni.Token) and i.name == Tok.RPAREN:
273
+ parts.pop()
274
+ parts.append(i.gen.doc_ir)
275
+ parts.append(self.space())
276
+ elif in_params:
277
+ if isinstance(i, uni.Token) and i.name == Tok.COMMA:
278
+ indent_parts.append(i.gen.doc_ir)
279
+ indent_parts.append(self.space())
280
+ else:
281
+ indent_parts.append(i.gen.doc_ir)
229
282
  else:
230
283
  parts.append(i.gen.doc_ir)
231
284
  parts.append(self.space())
@@ -254,7 +307,7 @@ class DocIRGenPass(UniPass):
254
307
  """Generate DocIR for assignments."""
255
308
  parts: list[doc.DocType] = []
256
309
  for i in node.kid:
257
- if isinstance(i, uni.Token) and i.name == Tok.SEMI:
310
+ if i == node.type_tag or (isinstance(i, uni.Token) and i.name == Tok.SEMI):
258
311
  parts.pop()
259
312
  parts.append(i.gen.doc_ir)
260
313
  parts.append(self.space())
@@ -266,25 +319,88 @@ class DocIRGenPass(UniPass):
266
319
  def exit_if_stmt(self, node: uni.IfStmt) -> None:
267
320
  """Generate DocIR for if statements."""
268
321
  parts: list[doc.DocType] = []
322
+ body_parts: list[doc.DocType] = []
323
+ in_body = False
269
324
  for i in node.kid:
270
- parts.append(i.gen.doc_ir)
271
- parts.append(self.space())
325
+ if isinstance(node.body, Sequence) and i in node.body:
326
+ if not in_body:
327
+ parts.pop()
328
+ body_parts.append(self.hard_line())
329
+ body_parts.append(i.gen.doc_ir)
330
+ body_parts.append(self.hard_line())
331
+ in_body = True
332
+ elif in_body:
333
+ in_body = False
334
+ body_parts.pop()
335
+ parts.append(self.indent(self.concat(body_parts)))
336
+ parts.append(self.hard_line())
337
+ parts.append(i.gen.doc_ir)
338
+ parts.append(self.space())
339
+ elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
340
+ parts.pop()
341
+ parts.append(i.gen.doc_ir)
342
+ parts.append(self.space())
343
+ else:
344
+ parts.append(i.gen.doc_ir)
345
+ parts.append(self.space())
272
346
  node.gen.doc_ir = self.finalize(parts)
273
347
 
274
348
  def exit_else_if(self, node: uni.ElseIf) -> None:
275
349
  """Generate DocIR for else if statements."""
276
350
  parts: list[doc.DocType] = []
351
+ body_parts: list[doc.DocType] = []
352
+ in_body = False
277
353
  for i in node.kid:
278
- parts.append(i.gen.doc_ir)
279
- parts.append(self.space())
354
+ if isinstance(node.body, Sequence) and i in node.body:
355
+ if not in_body:
356
+ parts.pop()
357
+ body_parts.append(self.hard_line())
358
+ body_parts.append(i.gen.doc_ir)
359
+ body_parts.append(self.hard_line())
360
+ in_body = True
361
+ elif in_body:
362
+ in_body = False
363
+ body_parts.pop()
364
+ parts.append(self.indent(self.concat(body_parts)))
365
+ parts.append(self.hard_line())
366
+ parts.append(i.gen.doc_ir)
367
+ parts.append(self.space())
368
+ elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
369
+ parts.pop()
370
+ parts.append(i.gen.doc_ir)
371
+ parts.append(self.space())
372
+ else:
373
+ parts.append(i.gen.doc_ir)
374
+ parts.append(self.space())
280
375
  node.gen.doc_ir = self.finalize(parts)
281
376
 
282
377
  def exit_else_stmt(self, node: uni.ElseStmt) -> None:
283
378
  """Generate DocIR for else statements."""
284
379
  parts: list[doc.DocType] = []
380
+ body_parts: list[doc.DocType] = []
381
+ in_body = False
285
382
  for i in node.kid:
286
- parts.append(i.gen.doc_ir)
287
- parts.append(self.space())
383
+ if isinstance(node.body, Sequence) and i in node.body:
384
+ if not in_body:
385
+ parts.pop()
386
+ body_parts.append(self.hard_line())
387
+ body_parts.append(i.gen.doc_ir)
388
+ body_parts.append(self.hard_line())
389
+ in_body = True
390
+ elif in_body:
391
+ in_body = False
392
+ body_parts.pop()
393
+ parts.append(self.indent(self.concat(body_parts)))
394
+ parts.append(self.hard_line())
395
+ parts.append(i.gen.doc_ir)
396
+ parts.append(self.space())
397
+ elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
398
+ parts.pop()
399
+ parts.append(i.gen.doc_ir)
400
+ parts.append(self.space())
401
+ else:
402
+ parts.append(i.gen.doc_ir)
403
+ parts.append(self.space())
288
404
  node.gen.doc_ir = self.finalize(parts)
289
405
 
290
406
  def exit_binary_expr(self, node: uni.BinaryExpr) -> None:
@@ -298,18 +414,8 @@ class DocIRGenPass(UniPass):
298
414
  def exit_expr_stmt(self, node: uni.ExprStmt) -> None:
299
415
  """Generate DocIR for expression statements."""
300
416
  parts: list[doc.DocType] = []
301
- is_fstring = (
302
- node.parent
303
- and isinstance(node.parent, uni.SubNodeList)
304
- and node.parent.parent
305
- and isinstance(node.parent.parent, uni.FString)
306
- )
307
417
  for i in node.kid:
308
- if is_fstring:
309
- parts.append(self.text("{"))
310
418
  parts.append(i.gen.doc_ir)
311
- if is_fstring:
312
- parts.append(self.text("}"))
313
419
  node.gen.doc_ir = self.group(self.concat(parts))
314
420
 
315
421
  def exit_concurrent_expr(self, node: uni.ConcurrentExpr) -> None:
@@ -335,8 +441,27 @@ class DocIRGenPass(UniPass):
335
441
  def exit_func_call(self, node: uni.FuncCall) -> None:
336
442
  """Generate DocIR for function calls."""
337
443
  parts: list[doc.DocType] = []
444
+ indent_parts: list[doc.DocType] = []
445
+ in_params = False
338
446
  for i in node.kid:
339
- parts.append(i.gen.doc_ir)
447
+ if isinstance(i, uni.Token) and i.name == Tok.LPAREN and node.params:
448
+ in_params = True
449
+ parts.append(i.gen.doc_ir)
450
+ elif isinstance(i, uni.Token) and i.name == Tok.RPAREN and node.params:
451
+ in_params = False
452
+ parts.append(
453
+ self.indent(self.concat([self.tight_line(), *indent_parts]))
454
+ )
455
+ parts.append(self.tight_line())
456
+ parts.append(i.gen.doc_ir)
457
+ elif in_params:
458
+ if isinstance(i, uni.Token) and i.name == Tok.COMMA:
459
+ indent_parts.append(i.gen.doc_ir)
460
+ indent_parts.append(self.space())
461
+ else:
462
+ indent_parts.append(i.gen.doc_ir)
463
+ else:
464
+ parts.append(i.gen.doc_ir)
340
465
  node.gen.doc_ir = self.group(self.concat(parts))
341
466
 
342
467
  def exit_atom_trailer(self, node: uni.AtomTrailer) -> None:
@@ -395,12 +520,10 @@ class DocIRGenPass(UniPass):
395
520
  if i == node.doc:
396
521
  parts.append(i.gen.doc_ir)
397
522
  parts.append(self.hard_line())
398
- elif (
399
- isinstance(i, uni.SubNodeList)
400
- and i == node.vars
401
- and isinstance(i.gen.doc_ir, doc.Concat)
402
- ):
403
- parts.append(self.align(i.gen.doc_ir))
523
+ elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
524
+ parts.pop()
525
+ parts.append(i.gen.doc_ir)
526
+ parts.append(self.space())
404
527
  else:
405
528
  parts.append(i.gen.doc_ir)
406
529
  parts.append(self.space())
@@ -472,12 +595,7 @@ class DocIRGenPass(UniPass):
472
595
  """Generate DocIR for set values."""
473
596
  parts: list[doc.DocType] = []
474
597
  for i in node.kid:
475
- if isinstance(i, uni.Token):
476
- parts.append(i.gen.doc_ir)
477
- elif isinstance(i, uni.SubNodeList):
478
- parts.append(self.if_break(self.line(), self.space()))
479
- parts.append(i.gen.doc_ir)
480
- parts.append(self.if_break(self.line(), self.space()))
598
+ parts.append(i.gen.doc_ir)
481
599
 
482
600
  node.gen.doc_ir = self.group(self.concat(parts))
483
601
 
@@ -722,21 +840,40 @@ class DocIRGenPass(UniPass):
722
840
  def exit_module_code(self, node: uni.ModuleCode) -> None:
723
841
  """Generate DocIR for module code."""
724
842
  parts: list[doc.DocType] = []
843
+ body_parts: list[doc.DocType] = []
844
+ in_body = False
725
845
  for i in node.kid:
726
- if i == node.doc:
846
+ if node.doc and i is node.doc:
727
847
  parts.append(i.gen.doc_ir)
728
848
  parts.append(self.hard_line())
729
- elif isinstance(i, uni.Token):
849
+ elif i == node.name:
850
+ parts.append(i.gen.doc_ir)
851
+ parts.append(self.space())
852
+ elif isinstance(node.body, Sequence) and i in node.body:
853
+ if not in_body:
854
+ parts.pop()
855
+ body_parts.append(self.hard_line())
856
+ body_parts.append(i.gen.doc_ir)
857
+ body_parts.append(self.hard_line())
858
+ in_body = True
859
+ elif in_body:
860
+ in_body = False
861
+ body_parts.pop()
862
+ parts.append(self.indent(self.concat(body_parts)))
863
+ parts.append(self.hard_line())
864
+ parts.append(i.gen.doc_ir)
865
+ parts.append(self.space())
866
+ elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
867
+ parts.pop()
730
868
  parts.append(i.gen.doc_ir)
731
869
  parts.append(self.space())
732
870
  else:
733
- parts.append(self.concat([i.gen.doc_ir]))
734
-
735
- node.gen.doc_ir = self.group(self.concat(parts))
871
+ parts.append(i.gen.doc_ir)
872
+ parts.append(self.space())
873
+ node.gen.doc_ir = self.finalize(parts)
736
874
 
737
875
  def exit_global_stmt(self, node: uni.GlobalStmt) -> None:
738
876
  """Generate DocIR for global statements."""
739
- # node.kid is [GLOBAL_OP_token, name_list_SubNodeList, SEMI_token]
740
877
  parts: list[doc.DocType] = []
741
878
  for i in node.kid:
742
879
  parts.append(i.gen.doc_ir)
@@ -1000,15 +1137,20 @@ class DocIRGenPass(UniPass):
1000
1137
  """Generate DocIR for match architecture patterns."""
1001
1138
  parts: list[doc.DocType] = []
1002
1139
  for i in node.kid:
1003
- parts.append(i.gen.doc_ir)
1004
- parts.append(self.space())
1140
+ if isinstance(i, uni.Token) and i.name == Tok.COMMA:
1141
+ parts.append(i.gen.doc_ir)
1142
+ parts.append(self.space())
1143
+ else:
1144
+ parts.append(i.gen.doc_ir)
1005
1145
  node.gen.doc_ir = self.finalize(parts)
1006
1146
 
1007
1147
  def exit_enum(self, node: uni.Enum) -> None:
1008
1148
  """Generate DocIR for enum declarations."""
1009
1149
  parts: list[doc.DocType] = []
1010
1150
  for i in node.kid:
1011
- if i in [node.doc, node.decorators]:
1151
+ if (node.doc and i is node.doc) or (
1152
+ node.decorators and i in node.decorators
1153
+ ):
1012
1154
  parts.append(i.gen.doc_ir)
1013
1155
  parts.append(self.hard_line())
1014
1156
  elif isinstance(i, uni.Token) and i.name == Tok.SEMI:
@@ -1028,74 +1170,11 @@ class DocIRGenPass(UniPass):
1028
1170
  parts.append(self.space())
1029
1171
  node.gen.doc_ir = self.finalize(parts)
1030
1172
 
1031
- def exit_sub_node_list(self, node: uni.SubNodeList) -> None:
1032
- """Generate DocIR for a SubNodeList."""
1033
- parts: list[doc.DocType] = []
1034
- indent_parts: list[doc.DocType] = []
1035
- prev_item: Optional[uni.UniNode] = None
1036
- in_codeblock = node.delim and node.delim.name == Tok.WS
1037
- in_archetype = (
1038
- node.parent
1039
- and isinstance(node.parent, (uni.Archetype, uni.Enum))
1040
- and node == node.parent.body
1041
- )
1042
- is_assignment = node.delim and node.delim.name == Tok.EQ
1043
- for i in node.kid:
1044
- if i == node.left_enc:
1045
- parts.append(i.gen.doc_ir)
1046
- indent_parts.append(self.hard_line())
1047
- elif i == node.right_enc:
1048
- if in_codeblock:
1049
- indent_parts.pop()
1050
- parts.append(self.indent(self.concat(indent_parts)))
1051
- if prev_item != node.left_enc:
1052
- parts.append(self.line())
1053
- parts.append(i.gen.doc_ir)
1054
- elif (
1055
- isinstance(i, uni.Token)
1056
- and node.delim
1057
- and i.name == node.delim.name
1058
- and i.name not in [Tok.DOT, Tok.DECOR_OP]
1059
- ):
1060
- indent_parts.append(i.gen.doc_ir)
1061
- indent_parts.append(self.line())
1062
- else:
1063
- if (
1064
- in_codeblock
1065
- and prev_item
1066
- and self.has_gap(prev_item, i)
1067
- and isinstance(i, uni.CommentToken)
1068
- and (
1069
- not in_archetype
1070
- or not prev_item
1071
- or not (
1072
- self.is_one_line(prev_item) and type(prev_item) is type(i)
1073
- )
1074
- )
1075
- ) or (
1076
- in_archetype
1077
- and prev_item not in [None, node.left_enc, node.right_enc]
1078
- and (
1079
- type(prev_item) is not type(i)
1080
- or (prev_item and not self.is_one_line(prev_item))
1081
- )
1082
- ):
1083
- indent_parts.append(self.hard_line())
1084
- indent_parts.append(i.gen.doc_ir)
1085
- if in_codeblock:
1086
- indent_parts.append(self.hard_line())
1087
- elif is_assignment:
1088
- indent_parts.append(self.space())
1089
- prev_item = i
1090
- if is_assignment:
1091
- indent_parts.pop()
1092
- node.gen.doc_ir = self.concat(parts) if parts else self.concat(indent_parts)
1093
-
1094
1173
  def exit_impl_def(self, node: uni.ImplDef) -> None:
1095
1174
  """Generate DocIR for implementation definitions."""
1096
1175
  parts: list[doc.DocType] = []
1097
1176
  for i in node.kid:
1098
- if i in [node.doc, node.decorators]:
1177
+ if i == node.doc or (node.decorators and i in node.decorators):
1099
1178
  parts.append(i.gen.doc_ir)
1100
1179
  parts.append(self.hard_line())
1101
1180
  elif i == node.target:
@@ -1154,9 +1233,7 @@ class DocIRGenPass(UniPass):
1154
1233
  is_escaped_curly = (
1155
1234
  node.lit_value in ["{", "}"]
1156
1235
  and node.parent
1157
- and isinstance(node.parent, uni.SubNodeList)
1158
- and node.parent.parent
1159
- and isinstance(node.parent.parent, uni.FString)
1236
+ and isinstance(node.parent, uni.FString)
1160
1237
  )
1161
1238
 
1162
1239
  if "\n" in node.value:
@@ -61,17 +61,12 @@ class JacProgram:
61
61
  self.errors_had: list[Alert] = []
62
62
  self.warnings_had: list[Alert] = []
63
63
 
64
- def get_bytecode(
65
- self, full_target: str, full_compile: bool = True
66
- ) -> Optional[types.CodeType]:
64
+ def get_bytecode(self, full_target: str) -> Optional[types.CodeType]:
67
65
  """Get the bytecode for a specific module."""
68
66
  if full_target in self.mod.hub:
69
67
  codeobj = self.mod.hub[full_target].gen.py_bytecode
70
68
  return marshal.loads(codeobj) if isinstance(codeobj, bytes) else None
71
- result = self.compile(
72
- file_path=full_target,
73
- mode=CompilerMode.COMPILE if full_compile else CompilerMode.COMPILE_SINGLE,
74
- )
69
+ result = self.compile(file_path=full_target, mode=CompilerMode.COMPILE_SINGLE)
75
70
  return marshal.loads(result.gen.py_bytecode) if result.gen.py_bytecode else None
76
71
 
77
72
  def compile(