jaclang 0.8.8__py3-none-any.whl → 0.8.10__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 (114) hide show
  1. jaclang/cli/cli.py +194 -10
  2. jaclang/cli/cmdreg.py +144 -8
  3. jaclang/compiler/__init__.py +6 -1
  4. jaclang/compiler/codeinfo.py +16 -1
  5. jaclang/compiler/constant.py +33 -8
  6. jaclang/compiler/jac.lark +154 -62
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +656 -149
  9. jaclang/compiler/passes/__init__.py +2 -1
  10. jaclang/compiler/passes/ast_gen/__init__.py +5 -0
  11. jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
  12. jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
  13. jaclang/compiler/passes/ecmascript/__init__.py +25 -0
  14. jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
  15. jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
  16. jaclang/compiler/passes/ecmascript/estree.py +972 -0
  17. jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
  18. jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
  19. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
  20. jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
  21. jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
  22. jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
  23. jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
  24. jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
  25. jaclang/compiler/passes/main/__init__.py +0 -3
  26. jaclang/compiler/passes/main/annex_pass.py +23 -1
  27. jaclang/compiler/passes/main/def_use_pass.py +1 -0
  28. jaclang/compiler/passes/main/pyast_gen_pass.py +413 -255
  29. jaclang/compiler/passes/main/pyast_load_pass.py +48 -11
  30. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  31. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  32. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  34. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  35. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  36. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  37. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -3
  38. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  39. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  40. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
  41. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  42. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  43. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +219 -20
  44. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  45. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  46. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  47. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
  48. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  49. jaclang/compiler/passes/transform.py +9 -1
  50. jaclang/compiler/passes/uni_pass.py +5 -7
  51. jaclang/compiler/program.py +27 -26
  52. jaclang/compiler/tests/test_client_codegen.py +113 -0
  53. jaclang/compiler/tests/test_importer.py +12 -10
  54. jaclang/compiler/tests/test_parser.py +249 -3
  55. jaclang/compiler/type_system/type_evaluator.jac +1078 -0
  56. jaclang/compiler/type_system/type_utils.py +1 -1
  57. jaclang/compiler/type_system/types.py +6 -0
  58. jaclang/compiler/unitree.py +438 -82
  59. jaclang/langserve/engine.jac +224 -288
  60. jaclang/langserve/sem_manager.jac +12 -8
  61. jaclang/langserve/server.jac +48 -48
  62. jaclang/langserve/tests/fixtures/greet.py +17 -0
  63. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  64. jaclang/langserve/tests/fixtures/user.jac +15 -0
  65. jaclang/langserve/tests/test_server.py +66 -371
  66. jaclang/lib.py +17 -0
  67. jaclang/runtimelib/archetype.py +25 -25
  68. jaclang/runtimelib/client_bundle.py +169 -0
  69. jaclang/runtimelib/client_runtime.jac +586 -0
  70. jaclang/runtimelib/constructs.py +4 -2
  71. jaclang/runtimelib/machine.py +308 -139
  72. jaclang/runtimelib/meta_importer.py +111 -22
  73. jaclang/runtimelib/mtp.py +15 -0
  74. jaclang/runtimelib/server.py +1089 -0
  75. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  76. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  77. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  78. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  79. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  80. jaclang/runtimelib/tests/test_client_render.py +63 -0
  81. jaclang/runtimelib/tests/test_serve.py +1069 -0
  82. jaclang/settings.py +0 -3
  83. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  84. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  85. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  86. jaclang/tests/fixtures/iife_functions.jac +142 -0
  87. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  88. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  89. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  90. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  91. jaclang/tests/fixtures/py2jac_empty.py +0 -0
  92. jaclang/tests/fixtures/py_run.py +7 -5
  93. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  94. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  95. jaclang/tests/test_cli.py +134 -18
  96. jaclang/tests/test_language.py +120 -32
  97. jaclang/tests/test_reference.py +20 -3
  98. jaclang/utils/NonGPT.py +375 -0
  99. jaclang/utils/helpers.py +64 -20
  100. jaclang/utils/lang_tools.py +31 -4
  101. jaclang/utils/tests/test_lang_tools.py +5 -16
  102. jaclang/utils/treeprinter.py +8 -3
  103. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  104. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/RECORD +106 -71
  105. jaclang/compiler/passes/main/binder_pass.py +0 -594
  106. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  107. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  108. jaclang/compiler/type_system/type_evaluator.py +0 -844
  109. jaclang/langserve/tests/session.jac +0 -294
  110. jaclang/langserve/tests/test_dev_server.py +0 -80
  111. jaclang/runtimelib/importer.py +0 -351
  112. jaclang/tests/test_typecheck.py +0 -542
  113. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  114. {jaclang-0.8.8.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
@@ -69,13 +69,13 @@ class AnchorReport:
69
69
  context: dict[str, Any]
70
70
 
71
71
 
72
- DataSpatialFilter: TypeAlias = (
72
+ ObjectSpatialFilter: TypeAlias = (
73
73
  Callable[["Archetype"], bool] | "Archetype" | list["Archetype"] | None
74
74
  )
75
75
 
76
76
 
77
77
  @dataclass(eq=False, repr=False)
78
- class DataSpatialDestination:
78
+ class ObjectSpatialDestination:
79
79
  """Object-Spatial Destination."""
80
80
 
81
81
  direction: EdgeDir
@@ -92,18 +92,18 @@ class DataSpatialDestination:
92
92
 
93
93
 
94
94
  @dataclass(eq=False, repr=False)
95
- class DataSpatialPath:
95
+ class ObjectSpatialPath:
96
96
  """Object-Spatial Path."""
97
97
 
98
98
  origin: list[NodeArchetype]
99
- destinations: list[DataSpatialDestination]
99
+ destinations: list[ObjectSpatialDestination]
100
100
  edge_only: bool
101
101
  from_visit: bool
102
102
 
103
103
  def __init__(
104
104
  self,
105
105
  origin: NodeArchetype | list[NodeArchetype],
106
- destinations: list[DataSpatialDestination] | None = None,
106
+ destinations: list[ObjectSpatialDestination] | None = None,
107
107
  ) -> None:
108
108
  """Override Init."""
109
109
  if not isinstance(origin, list):
@@ -115,7 +115,7 @@ class DataSpatialPath:
115
115
 
116
116
  def convert(
117
117
  self,
118
- filter: DataSpatialFilter,
118
+ filter: ObjectSpatialFilter,
119
119
  ) -> Callable[["Archetype"], bool] | None:
120
120
  """Convert filter."""
121
121
  if not filter:
@@ -129,44 +129,44 @@ class DataSpatialPath:
129
129
  def append(
130
130
  self,
131
131
  direction: EdgeDir,
132
- edge: DataSpatialFilter,
133
- node: DataSpatialFilter,
134
- ) -> DataSpatialPath:
132
+ edge: ObjectSpatialFilter,
133
+ node: ObjectSpatialFilter,
134
+ ) -> ObjectSpatialPath:
135
135
  """Append destination."""
136
136
  self.destinations.append(
137
- DataSpatialDestination(direction, self.convert(edge), self.convert(node))
137
+ ObjectSpatialDestination(direction, self.convert(edge), self.convert(node))
138
138
  )
139
139
  return self
140
140
 
141
- def _out(
142
- self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
143
- ) -> DataSpatialPath:
141
+ def edge_out(
142
+ self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
143
+ ) -> ObjectSpatialPath:
144
144
  """Override greater than function."""
145
145
  return self.append(EdgeDir.OUT, edge, node)
146
146
 
147
- def _in(
148
- self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
149
- ) -> DataSpatialPath:
147
+ def edge_in(
148
+ self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
149
+ ) -> ObjectSpatialPath:
150
150
  """Override greater than function."""
151
151
  return self.append(EdgeDir.IN, edge, node)
152
152
 
153
- def _any(
154
- self, edge: DataSpatialFilter = None, node: DataSpatialFilter = None
155
- ) -> DataSpatialPath:
153
+ def edge_any(
154
+ self, edge: ObjectSpatialFilter = None, node: ObjectSpatialFilter = None
155
+ ) -> ObjectSpatialPath:
156
156
  """Override greater than function."""
157
157
  return self.append(EdgeDir.ANY, edge, node)
158
158
 
159
- def edge(self) -> DataSpatialPath:
159
+ def edge(self) -> ObjectSpatialPath:
160
160
  """Set edge only."""
161
161
  self.edge_only = True
162
162
  return self
163
163
 
164
- def visit(self) -> DataSpatialPath:
164
+ def visit(self) -> ObjectSpatialPath:
165
165
  """Set from visit."""
166
166
  self.from_visit = True
167
167
  return self
168
168
 
169
- def repr_builder(self, repr: str, dest: DataSpatialDestination, mark: str) -> str:
169
+ def repr_builder(self, repr: str, dest: ObjectSpatialDestination, mark: str) -> str:
170
170
  """Repr builder."""
171
171
  repr += mark
172
172
  repr += f' (edge{" filter" if dest.edge else ""}) '
@@ -361,8 +361,8 @@ class ObjectAnchor(Anchor):
361
361
  class Archetype:
362
362
  """Archetype Protocol."""
363
363
 
364
- _jac_entry_funcs_: ClassVar[list[DataSpatialFunction]] = []
365
- _jac_exit_funcs_: ClassVar[list[DataSpatialFunction]] = []
364
+ _jac_entry_funcs_: ClassVar[list[ObjectSpatialFunction]] = []
365
+ _jac_exit_funcs_: ClassVar[list[ObjectSpatialFunction]] = []
366
366
 
367
367
  @cached_property
368
368
  def __jac__(self) -> Anchor:
@@ -454,7 +454,7 @@ class Root(NodeArchetype):
454
454
 
455
455
 
456
456
  @dataclass(eq=False)
457
- class DataSpatialFunction:
457
+ class ObjectSpatialFunction:
458
458
  """Object-Spatial Function."""
459
459
 
460
460
  name: str
@@ -0,0 +1,169 @@
1
+ """Client bundle generation utilities for Jac web front-ends."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from types import ModuleType
10
+ from typing import Any, Iterable, Sequence
11
+
12
+ from jaclang.compiler.program import JacProgram
13
+
14
+
15
+ class ClientBundleError(RuntimeError):
16
+ """Raised when the client bundle cannot be generated."""
17
+
18
+
19
+ @dataclass(slots=True)
20
+ class ClientBundle:
21
+ """Container for a compiled client bundle."""
22
+
23
+ module_name: str
24
+ code: str
25
+ client_functions: list[str]
26
+ client_globals: list[str]
27
+ hash: str
28
+
29
+
30
+ @dataclass(slots=True)
31
+ class _CachedBundle:
32
+ signature: str
33
+ bundle: ClientBundle
34
+
35
+
36
+ class ClientBundleBuilder:
37
+ """Compile Jac modules and runtime support into a browser-ready bundle."""
38
+
39
+ def __init__(self, runtime_path: Path | None = None) -> None:
40
+ """Initialise the builder with an optional override for the runtime path."""
41
+ self.runtime_path = runtime_path or Path(__file__).with_name(
42
+ "client_runtime.jac"
43
+ )
44
+ self._cache: dict[str, _CachedBundle] = {}
45
+
46
+ def build(self, module: ModuleType, force: bool = False) -> ClientBundle:
47
+ """Build (or reuse) a client bundle for the supplied module."""
48
+ # Derive source path from module __file__ (replace .py with .jac)
49
+ if not hasattr(module, "__file__") or not module.__file__:
50
+ raise ClientBundleError(
51
+ f"Module '{module.__name__}' has no __file__ attribute"
52
+ )
53
+ module_path = module.__file__.replace(".py", ".jac")
54
+
55
+ source_path = Path(module_path).resolve()
56
+ runtime_path = self.runtime_path.resolve()
57
+ signature = self._signature([source_path, runtime_path])
58
+
59
+ cached = self._cache.get(module.__name__)
60
+ if not force and cached and cached.signature == signature:
61
+ return cached.bundle
62
+
63
+ bundle = self._compile_bundle(module, source_path, runtime_path)
64
+ self._cache[module.__name__] = _CachedBundle(signature=signature, bundle=bundle)
65
+ return bundle
66
+
67
+ def _compile_bundle(
68
+ self,
69
+ module: ModuleType,
70
+ module_path: Path,
71
+ runtime_path: Path,
72
+ ) -> ClientBundle:
73
+ """Compile bundle pieces and stitch them together."""
74
+ runtime_js = self._compile_to_js(runtime_path)
75
+
76
+ # Get manifest from JacProgram
77
+ from jaclang.runtimelib.machine import JacMachine as Jac
78
+
79
+ mod = Jac.program.mod.hub.get(str(module_path))
80
+ manifest = mod.gen.client_manifest if mod else None
81
+
82
+ module_js = self._compile_to_js(module_path)
83
+
84
+ client_exports = sorted(dict.fromkeys(manifest.exports)) if manifest else []
85
+
86
+ client_globals_map: dict[str, Any] = {}
87
+ if manifest:
88
+ for name in manifest.globals:
89
+ if name in manifest.globals_values:
90
+ client_globals_map[name] = manifest.globals_values[name]
91
+ elif hasattr(module, name):
92
+ client_globals_map[name] = getattr(module, name)
93
+ else:
94
+ client_globals_map[name] = None
95
+ client_globals_map = {
96
+ key: client_globals_map[key] for key in sorted(client_globals_map)
97
+ }
98
+
99
+ registration_js = self._generate_registration_js(
100
+ module.__name__, client_exports, client_globals_map
101
+ )
102
+
103
+ bundle_pieces = [
104
+ "// Jac client runtime",
105
+ runtime_js,
106
+ "",
107
+ f"// Client module: {module.__name__}",
108
+ module_js,
109
+ "",
110
+ registration_js,
111
+ ]
112
+ bundle_code = "\n".join(piece for piece in bundle_pieces if piece is not None)
113
+ bundle_hash = hashlib.sha256(bundle_code.encode("utf-8")).hexdigest()
114
+
115
+ return ClientBundle(
116
+ module_name=module.__name__,
117
+ code=bundle_code,
118
+ client_functions=client_exports,
119
+ client_globals=list(client_globals_map.keys()),
120
+ hash=bundle_hash,
121
+ )
122
+
123
+ def _compile_to_js(self, source_path: Path) -> str:
124
+ """Compile the provided Jac file into JavaScript."""
125
+ program = JacProgram()
126
+ mod = program.compile(str(source_path))
127
+ if program.errors_had:
128
+ formatted = "\n".join(str(err) for err in program.errors_had)
129
+ raise ClientBundleError(
130
+ f"Failed to compile '{source_path}' for client bundle:\n{formatted}"
131
+ )
132
+ return mod.gen.js or ""
133
+
134
+ @staticmethod
135
+ def _signature(paths: Iterable[Path]) -> str:
136
+ """Compute a cache signature based on file modification times."""
137
+ parts: list[str] = []
138
+ for path in paths:
139
+ try:
140
+ stat = path.stat()
141
+ parts.append(f"{path}:{stat.st_mtime_ns}")
142
+ except FileNotFoundError:
143
+ parts.append(f"{path}:missing")
144
+ return hashlib.sha256("|".join(parts).encode("utf-8")).hexdigest()
145
+
146
+ @staticmethod
147
+ def _generate_registration_js(
148
+ module_name: str,
149
+ client_functions: Sequence[str],
150
+ client_globals: dict[str, Any],
151
+ ) -> str:
152
+ """Generate registration code that exposes client symbols globally."""
153
+ globals_entries: list[str] = []
154
+ for name, value in client_globals.items():
155
+ identifier = json.dumps(name)
156
+ try:
157
+ value_literal = json.dumps(value)
158
+ except TypeError:
159
+ value_literal = "null"
160
+ globals_entries.append(f"{identifier}: {value_literal}")
161
+
162
+ globals_literal = (
163
+ "{ " + ", ".join(globals_entries) + " }" if globals_entries else "{}"
164
+ )
165
+ functions_literal = json.dumps(list(client_functions))
166
+ module_literal = json.dumps(module_name)
167
+
168
+ # Use the registration function from client_runtime.jac
169
+ return f"__jacRegisterClientModule({module_literal}, {functions_literal}, {globals_literal});"