jaclang 0.7.13__py3-none-any.whl → 0.7.15__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 (49) hide show
  1. jaclang/cli/cli.py +11 -8
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +0 -2
  4. jaclang/compiler/absyntree.py +12 -12
  5. jaclang/compiler/parser.py +4 -0
  6. jaclang/compiler/passes/ir_pass.py +3 -12
  7. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
  8. jaclang/compiler/passes/main/import_pass.py +5 -2
  9. jaclang/compiler/passes/main/pyast_gen_pass.py +33 -39
  10. jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
  11. jaclang/compiler/passes/main/registry_pass.py +1 -1
  12. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -1
  13. jaclang/compiler/passes/tool/fuse_comments_pass.py +14 -2
  14. jaclang/compiler/passes/tool/jac_formatter_pass.py +23 -28
  15. jaclang/compiler/passes/transform.py +4 -0
  16. jaclang/compiler/semtable.py +5 -3
  17. jaclang/compiler/tests/test_importer.py +3 -0
  18. jaclang/langserve/engine.py +227 -94
  19. jaclang/langserve/server.py +6 -10
  20. jaclang/langserve/tests/fixtures/base_module_structure.jac +1 -1
  21. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  22. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  23. jaclang/langserve/tests/test_server.py +28 -4
  24. jaclang/langserve/utils.py +128 -95
  25. jaclang/plugin/builtin.py +1 -1
  26. jaclang/plugin/default.py +25 -16
  27. jaclang/plugin/feature.py +4 -5
  28. jaclang/plugin/spec.py +2 -2
  29. jaclang/{core → runtimelib}/architype.py +1 -1
  30. jaclang/{core → runtimelib}/context.py +4 -1
  31. jaclang/runtimelib/importer.py +414 -0
  32. jaclang/runtimelib/machine.py +19 -0
  33. jaclang/{core → runtimelib}/utils.py +1 -1
  34. jaclang/tests/fixtures/blankwithentry.jac +3 -0
  35. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  36. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  37. jaclang/tests/test_cli.py +1 -1
  38. jaclang/tests/test_language.py +16 -0
  39. jaclang/tests/test_man_code.py +17 -0
  40. jaclang/utils/treeprinter.py +0 -4
  41. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/METADATA +1 -1
  42. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/RECORD +48 -45
  43. jaclang/core/importer.py +0 -344
  44. /jaclang/{core → runtimelib}/__init__.py +0 -0
  45. /jaclang/{core → runtimelib}/constructs.py +0 -0
  46. /jaclang/{core → runtimelib}/memory.py +0 -0
  47. /jaclang/{core → runtimelib}/test.py +0 -0
  48. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/WHEEL +0 -0
  49. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py CHANGED
@@ -19,10 +19,10 @@ from jaclang.compiler.constant import Constants
19
19
  from jaclang.compiler.passes.main.pyast_load_pass import PyastBuildPass
20
20
  from jaclang.compiler.passes.main.schedules import py_code_gen_typed
21
21
  from jaclang.compiler.passes.tool.schedules import format_pass
22
- from jaclang.core.constructs import Architype
23
22
  from jaclang.plugin.builtin import dotgen
24
23
  from jaclang.plugin.feature import JacCmd as Cmd
25
24
  from jaclang.plugin.feature import JacFeature as Jac
25
+ from jaclang.runtimelib.constructs import Architype
26
26
  from jaclang.utils.helpers import debugger as db
27
27
  from jaclang.utils.lang_tools import AstTool
28
28
 
@@ -86,11 +86,10 @@ def run(
86
86
  else ""
87
87
  )
88
88
 
89
- Jac.context().init_memory(session)
90
-
91
89
  base, mod = os.path.split(filename)
92
90
  base = base if base else "./"
93
91
  mod = mod[:-4]
92
+ Jac.context().init_memory(base_path=base, session=session)
94
93
  if filename.endswith(".jac"):
95
94
  ret_module = jac_import(
96
95
  target=mod,
@@ -146,7 +145,7 @@ def get_object(id: str, session: str = "") -> dict:
146
145
  if session == "":
147
146
  session = cmd_registry.args.session if "session" in cmd_registry.args else ""
148
147
 
149
- Jac.context().init_memory(session)
148
+ Jac.context().init_memory(session=session)
150
149
 
151
150
  if id == "root":
152
151
  id_uuid = UUID(int=0)
@@ -357,11 +356,10 @@ def dot(
357
356
  else ""
358
357
  )
359
358
 
360
- Jac.context().init_memory(session)
361
-
362
359
  base, mod = os.path.split(filename)
363
360
  base = base if base else "./"
364
361
  mod = mod[:-4]
362
+ Jac.context().init_memory(base_path=base, session=session)
365
363
  if filename.endswith(".jac"):
366
364
  jac_import(
367
365
  target=mod,
@@ -437,12 +435,17 @@ def start_cli() -> None:
437
435
  parser = cmd_registry.parser
438
436
  args = parser.parse_args()
439
437
  cmd_registry.args = args
438
+
439
+ if args.version:
440
+ version = importlib.metadata.version("jaclang")
441
+ print(f"Jac version {version}")
442
+ return
443
+
440
444
  command = cmd_registry.get(args.command)
441
445
  if command:
442
446
  args_dict = vars(args)
443
447
  args_dict.pop("command")
444
- if command not in ["run"]:
445
- args_dict.pop("session")
448
+ args_dict.pop("version", None)
446
449
  ret = command.call(**args_dict)
447
450
  if ret:
448
451
  print(ret)
jaclang/cli/cmdreg.py CHANGED
@@ -38,7 +38,7 @@ class CommandRegistry:
38
38
  self.registry = {}
39
39
  self.parser = argparse.ArgumentParser(prog="jac")
40
40
  self.parser.add_argument(
41
- "--session", help="Session file path", nargs="?", default=""
41
+ "-V", "--version", action="store_true", help="Show the Jac version"
42
42
  )
43
43
  self.sub_parsers = self.parser.add_subparsers(title="commands", dest="command")
44
44
  self.args = argparse.Namespace()
@@ -172,17 +172,14 @@ class CommandShell(cmd.Cmd):
172
172
 
173
173
  def default(self, line: str) -> None:
174
174
  """Process the command line input."""
175
- try:
176
- args = vars(self.cmd_reg.parser.parse_args(line.split()))
177
- command = self.cmd_reg.get(args["command"])
178
- if command:
179
- args.pop("command")
180
- ret = command.call(**args)
181
- if ret:
182
- ret_str = pprint.pformat(ret, indent=2)
183
- self.stdout.write(f"{ret_str}\n")
184
- except Exception as e:
185
- print(e)
175
+ args = vars(self.cmd_reg.parser.parse_args(line.split()))
176
+ command = self.cmd_reg.get(args["command"])
177
+ if command:
178
+ args.pop("command")
179
+ ret = command.call(**args)
180
+ if ret:
181
+ ret_str = pprint.pformat(ret, indent=2)
182
+ self.stdout.write(f"{ret_str}\n")
186
183
 
187
184
  def do_help(self, arg: str) -> None:
188
185
  """Jac CLI 'help' implementaion."""
@@ -34,8 +34,6 @@ def generate_static_parser(force: bool = False) -> None:
34
34
  except Exception as e:
35
35
  logging.error(f"Error generating reference files: {e}")
36
36
 
37
-
38
- generate_static_parser()
39
37
  try:
40
38
  from jaclang.compiler.generated import jac_parser as jac_lark
41
39
  except ModuleNotFoundError:
@@ -55,15 +55,10 @@ class AstNode:
55
55
  """Get symbol table."""
56
56
  # sym_tab should never be accessed without being set in codebase
57
57
  if not self._sym_tab:
58
- import traceback
59
-
60
- if self.parent:
61
- print(f"Parent: {self.parent.pp()}")
62
- print("Node: ", self.pp())
63
- stack_trace = traceback.format_stack()
64
- print("".join(stack_trace))
65
58
  raise ValueError(
66
- f"Symbol table not set for {type(self).__name__}. Impossible."
59
+ f"Symbol table not set for {type(self).__name__}. Impossible.\n"
60
+ f"Node: {self.pp()}\n"
61
+ f"Parent: {self.parent.pp() if self.parent else None}\n"
67
62
  )
68
63
  return self._sym_tab
69
64
 
@@ -621,6 +616,7 @@ class Module(AstDocNode):
621
616
  doc: Optional[String],
622
617
  body: Sequence[ElementStmt | String | EmptyToken],
623
618
  is_imported: bool,
619
+ terminals: list[Token],
624
620
  kid: Sequence[AstNode],
625
621
  stub_only: bool = False,
626
622
  registry: Optional[SemRegistry] = None,
@@ -635,6 +631,7 @@ class Module(AstDocNode):
635
631
  self.test_mod: list[Module] = []
636
632
  self.mod_deps: dict[str, Module] = {}
637
633
  self.registry = registry
634
+ self.terminals: list[Token] = terminals
638
635
  AstNode.__init__(self, kid=kid)
639
636
  AstDocNode.__init__(self, doc=doc)
640
637
 
@@ -642,15 +639,15 @@ class Module(AstDocNode):
642
639
  def annexable_by(self) -> Optional[str]:
643
640
  """Get annexable by."""
644
641
  if not self.stub_only and (
645
- self.loc.mod_path.endswith("impl.jac")
646
- or self.loc.mod_path.endswith("test.jac")
642
+ self.loc.mod_path.endswith(".impl.jac")
643
+ or self.loc.mod_path.endswith(".test.jac")
647
644
  ):
648
645
  head_mod_name = self.name.split(".")[0]
649
646
  potential_path = os.path.join(
650
647
  os.path.dirname(self.loc.mod_path),
651
648
  f"{head_mod_name}.jac",
652
649
  )
653
- if os.path.exists(potential_path):
650
+ if os.path.exists(potential_path) and potential_path != self.loc.mod_path:
654
651
  return potential_path
655
652
  annex_dir = os.path.split(os.path.dirname(self.loc.mod_path))[-1]
656
653
  if annex_dir.endswith(".impl") or annex_dir.endswith(".test"):
@@ -661,7 +658,10 @@ class Module(AstDocNode):
661
658
  os.path.dirname(os.path.dirname(self.loc.mod_path)),
662
659
  f"{head_mod_name}.jac",
663
660
  )
664
- if os.path.exists(potential_path):
661
+ if (
662
+ os.path.exists(potential_path)
663
+ and potential_path != self.loc.mod_path
664
+ ):
665
665
  return potential_path
666
666
  return None
667
667
 
@@ -56,6 +56,7 @@ class JacParser(Pass):
56
56
  source=self.source,
57
57
  doc=None,
58
58
  body=[],
59
+ terminals=[],
59
60
  is_imported=False,
60
61
  kid=[ast.EmptyToken()],
61
62
  )
@@ -120,6 +121,7 @@ class JacParser(Pass):
120
121
  """Initialize transformer."""
121
122
  super().__init__(*args, **kwargs)
122
123
  self.parse_ref = parser
124
+ self.terminals: list[ast.Token] = []
123
125
 
124
126
  def ice(self) -> Exception:
125
127
  """Raise internal compiler error."""
@@ -160,6 +162,7 @@ class JacParser(Pass):
160
162
  doc=doc,
161
163
  body=body,
162
164
  is_imported=False,
165
+ terminals=self.terminals,
163
166
  kid=kid if len(kid) else [ast.EmptyToken()],
164
167
  )
165
168
  return self.nu(mod)
@@ -3983,4 +3986,5 @@ class JacParser(Pass):
3983
3986
  err.line = ret.loc.first_line
3984
3987
  err.column = ret.loc.col_start
3985
3988
  raise err
3989
+ self.terminals.append(ret)
3986
3990
  return self.nu(ret)
@@ -115,7 +115,7 @@ class Pass(Transform[T]):
115
115
  self.after_pass()
116
116
  self.time_taken = time.time() - start_time
117
117
  if settings.pass_timer:
118
- print(
118
+ self.log_info(
119
119
  f"Time taken in {self.__class__.__name__}: {self.time_taken:.4f} seconds"
120
120
  )
121
121
  return self.ir
@@ -136,21 +136,12 @@ class Pass(Transform[T]):
136
136
  self.exit_node(node)
137
137
  return node
138
138
 
139
- def update_code_loc(self, node: Optional[ast.AstNode] = None) -> None:
140
- """Update code location."""
141
- if node is None:
142
- node = self.cur_node
143
- if not isinstance(node, ast.AstNode):
144
- self.ice("Current node is not an AstNode.")
145
-
146
139
  def error(self, msg: str, node_override: Optional[ast.AstNode] = None) -> None:
147
140
  """Pass Error."""
148
- self.update_code_loc(node_override)
149
141
  self.log_error(f"{msg}", node_override=node_override)
150
142
 
151
143
  def warning(self, msg: str, node_override: Optional[ast.AstNode] = None) -> None:
152
144
  """Pass Error."""
153
- self.update_code_loc(node_override)
154
145
  self.log_warning(f"{msg}", node_override=node_override)
155
146
 
156
147
  def ice(self, msg: str = "Something went horribly wrong!") -> RuntimeError:
@@ -166,10 +157,10 @@ class PrinterPass(Pass):
166
157
 
167
158
  def enter_node(self, node: ast.AstNode) -> None:
168
159
  """Run on entering node."""
169
- print("Entering:", node)
160
+ self.log_info(f"Entering: {node.__class__.__name__}: {node.loc}")
170
161
  super().enter_node(node)
171
162
 
172
163
  def exit_node(self, node: ast.AstNode) -> None:
173
164
  """Run on exiting node."""
174
165
  super().exit_node(node)
175
- print("Exiting:", node)
166
+ self.log_info(f"Exiting: {node.__class__.__name__}: {node.loc}")
@@ -6,7 +6,6 @@ mypy apis into Jac and use jac py ast in it.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import traceback
10
9
  from typing import Callable, TypeVar
11
10
 
12
11
  import jaclang.compiler.absyntree as ast
@@ -32,7 +31,7 @@ class FuseTypeInfoPass(Pass):
32
31
 
33
32
  def __debug_print(self, *argv: object) -> None:
34
33
  if settings.fuse_type_info_debug:
35
- print("FuseTypeInfo::", *argv)
34
+ self.log_info("FuseTypeInfo::", *argv)
36
35
 
37
36
  def __call_type_handler(
38
37
  self, node: ast.AstSymbolNode, mypy_type: MypyTypes.ProperType
@@ -72,7 +71,9 @@ class FuseTypeInfoPass(Pass):
72
71
  ) -> Callable[[FuseTypeInfoPass, T], None]:
73
72
  def node_handler(self: FuseTypeInfoPass, node: T) -> None:
74
73
  if not isinstance(node, ast.AstSymbolNode):
75
- print(f"Warning {node.__class__.__name__} is not an AstSymbolNode")
74
+ self.__debug_print(
75
+ f"Warning {node.__class__.__name__} is not an AstSymbolNode"
76
+ )
76
77
 
77
78
  try:
78
79
  jac_node_str = f'jac node "{node.loc}::{node.__class__.__name__}'
@@ -119,8 +120,6 @@ class FuseTypeInfoPass(Pass):
119
120
  self.__debug_print(
120
121
  f'Internal error happened while parsing "{e.obj.__class__.__name__}"'
121
122
  )
122
- traceback.print_exc()
123
- print(e)
124
123
 
125
124
  return node_handler
126
125
 
@@ -17,6 +17,9 @@ from jaclang.compiler.passes import Pass
17
17
  from jaclang.compiler.passes.main import SubNodeTabPass
18
18
  from jaclang.settings import settings
19
19
  from jaclang.utils.helpers import import_target_to_relative_path, is_standard_lib_module
20
+ from jaclang.utils.log import logging
21
+
22
+ logger = logging.getLogger(__name__)
20
23
 
21
24
 
22
25
  class JacImportPass(Pass):
@@ -182,6 +185,7 @@ class JacImportPass(Pass):
182
185
  source=ast.JacSource("", mod_path=target),
183
186
  doc=None,
184
187
  body=[],
188
+ terminals=[],
185
189
  is_imported=False,
186
190
  stub_only=True,
187
191
  kid=[ast.EmptyToken()],
@@ -203,7 +207,7 @@ class JacImportPass(Pass):
203
207
  self.warnings_had += mod_pass.warnings_had
204
208
  mod = mod_pass.ir
205
209
  except Exception as e:
206
- print(e)
210
+ logger.info(e)
207
211
  mod = None
208
212
  if isinstance(mod, ast.Module):
209
213
  self.import_table[target] = mod
@@ -251,7 +255,6 @@ class PyImportPass(JacImportPass):
251
255
  if spec.origin in self.import_table:
252
256
  return self.import_table[spec.origin]
253
257
  with open(spec.origin, "r", encoding="utf-8") as f:
254
- # print(f"\nImporting python module {node.path_str}")
255
258
  mod = PyastBuildPass(
256
259
  input_ir=ast.PythonModuleAst(
257
260
  py_ast.parse(f.read()), mod_path=spec.origin
@@ -11,7 +11,7 @@ from typing import Optional, Sequence, TypeVar
11
11
  import jaclang.compiler.absyntree as ast
12
12
  from jaclang.compiler.constant import Constants as Con, EdgeDir, Tokens as Tok
13
13
  from jaclang.compiler.passes import Pass
14
- from jaclang.core.utils import extract_params, extract_type, get_sem_scope
14
+ from jaclang.runtimelib.utils import extract_params, extract_type, get_sem_scope
15
15
 
16
16
  T = TypeVar("T", bound=ast3.AST)
17
17
 
@@ -19,24 +19,25 @@ T = TypeVar("T", bound=ast3.AST)
19
19
  class PyastGenPass(Pass):
20
20
  """Jac blue transpilation to python pass."""
21
21
 
22
- @staticmethod
23
- def node_compilable_test(node: ast3.AST) -> None:
24
- """Convert any AST node to a compilable module node."""
25
- if isinstance(node, ast3.Module):
26
- pass
27
- elif isinstance(node, (ast3.Expr, ast3.stmt)):
28
- node = ast3.Module(body=[node], type_ignores=[])
29
- elif isinstance(node, list) and all(isinstance(n, ast3.stmt) for n in node):
30
- node = ast3.Module(body=node, type_ignores=[])
31
- else:
32
- node = ast3.Module(body=[], type_ignores=[])
33
- try:
34
- compile(node, "<ast>", "exec")
35
- except TypeError as e:
36
- print(ast3.dump(node, indent=2))
37
- raise e
38
- except Exception:
39
- pass
22
+ # TODO: This should live in utils and perhaps a test added using it
23
+ # @staticmethod
24
+ # def node_compilable_test(node: ast3.AST) -> None:
25
+ # """Convert any AST node to a compilable module node."""
26
+ # if isinstance(node, ast3.Module):
27
+ # pass
28
+ # elif isinstance(node, (ast3.Expr, ast3.stmt)):
29
+ # node = ast3.Module(body=[node], type_ignores=[])
30
+ # elif isinstance(node, list) and all(isinstance(n, ast3.stmt) for n in node):
31
+ # node = ast3.Module(body=node, type_ignores=[])
32
+ # else:
33
+ # node = ast3.Module(body=[], type_ignores=[])
34
+ # try:
35
+ # compile(node, "<ast>", "exec")
36
+ # except TypeError as e:
37
+ # print(ast3.dump(node, indent=2))
38
+ # raise e
39
+ # except Exception:
40
+ # pass
40
41
 
41
42
  def before_pass(self) -> None:
42
43
  """Initialize pass."""
@@ -80,7 +81,7 @@ class PyastGenPass(Pass):
80
81
 
81
82
  def needs_jac_import(self) -> None:
82
83
  """Check if import is needed."""
83
- if "jimport" in self.already_added:
84
+ if self.needs_jac_import.__name__ in self.already_added:
84
85
  return
85
86
  self.preamble.append(
86
87
  self.sync(
@@ -96,11 +97,11 @@ class PyastGenPass(Pass):
96
97
  jac_node=self.ir,
97
98
  )
98
99
  )
99
- self.already_added.append("jimport")
100
+ self.already_added.append(self.needs_jac_import.__name__)
100
101
 
101
102
  def needs_typing(self) -> None:
102
103
  """Check if enum is needed."""
103
- if "typing" in self.already_added:
104
+ if self.needs_typing.__name__ in self.already_added:
104
105
  return
105
106
  self.preamble.append(
106
107
  self.sync(
@@ -115,11 +116,11 @@ class PyastGenPass(Pass):
115
116
  jac_node=self.ir,
116
117
  )
117
118
  )
118
- self.already_added.append("typing")
119
+ self.already_added.append(self.needs_typing.__name__)
119
120
 
120
121
  def needs_enum(self) -> None:
121
122
  """Check if enum is needed."""
122
- if "enum" in self.already_added:
123
+ if self.needs_enum.__name__ in self.already_added:
123
124
  return
124
125
  self.preamble.append(
125
126
  self.sync(
@@ -134,11 +135,11 @@ class PyastGenPass(Pass):
134
135
  jac_node=self.ir,
135
136
  )
136
137
  )
137
- self.already_added.append("enum")
138
+ self.already_added.append(self.needs_enum.__name__)
138
139
 
139
140
  def needs_jac_feature(self) -> None:
140
141
  """Check if enum is needed."""
141
- if "jac_feature" in self.already_added:
142
+ if self.needs_jac_feature.__name__ in self.already_added:
142
143
  return
143
144
  self.preamble.append(
144
145
  self.sync(
@@ -164,11 +165,11 @@ class PyastGenPass(Pass):
164
165
  jac_node=self.ir,
165
166
  )
166
167
  )
167
- self.already_added.append("jac_feature")
168
+ self.already_added.append(self.needs_jac_feature.__name__)
168
169
 
169
170
  def needs_dataclass(self) -> None:
170
171
  """Check if enum is needed."""
171
- if "dataclass" in self.already_added:
172
+ if self.needs_dataclass.__name__ in self.already_added:
172
173
  return
173
174
  self.preamble.append(
174
175
  self.sync(
@@ -184,11 +185,11 @@ class PyastGenPass(Pass):
184
185
  jac_node=self.ir,
185
186
  )
186
187
  )
187
- self.already_added.append("dataclass")
188
+ self.already_added.append(self.needs_dataclass.__name__)
188
189
 
189
190
  def needs_dataclass_field(self) -> None:
190
191
  """Check if enum is needed."""
191
- if "dataclass_field" in self.already_added:
192
+ if self.needs_dataclass_field.__name__ in self.already_added:
192
193
  return
193
194
  self.preamble.append(
194
195
  self.sync(
@@ -202,7 +203,7 @@ class PyastGenPass(Pass):
202
203
  jac_node=self.ir,
203
204
  )
204
205
  )
205
- self.already_added.append("dataclass_field")
206
+ self.already_added.append(self.needs_dataclass_field.__name__)
206
207
 
207
208
  def flatten(self, body: list[T | list[T] | None]) -> list[T]:
208
209
  """Flatten ast list."""
@@ -434,14 +435,7 @@ class PyastGenPass(Pass):
434
435
  body: SubNodeList[CodeBlockStmt],
435
436
  doc: Optional[String],
436
437
  """
437
- if node.doc:
438
- doc = self.sync(ast3.Expr(value=node.doc.gen.py_ast[0]), jac_node=node.doc)
439
- if isinstance(node.body.gen.py_ast, list):
440
- node.gen.py_ast = [doc] + node.body.gen.py_ast
441
- else:
442
- raise self.ice()
443
- else:
444
- node.gen.py_ast = node.body.gen.py_ast
438
+ node.gen.py_ast = self.resolve_stmt_block(node.body, doc=node.doc)
445
439
  if node.name:
446
440
  node.gen.py_ast = [
447
441
  self.sync(
@@ -124,6 +124,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
124
124
  source=ast.JacSource("", mod_path=self.mod_path),
125
125
  doc=doc_str,
126
126
  body=valid[1:] if valid and isinstance(valid[0], ast.String) else valid,
127
+ terminals=[],
127
128
  is_imported=False,
128
129
  kid=valid,
129
130
  )
@@ -13,7 +13,7 @@ import jaclang.compiler.absyntree as ast
13
13
  from jaclang.compiler.constant import Constants as Con
14
14
  from jaclang.compiler.passes import Pass
15
15
  from jaclang.compiler.semtable import SemInfo, SemRegistry
16
- from jaclang.core.utils import get_sem_scope
16
+ from jaclang.runtimelib.utils import get_sem_scope
17
17
 
18
18
 
19
19
  class RegistryPass(Pass):
@@ -47,8 +47,12 @@ class ImportPassPassTests(TestCase):
47
47
  state = jac_file_to_pass(
48
48
  self.fixture_abs_path("incautoimpl.jac"), JacImportPass
49
49
  )
50
+ count = 0
50
51
  for i in state.ir.get_all_sub_nodes(ast.Module):
51
- self.assertEqual(i.annexable_by, self.fixture_abs_path("autoimpl.jac"))
52
+ if i.name != "autoimpl":
53
+ count += 1
54
+ self.assertEqual(i.annexable_by, self.fixture_abs_path("autoimpl.jac"))
55
+ self.assertEqual(count, 3)
52
56
 
53
57
  def test_py_resolve_list(self) -> None:
54
58
  """Basic test for pass."""
@@ -27,7 +27,12 @@ class FuseCommentsPass(Pass):
27
27
  """Insert comment tokens into all_tokens."""
28
28
  comment_stream = iter(self.comments) # Iterator for comments
29
29
  code_stream = iter(self.all_tokens) # Iterator for code tokens
30
- new_stream: list[ast.AstNode] = [] # New stream to hold ordered tokens
30
+ new_stream: list[ast.Token] = [] # New stream to hold ordered tokens
31
+
32
+ if not isinstance(self.ir, ast.Module):
33
+ raise self.ice(
34
+ f"FuseCommentsPass can only be run on a Module, not a {type(self.ir)}"
35
+ )
31
36
 
32
37
  try:
33
38
  next_comment = next(comment_stream) # Get the first comment
@@ -39,12 +44,20 @@ class FuseCommentsPass(Pass):
39
44
  except StopIteration:
40
45
  next_code = None
41
46
 
47
+ if next_comment and (not next_code or is_comment_next(next_comment, next_code)):
48
+ self.ir.terminals.insert(0, next_comment)
49
+
42
50
  while next_comment or next_code:
43
51
  if next_comment and (
44
52
  not next_code or is_comment_next(next_comment, next_code)
45
53
  ):
46
54
  # Add the comment to the new stream
55
+ last_tok = new_stream[-1] if len(new_stream) else None
47
56
  new_stream.append(next_comment)
57
+ if last_tok:
58
+ self.ir.terminals.insert(
59
+ self.ir.terminals.index(last_tok) + 1, next_comment
60
+ )
48
61
  try:
49
62
  next_comment = next(comment_stream)
50
63
  except StopIteration:
@@ -70,7 +83,6 @@ class FuseCommentsPass(Pass):
70
83
  parent_kids.insert(insert_index, token)
71
84
  prev_token.parent.set_kids(parent_kids)
72
85
  else:
73
- prev_token.pp()
74
86
  raise self.ice(
75
87
  "Token without parent in AST should be impossible"
76
88
  )
@@ -27,6 +27,22 @@ class JacFormatPass(Pass):
27
27
  node.gen.jac = ""
28
28
  super().enter_node(node)
29
29
 
30
+ def token_before(self, node: ast.Token) -> Optional[ast.Token]:
31
+ """Token before."""
32
+ if not isinstance(self.ir, ast.Module):
33
+ raise self.ice("IR must be module. Impossible")
34
+ if self.ir.terminals.index(node) == 0:
35
+ return None
36
+ return self.ir.terminals[self.ir.terminals.index(node) - 1]
37
+
38
+ def token_after(self, node: ast.Token) -> Optional[ast.Token]:
39
+ """Token after."""
40
+ if not isinstance(self.ir, ast.Module):
41
+ raise self.ice("IR must be module. Impossible")
42
+ if self.ir.terminals.index(node) == len(self.ir.terminals) - 1:
43
+ return None
44
+ return self.ir.terminals[self.ir.terminals.index(node) + 1]
45
+
30
46
  def indent_str(self) -> str:
31
47
  """Return string for indent."""
32
48
  return " " * self.indent_size * self.indent_level
@@ -196,7 +212,7 @@ class JacFormatPass(Pass):
196
212
  items: list[T],
197
213
  """
198
214
  prev_token = None
199
- for i, stmt in enumerate(node.kid):
215
+ for stmt in node.kid:
200
216
  if isinstance(node.parent, (ast.EnumDef, ast.Enum)) and stmt.gen.jac == ",":
201
217
  self.indent_level -= 1
202
218
  self.emit_ln(node, f"{stmt.gen.jac}")
@@ -220,40 +236,15 @@ class JacFormatPass(Pass):
220
236
  self.emit_ln(node, "")
221
237
  self.indent_level += 1
222
238
  if stmt.name == Tok.LBRACE:
223
- next_kid = node.kid[i + 1]
224
- if isinstance(next_kid, ast.CommentToken) and next_kid.is_inline:
225
- self.emit(node, f" {stmt.value}")
226
- else:
227
- self.emit(node, f" {stmt.value}")
239
+ self.emit(node, f" {stmt.value}")
228
240
  elif stmt.name == Tok.RBRACE:
229
241
  if self.indent_level > 0:
230
242
  self.indent_level -= 1
231
243
  if stmt.parent and stmt.parent.gen.jac.strip() == "{":
232
- self.emit_ln(node, stmt.gen.jac.strip())
233
- elif (
234
- stmt.parent
235
- and stmt.parent.parent
236
- and isinstance(
237
- stmt.parent.parent,
238
- (ast.ElseIf, ast.IfStmt, ast.IterForStmt, ast.TryStmt),
239
- )
240
- ):
241
244
  self.emit(node, f"{stmt.value}")
242
245
  else:
243
- next_kid = (
244
- node.kid[i + 1]
245
- if i < (len(node.kid) - 1)
246
- else ast.EmptyToken()
247
- )
248
- if (
249
- isinstance(next_kid, ast.CommentToken)
250
- and next_kid.is_inline
251
- ):
252
- self.emit(node, f" {stmt.value}")
253
- elif not (node.gen.jac).endswith("\n"):
254
- self.indent_level -= 1
246
+ if not (node.gen.jac).endswith("\n"):
255
247
  self.emit_ln(node, "")
256
- self.indent_level += 1
257
248
  self.emit(node, f"{stmt.value}")
258
249
  else:
259
250
  self.emit(node, f"{stmt.value}")
@@ -1392,8 +1383,12 @@ class JacFormatPass(Pass):
1392
1383
  self.emit(node, i.gen.jac)
1393
1384
  if isinstance(prev_token, ast.Semi):
1394
1385
  self.emit_ln(node, "")
1386
+ elif (tok := self.token_before(i)) and (i.line_no - tok.line_no > 1):
1387
+ self.emit_ln(node, "")
1388
+ self.emit_ln(node, i.gen.jac)
1395
1389
  else:
1396
1390
  self.emit_ln(node, i.gen.jac)
1391
+ self.emit_ln(node, "")
1397
1392
  elif isinstance(i, ast.Semi):
1398
1393
  self.emit(node, f"{i.gen.jac} ")
1399
1394
  elif isinstance(i, ast.SubNodeList) and i.gen.jac.startswith("@"):
@@ -70,3 +70,7 @@ class Transform(ABC, Generic[T]):
70
70
  )
71
71
  self.warnings_had.append(alrt)
72
72
  self.logger.warning(str(alrt))
73
+
74
+ def log_info(self, msg: str) -> None:
75
+ """Log info."""
76
+ self.logger.info(msg)
@@ -127,9 +127,11 @@ class SemRegistry:
127
127
  break
128
128
  return i
129
129
 
130
- def pp(self) -> None:
130
+ def pp(self) -> str:
131
131
  """Pretty print the registry."""
132
+ ret_str = ""
132
133
  for k, v in self.registry.items():
133
- print(k)
134
+ ret_str += f"{k}\n"
134
135
  for i in v:
135
- print(f" {i.name} {i.type} {i.semstr}")
136
+ ret_str += f" {i.name} {i.type} {i.semstr}\n"
137
+ return ret_str