jaclang 0.0.1__py3-none-any.whl → 0.0.3__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 (73) hide show
  1. jaclang/__init__.py +4 -0
  2. jaclang/cli/__init__.py +7 -0
  3. jaclang/cli/cli.jac +46 -0
  4. jaclang/cli/cmds.jac +14 -0
  5. jaclang/cli/impl/__init__.py +1 -0
  6. jaclang/cli/impl/cli_impl.jac +93 -0
  7. jaclang/cli/impl/cmds_impl.jac +26 -0
  8. jaclang/core/__init__.py +12 -0
  9. jaclang/core/impl/__init__.py +1 -0
  10. jaclang/core/impl/arch_impl.jac +112 -0
  11. jaclang/core/impl/element_impl.jac +95 -0
  12. jaclang/core/impl/exec_ctx_impl.jac +17 -0
  13. jaclang/core/impl/memory_impl.jac +57 -0
  14. jaclang/core/primitives.jac +104 -0
  15. jaclang/jac/__init__.py +1 -0
  16. jaclang/jac/absyntree.py +1787 -0
  17. jaclang/jac/constant.py +46 -0
  18. jaclang/jac/importer.py +130 -0
  19. jaclang/jac/lexer.py +538 -0
  20. jaclang/jac/parser.py +1474 -0
  21. jaclang/jac/passes/__init__.py +5 -0
  22. jaclang/jac/passes/blue/__init__.py +25 -0
  23. jaclang/jac/passes/blue/ast_build_pass.py +3190 -0
  24. jaclang/jac/passes/blue/blue_pygen_pass.py +1335 -0
  25. jaclang/jac/passes/blue/decl_def_match_pass.py +278 -0
  26. jaclang/jac/passes/blue/import_pass.py +75 -0
  27. jaclang/jac/passes/blue/sub_node_tab_pass.py +30 -0
  28. jaclang/jac/passes/blue/tests/__init__.py +1 -0
  29. jaclang/jac/passes/blue/tests/test_ast_build_pass.py +61 -0
  30. jaclang/jac/passes/blue/tests/test_blue_pygen_pass.py +117 -0
  31. jaclang/jac/passes/blue/tests/test_decl_def_match_pass.py +43 -0
  32. jaclang/jac/passes/blue/tests/test_import_pass.py +18 -0
  33. jaclang/jac/passes/blue/tests/test_sub_node_pass.py +26 -0
  34. jaclang/jac/passes/blue/tests/test_type_analyze_pass.py +53 -0
  35. jaclang/jac/passes/blue/type_analyze_pass.py +731 -0
  36. jaclang/jac/passes/ir_pass.py +154 -0
  37. jaclang/jac/passes/purple/__init__.py +17 -0
  38. jaclang/jac/passes/purple/impl/__init__.py +1 -0
  39. jaclang/jac/passes/purple/impl/purple_pygen_pass_impl.jac +289 -0
  40. jaclang/jac/passes/purple/purple_pygen_pass.jac +35 -0
  41. jaclang/jac/sym_table.py +127 -0
  42. jaclang/jac/tests/__init__.py +1 -0
  43. jaclang/jac/tests/fixtures/__init__.py +1 -0
  44. jaclang/jac/tests/fixtures/activity.py +10 -0
  45. jaclang/jac/tests/fixtures/fam.jac +68 -0
  46. jaclang/jac/tests/fixtures/hello_world.jac +5 -0
  47. jaclang/jac/tests/fixtures/lexer_fam.jac +61 -0
  48. jaclang/jac/tests/fixtures/stuff.jac +6 -0
  49. jaclang/jac/tests/test_importer.py +24 -0
  50. jaclang/jac/tests/test_lexer.py +57 -0
  51. jaclang/jac/tests/test_parser.py +50 -0
  52. jaclang/jac/tests/test_utils.py +12 -0
  53. jaclang/jac/transform.py +63 -0
  54. jaclang/jac/transpiler.py +69 -0
  55. jaclang/jac/utils.py +120 -0
  56. jaclang/utils/__init__.py +1 -0
  57. jaclang/utils/fstring_parser.py +73 -0
  58. jaclang/utils/log.py +9 -0
  59. jaclang/utils/sly/__init__.py +6 -0
  60. jaclang/utils/sly/docparse.py +62 -0
  61. jaclang/utils/sly/lex.py +510 -0
  62. jaclang/utils/sly/yacc.py +2398 -0
  63. jaclang/utils/test.py +81 -0
  64. jaclang/utils/tests/__init__.py +1 -0
  65. jaclang/utils/tests/test_fstring_parser.py +55 -0
  66. jaclang-0.0.3.dist-info/METADATA +12 -0
  67. jaclang-0.0.3.dist-info/RECORD +70 -0
  68. {jaclang-0.0.1.dist-info → jaclang-0.0.3.dist-info}/WHEEL +1 -1
  69. jaclang-0.0.3.dist-info/entry_points.txt +3 -0
  70. jaclang-0.0.3.dist-info/top_level.txt +1 -0
  71. jaclang-0.0.1.dist-info/METADATA +0 -7
  72. jaclang-0.0.1.dist-info/RECORD +0 -4
  73. jaclang-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,154 @@
1
+ """Abstract class for IR Passes for Jac."""
2
+ from typing import Optional, TypeVar
3
+
4
+ import jaclang.jac.absyntree as ast
5
+ from jaclang.jac.transform import Transform
6
+ from jaclang.jac.utils import pascal_to_snake
7
+
8
+ T = TypeVar("T", bound=ast.AstNode)
9
+
10
+
11
+ class Pass(Transform):
12
+ """Abstract class for IR passes."""
13
+
14
+ def __init__(
15
+ self,
16
+ mod_path: str,
17
+ input_ir: ast.AstNode,
18
+ base_path: str = "",
19
+ prior: Optional[Transform] = None,
20
+ ) -> None:
21
+ """Initialize parser."""
22
+ self.term_signal = False
23
+ self.prune_signal = False
24
+ self.cur_node = input_ir # tracks current node during traversal
25
+ Transform.__init__(self, mod_path, input_ir, base_path, prior)
26
+
27
+ def before_pass(self) -> None:
28
+ """Run once before pass."""
29
+ pass
30
+
31
+ def after_pass(self) -> None:
32
+ """Run once after pass."""
33
+ pass
34
+
35
+ def enter_node(self, node: ast.AstNode) -> None:
36
+ """Run on entering node."""
37
+ if hasattr(self, f"enter_{pascal_to_snake(type(node).__name__)}"):
38
+ getattr(self, f"enter_{pascal_to_snake(type(node).__name__)}")(node)
39
+ if isinstance(node, ast.Parse) and hasattr(self, f"enter_{node.name}"):
40
+ getattr(self, f"enter_{node.name}")(node)
41
+
42
+ def exit_node(self, node: ast.AstNode) -> None:
43
+ """Run on exiting node."""
44
+ if hasattr(self, f"exit_{pascal_to_snake(type(node).__name__)}"):
45
+ getattr(self, f"exit_{pascal_to_snake(type(node).__name__)}")(node)
46
+ if isinstance(node, ast.Parse) and hasattr(self, f"exit_{node.name}"):
47
+ getattr(self, f"exit_{node.name}")(node)
48
+
49
+ def terminate(self) -> None:
50
+ """Terminate traversal."""
51
+ self.term_signal = True
52
+
53
+ def prune(self) -> None:
54
+ """Prune traversal."""
55
+ self.prune_signal = True
56
+
57
+ def get_all_sub_nodes(
58
+ self, node: ast.AstNode, typ: type[T], brute_force: bool = False
59
+ ) -> list[T]:
60
+ """Get all sub nodes of type."""
61
+ result = []
62
+ # Assumes pass built the sub node table
63
+ if not node:
64
+ return result
65
+ elif len(node._sub_node_tab) and not brute_force:
66
+ result.extend(node._sub_node_tab[typ] if typ in node._sub_node_tab else [])
67
+ elif len(node.kid):
68
+ if not brute_force:
69
+ raise ValueError(f"Node has no sub_node_tab. {node}")
70
+ # Brute force search
71
+ else:
72
+ for i in node.kid:
73
+ if isinstance(i, typ):
74
+ result.append(i)
75
+ result.extend(self.get_all_sub_nodes(i, typ, brute_force))
76
+ return result
77
+
78
+ def recalculate_parents(self, node: ast.AstNode) -> None:
79
+ """Recalculate parents."""
80
+ if not node:
81
+ return
82
+ for i in node.kid:
83
+ if i:
84
+ i.parent = node
85
+ self.recalculate_parents(i)
86
+
87
+ # Transform Implementations
88
+ # -------------------------
89
+ def transform(self, ir: ast.AstNode) -> ast.AstNode:
90
+ """Run pass."""
91
+ self.before_pass()
92
+ if not isinstance(ir, ast.AstNode):
93
+ raise ValueError("Current node is not an AstNode.")
94
+ self.traverse(ir)
95
+ self.after_pass()
96
+ # Checks if self.ir is created during traversal
97
+ return self.ir if hasattr(self, "ir") else ir
98
+
99
+ def traverse(self, node: ast.AstNode) -> ast.AstNode:
100
+ """Traverse tree."""
101
+ if self.term_signal:
102
+ return node
103
+ self.cur_node = node
104
+ self.enter_node(node)
105
+ if not self.prune_signal:
106
+ for i in node.kid:
107
+ if i:
108
+ self.traverse(i)
109
+ else:
110
+ self.prune_signal = False
111
+ self.cur_node = node
112
+ self.exit_node(node)
113
+ return node
114
+
115
+ def update_code_loc(self) -> None:
116
+ """Update code location."""
117
+ if not isinstance(self.cur_node, ast.AstNode):
118
+ self.ice("Current node is not an AstNode.")
119
+ self.cur_line = self.cur_node.line
120
+ if self.cur_node.mod_link:
121
+ self.rel_mod_path = self.cur_node.mod_link.rel_mod_path
122
+
123
+ def error(self, msg: str) -> None:
124
+ """Pass Error."""
125
+ self.update_code_loc()
126
+ self.log_error(f"{msg}")
127
+
128
+ def warning(self, msg: str) -> None:
129
+ """Pass Error."""
130
+ self.update_code_loc()
131
+ self.log_warning(f"{msg}")
132
+
133
+ def ice(self, msg: str) -> None:
134
+ """Pass Error."""
135
+ if isinstance(self.cur_node, ast.AstNode):
136
+ self.cur_line = self.cur_node.line
137
+ self.log_error(f"ICE: Pass {self.__class__.__name__} - {msg}")
138
+ raise RuntimeError(
139
+ f"Internal Compiler Error: Pass {self.__class__.__name__} - {msg}"
140
+ )
141
+
142
+
143
+ class PrinterPass(Pass):
144
+ """Printer Pass for Jac AST."""
145
+
146
+ def enter_node(self, node: ast.AstNode) -> None:
147
+ """Run on entering node."""
148
+ print("Entering:", node)
149
+ super().enter_node(node)
150
+
151
+ def exit_node(self, node: ast.AstNode) -> None:
152
+ """Run on exiting node."""
153
+ super().exit_node(node)
154
+ print("Exiting:", node)
@@ -0,0 +1,17 @@
1
+ """Collection of purple passes for Jac IR."""
2
+ from jaclang import jac_blue_import
3
+ from jaclang.jac.passes.blue import pass_schedule
4
+
5
+
6
+ purple = jac_blue_import("purple_pygen_pass")
7
+ PurplePygenPass = purple.PurplePygenPass # type: ignore
8
+
9
+ __all__ = [
10
+ "PurplePygenPass",
11
+ ]
12
+
13
+
14
+ pass_schedule = [
15
+ *pass_schedule[:-1],
16
+ PurplePygenPass,
17
+ ]
@@ -0,0 +1 @@
1
+ """For setuptools to find this dir."""
@@ -0,0 +1,289 @@
1
+ """First Jac pass bootstrapped in Jac"""
2
+ import:py from jaclang.jac.constant, Constants as C, EdgeDir;
3
+
4
+ :object:PurplePygenPass:ability:add_element_import
5
+ (arch: str) {
6
+ (<self>.preamble, f"from jaclang.core import {arch} as _jac_{arch}_")
7
+ |> <self>.emit_ln_unique;
8
+ }
9
+
10
+ :object:PurplePygenPass:ability:add_exec_context {
11
+ (<self>.preamble, f"from jaclang.core import exec_ctx as {C.EXEC_CONTEXT}")
12
+ |> <self>.emit_ln_unique;
13
+ }
14
+
15
+ :object:PurplePygenPass:ability:add_edge_directions {
16
+ (<self>.preamble, f"from jaclang.jac.constant import EdgeDir as {C.EDGE_DIR}")
17
+ |> <self>.emit_ln_unique;
18
+ }
19
+
20
+ :object:PurplePygenPass:ability:needs_jac_import {
21
+ (<self>.preamble, "from jaclang import jac_purple_import as __jac_import__")
22
+ |> <self>.emit_ln_unique;
23
+ }
24
+
25
+ """
26
+ Sub objects.
27
+
28
+ name: Name,
29
+ arch_type: Token,
30
+ doc: Optional[DocString],
31
+ decorators: Optional["Decorators"],
32
+ access: Optional[Token],
33
+ base_classes: "BaseClasses",
34
+ body: Optional["ArchBlock"],
35
+ """
36
+ :object:PurplePygenPass:ability:exit_architype
37
+ (nd: ast.Architype) {
38
+ |> <self>.add_exec_context;
39
+ if nd.decorators {
40
+ (nd, nd.decorators.meta["py_code"]) |> <self>.emit_ln;
41
+ }
42
+ arch_type=nd.arch_type.name;
43
+ arch_insert = "";
44
+ if arch_type == Tok.KW_OBJECT {
45
+ "Object" |> <self>.add_element_import;
46
+ arch_insert = C.OBJECT_CLASS;
47
+ }
48
+ elif arch_type == Tok.KW_NODE {
49
+ "Node" |> <self>.add_element_import;
50
+ arch_insert = C.NODE_CLASS;
51
+ }
52
+ elif arch_type == Tok.KW_EDGE {
53
+ "Edge" |> <self>.add_element_import;
54
+ arch_insert = C.EDGE_CLASS;
55
+ }
56
+ elif arch_type == Tok.KW_WALKER {
57
+ "Walker" |> <self>.add_element_import;
58
+ arch_insert = C.WALKER_CLASS;
59
+ }
60
+ if nd.base_classes.base_classes |> len {
61
+ (nd, f"class {nd.name.meta['py_code']}"
62
+ f"({nd.base_classes.meta['py_code']}, {arch_insert}):")
63
+ |> <self>.emit_ln;
64
+ } else {
65
+ (nd, f"class {nd.name.meta['py_code']}({arch_insert}):") |> <self>.emit_ln;
66
+ }
67
+ <self>.indent_level += 1;
68
+ if nd.doc {
69
+ (nd, nd.doc.meta["py_code"]) |> <self>.emit_ln; }
70
+ if nd.body {
71
+ (nd, nd.body.meta["py_code"]) |> <self>.emit_ln; }
72
+ else {
73
+ nd.name.meta["py_code"] |> <self>.decl_def_missing; }
74
+ <self>.indent_level -= 1;
75
+ }
76
+
77
+
78
+ """Sub objects.
79
+
80
+ name: Name,
81
+ is_func: bool,
82
+ is_async: bool,
83
+ doc: Optional[DocString],
84
+ decorators: Optional["Decorators"],
85
+ access: Optional[Token],
86
+ signature: Optional["FuncSignature | TypeSpec | EventSignature"],
87
+ body: Optional["CodeBlock"],
88
+ arch_attached: Optional["ArchBlock"] = None,
89
+ """
90
+ :object:PurplePygenPass:ability:exit_ability
91
+ (nd: ast.Ability) {
92
+ "Object" |> <self>.add_element_import;
93
+ if nd.decorators {
94
+ (nd, nd.decorators.meta["py_code"]) |> <self>.emit_ln;
95
+ }
96
+ if nd.signature:>type == ast.EventSignature and nd.arch_attached and
97
+ nd.signature.event.name == Tok.KW_ENTRY {
98
+ type_list = nd.signature.arch_tag_info?.meta?["py_code"] ?: "";
99
+ (nd, f"@{C.OBJECT_CLASS}._jac_on_entry([{type_list.replace('|', ', ')}])") |> <self>.emit_ln;
100
+ }
101
+ elif nd.signature:>type == ast.EventSignature and nd.arch_attached and
102
+ nd.signature.event.name == Tok.KW_EXIT {
103
+ type_list = nd.signature.arch_tag_info?.meta?["py_code"] ?: "";
104
+ (nd, f"@{C.OBJECT_CLASS}._jac_on_exit([{type_list.replace('|', ', ')}])") |> <self>.emit_ln;
105
+ }
106
+ if nd.signature:>type in [ast.FuncSignature, ast.EventSignature] {
107
+ if nd.arch_attached and not nd.is_static {
108
+ (nd, f"def {nd.name.meta['py_code']}(self{nd.signature.meta['py_code']}:")
109
+ |> <self>.emit_ln;
110
+ } else {
111
+ if nd.arch_attached and nd.is_static {
112
+ (nd, f"@classmethod") |> <self>.emit_ln;
113
+ }
114
+ (nd, f"def {nd.name.meta['py_code']}({nd.signature.meta['py_code']}:")
115
+ |> <self>.emit_ln;
116
+ }
117
+ } else {
118
+ if nd.arch_attached {
119
+ (nd, f"def {nd.name.meta['py_code']}(self):") |> <self>.emit_ln;
120
+ } else {
121
+ (nd, f"def {nd.name.meta['py_code']}():") |> <self>.emit_ln;
122
+ }
123
+ }
124
+ <self>.indent_level += 1;
125
+ if nd.doc {
126
+ (nd, nd.doc.meta["py_code"]) |> <self>.emit_ln;
127
+ }
128
+ if nd.body {
129
+ (nd, "try:") |> <self>.emit_ln;
130
+ <self>.indent_level += 1;
131
+ (nd, nd.body.meta["py_code"]) |> <self>.emit;
132
+ <self>.indent_level -= 1;
133
+ (nd) |> <self>.emit_jac_error_handler;
134
+ } else {
135
+ nd.name.meta["py_code"] |> <self>.decl_def_missing;
136
+ }
137
+ <self>.indent_level -= 1;
138
+ }
139
+
140
+
141
+ """Sub objects.
142
+
143
+ event: Token,
144
+ arch_tag_info: Optional["TypeList | TypeSpec"],
145
+ return_type: Optional["TypeSpec"],
146
+ """
147
+ :object:PurplePygenPass:ability:exit_event_signature
148
+ (nd: ast.EventSignature) {
149
+ if (nd.parent|>type) == ast.Ability and nd.parent.arch_attached {
150
+ (nd, ", ") |> <self>.emit;
151
+ }
152
+ if nd.arch_tag_info {
153
+ (nd, f"{C.HERE}: {nd.arch_tag_info.meta['py_code']})")
154
+ |> <self>.emit;
155
+ } else {
156
+ (nd, f"{C.HERE})") |> <self>.emit;
157
+ }
158
+ if nd.return_type {
159
+ (nd, f" -> {nd.return_type.meta['py_code']}") |> <self>.emit;
160
+ }
161
+ }
162
+
163
+
164
+ """Sub objects.
165
+
166
+ ctrl: Token,
167
+ """
168
+ :object:PurplePygenPass:ability:exit_ctrl_stmt
169
+ (nd: ast.CtrlStmt) {
170
+ if nd.ctrl.name == Tok.KW_SKIP {
171
+ (nd, "return") |> <self>.emit_ln;
172
+ } else {
173
+ <super>.exit_ctrl_stmt(nd);
174
+ }
175
+ }
176
+
177
+
178
+ """Sub objects.
179
+
180
+ vis_type: Optional[Token],
181
+ target: ExprType,
182
+ else_body: Optional["ElseStmt"],
183
+ from_walker: bool = False,
184
+ """
185
+ :object:PurplePygenPass:ability:exit_visit_stmt
186
+ (nd: ast.VisitStmt) {
187
+ vis_type = nd.vis_type?.value ?: "";
188
+ loc = f"{'self' if nd.from_walker else C.HERE}";
189
+ (nd, f"if not {loc}.{C.WALKER_VISIT}({nd.target.meta['py_code']}): " )
190
+ |> <self>.emit_ln;
191
+ <self>.indent_level += 1;
192
+ if nd.else_body {
193
+ (nd, nd.else_body.body.meta["py_code"]) |> <self>.emit;
194
+ } else {
195
+ (nd, 'pass') |> <self>.emit_ln;
196
+ }
197
+ <self>.indent_level -= 1;
198
+ }
199
+
200
+
201
+ """Sub objects.
202
+ from_walker: bool = False,
203
+ """
204
+ :object:PurplePygenPass:ability:exit_disengage_stmt
205
+ (nd: ast.VisitStmt) {
206
+ loc = f"{'self' if nd.from_walker else C.HERE}";
207
+ (nd, f"return {loc}.{C.DISENGAGE}()") |> <self>.emit_ln;
208
+ }
209
+
210
+
211
+ """Sub objects.
212
+
213
+ left: ExprType,
214
+ right: ExprType,
215
+ op: Token,
216
+ """
217
+ :object:PurplePygenPass:ability:exit_binary_expr
218
+ (nd: ast.BinaryExpr) {
219
+ if (nd.op|>type) == ast.ConnectOp {
220
+ (nd, f"{nd.left.meta['py_code']}.{C.CONNECT_NODE}({nd.right.meta['py_code']}, {nd.op.meta['py_code']})")
221
+ |> <self>.emit;
222
+ } else {
223
+ <super>.exit_binary_expr(nd);
224
+ }
225
+ }
226
+
227
+
228
+ """Sub objects.
229
+
230
+ filter_type: Optional[ExprType],
231
+ filter_cond: Optional[FilterCompr],
232
+ edge_dir: EdgeDir,
233
+ """
234
+ :object:PurplePygenPass:ability:exit_edge_op_ref
235
+ (nd: ast.EdgeOpRef) {
236
+ edge_dir = f"{C.EDGE_DIR}.IN" if nd.edge_dir == EdgeDir.IN
237
+ else f"{C.EDGE_DIR}.OUT" if nd.edge_dir == EdgeDir.OUT
238
+ else f"{C.EDGE_DIR}.ANY";
239
+ if nd.filter_type and nd.filter_cond {
240
+ (nd, f"[{C.JAC_TMP} for {C.JAC_TMP} in {C.HERE}.{C.EDGES_TO_NODE}({edge_dir})"
241
+ f" if isinstance({C.JAC_TMP}, {nd.filter_type.meta['py_code']})"
242
+ f" and {nd.filter_cond.meta['py_code'].replace(C.PATCH, C.JAC_TMP)}]")
243
+ |> <self>.emit;
244
+ }
245
+ elif nd.filter_type {
246
+ (nd, f"[{C.JAC_TMP} for {C.JAC_TMP} in {C.HERE}.{C.EDGES_TO_NODE}({edge_dir})"
247
+ f" if isinstance({C.JAC_TMP}, {nd.filter_type.meta['py_code']})]")
248
+ |> <self>.emit;
249
+ }
250
+ else {
251
+ (nd, f"{C.HERE}.{C.EDGES_TO_NODE}({edge_dir})")
252
+ |> <self>.emit;
253
+ }
254
+ }
255
+
256
+
257
+ """Sub objects.
258
+
259
+ conn_type: Optional[ExprType],
260
+ conn_assign: Optional[AssignmentList],
261
+ edge_dir: EdgeDir,
262
+ """
263
+ :object:PurplePygenPass:ability:exit_connect_op
264
+ (nd: ast.ConnectOp) {
265
+ "Edge" |> <self>.add_element_import;
266
+ |> <self>.add_edge_directions;
267
+
268
+ if nd.conn_type {
269
+ (nd, f"{nd.conn_type.meta['py_code']}.{C.WITH_DIR}({C.EDGE_DIR}.{nd.edge_dir.name}") |> <self>.emit;
270
+ } else {
271
+ (nd, f"{C.EDGE_CLASS}().{C.WITH_DIR}({C.EDGE_DIR}.{nd.edge_dir.name}") |> <self>.emit;
272
+ }
273
+ if nd.conn_assign {
274
+ (nd, f", {nd.conn_assign.meta['py_code']})") |> <self>.emit;
275
+ } else {
276
+ (nd, ")") |> <self>.emit;
277
+ }
278
+ }
279
+
280
+ """Sub objects.
281
+
282
+ conn_type: Optional[ExprType],
283
+ conn_assign: Optional[AssignmentList],
284
+ edge_dir: EdgeDir,
285
+ """
286
+ :object:PurplePygenPass:ability:exit_disconnect_op
287
+ (nd: ast.DisconnectOp) {
288
+
289
+ }
@@ -0,0 +1,35 @@
1
+ """First Jac pass bootstrapped in Jac"""
2
+ import:py jaclang.jac.absyntree as ast;
3
+ import:py from jaclang.jac.passes.blue, BluePygenPass;
4
+ import:py from jaclang.core, Object, Node, Edge, Walker;
5
+ import:py from jaclang.jac.lexer, Tokens as Tok;
6
+
7
+
8
+ include:jac impl.purple_pygen_pass_impl;
9
+
10
+ """
11
+ This pass leverages data spacial lib to provide code
12
+ gen for full Jac language. It is bootstrapped in Jac blue.
13
+ """
14
+ object PurplePygenPass:BluePygenPass {
15
+
16
+ #*
17
+ These add functions generate necessary imports
18
+ given code being generated.
19
+ *#
20
+ can add_element_import(arch: str);
21
+ can add_exec_context;
22
+ can add_edge_directions;
23
+ can needs_jac_import;
24
+
25
+ can exit_architype(nd: ast.Architype);
26
+ can exit_ability(nd: ast.Ability);
27
+ can exit_event_signature(nd: ast.EventSignature);
28
+ can exit_ctrl_stmt(nd: ast.CtrlStmt);
29
+ can exit_visit_stmt(nd: ast.VisitStmt);
30
+ can exit_disengage_stmt(nd: ast.DisengageStmt);
31
+ can exit_binary_expr(nd: ast.BinaryExpr);
32
+ can exit_edge_op_ref(nd: ast.EdgeOpRef);
33
+ can exit_connect_op(nd: ast.ConnectOp);
34
+ can exit_disconnect_op(nd: ast.DisconnectOp);
35
+ }
@@ -0,0 +1,127 @@
1
+ """Jac Symbol Table."""
2
+ import pprint
3
+ from typing import Optional, TypeVar
4
+
5
+
6
+ import jaclang.jac.absyntree as ast
7
+
8
+
9
+ class Symbol:
10
+ """Symbol."""
11
+
12
+ def __init__(
13
+ self,
14
+ name: str,
15
+ node: Optional[ast.AstNode] = None,
16
+ ) -> None:
17
+ """Initialize."""
18
+ self.name = name
19
+ self.node = node
20
+
21
+ def pretty_print(self) -> str:
22
+ """Pretty print the symbol."""
23
+ return pprint.pformat(vars(self), indent=2)
24
+
25
+ def __repr__(self) -> str:
26
+ """Representation for printing Symbols."""
27
+ return f"{self.pretty_print()}"
28
+
29
+
30
+ T = TypeVar("T", bound=Symbol)
31
+
32
+
33
+ class TypedSymbol(Symbol):
34
+ """Typed Symbol."""
35
+
36
+ def __init__(
37
+ self,
38
+ name: str,
39
+ typ: type,
40
+ node: ast.AstNode,
41
+ access: Optional[str] = None,
42
+ ) -> None:
43
+ """Initialize."""
44
+ self.typ = typ
45
+ self.access = access
46
+ super().__init__(name, node)
47
+
48
+
49
+ class DefDeclSymbol(Symbol):
50
+ """DefDecl Symbol."""
51
+
52
+ def __init__(
53
+ self,
54
+ name: str,
55
+ node: Optional[ast.AstNode] = None,
56
+ other_node: Optional[ast.AstNode] = None,
57
+ has_def: bool = False,
58
+ has_decl: bool = False,
59
+ ) -> None:
60
+ """Initialize."""
61
+ self.other_node = other_node
62
+ self.has_def = has_def
63
+ self.has_decl = has_decl
64
+ super().__init__(name, node)
65
+
66
+
67
+ class SymbolTable:
68
+ """Symbol Table.
69
+
70
+ Functions as a stack by default, can create a separate table in pass
71
+ if keeping track of multiple scopes is necessary.
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ scope_name: str = "",
77
+ parent: Optional["SymbolTable"] = None,
78
+ ) -> None:
79
+ """Initialize."""
80
+ self.scope_name = scope_name
81
+ self.parent = parent
82
+ self.tab: dict = {}
83
+
84
+ def lookup(self, name: str, deep: bool = True) -> Optional[T]:
85
+ """Lookup a variable in the symbol table."""
86
+ if name in self.tab:
87
+ return self.tab[name]
88
+ if deep and self.parent:
89
+ return self.parent.lookup(name, deep)
90
+ return None
91
+
92
+ def set(self, sym: T, fresh_only: bool = False) -> bool:
93
+ """Set a variable in the symbol table."""
94
+ if fresh_only and sym.name in self.tab:
95
+ return False
96
+ self.tab[sym.name] = sym
97
+ return True
98
+
99
+ def update(self, sym: T, deep: bool = False) -> bool:
100
+ """Set a variable in the symbol table."""
101
+ if sym.name in self.tab:
102
+ self.tab[sym.name] = T
103
+ return True
104
+ elif deep and self.parent:
105
+ return self.parent.update(sym)
106
+ return False
107
+
108
+ def push(self, scope_name: str = "") -> "SymbolTable":
109
+ """Push a new scope onto the symbol table."""
110
+ return SymbolTable(scope_name, self)
111
+
112
+ def pop(self) -> "SymbolTable":
113
+ """Pop the current scope off the symbol table."""
114
+ return self.parent if self.parent else self
115
+
116
+ def pretty_print(self) -> str:
117
+ """Pretty print the symbol table and return the result as a string."""
118
+ output = f"Scope: {self.scope_name}\n"
119
+ output += pprint.pformat(self.tab)
120
+ if self.parent:
121
+ output += f"\nParent Scope: {self.parent.scope_name}\n"
122
+ output += self.parent.pretty_print()
123
+ return output
124
+
125
+ def __repr__(self) -> str:
126
+ """Return the symbol table as a string."""
127
+ return self.pretty_print()
@@ -0,0 +1 @@
1
+ """Tests for Jac tools."""
@@ -0,0 +1 @@
1
+ """Various fixtures for testing"""
@@ -0,0 +1,10 @@
1
+ """Activity class for testing."""
2
+
3
+
4
+ class Activity:
5
+ """Activity class for testing."""
6
+
7
+ def __init__(self, name: str = "basic", duration: int = 4) -> None:
8
+ """Activity class for testing."""
9
+ self.name = name
10
+ self.duration = duration