jaclang 0.7.22__py3-none-any.whl → 0.7.25__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 (65) hide show
  1. jaclang/__init__.py +5 -10
  2. jaclang/cli/cli.py +50 -30
  3. jaclang/compiler/__init__.py +2 -2
  4. jaclang/compiler/absyntree.py +87 -48
  5. jaclang/compiler/codeloc.py +7 -2
  6. jaclang/compiler/compile.py +10 -3
  7. jaclang/compiler/parser.py +26 -23
  8. jaclang/compiler/passes/ir_pass.py +2 -2
  9. jaclang/compiler/passes/main/def_impl_match_pass.py +46 -0
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +146 -123
  11. jaclang/compiler/passes/main/import_pass.py +6 -2
  12. jaclang/compiler/passes/main/pyast_load_pass.py +36 -35
  13. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -7
  14. jaclang/compiler/passes/main/registry_pass.py +3 -12
  15. jaclang/compiler/passes/main/tests/fixtures/defn_decl_mismatch.jac +19 -0
  16. jaclang/compiler/passes/main/tests/fixtures/fstrings.jac +2 -0
  17. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +59 -0
  18. jaclang/compiler/passes/main/tests/test_registry_pass.py +2 -10
  19. jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
  20. jaclang/compiler/passes/main/type_check_pass.py +8 -6
  21. jaclang/compiler/passes/transform.py +27 -3
  22. jaclang/compiler/passes/utils/mypy_ast_build.py +246 -26
  23. jaclang/compiler/symtable.py +6 -0
  24. jaclang/compiler/tests/test_importer.py +2 -2
  25. jaclang/langserve/engine.py +14 -12
  26. jaclang/langserve/server.py +7 -2
  27. jaclang/langserve/tests/test_server.py +1 -1
  28. jaclang/langserve/utils.py +17 -3
  29. jaclang/plugin/builtin.py +3 -3
  30. jaclang/plugin/default.py +612 -236
  31. jaclang/plugin/feature.py +274 -99
  32. jaclang/plugin/plugin.md +471 -0
  33. jaclang/plugin/spec.py +231 -86
  34. jaclang/plugin/tests/fixtures/other_root_access.jac +9 -9
  35. jaclang/plugin/tests/test_features.py +2 -2
  36. jaclang/runtimelib/architype.py +1 -370
  37. jaclang/runtimelib/constructs.py +2 -0
  38. jaclang/runtimelib/context.py +2 -4
  39. jaclang/runtimelib/importer.py +7 -2
  40. jaclang/runtimelib/machine.py +78 -6
  41. jaclang/runtimelib/memory.py +2 -4
  42. jaclang/settings.py +3 -0
  43. jaclang/tests/fixtures/arch_create_util.jac +7 -0
  44. jaclang/tests/fixtures/arch_rel_import_creation.jac +30 -0
  45. jaclang/tests/fixtures/builtin_dotgen.jac +6 -6
  46. jaclang/tests/fixtures/create_dynamic_architype.jac +35 -0
  47. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  48. jaclang/tests/fixtures/edges_walk.jac +1 -1
  49. jaclang/tests/fixtures/enum_inside_archtype.jac +16 -11
  50. jaclang/tests/fixtures/expr_type.jac +54 -0
  51. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  52. jaclang/tests/fixtures/glob_multivar_statement.jac +15 -0
  53. jaclang/tests/fixtures/registry.jac +20 -8
  54. jaclang/tests/fixtures/visit_order.jac +20 -0
  55. jaclang/tests/foo/__init__.jac +0 -0
  56. jaclang/tests/main.jac +2 -0
  57. jaclang/tests/test_cli.py +68 -4
  58. jaclang/tests/test_language.py +113 -27
  59. jaclang/utils/helpers.py +92 -14
  60. jaclang/utils/lang_tools.py +6 -2
  61. jaclang/utils/treeprinter.py +4 -2
  62. {jaclang-0.7.22.dist-info → jaclang-0.7.25.dist-info}/METADATA +2 -1
  63. {jaclang-0.7.22.dist-info → jaclang-0.7.25.dist-info}/RECORD +65 -55
  64. {jaclang-0.7.22.dist-info → jaclang-0.7.25.dist-info}/WHEEL +1 -1
  65. {jaclang-0.7.22.dist-info → jaclang-0.7.25.dist-info}/entry_points.txt +0 -0
@@ -7,12 +7,9 @@ from enum import IntEnum
7
7
  from logging import getLogger
8
8
  from pickle import dumps
9
9
  from types import UnionType
10
- from typing import Any, Callable, ClassVar, Iterable, Optional, TypeVar
10
+ from typing import Any, Callable, ClassVar, Optional, TypeVar
11
11
  from uuid import UUID, uuid4
12
12
 
13
- from jaclang.compiler.constant import EdgeDir
14
- from jaclang.runtimelib.utils import collect_node_connections
15
-
16
13
  logger = getLogger(__name__)
17
14
 
18
15
  TARCH = TypeVar("TARCH", bound="Architype")
@@ -77,128 +74,6 @@ class Anchor:
77
74
  persistent: bool = False
78
75
  hash: int = 0
79
76
 
80
- ##########################################################################
81
- # ACCESS CONTROL: TODO: Make Base Type #
82
- ##########################################################################
83
-
84
- def allow_root(
85
- self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
86
- ) -> None:
87
- """Allow all access from target root graph to current Architype."""
88
- level = AccessLevel.cast(level)
89
- access = self.access.roots
90
-
91
- _root_id = str(root_id)
92
- if level != access.anchors.get(_root_id, AccessLevel.NO_ACCESS):
93
- access.anchors[_root_id] = level
94
-
95
- def disallow_root(
96
- self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
97
- ) -> None:
98
- """Disallow all access from target root graph to current Architype."""
99
- level = AccessLevel.cast(level)
100
- access = self.access.roots
101
-
102
- access.anchors.pop(str(root_id), None)
103
-
104
- def unrestrict(self, level: AccessLevel | int | str = AccessLevel.READ) -> None:
105
- """Allow everyone to access current Architype."""
106
- level = AccessLevel.cast(level)
107
- if level != self.access.all:
108
- self.access.all = level
109
-
110
- def restrict(self) -> None:
111
- """Disallow others to access current Architype."""
112
- if self.access.all > AccessLevel.NO_ACCESS:
113
- self.access.all = AccessLevel.NO_ACCESS
114
-
115
- def has_read_access(self, to: Anchor) -> bool:
116
- """Read Access Validation."""
117
- if not (access_level := self.access_level(to) > AccessLevel.NO_ACCESS):
118
- logger.info(
119
- f"Current root doesn't have read access to {to.__class__.__name__}[{to.id}]"
120
- )
121
- return access_level
122
-
123
- def has_connect_access(self, to: Anchor) -> bool:
124
- """Write Access Validation."""
125
- if not (access_level := self.access_level(to) > AccessLevel.READ):
126
- logger.info(
127
- f"Current root doesn't have connect access to {to.__class__.__name__}[{to.id}]"
128
- )
129
- return access_level
130
-
131
- def has_write_access(self, to: Anchor) -> bool:
132
- """Write Access Validation."""
133
- if not (access_level := self.access_level(to) > AccessLevel.CONNECT):
134
- logger.info(
135
- f"Current root doesn't have write access to {to.__class__.__name__}[{to.id}]"
136
- )
137
- return access_level
138
-
139
- def access_level(self, to: Anchor) -> AccessLevel:
140
- """Access validation."""
141
- if not to.persistent:
142
- return AccessLevel.WRITE
143
-
144
- from jaclang.plugin.feature import JacFeature as Jac
145
-
146
- jctx = Jac.get_context()
147
-
148
- jroot = jctx.root
149
-
150
- # if current root is system_root
151
- # if current root id is equal to target anchor's root id
152
- # if current root is the target anchor
153
- if jroot == jctx.system_root or jroot.id == to.root or jroot == to:
154
- return AccessLevel.WRITE
155
-
156
- access_level = AccessLevel.NO_ACCESS
157
-
158
- # if target anchor have set access.all
159
- if (to_access := to.access).all > AccessLevel.NO_ACCESS:
160
- access_level = to_access.all
161
-
162
- # if target anchor's root have set allowed roots
163
- # if current root is allowed to the whole graph of target anchor's root
164
- if to.root and isinstance(to_root := jctx.mem.find_one(to.root), Anchor):
165
- if to_root.access.all > access_level:
166
- access_level = to_root.access.all
167
-
168
- level = to_root.access.roots.check(str(jroot.id))
169
- if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
170
- access_level = level
171
-
172
- # if target anchor have set allowed roots
173
- # if current root is allowed to target anchor
174
- level = to_access.roots.check(str(jroot.id))
175
- if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
176
- access_level = level
177
-
178
- return access_level
179
-
180
- # ---------------------------------------------------------------------- #
181
-
182
- def save(self) -> None:
183
- """Save Anchor."""
184
- from jaclang.plugin.feature import JacFeature as Jac
185
-
186
- jctx = Jac.get_context()
187
-
188
- self.persistent = True
189
- self.root = jctx.root.id
190
-
191
- jctx.mem.set(self.id, self)
192
-
193
- def destroy(self) -> None:
194
- """Destroy Anchor."""
195
- from jaclang.plugin.feature import JacFeature as Jac
196
-
197
- jctx = Jac.get_context()
198
-
199
- if jctx.root.has_write_access(self):
200
- jctx.mem.remove(self.id)
201
-
202
77
  def is_populated(self) -> bool:
203
78
  """Check if state."""
204
79
  return "architype" in self.__dict__
@@ -304,122 +179,6 @@ class NodeAnchor(Anchor):
304
179
  architype: NodeArchitype
305
180
  edges: list[EdgeAnchor]
306
181
 
307
- def get_edges(
308
- self,
309
- dir: EdgeDir,
310
- filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
311
- target_obj: Optional[list[NodeArchitype]],
312
- ) -> list[EdgeArchitype]:
313
- """Get edges connected to this node."""
314
- from jaclang.plugin.feature import JacFeature as Jac
315
-
316
- root = Jac.get_root().__jac__
317
- ret_edges: list[EdgeArchitype] = []
318
- for anchor in self.edges:
319
- if (
320
- (source := anchor.source)
321
- and (target := anchor.target)
322
- and (not filter_func or filter_func([anchor.architype]))
323
- and source.architype
324
- and target.architype
325
- ):
326
- if (
327
- dir in [EdgeDir.OUT, EdgeDir.ANY]
328
- and self == source
329
- and (not target_obj or target.architype in target_obj)
330
- and root.has_read_access(target)
331
- ):
332
- ret_edges.append(anchor.architype)
333
- if (
334
- dir in [EdgeDir.IN, EdgeDir.ANY]
335
- and self == target
336
- and (not target_obj or source.architype in target_obj)
337
- and root.has_read_access(source)
338
- ):
339
- ret_edges.append(anchor.architype)
340
- return ret_edges
341
-
342
- def edges_to_nodes(
343
- self,
344
- dir: EdgeDir,
345
- filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
346
- target_obj: Optional[list[NodeArchitype]],
347
- ) -> list[NodeArchitype]:
348
- """Get set of nodes connected to this node."""
349
- from jaclang.plugin.feature import JacFeature as Jac
350
-
351
- root = Jac.get_root().__jac__
352
- ret_edges: list[NodeArchitype] = []
353
- for anchor in self.edges:
354
- if (
355
- (source := anchor.source)
356
- and (target := anchor.target)
357
- and (not filter_func or filter_func([anchor.architype]))
358
- and source.architype
359
- and target.architype
360
- ):
361
- if (
362
- dir in [EdgeDir.OUT, EdgeDir.ANY]
363
- and self == source
364
- and (not target_obj or target.architype in target_obj)
365
- and root.has_read_access(target)
366
- ):
367
- ret_edges.append(target.architype)
368
- if (
369
- dir in [EdgeDir.IN, EdgeDir.ANY]
370
- and self == target
371
- and (not target_obj or source.architype in target_obj)
372
- and root.has_read_access(source)
373
- ):
374
- ret_edges.append(source.architype)
375
- return ret_edges
376
-
377
- def remove_edge(self, edge: EdgeAnchor) -> None:
378
- """Remove reference without checking sync status."""
379
- for idx, ed in enumerate(self.edges):
380
- if ed.id == edge.id:
381
- self.edges.pop(idx)
382
- break
383
-
384
- def gen_dot(self, dot_file: Optional[str] = None) -> str:
385
- """Generate Dot file for visualizing nodes and edges."""
386
- visited_nodes: set[NodeAnchor] = set()
387
- connections: set[tuple[NodeArchitype, NodeArchitype, str]] = set()
388
- unique_node_id_dict = {}
389
-
390
- collect_node_connections(self, visited_nodes, connections)
391
- dot_content = 'digraph {\nnode [style="filled", shape="ellipse", fillcolor="invis", fontcolor="black"];\n'
392
- for idx, i in enumerate([nodes_.architype for nodes_ in visited_nodes]):
393
- unique_node_id_dict[i] = (i.__class__.__name__, str(idx))
394
- dot_content += f'{idx} [label="{i}"];\n'
395
- dot_content += 'edge [color="gray", style="solid"];\n'
396
-
397
- for pair in list(set(connections)):
398
- dot_content += (
399
- f"{unique_node_id_dict[pair[0]][1]} -> {unique_node_id_dict[pair[1]][1]}"
400
- f' [label="{pair[2]}"];\n'
401
- )
402
- if dot_file:
403
- with open(dot_file, "w") as f:
404
- f.write(dot_content + "}")
405
- return dot_content + "}"
406
-
407
- def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
408
- """Invoke data spatial call."""
409
- return walk.spawn_call(self)
410
-
411
- def destroy(self) -> None:
412
- """Destroy Anchor."""
413
- from jaclang.plugin.feature import JacFeature as Jac
414
-
415
- jctx = Jac.get_context()
416
-
417
- if jctx.root.has_write_access(self):
418
- for edge in self.edges:
419
- edge.destroy()
420
-
421
- jctx.mem.remove(self.id)
422
-
423
182
  def __getstate__(self) -> dict[str, object]:
424
183
  """Serialize Node Anchor."""
425
184
  state = super().__getstate__()
@@ -439,30 +198,6 @@ class EdgeAnchor(Anchor):
439
198
  target: NodeAnchor
440
199
  is_undirected: bool
441
200
 
442
- def __post_init__(self) -> None:
443
- """Populate edge to source and target."""
444
- self.source.edges.append(self)
445
- self.target.edges.append(self)
446
-
447
- def detach(self) -> None:
448
- """Detach edge from nodes."""
449
- self.source.remove_edge(self)
450
- self.target.remove_edge(self)
451
-
452
- def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
453
- """Invoke data spatial call."""
454
- return walk.spawn_call(self.target)
455
-
456
- def destroy(self) -> None:
457
- """Destroy Anchor."""
458
- from jaclang.plugin.feature import JacFeature as Jac
459
-
460
- jctx = Jac.get_context()
461
-
462
- if jctx.root.has_write_access(self):
463
- self.detach()
464
- jctx.mem.remove(self.id)
465
-
466
201
  def __getstate__(self) -> dict[str, object]:
467
202
  """Serialize Node Anchor."""
468
203
  state = super().__getstate__()
@@ -489,99 +224,6 @@ class WalkerAnchor(Anchor):
489
224
  ignores: list[Anchor] = field(default_factory=list)
490
225
  disengaged: bool = False
491
226
 
492
- def visit_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
493
- """Walker visits node."""
494
- before_len = len(self.next)
495
- for anchor in anchors:
496
- if anchor not in self.ignores:
497
- if isinstance(anchor, NodeAnchor):
498
- self.next.append(anchor)
499
- elif isinstance(anchor, EdgeAnchor):
500
- if target := anchor.target:
501
- self.next.append(target)
502
- else:
503
- raise ValueError("Edge has no target.")
504
- return len(self.next) > before_len
505
-
506
- def ignore_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
507
- """Walker ignores node."""
508
- before_len = len(self.ignores)
509
- for anchor in anchors:
510
- if anchor not in self.ignores:
511
- if isinstance(anchor, NodeAnchor):
512
- self.ignores.append(anchor)
513
- elif isinstance(anchor, EdgeAnchor):
514
- if target := anchor.target:
515
- self.ignores.append(target)
516
- else:
517
- raise ValueError("Edge has no target.")
518
- return len(self.ignores) > before_len
519
-
520
- def disengage_now(self) -> None:
521
- """Disengage walker from traversal."""
522
- self.disengaged = True
523
-
524
- def spawn_call(self, node: Anchor) -> WalkerArchitype:
525
- """Invoke data spatial call."""
526
- if walker := self.architype:
527
- self.path = []
528
- self.next = [node]
529
- if self.next:
530
- current_node = self.next[-1].architype
531
- for i in walker._jac_entry_funcs_:
532
- if not i.trigger:
533
- if i.func:
534
- i.func(walker, current_node)
535
- else:
536
- raise ValueError(f"No function {i.name} to call.")
537
- while len(self.next):
538
- if current_node := self.next.pop(0).architype:
539
- for i in current_node._jac_entry_funcs_:
540
- if not i.trigger or isinstance(walker, i.trigger):
541
- if i.func:
542
- i.func(current_node, walker)
543
- else:
544
- raise ValueError(f"No function {i.name} to call.")
545
- if self.disengaged:
546
- return walker
547
- for i in walker._jac_entry_funcs_:
548
- if not i.trigger or isinstance(current_node, i.trigger):
549
- if i.func and i.trigger:
550
- i.func(walker, current_node)
551
- elif not i.trigger:
552
- continue
553
- else:
554
- raise ValueError(f"No function {i.name} to call.")
555
- if self.disengaged:
556
- return walker
557
- for i in walker._jac_exit_funcs_:
558
- if not i.trigger or isinstance(current_node, i.trigger):
559
- if i.func and i.trigger:
560
- i.func(walker, current_node)
561
- elif not i.trigger:
562
- continue
563
- else:
564
- raise ValueError(f"No function {i.name} to call.")
565
- if self.disengaged:
566
- return walker
567
- for i in current_node._jac_exit_funcs_:
568
- if not i.trigger or isinstance(walker, i.trigger):
569
- if i.func:
570
- i.func(current_node, walker)
571
- else:
572
- raise ValueError(f"No function {i.name} to call.")
573
- if self.disengaged:
574
- return walker
575
- for i in walker._jac_exit_funcs_:
576
- if not i.trigger:
577
- if i.func:
578
- i.func(walker, current_node)
579
- else:
580
- raise ValueError(f"No function {i.name} to call.")
581
- self.ignores = []
582
- return walker
583
- raise Exception(f"Invalid Reference {self.id}")
584
-
585
227
 
586
228
  class Architype:
587
229
  """Architype Protocol."""
@@ -613,17 +255,6 @@ class EdgeArchitype(Architype):
613
255
 
614
256
  __jac__: EdgeAnchor
615
257
 
616
- def __attach__(
617
- self,
618
- source: NodeAnchor,
619
- target: NodeAnchor,
620
- is_undirected: bool,
621
- ) -> None:
622
- """Attach EdgeAnchor properly."""
623
- self.__jac__ = EdgeAnchor(
624
- architype=self, source=source, target=target, is_undirected=is_undirected
625
- )
626
-
627
258
 
628
259
  class WalkerArchitype(Architype):
629
260
  """Walker Architype Protocol."""
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
 
6
6
  from .architype import (
7
+ AccessLevel,
7
8
  Anchor,
8
9
  Architype,
9
10
  DSFunc,
@@ -21,6 +22,7 @@ from .memory import Memory, ShelfStorage
21
22
  from .test import JacTestCheck, JacTestResult, JacTextTestRunner
22
23
 
23
24
  __all__ = [
25
+ "AccessLevel",
24
26
  "Anchor",
25
27
  "NodeAnchor",
26
28
  "EdgeAnchor",
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import unittest
6
6
  from contextvars import ContextVar
7
+ from dataclasses import MISSING
7
8
  from typing import Any, Callable, Optional, cast
8
9
  from uuid import UUID
9
10
 
@@ -26,6 +27,7 @@ class ExecutionContext:
26
27
 
27
28
  mem: Memory
28
29
  reports: list[Any]
30
+ custom: Any = MISSING
29
31
  system_root: NodeAnchor
30
32
  root: NodeAnchor
31
33
  entry_node: NodeAnchor
@@ -42,10 +44,6 @@ class ExecutionContext:
42
44
  raise ValueError(f"Invalid anchor id {anchor_id} !")
43
45
  return default
44
46
 
45
- def validate_access(self) -> bool:
46
- """Validate access."""
47
- return self.root.has_read_access(self.entry_node)
48
-
49
47
  def set_entry_node(self, entry_node: str | None) -> None:
50
48
  """Override entry."""
51
49
  self.entry_node = self.init_anchor(entry_node, self.root)
@@ -344,14 +344,19 @@ class JacImporter(Importer):
344
344
  cachable=spec.cachable,
345
345
  reload=reload if reload else False,
346
346
  )
347
+
348
+ # Since this is a compile time error, we can safely raise an exception here.
349
+ if not codeobj:
350
+ raise ImportError(f"No bytecode found for {spec.full_target}")
351
+
347
352
  try:
348
- if not codeobj:
349
- raise ImportError(f"No bytecode found for {spec.full_target}")
350
353
  with sys_path_context(spec.caller_dir):
351
354
  exec(codeobj, module.__dict__)
352
355
  except Exception as e:
356
+ logger.error(e)
353
357
  logger.error(dump_traceback(e))
354
358
  raise e
359
+
355
360
  import_return = ImportReturn(module, unique_loaded_items, self)
356
361
  if spec.items:
357
362
  import_return.process_items(
@@ -1,9 +1,12 @@
1
1
  """Jac Machine module."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import inspect
4
6
  import marshal
5
7
  import os
6
8
  import sys
9
+ import tempfile
7
10
  import types
8
11
  from contextvars import ContextVar
9
12
  from typing import Optional, Union
@@ -11,6 +14,7 @@ from typing import Optional, Union
11
14
  from jaclang.compiler.absyntree import Module
12
15
  from jaclang.compiler.compile import compile_jac
13
16
  from jaclang.compiler.constant import Constants as Con
17
+ from jaclang.compiler.semtable import SemRegistry
14
18
  from jaclang.runtimelib.architype import (
15
19
  Architype,
16
20
  EdgeArchitype,
@@ -69,6 +73,14 @@ class JacMachine:
69
73
  )
70
74
  return None
71
75
 
76
+ def get_sem_ir(self, mod_sem_ir: SemRegistry | None) -> None:
77
+ """Update semtable on the attached JacProgram."""
78
+ if self.jac_program and mod_sem_ir:
79
+ if self.jac_program.sem_ir:
80
+ self.jac_program.sem_ir.registry.update(mod_sem_ir.registry)
81
+ else:
82
+ self.jac_program.sem_ir = mod_sem_ir
83
+
72
84
  def load_module(self, module_name: str, module: types.ModuleType) -> None:
73
85
  """Load a module into the machine."""
74
86
  self.loaded_modules[module_name] = module
@@ -111,6 +123,62 @@ class JacMachine:
111
123
  return nodes
112
124
  return []
113
125
 
126
+ def create_architype_from_source(
127
+ self,
128
+ source_code: str,
129
+ module_name: Optional[str] = None,
130
+ base_path: Optional[str] = None,
131
+ cachable: bool = False,
132
+ keep_temporary_files: bool = False,
133
+ ) -> Optional[types.ModuleType]:
134
+ """Dynamically creates architypes (nodes, walkers, etc.) from Jac source code."""
135
+ from jaclang.runtimelib.importer import JacImporter, ImportPathSpec
136
+
137
+ if not base_path:
138
+ base_path = self.base_path or os.getcwd()
139
+
140
+ if base_path and not os.path.exists(base_path):
141
+ os.makedirs(base_path)
142
+ if not module_name:
143
+ module_name = f"_dynamic_module_{len(self.loaded_modules)}"
144
+ with tempfile.NamedTemporaryFile(
145
+ mode="w",
146
+ suffix=".jac",
147
+ prefix=module_name + "_",
148
+ dir=base_path,
149
+ delete=False,
150
+ ) as tmp_file:
151
+ tmp_file_path = tmp_file.name
152
+ tmp_file.write(source_code)
153
+
154
+ try:
155
+ importer = JacImporter(self)
156
+ tmp_file_basename = os.path.basename(tmp_file_path)
157
+ tmp_module_name, _ = os.path.splitext(tmp_file_basename)
158
+
159
+ spec = ImportPathSpec(
160
+ target=tmp_module_name,
161
+ base_path=base_path,
162
+ absorb=False,
163
+ cachable=cachable,
164
+ mdl_alias=None,
165
+ override_name=module_name,
166
+ lng="jac",
167
+ items=None,
168
+ )
169
+
170
+ import_result = importer.run_import(spec, reload=False)
171
+ module = import_result.ret_mod
172
+
173
+ self.loaded_modules[module_name] = module
174
+ return module
175
+ except Exception as e:
176
+ logger.error(f"Error importing dynamic module '{module_name}': {e}")
177
+ return None
178
+ finally:
179
+ if not keep_temporary_files and os.path.exists(tmp_file_path):
180
+ os.remove(tmp_file_path)
181
+
114
182
  def update_walker(
115
183
  self, module_name: str, items: Optional[dict[str, Union[str, Optional[str]]]]
116
184
  ) -> tuple[types.ModuleType, ...]:
@@ -206,14 +274,18 @@ class JacMachine:
206
274
 
207
275
 
208
276
  class JacProgram:
209
- """Class to hold the mod_bundle and bytecode for Jac modules."""
277
+ """Class to hold the mod_bundle bytecode and sem_ir for Jac modules."""
210
278
 
211
279
  def __init__(
212
- self, mod_bundle: Optional[Module], bytecode: Optional[dict[str, bytes]]
280
+ self,
281
+ mod_bundle: Optional[Module],
282
+ bytecode: Optional[dict[str, bytes]],
283
+ sem_ir: Optional[SemRegistry],
213
284
  ) -> None:
214
285
  """Initialize the JacProgram object."""
215
286
  self.mod_bundle = mod_bundle
216
287
  self.bytecode = bytecode or {}
288
+ self.sem_ir = sem_ir if sem_ir else SemRegistry()
217
289
 
218
290
  def get_bytecode(
219
291
  self,
@@ -235,10 +307,10 @@ class JacProgram:
235
307
 
236
308
  result = compile_jac(full_target, cache_result=cachable)
237
309
  if result.errors_had or not result.ir.gen.py_bytecode:
238
- logger.error(
239
- f"While importing {len(result.errors_had)} errors"
240
- f" found in {full_target}"
241
- )
310
+ for alrt in result.errors_had:
311
+ # We're not logging here, it already gets logged as the errors were added to the errors_had list.
312
+ # Regardless of the logging, this needs to be sent to the end user, so we'll printing it to stderr.
313
+ logger.error(alrt.pretty_print())
242
314
  return None
243
315
  if result.ir.gen.py_bytecode is not None:
244
316
  return marshal.loads(result.ir.gen.py_bytecode)
@@ -82,8 +82,6 @@ class ShelfStorage(Memory[UUID, Anchor]):
82
82
  if isinstance(self.__shelf__, Shelf):
83
83
  from jaclang.plugin.feature import JacFeature as Jac
84
84
 
85
- root = Jac.get_root().__jac__
86
-
87
85
  for anchor in self.__gc__:
88
86
  self.__shelf__.pop(str(anchor.id), None)
89
87
  self.__mem__.pop(anchor.id, None)
@@ -96,14 +94,14 @@ class ShelfStorage(Memory[UUID, Anchor]):
96
94
  isinstance(p_d, NodeAnchor)
97
95
  and isinstance(d, NodeAnchor)
98
96
  and p_d.edges != d.edges
99
- and root.has_connect_access(d)
97
+ and Jac.check_connect_access(d)
100
98
  ):
101
99
  if not d.edges:
102
100
  self.__shelf__.pop(_id, None)
103
101
  continue
104
102
  p_d.edges = d.edges
105
103
 
106
- if root.has_write_access(d):
104
+ if Jac.check_write_access(d):
107
105
  if hash(dumps(p_d.access)) != hash(dumps(d.access)):
108
106
  p_d.access = d.access
109
107
  if hash(dumps(p_d.architype)) != hash(dumps(d.architype)):
jaclang/settings.py CHANGED
@@ -58,6 +58,9 @@ class Settings:
58
58
  """Override settings from environment variables if available."""
59
59
  for key in [f.name for f in fields(self)]:
60
60
  env_value = os.getenv("JACLANG_" + key.upper())
61
+ env_value = (
62
+ env_value if env_value is not None else os.getenv("JAC_" + key.upper())
63
+ )
61
64
  if env_value is not None:
62
65
  setattr(self, key, self.convert_type(env_value))
63
66
 
@@ -0,0 +1,7 @@
1
+ node UtilityNode {
2
+ has data: int;
3
+
4
+ can display_data with entry {
5
+ print("UtilityNode Data:", f'{self.data}');
6
+ }
7
+ }
@@ -0,0 +1,30 @@
1
+ import:py from jaclang.runtimelib.machine { JacMachine }
2
+
3
+ glob dynamic_module_source = """
4
+ import from .arch_create_util {UtilityNode}
5
+
6
+ walker DynamicWalker {
7
+ can start with entry {
8
+ print("DynamicWalker Started");
9
+ here ++> UtilityNode(data=42);
10
+ visit [-->](`?UtilityNode);
11
+ }
12
+
13
+ can UtilityNode {
14
+ here.display_data();
15
+ }
16
+ }
17
+ """;
18
+
19
+ with entry {
20
+ node_arch = JacMachine.get().create_architype_from_source(
21
+ dynamic_module_source,
22
+ module_name="dynamic_module"
23
+ );
24
+ walker_obj = JacMachine.get().spawn_walker(
25
+ 'DynamicWalker',
26
+ module_name="dynamic_module",
27
+
28
+ );
29
+ root spawn walker_obj;
30
+ }