koskript 1.2.0__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.
koskript/__init__.py ADDED
@@ -0,0 +1,86 @@
1
+ from lark import Lark
2
+ from .lang.emtypes import KoskriptObject
3
+ from .lang.interpreter import KoskripInterpreter
4
+ from .lang.astgen import KoskriptTransformer
5
+ from .lang.errors import Errors
6
+ import pathlib, os
7
+
8
+ _grammar_path = os.path.join(pathlib.Path(__file__).resolve().parent, "grammar.lark")
9
+ with open(_grammar_path, "r") as _grammar_file:
10
+ grammar = Lark(_grammar_file, parser="lalr", maybe_placeholders=False)
11
+
12
+
13
+ def _wrap(value):
14
+ return value if isinstance(value, KoskriptObject) else KoskriptObject(value=value)
15
+
16
+
17
+ class KoskriptRuntime(object):
18
+ """Embeddable Koskript runtime.
19
+
20
+ Any Python value or callable exposed to scripts is wrapped automatically,
21
+ so you can pass plain functions without building ``KoskriptObject`` by hand.
22
+
23
+ >>> rt = KoskriptRuntime({"print": print})
24
+ >>> rt.execute("local x = 10\\nx + 26")
25
+ 36
26
+ """
27
+
28
+ def __init__(self, globals=None, _globals_=None):
29
+ if _globals_ is not None:
30
+ globals = {**(globals or {}), **_globals_}
31
+
32
+ self.globals = {}
33
+ self.__interpreter__ = KoskripInterpreter()
34
+ self.__ast__ = KoskriptTransformer()
35
+
36
+ if globals:
37
+ self.register_many(globals)
38
+
39
+ def register(self, name: str, value):
40
+ """Expose a Python value or callable to scripts.
41
+
42
+ Returns the runtime so calls can be chained.
43
+ """
44
+ obj = _wrap(value)
45
+ self.globals[name] = obj
46
+ self.__interpreter__.set_global(name, obj)
47
+ return self
48
+
49
+ def register_many(self, mapping: dict):
50
+ for name, value in mapping.items():
51
+ self.register(name, value)
52
+ return self
53
+
54
+ def __setitem__(self, name: str, value):
55
+ self.register(name, value)
56
+
57
+ def __getitem__(self, name: str):
58
+ return self.__interpreter__.get_global(name).value
59
+
60
+ def execute(self, code: str):
61
+ """Parse and run ``code``.
62
+
63
+ Returns the value of the last evaluated expression, or the value of a
64
+ top-level ``return`` if the script uses one.
65
+ """
66
+ tree = grammar.parse(code)
67
+ ast = self.__ast__.transform(tree)
68
+
69
+ if not isinstance(ast, list):
70
+ ast = [ast]
71
+
72
+ return self.__interpreter__.run(ast)
73
+
74
+
75
+ def run(code: str, globals: dict = None, **kwargs):
76
+ """One-shot convenience helper.
77
+
78
+ >>> run("print(1 + 2)", print=print)
79
+ 3
80
+ """
81
+ merged = dict(globals or {})
82
+ merged.update(kwargs)
83
+ return KoskriptRuntime(merged).execute(code)
84
+
85
+
86
+ __all__ = ["KoskriptRuntime", "KoskriptObject", "Errors", "run"]
@@ -0,0 +1,213 @@
1
+ from lark import Transformer
2
+ from .emtypes import *
3
+
4
+ _ESCAPES = {
5
+ "n": "\n",
6
+ "t": "\t",
7
+ "r": "\r",
8
+ "0": "\0",
9
+ "\\": "\\",
10
+ '"': '"',
11
+ "'": "'",
12
+ }
13
+
14
+
15
+ def _unescape(raw: str) -> str:
16
+ out = []
17
+ i = 0
18
+ while i < len(raw):
19
+ ch = raw[i]
20
+ if ch == "\\" and i + 1 < len(raw):
21
+ nxt = raw[i + 1]
22
+ out.append(_ESCAPES.get(nxt, nxt))
23
+ i += 2
24
+ else:
25
+ out.append(ch)
26
+ i += 1
27
+ return "".join(out)
28
+
29
+
30
+ class KoskriptTransformer(Transformer):
31
+ def start(self, tree): return (tree)
32
+
33
+ def NUMBER(self, token):
34
+ text = str(token)
35
+ if "." in text:
36
+ return FloatLit(value=float(text))
37
+ return IntLit(value=int(text))
38
+
39
+ def NAME(self, token): return NameRef(name=str(token))
40
+ def STRING(self, token): return StrLit(value=_unescape(str(token)[1:-1]))
41
+ def bool_true(self, tree): return BoolLit(value=True)
42
+ def bool_false(self, tree): return BoolLit(value=False)
43
+ def null_lit(self, tree): return NullLit(value=None)
44
+
45
+ def lambda_fn(self, tree):
46
+ return LambdaFnDef(params=[], body=tree[0])
47
+
48
+ def lambda_fn_args(self, tree):
49
+ params = [obj.name for obj in tree[:-1]]
50
+ body = tree[-1]
51
+ return LambdaFnDef(params=params, body=body)
52
+
53
+ def array(self, tree): return ArrayLit(value=tree)
54
+ def map(self, tree): return MapLit(value=tree)
55
+ def map_obj(self, tree): return MapValue(key=tree[0], value=tree[1])
56
+
57
+ def arg_list(self, tree): return tree
58
+
59
+ def add_stmt(self, tree):
60
+ left, right = tree
61
+ return AddStmt(left=left, right=right)
62
+
63
+ def sub_stmt(self, tree):
64
+ left, right = tree
65
+ return SubStmt(left=left, right=right)
66
+
67
+ def mul_stmt(self, tree):
68
+ left, right = tree
69
+ return MulStmt(left=left, right=right)
70
+
71
+ def div_stmt(self, tree):
72
+ left, right = tree
73
+ return DivStmt(left=left, right=right)
74
+
75
+ def mod_stmt(self, tree):
76
+ left, right = tree
77
+ return ModStmt(left=left, right=right)
78
+
79
+ def neg_stmt(self, tree):
80
+ return NegStmt(value=tree[0])
81
+
82
+ def param_list(self, tree):
83
+ return [obj.name for obj in tree]
84
+
85
+ def block(self, tree):
86
+ return tree
87
+
88
+ # conditions and comparisons
89
+ def and_cond(self, tree):
90
+ left, right = tree
91
+ return AndCond(left=left, right=right)
92
+
93
+ def not_cond(self, tree):
94
+ return NotCond(tree)
95
+
96
+ def or_cond(self, tree):
97
+ left, right = tree
98
+ return OrCond(left=left, right=right)
99
+
100
+
101
+ def equ(self, tree):
102
+ left, right = tree
103
+ return EquComp(left=left, right=right)
104
+ def nequ(self, tree):
105
+ left, right = tree
106
+ return NequComp(left=left, right=right)
107
+ def gte(self, tree):
108
+ left, right = tree
109
+ return GteComp(left=left, right=right)
110
+ def lte(self, tree):
111
+ left, right = tree
112
+ return LteComp(left=left, right=right)
113
+ def lt(self, tree):
114
+ left, right = tree
115
+ return LtComp(left=left, right=right)
116
+ def gt(self, tree):
117
+ left, right = tree
118
+ return GtComp(left=left, right=right)
119
+
120
+ def member_access(self, tree):
121
+ value, attr = tree
122
+ return MemberAccess(name=value, attrs=[attr])
123
+
124
+ def index_access(self, tree):
125
+ value, index = tree
126
+ return IndexAccess(value=value, index=index)
127
+
128
+ def if_stmt(self, tree):
129
+ condition = tree[0]
130
+ block = tree[1]
131
+ anexed_ifs = tree[2:]
132
+
133
+ return IfStmt(
134
+ condition=condition,
135
+ body=block,
136
+ if_tree=anexed_ifs
137
+ )
138
+
139
+ def elseif_stmt(self, tree):
140
+ condition, block = tree
141
+ return ElseIfStmt(
142
+ condition=condition,
143
+ body=block
144
+ )
145
+
146
+ def elsestmt(self, tree):
147
+ return ElseStmt(
148
+ body=tree[0]
149
+ )
150
+
151
+ # declarations
152
+ def local_decl(self, tree):
153
+ name, expr = tree
154
+
155
+ return LocalDecl(
156
+ name=name.name,
157
+ value=expr
158
+ )
159
+
160
+ def decl(self, tree):
161
+ name, expr = tree
162
+ return DeclStmt(
163
+ name=name.name, value=expr
164
+ )
165
+
166
+ def fn_def(self, tree):
167
+ name, params, block = tree
168
+ return FnDef(
169
+ name=name.name,
170
+ params=params,
171
+ body=block
172
+ )
173
+
174
+ def fn_def_nargs(self, tree):
175
+ name, block = tree
176
+ return FnDef(
177
+ name=name.name,
178
+ params=[],
179
+ body=block
180
+ )
181
+
182
+ # flow
183
+ def return_value(self, tree):
184
+ if len(tree) >= 1:
185
+ return ReturnStmt(value=tree[0])
186
+ return ReturnStmt(value=None)
187
+
188
+ def return_void(self, tree):
189
+ return ReturnStmt(value=None)
190
+
191
+ def while_stmt(self, tree):
192
+ condition, block = tree
193
+ return WhileStmt(condition=condition, body=block)
194
+
195
+ def break_stmt(self, tree):
196
+ return BreakStmt()
197
+
198
+ def continue_stmt(self, tree):
199
+ return ContinueStmt()
200
+
201
+ def for_stmt(self, tree):
202
+ varname, iterable, block = tree
203
+ return ForStmt(var=varname.name, iterable=iterable, body=block)
204
+
205
+ def foritem_stmt(self, tree):
206
+ key, value, iterable, block = tree
207
+ return ForItemStmt(key=key.name, var=value.name, iterable=iterable, body=block)
208
+
209
+ # Otros
210
+ def fn_call(self, tree):
211
+ name = tree[0]
212
+ args = tree[1] if len(tree) > 1 else []
213
+ return FnCall(name=name, args=args)
@@ -0,0 +1,84 @@
1
+ from collections import namedtuple
2
+ from .errors import Errors
3
+ from typing import Any
4
+
5
+ # Literals and references
6
+ IntLit = namedtuple("IntLit", ["value"])
7
+ FloatLit = namedtuple("FloatLit", ["value"])
8
+ StrLit = namedtuple("StrLit", ["value"])
9
+ BoolLit = namedtuple("BoolLit", ["value"])
10
+ NullLit = namedtuple("NullLit", ["value"])
11
+ ArrayLit = namedtuple("ArrayLit", ["value"])
12
+ MapLit = namedtuple("MapLit", ["value"])
13
+ MapValue = namedtuple("MapValue", ["key", "value"])
14
+ NameRef = namedtuple("NameRef", ["name"])
15
+ Function = namedtuple("Function", ["params", "body"])
16
+ MemberAccess = namedtuple("MemberAccess", ["name", "attrs"])
17
+ IndexAccess = namedtuple("IndexAccess", ["value", "index"])
18
+
19
+ # Control-flow signal raised by `return` so it can unwind out of
20
+ # if/while/for blocks and be caught by the enclosing function call.
21
+ class ReturnSignal(Exception):
22
+ def __init__(self, value=None):
23
+ super().__init__("return")
24
+ self.value = value
25
+
26
+ # Signals raised by `break` / `continue`, caught by the nearest loop.
27
+ class BreakSignal(Exception):
28
+ def __init__(self):
29
+ super().__init__("break")
30
+
31
+ class ContinueSignal(Exception):
32
+ def __init__(self):
33
+ super().__init__("continue")
34
+
35
+ # Node Objects
36
+ LocalDecl = namedtuple("LocalDecl", ["name", "value"])
37
+ DeclStmt = namedtuple("DeclStmt", ["name", "value"])
38
+ ReturnStmt = namedtuple("ReturnStmt", ["value"])
39
+ FnDef = namedtuple("FnDef", ["name", "params", "body"])
40
+ FnCall = namedtuple("FnCall", ["name", "args"])
41
+ LambdaFnDef = namedtuple("LambdaFnDef", ["params", "body"])
42
+ WhileStmt = namedtuple("WhileStmt", ["condition", "body"])
43
+ ForStmt = namedtuple("ForStmt", ["var", "iterable", "body"])
44
+ ForItemStmt = namedtuple("ForItemStmt", ["key", "var", "iterable", "body"])
45
+ BreakStmt = namedtuple("BreakStmt", [])
46
+ ContinueStmt = namedtuple("ContinueStmt", [])
47
+ AddStmt = namedtuple("AddStmt", ["left", "right"])
48
+ SubStmt = namedtuple("SubStmt", ["left", "right"])
49
+ MulStmt = namedtuple("MulStmt", ["left", "right"])
50
+ DivStmt = namedtuple("DivStmt", ["left", "right"])
51
+ ModStmt = namedtuple("ModStmt", ["left", "right"])
52
+ NegStmt = namedtuple("NegStmt", ["value"])
53
+ IfStmt = namedtuple("IfStmt", ["condition", "body", "if_tree"])
54
+ ElseIfStmt = namedtuple("ElseIfStmt", ["condition", "body"])
55
+ ElseStmt = namedtuple("ElseStmt", ["body"])
56
+
57
+ # Conditions
58
+ AndCond = namedtuple("AndCond", ["left", "right"])
59
+ OrCond = namedtuple("OrCond", ["left", "right"])
60
+ NotCond = namedtuple("NotCond", ["comparison"])
61
+
62
+ # Comparisons
63
+ EquComp = namedtuple("EquComp", ["left", "right"])
64
+ NequComp = namedtuple("NequComp", ["left", "right"])
65
+ LteComp = namedtuple("LteComp", ["left", "right"])
66
+ GteComp = namedtuple("GteComp", ["left", "right"])
67
+ LtComp = namedtuple("LtComp", ["left", "right"])
68
+ GtComp = namedtuple("GtComp", ["left", "right"])
69
+
70
+
71
+ class KoskriptObject(object):
72
+ def __init__(self, value: Any, read_only: bool = False):
73
+ self.value = value
74
+ self.read_only = read_only
75
+
76
+ def set_value(self, value: Any):
77
+ if self.read_only:
78
+ raise Errors.ProtectedObject("cannot modify a constant value.")
79
+
80
+ self.value = value
81
+
82
+ def __repr__(self):
83
+ return f"KoskriptObject(value={self.value}, read_only={self.read_only})"
84
+
@@ -0,0 +1,12 @@
1
+ class Errors:
2
+ class MismatchType(Exception):
3
+ def __init__(self, *args):
4
+ super().__init__(*args)
5
+
6
+ class ProtectedObject(Exception):
7
+ def __init__(self, *args):
8
+ super().__init__(*args)
9
+
10
+ class RuntimeError(Exception):
11
+ def __init__(self, *args):
12
+ super().__init__(*args)