jaclang 0.8.9__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 (103) hide show
  1. jaclang/cli/cli.py +147 -25
  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 -13
  6. jaclang/compiler/jac.lark +130 -31
  7. jaclang/compiler/larkparse/jac_parser.py +2 -2
  8. jaclang/compiler/parser.py +567 -176
  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/pyast_gen_pass.py +324 -234
  28. jaclang/compiler/passes/main/pyast_load_pass.py +46 -11
  29. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
  30. jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
  31. jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
  32. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
  33. jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
  34. jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
  35. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
  36. jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -2
  37. jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
  38. jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
  39. jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
  40. jaclang/compiler/passes/main/type_checker_pass.py +7 -0
  41. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +115 -0
  42. jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
  43. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
  44. jaclang/compiler/passes/transform.py +9 -1
  45. jaclang/compiler/passes/uni_pass.py +5 -7
  46. jaclang/compiler/program.py +22 -25
  47. jaclang/compiler/tests/test_client_codegen.py +113 -0
  48. jaclang/compiler/tests/test_importer.py +12 -10
  49. jaclang/compiler/tests/test_parser.py +249 -3
  50. jaclang/compiler/type_system/type_evaluator.jac +169 -50
  51. jaclang/compiler/type_system/type_utils.py +1 -1
  52. jaclang/compiler/type_system/types.py +6 -0
  53. jaclang/compiler/unitree.py +430 -84
  54. jaclang/langserve/engine.jac +224 -288
  55. jaclang/langserve/sem_manager.jac +12 -8
  56. jaclang/langserve/server.jac +48 -48
  57. jaclang/langserve/tests/fixtures/greet.py +17 -0
  58. jaclang/langserve/tests/fixtures/md_path.jac +22 -0
  59. jaclang/langserve/tests/fixtures/user.jac +15 -0
  60. jaclang/langserve/tests/test_server.py +66 -371
  61. jaclang/lib.py +1 -1
  62. jaclang/runtimelib/client_bundle.py +169 -0
  63. jaclang/runtimelib/client_runtime.jac +586 -0
  64. jaclang/runtimelib/constructs.py +2 -0
  65. jaclang/runtimelib/machine.py +259 -100
  66. jaclang/runtimelib/meta_importer.py +111 -22
  67. jaclang/runtimelib/mtp.py +15 -0
  68. jaclang/runtimelib/server.py +1089 -0
  69. jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
  70. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  71. jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
  72. jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
  73. jaclang/runtimelib/tests/test_client_bundle.py +55 -0
  74. jaclang/runtimelib/tests/test_client_render.py +63 -0
  75. jaclang/runtimelib/tests/test_serve.py +1069 -0
  76. jaclang/settings.py +0 -2
  77. jaclang/tests/fixtures/iife_functions.jac +142 -0
  78. jaclang/tests/fixtures/iife_functions_client.jac +143 -0
  79. jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
  80. jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
  81. jaclang/tests/fixtures/needs_import_dup.jac +6 -4
  82. jaclang/tests/fixtures/py_run.py +7 -5
  83. jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
  84. jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
  85. jaclang/tests/test_cli.py +1 -1
  86. jaclang/tests/test_language.py +10 -39
  87. jaclang/tests/test_reference.py +17 -2
  88. jaclang/utils/NonGPT.py +375 -0
  89. jaclang/utils/helpers.py +44 -16
  90. jaclang/utils/lang_tools.py +31 -4
  91. jaclang/utils/tests/test_lang_tools.py +1 -1
  92. jaclang/utils/treeprinter.py +8 -3
  93. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
  94. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/RECORD +96 -66
  95. jaclang/compiler/passes/main/binder_pass.py +0 -594
  96. jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
  97. jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
  98. jaclang/langserve/tests/session.jac +0 -294
  99. jaclang/langserve/tests/test_dev_server.py +0 -80
  100. jaclang/runtimelib/importer.py +0 -351
  101. jaclang/tests/test_typecheck.py +0 -542
  102. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
  103. {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py CHANGED
@@ -22,9 +22,6 @@ from jaclang.settings import settings
22
22
  from jaclang.utils.helpers import debugger as db
23
23
  from jaclang.utils.lang_tools import AstTool
24
24
 
25
- Jac.create_cmd()
26
- Jac.setup()
27
-
28
25
 
29
26
  @cmd_registry.register
30
27
  def format(path: str, outfile: str = "", to_screen: bool = False) -> None:
@@ -150,7 +147,9 @@ def run(
150
147
  lng=lng,
151
148
  )
152
149
  except Exception as e:
153
- print(f"Error running {filename}: {e}", file=sys.stderr)
150
+ from jaclang.utils.helpers import dump_traceback
151
+
152
+ print(dump_traceback(e), file=sys.stderr)
154
153
  mach.close()
155
154
  exit(1)
156
155
  elif filename.endswith(".jir"):
@@ -164,7 +163,9 @@ def run(
164
163
  lng=lng,
165
164
  )
166
165
  except Exception as e:
167
- print(f"Error running {filename}: {e}", file=sys.stderr)
166
+ from jaclang.utils.helpers import dump_traceback
167
+
168
+ print(dump_traceback(e), file=sys.stderr)
168
169
  mach.close()
169
170
  exit(1)
170
171
 
@@ -640,9 +641,10 @@ def py2jac(filename: str) -> None:
640
641
  def jac2py(filename: str) -> None:
641
642
  """Convert a Jac file to Python code.
642
643
 
643
- Translates Jac source code to equivalent Python code. This is useful for
644
- understanding how Jac code is executed or for integrating Jac components
645
- with Python projects.
644
+ Translates Jac source code to equivalent Python code. The generated Python
645
+ uses direct imports from jaclang.lib, making the output clean and suitable
646
+ for use as a standalone library or for integrating Jac components with
647
+ Python projects.
646
648
 
647
649
  Args:
648
650
  filename: Path to the .jac file to convert
@@ -662,38 +664,158 @@ def jac2py(filename: str) -> None:
662
664
 
663
665
 
664
666
  @cmd_registry.register
665
- def jac2lib(filename: str) -> None:
666
- """Convert a Jac file to Python library code.
667
+ def js(filename: str) -> None:
668
+ """Convert a Jac file to JavaScript code.
667
669
 
668
- Translates Jac source code to equivalent Python code with library mode enabled.
669
- In library mode, the generated Python uses direct imports from jaclang.lib
670
- instead of aliased imports, making the output cleaner and more suitable for
671
- use as a standalone library.
670
+ Translates Jac source code to equivalent JavaScript/ECMAScript code using
671
+ the ESTree AST specification. This allows Jac programs to run in JavaScript
672
+ environments like Node.js or web browsers.
672
673
 
673
674
  Args:
674
675
  filename: Path to the .jac file to convert
675
676
 
676
677
  Examples:
677
- jac jac2lib myprogram.jac > mylib.py
678
+ jac js myprogram.jac > myprogram.js
679
+ jac js myprogram.jac
678
680
  """
679
681
  if filename.endswith(".jac"):
680
- # Temporarily enable library mode
681
- original_library_mode = settings.library_mode
682
- settings.library_mode = True
683
682
  try:
684
- code = JacProgram().compile(file_path=filename).gen.py
685
- if code:
686
- print(code)
687
- else:
683
+ prog = JacProgram()
684
+ ir = prog.compile(file_path=filename)
685
+
686
+ if prog.errors_had:
687
+ for error in prog.errors_had:
688
+ print(f"Error: {error}", file=sys.stderr)
688
689
  exit(1)
689
- finally:
690
- # Restore original setting
691
- settings.library_mode = original_library_mode
690
+ js_output = ir.gen.js or ""
691
+ if not js_output.strip():
692
+ print(
693
+ "ECMAScript code generation produced no output.",
694
+ file=sys.stderr,
695
+ )
696
+ exit(1)
697
+ print(js_output)
698
+ except Exception as e:
699
+ print(f"Error generating JavaScript: {e}", file=sys.stderr)
700
+ import traceback
701
+
702
+ traceback.print_exc()
703
+ exit(1)
692
704
  else:
693
705
  print("Not a .jac file.", file=sys.stderr)
694
706
  exit(1)
695
707
 
696
708
 
709
+ # Register core commands first (before plugins load)
710
+ # These can be overridden by plugins with higher priority
711
+
712
+
713
+ @cmd_registry.register
714
+ def serve(
715
+ filename: str,
716
+ session: str = "",
717
+ port: int = 8000,
718
+ main: bool = True,
719
+ faux: bool = False,
720
+ ) -> None:
721
+ """Start a REST API server for the specified .jac file.
722
+
723
+ Executes the target module and turns all functions into authenticated REST API
724
+ endpoints. Function signatures are introspected to create the API interface.
725
+ Walkers are converted to REST APIs where their fields become the interface,
726
+ with an additional target_node field for spawning location.
727
+
728
+ Each user gets their own persistent root node that persists across runs.
729
+ Users must create an account and authenticate to access the API.
730
+
731
+ Args:
732
+ filename: Path to the .jac file to serve
733
+ session: Session identifier for persistent state (default: auto-generated)
734
+ port: Port to run the server on (default: 8000)
735
+ main: Treat the module as __main__ (default: True)
736
+ faux: Perform introspection and print endpoint docs without starting server (default: False)
737
+
738
+ Examples:
739
+ jac serve myprogram.jac
740
+ jac serve myprogram.jac --port 8080
741
+ jac serve myprogram.jac --session myapp.session
742
+ jac serve myprogram.jac --faux
743
+ """
744
+ from jaclang.runtimelib.server import JacAPIServer
745
+
746
+ # Process file and session
747
+ base, mod, mach = proc_file_sess(filename, session)
748
+ lng = filename.split(".")[-1]
749
+ Jac.set_base_path(base)
750
+
751
+ # Import the module
752
+ if filename.endswith((".jac", ".py")):
753
+ try:
754
+ Jac.jac_import(
755
+ target=mod,
756
+ base_path=base,
757
+ lng=lng,
758
+ )
759
+ except Exception as e:
760
+ print(f"Error loading {filename}: {e}", file=sys.stderr)
761
+ mach.close()
762
+ exit(1)
763
+ elif filename.endswith(".jir"):
764
+ try:
765
+ with open(filename, "rb") as f:
766
+ Jac.attach_program(pickle.load(f))
767
+ Jac.jac_import(
768
+ target=mod,
769
+ base_path=base,
770
+ lng=lng,
771
+ )
772
+ except Exception as e:
773
+ print(f"Error loading {filename}: {e}", file=sys.stderr)
774
+ mach.close()
775
+ exit(1)
776
+
777
+ # Create and start the API server
778
+ # Use session path for persistent storage across user sessions
779
+ session_path = session if session else os.path.join(base, f"{mod}.session")
780
+
781
+ server = JacAPIServer(
782
+ module_name=mod,
783
+ session_path=session_path,
784
+ port=port,
785
+ base_path=base,
786
+ )
787
+
788
+ # If faux mode, print endpoint documentation and exit
789
+ if faux:
790
+ try:
791
+ server.print_endpoint_docs()
792
+ mach.close()
793
+ return
794
+ except Exception as e:
795
+ print(f"Error generating endpoint documentation: {e}", file=sys.stderr)
796
+ mach.close()
797
+ exit(1)
798
+
799
+ # Don't close the context - keep the module loaded for the server
800
+ # mach.close()
801
+
802
+ try:
803
+ server.start()
804
+ except KeyboardInterrupt:
805
+ print("\nServer stopped.")
806
+ mach.close() # Close on shutdown
807
+ except Exception as e:
808
+ print(f"Server error: {e}", file=sys.stderr)
809
+ mach.close()
810
+ exit(1)
811
+
812
+
813
+ Jac.create_cmd()
814
+ Jac.setup()
815
+
816
+ cmd_registry.finalize()
817
+
818
+
697
819
  def start_cli() -> None:
698
820
  """
699
821
  Start the command line interface.
jaclang/cli/cmdreg.py CHANGED
@@ -4,23 +4,47 @@ from __future__ import annotations
4
4
 
5
5
  import argparse
6
6
  import inspect
7
+ import os
7
8
  import re
9
+ import sys
8
10
  from dataclasses import fields as dataclass_fields
11
+ from enum import IntEnum
9
12
  from typing import Callable, Dict, Optional
10
13
 
11
14
  from jaclang.settings import Settings as JacSettings
12
15
 
13
16
 
17
+ class CommandPriority(IntEnum):
18
+ """Priority levels for command registration.
19
+
20
+ Higher values take precedence when multiple commands with the same name are registered.
21
+ This allows plugins to override core commands in a controlled manner.
22
+ """
23
+
24
+ CORE = 100 # Core jaclang commands (lowest priority, can be overridden)
25
+ PLUGIN = 200 # Plugin-provided commands (override core)
26
+ USER = 300 # User-defined commands (highest priority, override everything)
27
+
28
+
14
29
  class Command:
15
30
  """Represents a command in the command line interface."""
16
31
 
17
32
  func: Callable
18
33
  sig: inspect.Signature
19
-
20
- def __init__(self, func: Callable) -> None:
34
+ priority: CommandPriority
35
+ source: str # Source plugin/module name
36
+
37
+ def __init__(
38
+ self,
39
+ func: Callable,
40
+ priority: CommandPriority = CommandPriority.CORE,
41
+ source: str = "core",
42
+ ) -> None:
21
43
  """Initialize a Command instance."""
22
44
  self.func = func
23
45
  self.sig = inspect.signature(func)
46
+ self.priority = priority
47
+ self.source = source
24
48
 
25
49
  def call(self, *args: list, **kwargs: dict) -> str:
26
50
  """Call the associated function with the specified arguments and keyword arguments."""
@@ -82,13 +106,17 @@ class CommandRegistry:
82
106
  """Registry for managing commands in the command line interface."""
83
107
 
84
108
  registry: dict[str, Command]
109
+ pending_commands: dict[str, list[Command]] # Commands waiting to be bound
85
110
  sub_parsers: argparse._SubParsersAction
86
111
  parser: argparse.ArgumentParser
87
112
  args: argparse.Namespace
113
+ _finalized: bool # Whether command registration has been finalized
88
114
 
89
115
  def __init__(self) -> None:
90
116
  """Initialize a CommandRegistry instance."""
91
117
  self.registry = {}
118
+ self.pending_commands = {}
119
+ self._finalized = False
92
120
  self.parser = argparse.ArgumentParser(
93
121
  prog="jac",
94
122
  description="Jac Programming Language CLI - A tool for working with Jac programs",
@@ -150,11 +178,55 @@ class CommandRegistry:
150
178
  help=f"str - Override setting '{name}'",
151
179
  )
152
180
 
153
- def register(self, func: Callable) -> Callable:
154
- """Register a command in the registry."""
155
- name = func.__name__
156
- cmd = Command(func)
157
- self.registry[name] = cmd
181
+ def register(
182
+ self,
183
+ func: Callable | None = None,
184
+ *,
185
+ priority: CommandPriority = CommandPriority.CORE,
186
+ source: str = "core",
187
+ ) -> Callable:
188
+ """Register a command in the registry.
189
+
190
+ This method supports both decorator syntax with and without arguments:
191
+ @cmd_registry.register
192
+ def my_cmd(): ...
193
+
194
+ @cmd_registry.register(priority=CommandPriority.PLUGIN, source="my-plugin")
195
+ def my_cmd(): ...
196
+
197
+ Args:
198
+ func: The command function to register
199
+ priority: Priority level for conflict resolution
200
+ source: Source plugin/module name for introspection
201
+
202
+ Returns:
203
+ The original function (for decorator usage)
204
+ """
205
+
206
+ def _register(f: Callable) -> Callable:
207
+ """Inner registration function."""
208
+ name = f.__name__
209
+ cmd = Command(f, priority=priority, source=source)
210
+
211
+ # Add to pending commands for priority resolution
212
+ if name not in self.pending_commands:
213
+ self.pending_commands[name] = []
214
+ self.pending_commands[name].append(cmd)
215
+
216
+ # If already finalized, bind immediately (late registration)
217
+ if self._finalized:
218
+ self._resolve_and_bind_command(name)
219
+
220
+ return f
221
+
222
+ # Support both @register and @register(...) syntax
223
+ if func is not None:
224
+ return _register(func)
225
+ return _register
226
+
227
+ def _bind_command_to_argparse(self, name: str, cmd: Command) -> None:
228
+ """Bind a command to argparse subparser."""
229
+ func = cmd.func
158
230
  # Extract the first paragraph from the docstring for brief description
159
231
  doc = func.__doc__ or ""
160
232
  brief_desc = doc.split("\n\n")[0].strip()
@@ -264,7 +336,51 @@ class CommandRegistry:
264
336
  else param.annotation
265
337
  ),
266
338
  )
267
- return func
339
+
340
+ def _resolve_and_bind_command(self, name: str) -> None:
341
+ """Resolve command conflicts by priority and bind to argparse."""
342
+ if name not in self.pending_commands:
343
+ return
344
+
345
+ commands = self.pending_commands[name]
346
+ if not commands:
347
+ return
348
+
349
+ # Sort by priority (highest first)
350
+ commands.sort(key=lambda c: c.priority, reverse=True)
351
+
352
+ # Winner is the highest priority command
353
+ winner = commands[0]
354
+
355
+ # Warn about conflicts if multiple commands with different priorities
356
+ # Only warn if JAC_CLI_VERBOSE environment variable is set
357
+ if len(commands) > 1 and os.getenv("JAC_CLI_VERBOSE"):
358
+ conflicts = [f"{c.source} (priority={c.priority})" for c in commands[1:]]
359
+ print(
360
+ f"Warning: Command '{name}' registered by multiple sources. "
361
+ f"Using {winner.source} (priority={winner.priority}). "
362
+ f"Overriding: {', '.join(conflicts)}",
363
+ file=sys.stderr,
364
+ )
365
+
366
+ # Register the winner
367
+ self.registry[name] = winner
368
+ self._bind_command_to_argparse(name, winner)
369
+
370
+ def finalize(self) -> None:
371
+ """Finalize command registration by resolving conflicts and binding to argparse.
372
+
373
+ This should be called after all plugins have had a chance to register commands.
374
+ """
375
+ if self._finalized:
376
+ return
377
+
378
+ # Resolve all pending commands
379
+ for name in list(self.pending_commands.keys()):
380
+ self._resolve_and_bind_command(name)
381
+
382
+ self._finalized = True
383
+ self.pending_commands.clear()
268
384
 
269
385
  def get(self, name: str) -> Optional[Command]:
270
386
  """Get the Command instance for a given command name."""
@@ -279,6 +395,26 @@ class CommandRegistry:
279
395
  all_commands[name] = (doc, args)
280
396
  return all_commands
281
397
 
398
+ def has_command(self, name: str) -> bool:
399
+ """Check if a command is already registered."""
400
+ return name in self.registry
401
+
402
+ def list_commands(self) -> dict[str, dict[str, CommandPriority | str]]:
403
+ """List all registered commands with metadata.
404
+
405
+ Returns:
406
+ Dictionary mapping command names to metadata including source and priority
407
+ """
408
+ return {
409
+ name: {
410
+ "source": cmd.source,
411
+ "priority": cmd.priority,
412
+ "priority_name": cmd.priority.name,
413
+ "doc": cmd.func.__doc__ or "No documentation",
414
+ }
415
+ for name, cmd in self.registry.items()
416
+ }
417
+
282
418
 
283
419
  cmd_registry = CommandRegistry()
284
420
 
@@ -36,7 +36,12 @@ except ModuleNotFoundError:
36
36
  generate_static_parser(force=True)
37
37
  from jaclang.compiler.larkparse import jac_parser as jac_lark
38
38
 
39
- jac_lark.logger.setLevel(logging.DEBUG)
39
+ if not hasattr(jac_lark, "Lark_StandAlone"):
40
+ generate_static_parser(force=True)
41
+ from jaclang.compiler.larkparse import jac_parser as jac_lark
42
+
43
+ with contextlib.suppress(AttributeError):
44
+ jac_lark.logger.setLevel(logging.DEBUG)
40
45
  contextlib.suppress(ModuleNotFoundError)
41
46
 
42
47
  TOKEN_MAP = {
@@ -3,12 +3,25 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import ast as ast3
6
- from typing import Optional, TYPE_CHECKING
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, Optional, TYPE_CHECKING
7
8
 
8
9
  if TYPE_CHECKING:
10
+ from jaclang.compiler.passes.ecmascript.estree import Node as EsNode
9
11
  from jaclang.compiler.unitree import Source, Token
10
12
 
11
13
 
14
+ @dataclass
15
+ class ClientManifest:
16
+ """Client-side rendering manifest metadata."""
17
+
18
+ exports: list[str] = field(default_factory=list)
19
+ globals: list[str] = field(default_factory=list)
20
+ params: dict[str, list[str]] = field(default_factory=dict)
21
+ globals_values: dict[str, Any] = field(default_factory=dict)
22
+ has_client: bool = False
23
+
24
+
12
25
  class CodeGenTarget:
13
26
  """Code generation target."""
14
27
 
@@ -20,8 +33,10 @@ class CodeGenTarget:
20
33
  self.jac: str = ""
21
34
  self.doc_ir: doc.DocType = doc.Text("")
22
35
  self.js: str = ""
36
+ self.client_manifest: ClientManifest = ClientManifest()
23
37
  self.py_ast: list[ast3.AST] = []
24
38
  self.py_bytecode: Optional[bytes] = None
39
+ self.es_ast: Optional[EsNode] = None
25
40
 
26
41
 
27
42
  class CodeLocInfo:
@@ -247,6 +247,7 @@ class Tokens(str, Enum):
247
247
  KW_OVERRIDE = "KW_OVERRIDE"
248
248
  KW_MATCH = "KW_MATCH"
249
249
  KW_CASE = "KW_CASE"
250
+ KW_CLIENT = "KW_CLIENT"
250
251
  PLUS = "PLUS"
251
252
  MINUS = "MINUS"
252
253
  STAR_MUL = "STAR_MUL"
@@ -295,21 +296,40 @@ class Tokens(str, Enum):
295
296
  RETURN_HINT = "RETURN_HINT"
296
297
  NULL_OK = "NULL_OK"
297
298
  DECOR_OP = "DECOR_OP"
298
- FSTR_START = "FSTR_START"
299
- FSTR_END = "FSTR_END"
300
- FSTR_SQ_START = "FSTR_SQ_START"
301
- FSTR_SQ_END = "FSTR_SQ_END"
302
- FSTR_TRIPLE_START = "FSTR_TRIPLE_START"
303
- FSTR_TRIPLE_END = "FSTR_TRIPLE_END"
304
- FSTR_SQ_TRIPLE_START = "FSTR_SQ_TRIPLE_START"
305
- FSTR_SQ_TRIPLE_END = "FSTR_SQ_TRIPLE_END"
306
- FSTR_PIECE = "FSTR_PIECE"
307
- FSTR_SQ_PIECE = "FSTR_SQ_PIECE"
308
- FSTR_TRIPLE_PIECE = "FSTR_TRIPLE_PIECE"
309
- FSTR_SQ_TRIPLE_PIECE = "FSTR_SQ_TRIPLE_PIECE"
310
- FSTR_BESC = "FSTR_BESC"
299
+ JSX_TEXT = "JSX_TEXT"
300
+ JSX_OPEN_START = "JSX_OPEN_START"
301
+ JSX_SELF_CLOSE = "JSX_SELF_CLOSE"
302
+ JSX_TAG_END = "JSX_TAG_END"
303
+ JSX_CLOSE_START = "JSX_CLOSE_START"
304
+ JSX_FRAG_OPEN = "JSX_FRAG_OPEN"
305
+ JSX_FRAG_CLOSE = "JSX_FRAG_CLOSE"
306
+ JSX_NAME = "JSX_NAME"
311
307
  COMMENT = "COMMENT"
312
308
  WS = "WS"
309
+ F_DQ_START = "F_DQ_START"
310
+ F_SQ_START = "F_SQ_START"
311
+ F_TDQ_START = "F_TDQ_START"
312
+ F_TSQ_START = "F_TSQ_START"
313
+ RF_DQ_START = "RF_DQ_START"
314
+ RF_SQ_START = "RF_SQ_START"
315
+ RF_TDQ_START = "RF_TDQ_START"
316
+ RF_TSQ_START = "RF_TSQ_START"
317
+ F_DQ_END = "F_DQ_END"
318
+ F_SQ_END = "F_SQ_END"
319
+ F_TDQ_END = "F_TDQ_END"
320
+ F_TSQ_END = "F_TSQ_END"
321
+ F_TEXT_DQ = "F_TEXT_DQ"
322
+ F_TEXT_SQ = "F_TEXT_SQ"
323
+ F_TEXT_TDQ = "F_TEXT_TDQ"
324
+ F_TEXT_TSQ = "F_TEXT_TSQ"
325
+ RF_TEXT_DQ = "RF_TEXT_DQ"
326
+ RF_TEXT_SQ = "RF_TEXT_SQ"
327
+ RF_TEXT_TDQ = "RF_TEXT_TDQ"
328
+ RF_TEXT_TSQ = "RF_TEXT_TSQ"
329
+ D_LBRACE = "D_LBRACE"
330
+ D_RBRACE = "D_RBRACE"
331
+ CONV = "CONV"
332
+ F_FORMAT_TEXT = "F_FORMAT_TEXT"
313
333
 
314
334
  def __str__(self) -> str:
315
335
  return self.value