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.
- jaclang/__init__.py +4 -0
- jaclang/cli/__init__.py +7 -0
- jaclang/cli/cli.jac +46 -0
- jaclang/cli/cmds.jac +14 -0
- jaclang/cli/impl/__init__.py +1 -0
- jaclang/cli/impl/cli_impl.jac +93 -0
- jaclang/cli/impl/cmds_impl.jac +26 -0
- jaclang/core/__init__.py +12 -0
- jaclang/core/impl/__init__.py +1 -0
- jaclang/core/impl/arch_impl.jac +112 -0
- jaclang/core/impl/element_impl.jac +95 -0
- jaclang/core/impl/exec_ctx_impl.jac +17 -0
- jaclang/core/impl/memory_impl.jac +57 -0
- jaclang/core/primitives.jac +104 -0
- jaclang/jac/__init__.py +1 -0
- jaclang/jac/absyntree.py +1787 -0
- jaclang/jac/constant.py +46 -0
- jaclang/jac/importer.py +130 -0
- jaclang/jac/lexer.py +538 -0
- jaclang/jac/parser.py +1474 -0
- jaclang/jac/passes/__init__.py +5 -0
- jaclang/jac/passes/blue/__init__.py +25 -0
- jaclang/jac/passes/blue/ast_build_pass.py +3190 -0
- jaclang/jac/passes/blue/blue_pygen_pass.py +1335 -0
- jaclang/jac/passes/blue/decl_def_match_pass.py +278 -0
- jaclang/jac/passes/blue/import_pass.py +75 -0
- jaclang/jac/passes/blue/sub_node_tab_pass.py +30 -0
- jaclang/jac/passes/blue/tests/__init__.py +1 -0
- jaclang/jac/passes/blue/tests/test_ast_build_pass.py +61 -0
- jaclang/jac/passes/blue/tests/test_blue_pygen_pass.py +117 -0
- jaclang/jac/passes/blue/tests/test_decl_def_match_pass.py +43 -0
- jaclang/jac/passes/blue/tests/test_import_pass.py +18 -0
- jaclang/jac/passes/blue/tests/test_sub_node_pass.py +26 -0
- jaclang/jac/passes/blue/tests/test_type_analyze_pass.py +53 -0
- jaclang/jac/passes/blue/type_analyze_pass.py +731 -0
- jaclang/jac/passes/ir_pass.py +154 -0
- jaclang/jac/passes/purple/__init__.py +17 -0
- jaclang/jac/passes/purple/impl/__init__.py +1 -0
- jaclang/jac/passes/purple/impl/purple_pygen_pass_impl.jac +289 -0
- jaclang/jac/passes/purple/purple_pygen_pass.jac +35 -0
- jaclang/jac/sym_table.py +127 -0
- jaclang/jac/tests/__init__.py +1 -0
- jaclang/jac/tests/fixtures/__init__.py +1 -0
- jaclang/jac/tests/fixtures/activity.py +10 -0
- jaclang/jac/tests/fixtures/fam.jac +68 -0
- jaclang/jac/tests/fixtures/hello_world.jac +5 -0
- jaclang/jac/tests/fixtures/lexer_fam.jac +61 -0
- jaclang/jac/tests/fixtures/stuff.jac +6 -0
- jaclang/jac/tests/test_importer.py +24 -0
- jaclang/jac/tests/test_lexer.py +57 -0
- jaclang/jac/tests/test_parser.py +50 -0
- jaclang/jac/tests/test_utils.py +12 -0
- jaclang/jac/transform.py +63 -0
- jaclang/jac/transpiler.py +69 -0
- jaclang/jac/utils.py +120 -0
- jaclang/utils/__init__.py +1 -0
- jaclang/utils/fstring_parser.py +73 -0
- jaclang/utils/log.py +9 -0
- jaclang/utils/sly/__init__.py +6 -0
- jaclang/utils/sly/docparse.py +62 -0
- jaclang/utils/sly/lex.py +510 -0
- jaclang/utils/sly/yacc.py +2398 -0
- jaclang/utils/test.py +81 -0
- jaclang/utils/tests/__init__.py +1 -0
- jaclang/utils/tests/test_fstring_parser.py +55 -0
- jaclang-0.0.3.dist-info/METADATA +12 -0
- jaclang-0.0.3.dist-info/RECORD +70 -0
- {jaclang-0.0.1.dist-info → jaclang-0.0.3.dist-info}/WHEEL +1 -1
- jaclang-0.0.3.dist-info/entry_points.txt +3 -0
- jaclang-0.0.3.dist-info/top_level.txt +1 -0
- jaclang-0.0.1.dist-info/METADATA +0 -7
- jaclang-0.0.1.dist-info/RECORD +0 -4
- jaclang-0.0.1.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# This is a comment
|
|
2
|
+
#*
|
|
3
|
+
This is multi line comment
|
|
4
|
+
Many styles!
|
|
5
|
+
*#
|
|
6
|
+
"""
|
|
7
|
+
These are doc strings and can be used as comments
|
|
8
|
+
Use them to document your code
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import:jac .stuff;
|
|
12
|
+
import:py from .activity, Activity as Actvy;
|
|
13
|
+
|
|
14
|
+
node location {
|
|
15
|
+
has x: int, y: int, name: str = True;
|
|
16
|
+
has activities: list[Activity];
|
|
17
|
+
has intro: str = "Welcome";
|
|
18
|
+
has visited: int = 0;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
can record with tourist entry {
|
|
22
|
+
visited+=1;
|
|
23
|
+
for i in activities {
|
|
24
|
+
i.duration = visitor.duration;
|
|
25
|
+
if <h>.name not in visitor.passport {
|
|
26
|
+
if i.name == "Hiking" {
|
|
27
|
+
for j=0 to j<3 by j+=1 {
|
|
28
|
+
i.duration += 1;
|
|
29
|
+
}
|
|
30
|
+
i.duration += 1;
|
|
31
|
+
}
|
|
32
|
+
visitor.passport.append(<h>.name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
walker tourist {
|
|
39
|
+
has duration: float = .435e-2;
|
|
40
|
+
has budget: int;
|
|
41
|
+
has passport: list[str];
|
|
42
|
+
|
|
43
|
+
can make_visit with location|other exit {
|
|
44
|
+
b=:g:stuff:w:tourist;
|
|
45
|
+
a = spawn :g:stuff:w:tourist;
|
|
46
|
+
a = spawn :w:tourist;
|
|
47
|
+
a = <-[:g:edget:e:myedge:a==1,b==2]-;
|
|
48
|
+
a = b +[:g:edget:e:myedge:a=1]+> c;
|
|
49
|
+
a = b <+[:g:edget:e:myedge:a=1]+ c;
|
|
50
|
+
a = <here> +[:g:edget:e:myedge:a=1]+> spawn :g:stuff:n:tourist;
|
|
51
|
+
:g:stuff:w:tourist |> <here>;
|
|
52
|
+
report <h>.activities;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
can do_something(app: int =Actvy) -> Actvy {
|
|
56
|
+
app.do_something();
|
|
57
|
+
}
|
|
58
|
+
can do_something(app: int = Actvy, other: float = Actvy) -> Actvy {
|
|
59
|
+
return app?.do_something(other);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
can outside_func() -> None;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
:walker:tourist:ability:outside_func {
|
|
67
|
+
|
|
68
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# This is a comment
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
These are doc strings and can be used as comments
|
|
5
|
+
Use them to document your code
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import:jac .stuff;
|
|
9
|
+
import:py from .activity, Activity as Actvy;
|
|
10
|
+
|
|
11
|
+
node location {
|
|
12
|
+
has x: int, y: int, name: str;
|
|
13
|
+
has anchor activities: list[Activity];
|
|
14
|
+
has hidden intro: str = "Welcome";
|
|
15
|
+
has visited: int = 0;
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
can record with tourist entry {
|
|
19
|
+
visited+=1;
|
|
20
|
+
for i in activities {
|
|
21
|
+
i.duration = visitor.duration;
|
|
22
|
+
if <h>.name not in visitor.passport {
|
|
23
|
+
if i.name == "Hiking" {
|
|
24
|
+
for j=0 to j<3 by j+=1 {
|
|
25
|
+
i.duration += 1;
|
|
26
|
+
}
|
|
27
|
+
i.duration += 1;
|
|
28
|
+
}
|
|
29
|
+
visitor.passport.append(<h>.name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
walker tourist {
|
|
36
|
+
has duration: int;
|
|
37
|
+
has budget: int;
|
|
38
|
+
has passport: list[str];
|
|
39
|
+
|
|
40
|
+
can make_visit with location, other exit {
|
|
41
|
+
b=:g:stuff:w:tourist;
|
|
42
|
+
a = spawn :g:stuff:w:tourist;
|
|
43
|
+
a = spawn (:w:tourist)::;
|
|
44
|
+
:g:stuff:w:tourist::;
|
|
45
|
+
report <h>.activities;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
can do_something(app: int =Actvy) -> Actvy {
|
|
49
|
+
app.do_something();
|
|
50
|
+
}
|
|
51
|
+
can do_something(app: int = Actvy, other: float = Actvy) -> Actvy {
|
|
52
|
+
return app?.do_something(other);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
can outside_func() -> None;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
:walker:tourist:ability:outside_func {
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Tests for Jac Loader."""
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from jaclang import jac_blue_import
|
|
5
|
+
from jaclang.utils.test import TestCase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestLoader(TestCase):
|
|
9
|
+
"""Test Jac self.prse."""
|
|
10
|
+
|
|
11
|
+
def setUp(self) -> None:
|
|
12
|
+
"""Set up test."""
|
|
13
|
+
return super().setUp()
|
|
14
|
+
|
|
15
|
+
def test_import_basic_python(self) -> None:
|
|
16
|
+
"""Test basic self loading."""
|
|
17
|
+
h = jac_blue_import("fixtures.hello_world")
|
|
18
|
+
self.assertEqual(h.hello(), "Hello World!") # type: ignore
|
|
19
|
+
|
|
20
|
+
def test_modules_correct(self) -> None:
|
|
21
|
+
"""Test basic self loading."""
|
|
22
|
+
jac_blue_import("fixtures.hello_world")
|
|
23
|
+
self.assertIn("module 'hello_world'", str(sys.modules))
|
|
24
|
+
self.assertIn("/tests/fixtures/hello_world.jac", str(sys.modules))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Tests for Jac lexer."""
|
|
2
|
+
from typing import Generator
|
|
3
|
+
|
|
4
|
+
from jaclang.jac.lexer import JacLexer, Tokens
|
|
5
|
+
from jaclang.utils.test import TestCase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestLexer(TestCase):
|
|
9
|
+
"""Test Jac lexer."""
|
|
10
|
+
|
|
11
|
+
def test_lexer(self) -> None:
|
|
12
|
+
"""Basic test for lexer."""
|
|
13
|
+
tokens = []
|
|
14
|
+
ir = JacLexer(mod_path="", input_ir=self.load_fixture("lexer_fam.jac")).ir
|
|
15
|
+
if not isinstance(ir, Generator):
|
|
16
|
+
raise ValueError("Lexer did not return generator.")
|
|
17
|
+
for t in ir:
|
|
18
|
+
tokens.append(t)
|
|
19
|
+
self.assertEqual(tokens[0].type, "DOC_STRING")
|
|
20
|
+
self.assertEqual(
|
|
21
|
+
[
|
|
22
|
+
tokens[10].type,
|
|
23
|
+
tokens[10].value,
|
|
24
|
+
tokens[10].lineno,
|
|
25
|
+
tokens[10].index,
|
|
26
|
+
tokens[10].end,
|
|
27
|
+
],
|
|
28
|
+
["KW_FROM", "from", 9, 140, 144],
|
|
29
|
+
)
|
|
30
|
+
self.assertEqual(
|
|
31
|
+
[
|
|
32
|
+
tokens[-1].type,
|
|
33
|
+
tokens[-1].value,
|
|
34
|
+
tokens[-1].lineno,
|
|
35
|
+
],
|
|
36
|
+
["RBRACE", "}", 61],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def test_col_idxs(self) -> None:
|
|
40
|
+
"""Basic test for lexer."""
|
|
41
|
+
tokens = []
|
|
42
|
+
ir = JacLexer(mod_path="", input_ir=self.load_fixture("lexer_fam.jac")).ir
|
|
43
|
+
if not isinstance(ir, Generator):
|
|
44
|
+
raise ValueError("Lexer did not return generator.")
|
|
45
|
+
for t in ir:
|
|
46
|
+
tokens.append((t.value, t.lineno, t.index - t.lineidx, t.end - t.lineidx))
|
|
47
|
+
self.assertEqual(tokens[12], ("activity", 9, 16, 24))
|
|
48
|
+
self.assertEqual(tokens[-3], ("outside_func", 59, 24, 36))
|
|
49
|
+
|
|
50
|
+
def test_enum_matches_lexer_toks(self) -> None:
|
|
51
|
+
"""Test that enum stays synced with lexer."""
|
|
52
|
+
for token in JacLexer.tokens:
|
|
53
|
+
self.assertIn(token, Tokens.__members__)
|
|
54
|
+
for token in Tokens:
|
|
55
|
+
self.assertIn(token.name, JacLexer.tokens)
|
|
56
|
+
for token in Tokens:
|
|
57
|
+
self.assertIn(token.value, JacLexer.tokens)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Tests for Jac parser."""
|
|
2
|
+
from jaclang.jac.absyntree import AstNode
|
|
3
|
+
from jaclang.jac.lexer import JacLexer
|
|
4
|
+
from jaclang.jac.parser import JacParser
|
|
5
|
+
from jaclang.utils.test import TestCaseMicroSuite
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestParser(TestCaseMicroSuite):
|
|
9
|
+
"""Test Jac self.prse."""
|
|
10
|
+
|
|
11
|
+
def setUp(self) -> None:
|
|
12
|
+
"""Set up test."""
|
|
13
|
+
return super().setUp()
|
|
14
|
+
|
|
15
|
+
def micro_suite_test(self, filename: str) -> None:
|
|
16
|
+
"""Parse micro jac file."""
|
|
17
|
+
lex = JacLexer(mod_path=filename, input_ir=self.file_to_str(filename)).ir
|
|
18
|
+
prse = JacParser(mod_path=filename, input_ir=lex)
|
|
19
|
+
self.assertFalse(prse.errors_had)
|
|
20
|
+
|
|
21
|
+
def test_shift_reduce_conflict(self) -> None:
|
|
22
|
+
"""Test for shift reduce conflict."""
|
|
23
|
+
self.assertEqual(len(JacParser._lrtable.sr_conflicts), 0)
|
|
24
|
+
|
|
25
|
+
def test_reduce_reduce_conflict(self) -> None:
|
|
26
|
+
"""Test for reduce reduce conflict."""
|
|
27
|
+
self.assertEqual(len(JacParser._lrtable.rr_conflicts), 0)
|
|
28
|
+
|
|
29
|
+
def test_basci_parsing(self) -> None:
|
|
30
|
+
"""Basic test for parsing."""
|
|
31
|
+
lex = JacLexer(mod_path="fam.jac", input_ir=self.load_fixture("fam.jac")).ir
|
|
32
|
+
prse = JacParser(mod_path="fam.jac", input_ir=lex)
|
|
33
|
+
output = prse.ir
|
|
34
|
+
self.assertFalse(prse.errors_had)
|
|
35
|
+
if isinstance(output, AstNode):
|
|
36
|
+
self.assertGreater(len(str(output.to_dict())), 1000)
|
|
37
|
+
else:
|
|
38
|
+
self.fail("Output is not an AstNode.")
|
|
39
|
+
|
|
40
|
+
def test_parsing_jac_cli(self) -> None:
|
|
41
|
+
"""Basic test for parsing."""
|
|
42
|
+
lex = JacLexer(
|
|
43
|
+
mod_path="../../../cli/cli.jac",
|
|
44
|
+
input_ir=self.load_fixture("../../../cli/cli.jac"),
|
|
45
|
+
).ir
|
|
46
|
+
prse = JacParser(mod_path="../../../cli/cli.jac", input_ir=lex)
|
|
47
|
+
self.assertFalse(prse.errors_had)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
TestParser.self_attach_micro_tests()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Tests for Jac utils."""
|
|
2
|
+
from jaclang.jac.utils import load_ast_and_print_pass_template
|
|
3
|
+
from jaclang.utils.test import TestCase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestLexer(TestCase):
|
|
7
|
+
"""Test Jac utils."""
|
|
8
|
+
|
|
9
|
+
def test_ast_template_loader(self) -> None:
|
|
10
|
+
"""Basic test for ast template loader."""
|
|
11
|
+
output = load_ast_and_print_pass_template()
|
|
12
|
+
self.assertGreater(len(output), 1000)
|
jaclang/jac/transform.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Standardized transformation process and error interface."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from abc import ABC, ABCMeta, abstractmethod
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from jaclang.jac.absyntree import AstNode
|
|
10
|
+
from jaclang.utils.log import logging
|
|
11
|
+
from jaclang.utils.sly.lex import LexerMeta
|
|
12
|
+
from jaclang.utils.sly.yacc import ParserMeta
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Transform(ABC):
|
|
16
|
+
"""Abstract class for IR passes."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
mod_path: str,
|
|
21
|
+
input_ir: AstNode,
|
|
22
|
+
base_path: str = "",
|
|
23
|
+
prior: Optional[Transform] = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Initialize pass."""
|
|
26
|
+
self.logger = logging.getLogger(self.__class__.__module__)
|
|
27
|
+
self.errors_had = [] if not prior else prior.errors_had
|
|
28
|
+
self.warnings_had = [] if not prior else prior.warnings_had
|
|
29
|
+
self.cur_line = 0
|
|
30
|
+
self.mod_path = mod_path
|
|
31
|
+
self.rel_mod_path = (
|
|
32
|
+
mod_path.replace(base_path, "") if base_path else mod_path.split(os.sep)[-1]
|
|
33
|
+
)
|
|
34
|
+
self.ir = self.transform(ir=input_ir)
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def transform(self, ir: AstNode) -> AstNode:
|
|
38
|
+
"""Transform interface."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def log_error(self, msg: str) -> None:
|
|
42
|
+
"""Pass Error."""
|
|
43
|
+
msg = f"Mod {self.rel_mod_path}: Line {self.cur_line}, " + msg
|
|
44
|
+
self.errors_had.append(msg)
|
|
45
|
+
self.logger.error(msg)
|
|
46
|
+
|
|
47
|
+
def log_warning(self, msg: str) -> None:
|
|
48
|
+
"""Pass Error."""
|
|
49
|
+
msg = f"Mod {self.rel_mod_path}: Line {self.cur_line}, " + msg
|
|
50
|
+
self.warnings_had.append(msg)
|
|
51
|
+
self.logger.warning(msg)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ABCLexerMeta(ABCMeta, LexerMeta):
|
|
55
|
+
"""Metaclass for Jac Lexer."""
|
|
56
|
+
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ABCParserMeta(ABCMeta, ParserMeta):
|
|
61
|
+
"""Metaclass for Jac Lexer."""
|
|
62
|
+
|
|
63
|
+
pass
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Transpilation functions."""
|
|
2
|
+
from typing import Type, TypeVar
|
|
3
|
+
|
|
4
|
+
import jaclang.jac.absyntree as ast
|
|
5
|
+
from jaclang.jac.parser import JacLexer
|
|
6
|
+
from jaclang.jac.parser import JacParser
|
|
7
|
+
from jaclang.jac.passes import Pass
|
|
8
|
+
from jaclang.jac.passes.blue import BluePygenPass, pass_schedule
|
|
9
|
+
from jaclang.jac.transform import Transform
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T", bound=Pass)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def jac_file_to_parse_tree(file_path: str, base_dir: str) -> Transform:
|
|
16
|
+
"""Convert a Jac file to an AST."""
|
|
17
|
+
with open(file_path) as file:
|
|
18
|
+
lex = JacLexer(mod_path=file_path, input_ir=file.read(), base_path=base_dir)
|
|
19
|
+
prse = JacParser(
|
|
20
|
+
mod_path=file_path, input_ir=lex.ir, base_path=base_dir, prior=lex
|
|
21
|
+
)
|
|
22
|
+
return prse
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def transpile_jac_blue(file_path: str, base_dir: str) -> str:
|
|
26
|
+
"""Transpiler Jac file and return python code as string."""
|
|
27
|
+
code = jac_file_to_pass(
|
|
28
|
+
file_path=file_path, base_dir=base_dir, target=BluePygenPass
|
|
29
|
+
)
|
|
30
|
+
if isinstance(code.ir, ast.Module):
|
|
31
|
+
return code.ir.meta["py_code"]
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError("Transpilation of Jac file failed.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def transpile_jac_purple(file_path: str, base_dir: str) -> str:
|
|
37
|
+
"""Transpiler Jac file and return python code as string."""
|
|
38
|
+
from jaclang.jac.passes.purple import pass_schedule, PurplePygenPass
|
|
39
|
+
|
|
40
|
+
code = jac_file_to_pass(
|
|
41
|
+
file_path=file_path,
|
|
42
|
+
base_dir=base_dir,
|
|
43
|
+
target=PurplePygenPass,
|
|
44
|
+
schedule=pass_schedule,
|
|
45
|
+
)
|
|
46
|
+
if isinstance(code.ir, ast.Module):
|
|
47
|
+
return code.ir.meta["py_code"]
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError("Transpilation of Jac file failed.")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def jac_file_to_pass(
|
|
53
|
+
file_path: str,
|
|
54
|
+
base_dir: str = "",
|
|
55
|
+
target: Type[T] = BluePygenPass,
|
|
56
|
+
schedule: list[Type[T]] = pass_schedule,
|
|
57
|
+
) -> T:
|
|
58
|
+
"""Convert a Jac file to an AST."""
|
|
59
|
+
ast_ret = jac_file_to_parse_tree(file_path, base_dir)
|
|
60
|
+
for i in schedule:
|
|
61
|
+
if i == target:
|
|
62
|
+
break
|
|
63
|
+
ast_ret = i(
|
|
64
|
+
mod_path=file_path, input_ir=ast_ret.ir, base_path=base_dir, prior=ast_ret
|
|
65
|
+
)
|
|
66
|
+
ast_ret = target(
|
|
67
|
+
mod_path=file_path, input_ir=ast_ret.ir, base_path=base_dir, prior=ast_ret
|
|
68
|
+
)
|
|
69
|
+
return ast_ret
|
jaclang/jac/utils.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Utility functions and classes for Jac compilation toolchain."""
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
import jaclang.jac.absyntree as ast
|
|
5
|
+
from jaclang.jac.parser import JacLexer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_all_jac_keywords() -> str:
|
|
9
|
+
"""Get all Jac keywords as an or string."""
|
|
10
|
+
ret = ""
|
|
11
|
+
for k in JacLexer._remapping["NAME"].keys():
|
|
12
|
+
ret += f"{k}|"
|
|
13
|
+
return ret[:-1]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def pascal_to_snake(pascal_string: str) -> str:
|
|
17
|
+
"""Convert pascal case to snake case."""
|
|
18
|
+
snake_string = re.sub(r"(?<!^)(?=[A-Z])", "_", pascal_string).lower()
|
|
19
|
+
return snake_string
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def add_line_numbers(s: str) -> str:
|
|
23
|
+
"""Add line numbers to a string."""
|
|
24
|
+
lines = s.split("\n")
|
|
25
|
+
return "\n".join(f"{i+1}: \t{line}" for i, line in enumerate(lines))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def clip_code_section(s: str, target_line: int, line_range: int) -> str:
|
|
29
|
+
"""Clip a section of code and highlight target line."""
|
|
30
|
+
lines = s.split("\n")
|
|
31
|
+
start = max(0, target_line - line_range - 1)
|
|
32
|
+
end = min(target_line + line_range, len(lines))
|
|
33
|
+
|
|
34
|
+
result = []
|
|
35
|
+
for i in range(start, end):
|
|
36
|
+
line = lines[i]
|
|
37
|
+
if i == target_line - 1:
|
|
38
|
+
line = "*" + line
|
|
39
|
+
result.append(line)
|
|
40
|
+
return "\n".join(result)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_ast_nodes_as_snake_case() -> list[str]:
|
|
44
|
+
"""Get all AST nodes as snake case."""
|
|
45
|
+
import inspect
|
|
46
|
+
import sys
|
|
47
|
+
|
|
48
|
+
module_name = ast.__name__
|
|
49
|
+
module = sys.modules[module_name]
|
|
50
|
+
|
|
51
|
+
# Retrieve the source code of the module
|
|
52
|
+
source_code = inspect.getsource(module)
|
|
53
|
+
|
|
54
|
+
classes = inspect.getmembers(module, inspect.isclass)
|
|
55
|
+
ast_node_classes = [cls for _, cls in classes if issubclass(cls, ast.AstNode)]
|
|
56
|
+
|
|
57
|
+
ordered_classes = sorted(
|
|
58
|
+
ast_node_classes, key=lambda cls: source_code.find(f"class {cls.__name__}")
|
|
59
|
+
)
|
|
60
|
+
snake_names = []
|
|
61
|
+
for cls in ordered_classes:
|
|
62
|
+
class_name = cls.__name__
|
|
63
|
+
snake_names.append(pascal_to_snake(class_name))
|
|
64
|
+
return snake_names
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def load_ast_and_print_pass_template() -> str:
|
|
68
|
+
"""Load and print classes."""
|
|
69
|
+
import inspect
|
|
70
|
+
import sys
|
|
71
|
+
|
|
72
|
+
module_name = ast.__name__
|
|
73
|
+
module = sys.modules[module_name]
|
|
74
|
+
|
|
75
|
+
# Retrieve the source code of the module
|
|
76
|
+
source_code = inspect.getsource(module)
|
|
77
|
+
|
|
78
|
+
classes = inspect.getmembers(module, inspect.isclass)
|
|
79
|
+
ast_node_classes = [cls for _, cls in classes if issubclass(cls, ast.AstNode)]
|
|
80
|
+
|
|
81
|
+
ordered_classes = sorted(
|
|
82
|
+
ast_node_classes, key=lambda cls: source_code.find(f"class {cls.__name__}")
|
|
83
|
+
)
|
|
84
|
+
output = ""
|
|
85
|
+
for cls in ordered_classes:
|
|
86
|
+
class_name = cls.__name__
|
|
87
|
+
snake_case_name = pascal_to_snake(class_name)
|
|
88
|
+
|
|
89
|
+
output += f"def exit_{snake_case_name}(self, node: ast.{class_name}) -> None:\n"
|
|
90
|
+
output += ' """Sub objects.\n\n'
|
|
91
|
+
|
|
92
|
+
init_func = cls.__init__
|
|
93
|
+
init_signature = inspect.signature(init_func)
|
|
94
|
+
|
|
95
|
+
for param_name, param in init_signature.parameters.items():
|
|
96
|
+
if param_name not in ["self", "parent", "kid", "line"]:
|
|
97
|
+
param_type = (
|
|
98
|
+
param.annotation
|
|
99
|
+
if param.annotation != inspect.Parameter.empty
|
|
100
|
+
else "Any"
|
|
101
|
+
)
|
|
102
|
+
param_default = (
|
|
103
|
+
param.default if param.default != inspect.Parameter.empty else None
|
|
104
|
+
)
|
|
105
|
+
output += f" {param_name}: {param_type}{' ='+param_default if param_default else ''},\n"
|
|
106
|
+
|
|
107
|
+
output += ' """\n\n'
|
|
108
|
+
output = output.replace("jaclang.jac.absyntree.", "")
|
|
109
|
+
output = output.replace("typing.", "")
|
|
110
|
+
output = output.replace("<enum '", "")
|
|
111
|
+
output = output.replace("'>", "")
|
|
112
|
+
output = output.replace("<class '", "")
|
|
113
|
+
output = output.replace("ForwardRef('", "")
|
|
114
|
+
output = output.replace("')", "")
|
|
115
|
+
return output
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
print(get_all_jac_keywords())
|
|
120
|
+
print(load_ast_and_print_pass_template())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Jaseci utility functions and libraries."""
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
"""Python Like F-String Parser."""
|
|
3
|
+
from jaclang.utils.sly.lex import Lexer
|
|
4
|
+
from jaclang.utils.sly.yacc import Parser, YaccProduction
|
|
5
|
+
|
|
6
|
+
_ = None # For flake8 linting and sly compatibility
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FStringLexer(Lexer):
|
|
10
|
+
"""Python Like F-String Lexer."""
|
|
11
|
+
|
|
12
|
+
tokens = {
|
|
13
|
+
"STRING_START",
|
|
14
|
+
"STRING_END",
|
|
15
|
+
"EXPR_START",
|
|
16
|
+
"EXPR_END",
|
|
17
|
+
"PIECE",
|
|
18
|
+
}
|
|
19
|
+
# ignore = " \t"
|
|
20
|
+
|
|
21
|
+
# Tokens
|
|
22
|
+
|
|
23
|
+
STRING_START = r"f\""
|
|
24
|
+
STRING_END = r"\""
|
|
25
|
+
PIECE = r"[^\{\}\"]+|\{\{|\}\}"
|
|
26
|
+
EXPR_START = r"(?<!\{)\{(?!\{)"
|
|
27
|
+
EXPR_END = r"(?<!\})\}(?!\})"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class FStringParser(Parser):
|
|
31
|
+
"""Python Like F-String Parser."""
|
|
32
|
+
|
|
33
|
+
tokens = FStringLexer.tokens
|
|
34
|
+
start = "fstring"
|
|
35
|
+
|
|
36
|
+
@_("STRING_START fstr_parts STRING_END")
|
|
37
|
+
def fstring(self, p: YaccProduction) -> YaccProduction:
|
|
38
|
+
"""Start rule for fstring."""
|
|
39
|
+
return p
|
|
40
|
+
|
|
41
|
+
@_(
|
|
42
|
+
"fstr_parts fstr_expr",
|
|
43
|
+
"fstr_parts PIECE",
|
|
44
|
+
"PIECE",
|
|
45
|
+
"fstr_expr",
|
|
46
|
+
"fstring",
|
|
47
|
+
)
|
|
48
|
+
def fstr_parts(self, p: YaccProduction) -> YaccProduction:
|
|
49
|
+
"""Parts of the string both in string and in expression."""
|
|
50
|
+
return p
|
|
51
|
+
|
|
52
|
+
@_("EXPR_START fstr_parts EXPR_END")
|
|
53
|
+
def fstr_expr(self, p: YaccProduction) -> YaccProduction:
|
|
54
|
+
"""Expressions rule."""
|
|
55
|
+
return p
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
"""Run the parser for live testing."""
|
|
60
|
+
lexer = FStringLexer()
|
|
61
|
+
parser = FStringParser()
|
|
62
|
+
while True:
|
|
63
|
+
try:
|
|
64
|
+
text = input("fstring > ")
|
|
65
|
+
except EOFError:
|
|
66
|
+
break
|
|
67
|
+
if text:
|
|
68
|
+
tokens = lexer.tokenize(text)
|
|
69
|
+
for i in tokens:
|
|
70
|
+
print(i, end=", ")
|
|
71
|
+
tokens = lexer.tokenize(text)
|
|
72
|
+
result = parser.parse(tokens)
|
|
73
|
+
print(result)
|
jaclang/utils/log.py
ADDED