-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrene.py
174 lines (127 loc) · 4.79 KB
/
rene.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import lark
import sys
from lark import Lark, Transformer, v_args
from lark.indenter import Indenter
@lark.v_args(inline=True)
class MainTransformer(Transformer):
boilerplate = """######## rene boilerplate ########
import numpy as np
INT_DTYPE = np.int64
MAX_INT = INFINITY = np.iinfo(INT_DTYPE).max
MIN_INT = NEGATIVE_INFINITY = np.iinfo(INT_DTYPE).min
def Array(*dimensions, dtype=INT_DTYPE):
return np.empty([x + 1 for x in dimensions], dtype)
Table = Array
def array_from_iterable(it):
arr = np.array(tuple(it))
padded = np.empty([1 + x for x in arr.shape], dtype=arr.dtype)
padded[tuple([slice(1, None) for _ in arr.shape])] = arr
return padded
######## end rene boilerplate ########
"""
def __init__(self):
self.indent = 1
def ws(self, amount=None):
return " " * (self.indent if amount is None else amount)
def start(self, *statements):
return self.boilerplate + "\n".join(statements)
def assignment(self, lhs, expr):
return f"{lhs} = {expr}"
def identifier(self, identifier):
return str(identifier)
def paren_expr(self, expr):
return f"({expr})"
def binary_operator(self, val):
return str(val).replace("||", "or").replace("&&", "and")
def binary_operation(self, left_expr, val, right_expr):
return f"{left_expr} {val} {right_expr}"
def number(self, value):
return str(value)
def string(self, value):
return str(value)
def func_call(self, name, *exprs):
return f"{name}({', '.join(exprs)})"
def array_access(self, name, *exprs):
return f"{name}{''.join([f'[{x}]' for x in exprs])}"
def import_(self, name):
return f"import {name}"
def from_import(self, name, *modules):
return f"from {name} import {', '.join(modules)}"
def array_assignment(self, name, *args):
*exprs, expr = args
return f"{name}{''.join([f'[{x}]' for x in exprs])} = {expr}"
def comment(self, comment_open, comment, newline):
space = "" if comment.startswith(comment_open) else " "
return f"#{space}{comment.value}"
def block(self, indent, *statements):
return "\n".join([self.ws() + x for x in statements[:-1]])
def return_statement(self, expr):
return f"return {expr}"
def indent_(self):
self.indent += 1
return ""
def dedent(self):
self.indent -= 1
return ""
def if_block(self, if_, expr, colon, block, *args):
indent = self.ws(self.indent - 1)
result = [f"if {expr}:\n{block}"]
for i, x in enumerate(args):
if isinstance(x, lark.lexer.Token):
if x.value == "elseif":
elseif_expr = args[i+1]
elseif_block = args[i+3]
elseif = f"{indent}elif {elseif_expr}:\n{elseif_block}"
result.append(elseif)
elif x.value == "else":
else_block = args[i+2]
result.append(f"{indent}else:\n{else_block}")
return "\n".join(result)
def for_block(self, name, expr_start, expr_end, block):
return f"for {name} in range({expr_start}, ({expr_end}) + 1):\n{block}"
def blank_line(self):
return ""
def type_(self, value):
return str(value)
def func_def(self, func_name, *args):
*params, block = args
arrs = []
for name, type_ in zip(params[::2], params[1::2]):
if type_ == "Array":
arrs.append(f"{self.ws()}{name} = array_from_iterable({name})")
arrs = "\n".join(arrs)
return f"def {func_name}({', '.join(params[::2])}):\n{arrs}\n{block}"
class MainIndenter(Indenter):
NL_type = "_NL"
OPEN_PAREN_types = []
CLOSE_PAREN_types = []
INDENT_type = "_INDENT"
DEDENT_type = "_DEDENT"
tab_len = 4
def generate_code(source_file=None, source_string=None, out_file=None):
if source_string and source_file:
raise ArgumentError("choose either source_string or source_file")
parser = Lark.open(
"rene.lark",
rel_to=__file__,
parser="lalr",
postlex=MainIndenter(),
)
if source_file:
with open(source_file) as f:
tree = parser.parse(f.read())
else:
tree = parser.parse(source_string)
py_code = MainTransformer().transform(tree)
if out_file is not None:
with open(out_file, "w") as f:
f.write(py_code)
return py_code
if __name__ == "__main__":
if len(sys.argv) < 2 or len(sys.argv) > 3:
print("usage: python3 rene.py source_file.rene [out_file.py]")
sys.exit()
if len(sys.argv) < 3:
print(generate_code(source_file=sys.argv[1]))
else:
generate_code(source_file=sys.argv[1], out_file=sys.argv[2])