-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlang_ast.py
145 lines (127 loc) · 4.53 KB
/
lang_ast.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
#!/usr/bin/env python
import os
import signal
import eval as safe_eval
import nodes
import settings
from nodes import Node
is_windows = hasattr(os.sys, 'winver')
class AST(object):
END_CHARS = b")("
run = True
def __repr__(self):
return " ".join("<{}>".format(i) for i in self.nodes) or "EmptyAST"
def setup(self, code, first=False):
self.first = first
self.nodes = []
self.uses_i = False
self.uses_j = False
while code and (self.first or code[:1] not in AST.END_CHARS):
code, node = self.add_node(code)
if node.uses_i:
self.uses_i = True
if node.char == b"i":
self.i_node = node.__class__
else:
self.i_node = node.ast.i_node
if node.uses_j:
self.uses_j = True
if node.char == b"j":
self.j_node = node.__class__
else:
self.j_node = node.ast.j_node
if code and code[0] != b"(":
code = code[1:]
if len(self.nodes) > 1:
if isinstance(self.nodes[-1], nodes.nodes[nodes.Node.StringLiteral]):
self.nodes[-2], self.nodes[-1] = self.nodes[-1], self.nodes[-2]
return code
def run(self, stack=None):
restore_point = None
if stack is None:
stack = []
if self.uses_i:
if hasattr(self.i_node, "contents"):
old_i = self.i_node.contents
if stack:
self.i_node.contents = [stack[0]]
counter = 0
while counter != len(self.nodes) and AST.run:
cur_node = self.nodes[counter]
counter += 1
try:
cur_node.prepare(stack)
no_args = cur_node.args
stack, args = stack[no_args:], stack[:no_args]
stack = cur_node(args) + stack
except GotoStart as goto:
if not self.first:
raise
stack = goto.stack
restore_point = [stack, counter]
counter = 0
except:
if restore_point is not None:
stack, counter = restore_point
restore_point = None
else:
raise
try:
self.i_node.contents = old_i
except NameError:
pass
if self.first:
stack = self.implicit_complete(stack)
return stack
def add_node(self, code):
code, node = AST._add_node(code)
self.nodes.append(node)
return code, node
@staticmethod
def _add_node(code):
accepting = []
for name in nodes.nodes:
node = nodes.nodes[name]
new_code, new_node = node.accepts(code[:])
if new_code is not None:
assert(new_node is not None)
accepting.append((node.char, new_code, new_node))
if not accepting:
raise SyntaxError("No nodes will accept code: {}".format(repr(code)))
return sorted(accepting, key=lambda node:-len(node[0]))[0][1:]
def implicit_complete(self, stack):
stack_types = {Node.infinite: self.strip_infinite}
for i, value in enumerate(stack):
for stack_type, func in stack_types.items():
if isinstance(value, stack_type):
stack[i] = func(stack[i])
return stack
def strip_infinite(self, infinite):
infinite.prepend(next(infinite))
try:
arg = safe_eval.evals[settings.SAFE](input())
except EOFError:
return infinite
if arg == "":
return infinite
if isinstance(arg, str) and arg.isnumeric():
arg = int(arg)
return "\n".join(map(str, infinite[:arg]))
elif isinstance(arg, int):
return infinite[arg]
elif isinstance(arg, Node.sequence):
sequence = infinite[:max(arg)+1]
return "\n".join(str(sequence[i]) for i in arg)
return infinite
if is_windows:
signal.signal(signal.SIGBREAK, lambda: setattr(AST, "run", False))
else:
signal.signal(signal.SIGTERM, lambda: setattr(AST, "run", False))
class GotoStart(RuntimeError):
def __init__(self, stack):
self.stack = stack
def test_code(code, out_stack):
ast = AST()
ast.setup(code, first=True)
rtn_stack = ast.run()
assert rtn_stack == out_stack, "rtn: %s, wanted: %s (code: %s)" % (rtn_stack, out_stack, code)