jaclang 0.7.1__py3-none-any.whl → 0.7.5__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 (85) hide show
  1. jaclang/cli/cli.py +2 -2
  2. jaclang/compiler/absyntree.py +378 -277
  3. jaclang/compiler/codeloc.py +2 -2
  4. jaclang/compiler/constant.py +2 -0
  5. jaclang/compiler/jac.lark +25 -19
  6. jaclang/compiler/parser.py +115 -92
  7. jaclang/compiler/passes/main/access_modifier_pass.py +15 -9
  8. jaclang/compiler/passes/main/def_impl_match_pass.py +29 -11
  9. jaclang/compiler/passes/main/def_use_pass.py +48 -17
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +49 -30
  11. jaclang/compiler/passes/main/import_pass.py +12 -7
  12. jaclang/compiler/passes/main/pyast_gen_pass.py +110 -47
  13. jaclang/compiler/passes/main/pyast_load_pass.py +49 -13
  14. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +25 -11
  15. jaclang/compiler/passes/main/pyout_pass.py +3 -1
  16. jaclang/compiler/passes/main/registry_pass.py +6 -6
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +30 -72
  18. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +21 -4
  19. jaclang/compiler/passes/main/tests/test_def_use_pass.py +5 -10
  20. jaclang/compiler/passes/main/tests/test_import_pass.py +8 -0
  21. jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
  22. jaclang/compiler/passes/main/type_check_pass.py +2 -1
  23. jaclang/compiler/passes/tool/jac_formatter_pass.py +44 -11
  24. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +16 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +16 -0
  26. jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
  27. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
  28. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
  29. jaclang/compiler/passes/transform.py +2 -4
  30. jaclang/{core/registry.py → compiler/semtable.py} +1 -3
  31. jaclang/compiler/symtable.py +39 -31
  32. jaclang/compiler/tests/test_parser.py +2 -2
  33. jaclang/core/aott.py +112 -16
  34. jaclang/core/{construct.py → architype.py} +44 -93
  35. jaclang/core/constructs.py +44 -0
  36. jaclang/core/context.py +157 -0
  37. jaclang/core/importer.py +18 -9
  38. jaclang/core/llms/anthropic.py +31 -2
  39. jaclang/core/llms/base.py +3 -3
  40. jaclang/core/llms/groq.py +4 -1
  41. jaclang/core/llms/huggingface.py +4 -1
  42. jaclang/core/llms/ollama.py +4 -1
  43. jaclang/core/llms/openai.py +6 -2
  44. jaclang/core/llms/togetherai.py +4 -1
  45. jaclang/core/memory.py +53 -2
  46. jaclang/core/test.py +90 -0
  47. jaclang/core/utils.py +2 -2
  48. jaclang/langserve/engine.py +119 -122
  49. jaclang/langserve/server.py +27 -5
  50. jaclang/langserve/tests/fixtures/circle.jac +16 -12
  51. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  52. jaclang/langserve/tests/fixtures/circle_pure.impl.jac +8 -4
  53. jaclang/langserve/tests/fixtures/circle_pure.jac +2 -2
  54. jaclang/langserve/tests/test_server.py +114 -0
  55. jaclang/langserve/utils.py +104 -10
  56. jaclang/plugin/builtin.py +1 -1
  57. jaclang/plugin/default.py +46 -90
  58. jaclang/plugin/feature.py +32 -16
  59. jaclang/plugin/spec.py +17 -19
  60. jaclang/plugin/tests/test_features.py +0 -33
  61. jaclang/settings.py +4 -0
  62. jaclang/tests/fixtures/abc.jac +16 -12
  63. jaclang/tests/fixtures/byllmissue.jac +12 -0
  64. jaclang/tests/fixtures/edgetypetest.jac +16 -0
  65. jaclang/tests/fixtures/hash_init_check.jac +17 -0
  66. jaclang/tests/fixtures/impl_match_confused.impl.jac +1 -0
  67. jaclang/tests/fixtures/impl_match_confused.jac +5 -0
  68. jaclang/tests/fixtures/math_question.jpg +0 -0
  69. jaclang/tests/fixtures/maxfail_run_test.jac +17 -5
  70. jaclang/tests/fixtures/nosigself.jac +19 -0
  71. jaclang/tests/fixtures/run_test.jac +17 -5
  72. jaclang/tests/fixtures/walker_override.jac +21 -0
  73. jaclang/tests/fixtures/with_llm_vision.jac +25 -0
  74. jaclang/tests/test_bugs.py +19 -0
  75. jaclang/tests/test_cli.py +1 -1
  76. jaclang/tests/test_language.py +116 -11
  77. jaclang/tests/test_reference.py +1 -1
  78. jaclang/utils/lang_tools.py +5 -4
  79. jaclang/utils/test.py +2 -1
  80. jaclang/utils/treeprinter.py +35 -4
  81. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/METADATA +3 -2
  82. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/RECORD +84 -71
  83. jaclang/core/shelve_storage.py +0 -55
  84. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/WHEEL +0 -0
  85. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/entry_points.txt +0 -0
@@ -22,7 +22,7 @@ class Alert:
22
22
  def __str__(self) -> str:
23
23
  """Return string representation of alert."""
24
24
  return (
25
- f"{self.loc.mod_path}, line {self.loc.first_line},"
25
+ f" {self.loc.mod_path}, line {self.loc.first_line},"
26
26
  f" col {self.loc.col_start}: {self.msg}"
27
27
  )
28
28
 
@@ -40,7 +40,7 @@ class Transform(ABC, Generic[T]):
40
40
  prior: Optional[Transform] = None,
41
41
  ) -> None:
42
42
  """Initialize pass."""
43
- self.logger = logging.getLogger(self.__class__.__module__)
43
+ self.logger = logging.getLogger(self.__class__.__name__)
44
44
  self.errors_had: list[Alert] = [] if not prior else prior.errors_had
45
45
  self.warnings_had: list[Alert] = [] if not prior else prior.warnings_had
46
46
  self.cur_node: AstNode = input_ir # tracks current node during traversal
@@ -59,7 +59,6 @@ class Transform(ABC, Generic[T]):
59
59
  self.__class__,
60
60
  )
61
61
  self.errors_had.append(alrt)
62
- # print("Error:", str(alrt))
63
62
  self.logger.error(str(alrt))
64
63
 
65
64
  def log_warning(self, msg: str, node_override: Optional[AstNode] = None) -> None:
@@ -70,5 +69,4 @@ class Transform(ABC, Generic[T]):
70
69
  self.__class__,
71
70
  )
72
71
  self.warnings_had.append(alrt)
73
- # print("Warning:", str(alrt))
74
72
  self.logger.warning(str(alrt))
@@ -12,9 +12,7 @@ from typing import Optional
12
12
  class SemInfo:
13
13
  """Semantic information class."""
14
14
 
15
- def __init__(
16
- self, name: str, type: Optional[str] = None, semstr: Optional[str] = None
17
- ) -> None:
15
+ def __init__(self, name: str, type: Optional[str] = None, semstr: str = "") -> None:
18
16
  """Initialize the class."""
19
17
  self.name = name
20
18
  self.type = type
@@ -17,7 +17,7 @@ class SymbolType(Enum):
17
17
 
18
18
  MODULE = "module" # LSP: Module
19
19
  MOD_VAR = "mod_var" # LSP: Variable
20
- VAR = "var" # LSP: Variable
20
+ VAR = "variable" # LSP: Variable
21
21
  IMM_VAR = "immutable" # LSP: Constant
22
22
  ABILITY = "ability" # LSP: Function
23
23
  OBJECT_ARCH = "object" # LSP: Class
@@ -37,24 +37,13 @@ class SymbolType(Enum):
37
37
  BOOL = "bool" # LSP: Boolean
38
38
  SEQUENCE = "sequence" # LSP: Array
39
39
  NULL = "null" # LSP: Null
40
+ UNKNOWN = "unknown" # LSP: Unknown
40
41
 
41
42
  def __str__(self) -> str:
42
43
  """Stringify."""
43
44
  return self.value
44
45
 
45
46
 
46
- class SymbolInfo:
47
- """Symbol Info."""
48
-
49
- def __init__(
50
- self, typ: str = "NoType", acc_tag: Optional[SymbolAccess] = None
51
- ) -> None: # noqa: ANN401
52
- """Initialize."""
53
- self.typ = typ
54
- self.acc_tag: Optional[SymbolAccess] = acc_tag
55
- self.typ_sym_table: Optional[SymbolTable] = None
56
-
57
-
58
47
  class SymbolAccess(Enum):
59
48
  """Symbol types."""
60
49
 
@@ -74,20 +63,19 @@ class Symbol:
74
63
 
75
64
  def __init__(
76
65
  self,
77
- defn: ast.AstSymbolNode,
66
+ defn: ast.NameAtom,
78
67
  access: SymbolAccess,
79
68
  parent_tab: SymbolTable,
80
- typ: Optional[type] = None,
81
69
  ) -> None:
82
70
  """Initialize."""
83
- self.typ = typ
84
- self.defn: list[ast.AstSymbolNode] = [defn]
85
- defn.sym_link = self
86
- self.access = access
71
+ self.defn: list[ast.NameAtom] = [defn]
72
+ self.uses: list[ast.NameAtom] = []
73
+ defn.sym = self
74
+ self.access: SymbolAccess = access
87
75
  self.parent_tab = parent_tab
88
76
 
89
77
  @property
90
- def decl(self) -> ast.AstSymbolNode:
78
+ def decl(self) -> ast.NameAtom:
91
79
  """Get decl."""
92
80
  return self.defn[0]
93
81
 
@@ -99,19 +87,35 @@ class Symbol:
99
87
  @property
100
88
  def sym_type(self) -> SymbolType:
101
89
  """Get sym_type."""
102
- return self.decl.sym_type
90
+ return self.decl.sym_category
103
91
 
104
- def add_defn(self, node: ast.AstSymbolNode) -> None:
92
+ @property
93
+ def sym_dotted_name(self) -> str:
94
+ """Return a full path of the symbol."""
95
+ out = [self.defn[0].sym_name]
96
+ current_tab = self.parent_tab
97
+ while current_tab is not None:
98
+ out.append(current_tab.name)
99
+ if current_tab.has_parent():
100
+ current_tab = current_tab.parent
101
+ else:
102
+ break
103
+ out.reverse()
104
+ return ".".join(out)
105
+
106
+ def add_defn(self, node: ast.NameAtom) -> None:
105
107
  """Add defn."""
106
108
  self.defn.append(node)
107
- node.sym_link = self
109
+ node.sym = self
110
+
111
+ def add_use(self, node: ast.NameAtom) -> None:
112
+ """Add use."""
113
+ self.uses.append(node)
114
+ node.sym = self
108
115
 
109
116
  def __repr__(self) -> str:
110
117
  """Repr."""
111
- return (
112
- f"Symbol({self.sym_name}, {self.sym_type}, {self.access}, "
113
- f"{self.typ}, {self.defn})"
114
- )
118
+ return f"Symbol({self.sym_name}, {self.sym_type}, {self.access}, {self.defn})"
115
119
 
116
120
 
117
121
  class SymbolTable:
@@ -126,7 +130,7 @@ class SymbolTable:
126
130
  self.parent = parent if parent else self
127
131
  self.kid: list[SymbolTable] = []
128
132
  self.tab: dict[str, Symbol] = {}
129
- self.uses: list[ast.AstSymbolNode] = []
133
+ self.inherit: list[SymbolTable] = []
130
134
 
131
135
  def has_parent(self) -> bool:
132
136
  """Check if has parent."""
@@ -142,6 +146,10 @@ class SymbolTable:
142
146
  """Lookup a variable in the symbol table."""
143
147
  if name in self.tab:
144
148
  return self.tab[name]
149
+ for i in self.inherit:
150
+ found = i.lookup(name, deep=False)
151
+ if found:
152
+ return found
145
153
  if deep and self.has_parent():
146
154
  return self.get_parent().lookup(name, deep)
147
155
  return None
@@ -164,7 +172,7 @@ class SymbolTable:
164
172
  )
165
173
  if node.sym_name not in self.tab:
166
174
  self.tab[node.sym_name] = Symbol(
167
- defn=node,
175
+ defn=node.name_spec,
168
176
  access=(
169
177
  access_spec
170
178
  if isinstance(access_spec, SymbolAccess)
@@ -173,8 +181,8 @@ class SymbolTable:
173
181
  parent_tab=self,
174
182
  )
175
183
  else:
176
- self.tab[node.sym_name].add_defn(node)
177
- node.sym_link = self.tab[node.sym_name]
184
+ self.tab[node.sym_name].add_defn(node.name_spec)
185
+ node.name_spec.sym = self.tab[node.sym_name]
178
186
  return collision
179
187
 
180
188
  def find_scope(self, name: str) -> Optional[SymbolTable]:
@@ -102,9 +102,9 @@ class TestLarkParser(TestCaseMicroSuite):
102
102
  "JacSource",
103
103
  "EmptyToken",
104
104
  "AstSymbolNode",
105
+ "AstSymbolStubNode",
105
106
  "AstImplNeedingNode",
106
107
  "AstAccessNode",
107
- "TokenSymbol",
108
108
  "Literal",
109
109
  "AstDocNode",
110
110
  "AstSemStrNode",
@@ -119,7 +119,7 @@ class TestLarkParser(TestCaseMicroSuite):
119
119
  "ArchBlockStmt",
120
120
  "EnumBlockStmt",
121
121
  "CodeBlockStmt",
122
- "NameSpec",
122
+ "NameAtom",
123
123
  "ArchSpec",
124
124
  "MatchPattern",
125
125
  ]
jaclang/core/aott.py CHANGED
@@ -4,18 +4,30 @@ AOTT: Automated Operational Type Transformation.
4
4
  This has all the necessary functions to perform the AOTT operations.
5
5
  """
6
6
 
7
+ import base64
8
+ import logging
7
9
  import re
8
10
  from enum import Enum
11
+ from io import BytesIO
9
12
  from typing import Any
10
13
 
14
+
15
+ try:
16
+ from PIL import Image
17
+ except ImportError:
18
+ Image = None
19
+
20
+ from jaclang.compiler.semtable import SemInfo, SemRegistry, SemScope
11
21
  from jaclang.core.llms.base import BaseLLM
12
- from jaclang.core.registry import SemInfo, SemRegistry, SemScope
22
+
23
+
24
+ IMG_FORMATS = ["PngImageFile", "JpegImageFile"]
13
25
 
14
26
 
15
27
  def aott_raise(
16
28
  model: BaseLLM,
17
29
  information: str,
18
- inputs_information: str,
30
+ inputs_information: str | list[dict],
19
31
  output_information: str,
20
32
  type_explanations: str,
21
33
  action: str,
@@ -25,18 +37,43 @@ def aott_raise(
25
37
  model_params: dict,
26
38
  ) -> str:
27
39
  """AOTT Raise uses the information (Meanings types values) provided to generate a prompt(meaning in)."""
40
+ system_prompt = model.MTLLM_SYSTEM_PROMPT
41
+ meaning_in: str | list[dict]
28
42
  if method != "ReAct":
29
- system_prompt = model.MTLLM_SYSTEM_PROMPT
30
- mtllm_prompt = model.MTLLM_PROMPT.format(
31
- information=information,
32
- inputs_information=inputs_information,
33
- output_information=output_information,
34
- type_explanations=type_explanations,
35
- action=action,
36
- context=context,
37
- )
38
43
  method_prompt = model.MTLLM_METHOD_PROMPTS[method]
39
- meaning_in = f"{system_prompt}\n{mtllm_prompt}\n{method_prompt}"
44
+ if isinstance(inputs_information, str):
45
+ mtllm_prompt = model.MTLLM_PROMPT.format(
46
+ information=information,
47
+ inputs_information=inputs_information,
48
+ output_information=output_information,
49
+ type_explanations=type_explanations,
50
+ action=action,
51
+ context=context,
52
+ ).strip()
53
+ meaning_in = f"{system_prompt}\n{mtllm_prompt}\n{method_prompt}".strip()
54
+ else:
55
+ upper_half = model.MTLLM_PROMPT.split("{inputs_information}")[0]
56
+ lower_half = model.MTLLM_PROMPT.split("{inputs_information}")[1]
57
+ upper_half = upper_half.format(
58
+ information=information,
59
+ context=context,
60
+ )
61
+ lower_half = lower_half.format(
62
+ output_information=output_information,
63
+ type_explanations=type_explanations,
64
+ action=action,
65
+ )
66
+ meaning_in = (
67
+ [
68
+ {"type": "text", "text": system_prompt},
69
+ {"type": "text", "text": upper_half},
70
+ ]
71
+ + inputs_information
72
+ + [
73
+ {"type": "text", "text": lower_half},
74
+ {"type": "text", "text": method_prompt},
75
+ ]
76
+ )
40
77
  return model(meaning_in, **model_params)
41
78
  else:
42
79
  assert tools, "Tools must be provided for the ReAct method."
@@ -71,7 +108,7 @@ def get_info_types(
71
108
  else None
72
109
  )
73
110
  info_str.append(
74
- f"{sem_info.semstr} ({sem_info.name}) ({sem_info.type}) = {get_object_string(incl[1])}"
111
+ f"{sem_info.semstr} ({sem_info.name}) ({sem_info.type}) = {get_object_string(incl[1])}".strip()
75
112
  )
76
113
  return ("\n".join(info_str), collected_types)
77
114
 
@@ -142,7 +179,7 @@ def get_type_explanation(
142
179
  if sem_info.type == "Enum" and isinstance(type_info, list):
143
180
  for enum_item in type_info:
144
181
  type_info_str.append(
145
- f"{enum_item.semstr} ({enum_item.name}) (EnumItem)"
182
+ f"{enum_item.semstr} ({enum_item.name}) (EnumItem)".strip()
146
183
  )
147
184
  type_example[0] = type_example[0].replace("(", f".{enum_item.name}")
148
185
  elif sem_info.type in ["obj", "class", "node", "edge"] and isinstance(
@@ -152,7 +189,7 @@ def get_type_explanation(
152
189
  if arch_item.type in ["obj", "class", "node", "edge"]:
153
190
  continue
154
191
  type_info_str.append(
155
- f"{arch_item.semstr} ({arch_item.name}) ({arch_item.type})"
192
+ f"{arch_item.semstr} ({arch_item.name}) ({arch_item.type})".strip()
156
193
  )
157
194
  type_example.append(f"{arch_item.name}={arch_item.type}, ")
158
195
  if arch_item.type and extract_non_primary_type(arch_item.type):
@@ -162,7 +199,7 @@ def get_type_explanation(
162
199
  else:
163
200
  type_example.append(")")
164
201
  return (
165
- f"{sem_info.semstr} ({sem_info.name}) ({sem_info.type}) eg:- {''.join(type_example)} -> {', '.join(type_info_str)}", # noqa: E501
202
+ f"{sem_info.semstr} ({sem_info.name}) ({sem_info.type}) eg:- {''.join(type_example)} -> {', '.join(type_info_str)}".strip(), # noqa: E501
166
203
  set(type_info_types),
167
204
  )
168
205
  return None, None
@@ -212,3 +249,62 @@ class Tool:
212
249
  """Initialize the Tool class."""
213
250
  # TODO: Implement the Tool class
214
251
  pass
252
+
253
+
254
+ def get_input_information(
255
+ inputs: list[tuple[str, str, str, Any]], type_collector: list
256
+ ) -> str | list[dict]:
257
+ """
258
+ Get the input information for the AOTT operation.
259
+
260
+ Returns:
261
+ str | list[dict]: If the input does not contain images, returns a string with the input information.
262
+ If the input contains images, returns a list of dictionaries representing the input information,
263
+ where each dictionary contains either text or image_url.
264
+
265
+ """
266
+ contains_imgs = any(get_type_annotation(i[3]) in IMG_FORMATS for i in inputs)
267
+ if not contains_imgs:
268
+ inputs_information_list = []
269
+ for i in inputs:
270
+ typ_anno = get_type_annotation(i[3])
271
+ type_collector.extend(extract_non_primary_type(typ_anno))
272
+ inputs_information_list.append(
273
+ f"{i[0] if i[0] else ''} ({i[2]}) ({typ_anno}) = {get_object_string(i[3])}".strip()
274
+ )
275
+ return "\n".join(inputs_information_list)
276
+ else:
277
+ inputs_information_dict_list: list[dict] = []
278
+ for i in inputs:
279
+ if get_type_annotation(i[3]) in IMG_FORMATS:
280
+ img_base64 = image_to_base64(i[3])
281
+ image_repr: list[dict] = [
282
+ {
283
+ "type": "text",
284
+ "text": f"{i[0] if i[0] else ''} ({i[2]}) (Image) = ".strip(),
285
+ },
286
+ {"type": "image_url", "image_url": {"url": img_base64}},
287
+ ]
288
+ inputs_information_dict_list.extend(image_repr)
289
+ continue
290
+ typ_anno = get_type_annotation(i[3])
291
+ type_collector.extend(extract_non_primary_type(typ_anno))
292
+ inputs_information_dict_list.append(
293
+ {
294
+ "type": "text",
295
+ "text": f"{i[0] if i[0] else ''} ({i[2]}) ({typ_anno}) = {get_object_string(i[3])}".strip(),
296
+ }
297
+ )
298
+ return inputs_information_dict_list
299
+
300
+
301
+ def image_to_base64(image: Image) -> str:
302
+ """Convert an image to base64 expected by OpenAI."""
303
+ if not Image:
304
+ log = logging.getLogger(__name__)
305
+ log.error("Pillow is not installed. Please install Pillow to use images.")
306
+ return ""
307
+ img_format = image.format
308
+ with BytesIO() as buffer:
309
+ image.save(buffer, format=img_format, quality=100)
310
+ return f"data:image/{img_format.lower()};base64,{base64.b64encode(buffer.getvalue()).decode()}"
@@ -2,15 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import unittest
5
+ import types
6
6
  from dataclasses import dataclass, field
7
- from typing import Callable, Optional
7
+ from typing import Any, Callable, Optional
8
8
  from uuid import UUID, uuid4
9
9
 
10
10
  from jaclang.compiler.constant import EdgeDir
11
11
  from jaclang.core.utils import collect_node_connections
12
- from jaclang.plugin.feature import JacFeature as Jac
13
- from jaclang.plugin.spec import DSFunc
14
12
 
15
13
 
16
14
  @dataclass(eq=False)
@@ -57,6 +55,8 @@ class NodeAnchor(ObjectAnchor):
57
55
 
58
56
  def populate_edges(self) -> None:
59
57
  """Populate edges from edge ids."""
58
+ from jaclang.plugin.feature import JacFeature as Jac
59
+
60
60
  if len(self.edges) == 0 and len(self.edge_ids) > 0:
61
61
  for e_id in self.edge_ids:
62
62
  edge = Jac.context().get_obj(e_id)
@@ -229,7 +229,13 @@ class WalkerAnchor(ObjectAnchor):
229
229
 
230
230
  def visit_node(
231
231
  self,
232
- nds: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
232
+ nds: (
233
+ list[NodeArchitype | EdgeArchitype]
234
+ | list[NodeArchitype]
235
+ | list[EdgeArchitype]
236
+ | NodeArchitype
237
+ | EdgeArchitype
238
+ ),
233
239
  ) -> bool:
234
240
  """Walker visits node."""
235
241
  nd_list: list[NodeArchitype | EdgeArchitype]
@@ -251,7 +257,13 @@ class WalkerAnchor(ObjectAnchor):
251
257
 
252
258
  def ignore_node(
253
259
  self,
254
- nds: list[NodeArchitype | EdgeArchitype] | NodeArchitype | EdgeArchitype,
260
+ nds: (
261
+ list[NodeArchitype | EdgeArchitype]
262
+ | list[NodeArchitype]
263
+ | list[EdgeArchitype]
264
+ | NodeArchitype
265
+ | EdgeArchitype
266
+ ),
255
267
  ) -> bool:
256
268
  """Walker ignores node."""
257
269
  nd_list: list[NodeArchitype | EdgeArchitype]
@@ -354,11 +366,15 @@ class NodeArchitype(Architype):
354
366
 
355
367
  def __init__(self) -> None:
356
368
  """Create node architype."""
369
+ from jaclang.plugin.feature import JacFeature as Jac
370
+
357
371
  self._jac_: NodeAnchor = NodeAnchor(obj=self)
358
372
  Jac.context().save_obj(self, persistent=self._jac_.persistent)
359
373
 
360
374
  def save(self) -> None:
361
375
  """Save the node to the memory/storage hierarchy."""
376
+ from jaclang.plugin.feature import JacFeature as Jac
377
+
362
378
  self._jac_.persistent = True
363
379
  Jac.context().save_obj(self, persistent=True)
364
380
 
@@ -383,11 +399,15 @@ class EdgeArchitype(Architype):
383
399
 
384
400
  def __init__(self) -> None:
385
401
  """Create edge architype."""
402
+ from jaclang.plugin.feature import JacFeature as Jac
403
+
386
404
  self._jac_: EdgeAnchor = EdgeAnchor(obj=self)
387
405
  Jac.context().save_obj(self, persistent=self.persistent)
388
406
 
389
407
  def save(self) -> None:
390
408
  """Save the edge to the memory/storage hierarchy."""
409
+ from jaclang.plugin.feature import JacFeature as Jac
410
+
391
411
  self.persistent = True
392
412
  Jac.context().save_obj(self, persistent=True)
393
413
 
@@ -405,6 +425,8 @@ class EdgeArchitype(Architype):
405
425
 
406
426
  def populate_nodes(self) -> None:
407
427
  """Populate nodes for the edges from node ids."""
428
+ from jaclang.plugin.feature import JacFeature as Jac
429
+
408
430
  if self._jac_.source_id:
409
431
  obj = Jac.context().get_obj(self._jac_.source_id)
410
432
  if obj is None:
@@ -439,6 +461,13 @@ class WalkerArchitype(Architype):
439
461
  self._jac_: WalkerAnchor = WalkerAnchor(obj=self)
440
462
 
441
463
 
464
+ class GenericEdge(EdgeArchitype):
465
+ """Generic Root Node."""
466
+
467
+ _jac_entry_funcs_ = []
468
+ _jac_exit_funcs_ = []
469
+
470
+
442
471
  class Root(NodeArchitype):
443
472
  """Generic Root Node."""
444
473
 
@@ -460,92 +489,14 @@ class Root(NodeArchitype):
460
489
  self._jac_.edges = []
461
490
 
462
491
 
463
- class GenericEdge(EdgeArchitype):
464
- """Generic Root Node."""
465
-
466
- _jac_entry_funcs_ = []
467
- _jac_exit_funcs_ = []
468
-
469
-
470
- class JacTestResult(unittest.TextTestResult):
471
- """Jac test result class."""
472
-
473
- def __init__(
474
- self,
475
- stream, # noqa
476
- descriptions, # noqa
477
- verbosity: int,
478
- max_failures: Optional[int] = None,
479
- ) -> None:
480
- """Initialize FailFastTestResult object."""
481
- super().__init__(stream, descriptions, verbosity) # noqa
482
- self.failures_count = JacTestCheck.failcount
483
- self.max_failures = max_failures
484
-
485
- def addFailure(self, test, err) -> None: # noqa
486
- """Count failures and stop."""
487
- super().addFailure(test, err)
488
- self.failures_count += 1
489
- if self.max_failures is not None and self.failures_count >= self.max_failures:
490
- self.stop()
491
-
492
- def stop(self) -> None:
493
- """Stop the test execution."""
494
- self.shouldStop = True
495
-
496
-
497
- class JacTextTestRunner(unittest.TextTestRunner):
498
- """Jac test runner class."""
499
-
500
- def __init__(self, max_failures: Optional[int] = None, **kwargs) -> None: # noqa
501
- """Initialize JacTextTestRunner object."""
502
- self.max_failures = max_failures
503
- super().__init__(**kwargs)
504
-
505
- def _makeResult(self) -> JacTestResult: # noqa
506
- """Override the method to return an instance of JacTestResult."""
507
- return JacTestResult(
508
- self.stream,
509
- self.descriptions,
510
- self.verbosity,
511
- max_failures=self.max_failures,
512
- )
513
-
514
-
515
- class JacTestCheck:
516
- """Jac Testing and Checking."""
517
-
518
- test_case = unittest.TestCase()
519
- test_suite = unittest.TestSuite()
520
- breaker = False
521
- failcount = 0
522
-
523
- @staticmethod
524
- def reset() -> None:
525
- """Clear the test suite."""
526
- JacTestCheck.test_case = unittest.TestCase()
527
- JacTestCheck.test_suite = unittest.TestSuite()
528
-
529
- @staticmethod
530
- def run_test(xit: bool, maxfail: int | None, verbose: bool) -> None:
531
- """Run the test suite."""
532
- verb = 2 if verbose else 1
533
- runner = JacTextTestRunner(max_failures=maxfail, failfast=xit, verbosity=verb)
534
- result = runner.run(JacTestCheck.test_suite)
535
- if result.wasSuccessful():
536
- print("Passed successfully.")
537
- else:
538
- fails = len(result.failures)
539
- JacTestCheck.failcount += fails
540
- JacTestCheck.breaker = (
541
- (JacTestCheck.failcount >= maxfail) if maxfail else True
542
- )
492
+ @dataclass(eq=False)
493
+ class DSFunc:
494
+ """Data Spatial Function."""
543
495
 
544
- @staticmethod
545
- def add_test(test_fun: Callable) -> None:
546
- """Create a new test."""
547
- JacTestCheck.test_suite.addTest(unittest.FunctionTestCase(test_fun))
496
+ name: str
497
+ trigger: type | types.UnionType | tuple[type | types.UnionType, ...] | None
498
+ func: Callable[[Any, Any], Any] | None = None
548
499
 
549
- def __getattr__(self, name: str) -> object:
550
- """Make convenient check.Equal(...) etc."""
551
- return getattr(JacTestCheck.test_case, name)
500
+ def resolve(self, cls: type) -> None:
501
+ """Resolve the function."""
502
+ self.func = getattr(cls, self.name)
@@ -0,0 +1,44 @@
1
+ """Core constructs for Jac Language."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ from .architype import (
7
+ Architype,
8
+ DSFunc,
9
+ EdgeAnchor,
10
+ EdgeArchitype,
11
+ ElementAnchor,
12
+ GenericEdge,
13
+ NodeAnchor,
14
+ NodeArchitype,
15
+ ObjectAnchor,
16
+ Root,
17
+ WalkerAnchor,
18
+ WalkerArchitype,
19
+ )
20
+ from .context import ExecutionContext, exec_context
21
+ from .memory import Memory, ShelveStorage
22
+ from .test import JacTestCheck, JacTestResult, JacTextTestRunner
23
+
24
+ __all__ = [
25
+ "ElementAnchor",
26
+ "ObjectAnchor",
27
+ "NodeAnchor",
28
+ "EdgeAnchor",
29
+ "WalkerAnchor",
30
+ "Architype",
31
+ "NodeArchitype",
32
+ "EdgeArchitype",
33
+ "WalkerArchitype",
34
+ "GenericEdge",
35
+ "Root",
36
+ "DSFunc",
37
+ "Memory",
38
+ "ShelveStorage",
39
+ "ExecutionContext",
40
+ "exec_context",
41
+ "JacTestResult",
42
+ "JacTextTestRunner",
43
+ "JacTestCheck",
44
+ ]