pitybas 0.1.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.
- pitybas/__init__.py +1 -0
- pitybas/__main__.py +4 -0
- pitybas/cli.py +68 -0
- pitybas/common.py +41 -0
- pitybas/expression.py +285 -0
- pitybas/interpret.py +269 -0
- pitybas/io/__init__.py +0 -0
- pitybas/io/simple.py +71 -0
- pitybas/io/vt100.py +286 -0
- pitybas/parse.py +367 -0
- pitybas/tokens.py +1356 -0
- pitybas-0.1.0.dist-info/METADATA +67 -0
- pitybas-0.1.0.dist-info/RECORD +17 -0
- pitybas-0.1.0.dist-info/WHEEL +5 -0
- pitybas-0.1.0.dist-info/entry_points.txt +2 -0
- pitybas-0.1.0.dist-info/licenses/LICENSE +19 -0
- pitybas-0.1.0.dist-info/top_level.txt +1 -0
pitybas/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
pitybas/__main__.py
ADDED
pitybas/cli.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
from optparse import OptionParser
|
|
4
|
+
|
|
5
|
+
from .interpret import Interpreter, Repl
|
|
6
|
+
from .common import Error
|
|
7
|
+
from pitybas.io.vt100 import IO as vt100
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main(argv=None):
|
|
11
|
+
parser = OptionParser(usage='Usage: pb [options] filename')
|
|
12
|
+
parser.add_option('-a', '--ast', dest="ast", action="store_true", help="parse, print ast, and quit")
|
|
13
|
+
parser.add_option('-d', '--dump', dest="vardump", action="store_true", help="dump variables in stacktrace")
|
|
14
|
+
parser.add_option('-s', '--stacktrace', dest="stacktrace", action="store_true", help="always stacktrace")
|
|
15
|
+
parser.add_option('-v', '--verbose', dest="verbose", action="store_true", help="verbose output")
|
|
16
|
+
parser.add_option('-i', '--io', dest="io", help="select an IO system: simple (default), vt100")
|
|
17
|
+
|
|
18
|
+
(options, args) = parser.parse_args(argv)
|
|
19
|
+
|
|
20
|
+
if len(args) > 1:
|
|
21
|
+
parser.print_help()
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
io = None
|
|
25
|
+
if options.io == 'vt100':
|
|
26
|
+
io = vt100
|
|
27
|
+
|
|
28
|
+
if args:
|
|
29
|
+
vm = Interpreter.from_file(args[0], history=20, io=io)
|
|
30
|
+
else:
|
|
31
|
+
print('Welcome to pitybas. Press Ctrl-D to exit.')
|
|
32
|
+
print()
|
|
33
|
+
vm = Repl(history=20, io=io)
|
|
34
|
+
|
|
35
|
+
if options.verbose:
|
|
36
|
+
vm.print_tokens()
|
|
37
|
+
print()
|
|
38
|
+
if args:
|
|
39
|
+
print('-===[ Running %s ]===-' % args[0])
|
|
40
|
+
|
|
41
|
+
if options.ast:
|
|
42
|
+
vm.print_ast()
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
vm.execute()
|
|
47
|
+
if options.stacktrace:
|
|
48
|
+
vm.print_stacktrace(options.vardump)
|
|
49
|
+
except KeyboardInterrupt:
|
|
50
|
+
print()
|
|
51
|
+
vm.print_stacktrace(options.vardump)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
print()
|
|
54
|
+
print()
|
|
55
|
+
vm.print_stacktrace(options.vardump)
|
|
56
|
+
|
|
57
|
+
print('%s on line %i:' % (e.__class__.__name__, vm.line), end=' ')
|
|
58
|
+
|
|
59
|
+
if isinstance(e, Error):
|
|
60
|
+
print(e.msg)
|
|
61
|
+
else:
|
|
62
|
+
print()
|
|
63
|
+
print('-===[ Python traceback ]===-')
|
|
64
|
+
print(traceback.format_exc())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == '__main__':
|
|
68
|
+
main()
|
pitybas/common.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class Error(Exception):
|
|
2
|
+
def __init__(self, msg):
|
|
3
|
+
self.msg = msg
|
|
4
|
+
|
|
5
|
+
def __str__(self):
|
|
6
|
+
return self.msg
|
|
7
|
+
|
|
8
|
+
class StopError(Exception): pass
|
|
9
|
+
class ReturnError(Exception): pass
|
|
10
|
+
|
|
11
|
+
class ParseError(Error): pass
|
|
12
|
+
class ExecutionError(Error): pass
|
|
13
|
+
class ExpressionError(Error): pass
|
|
14
|
+
|
|
15
|
+
class Pri:
|
|
16
|
+
# evaluation happens in the following order:
|
|
17
|
+
# skip: expressions, functions, variables
|
|
18
|
+
# 1. exponents, factorials
|
|
19
|
+
# 2. multiplication, division
|
|
20
|
+
# 3. addition, subtraction
|
|
21
|
+
# 4. logic operators
|
|
22
|
+
# 5. boolean operators
|
|
23
|
+
# 6. variable setting
|
|
24
|
+
|
|
25
|
+
# these won't be parsed into expressions at all
|
|
26
|
+
INVALID = -2
|
|
27
|
+
# NONE means store but don't execute directly
|
|
28
|
+
# used for variables, lazy loading functions and expressions
|
|
29
|
+
NONE = -1
|
|
30
|
+
|
|
31
|
+
PROB = 0
|
|
32
|
+
EXPONENT = 1
|
|
33
|
+
MULTDIV = 2
|
|
34
|
+
ADDSUB = 3
|
|
35
|
+
|
|
36
|
+
LOGIC = 4
|
|
37
|
+
BOOL = 5
|
|
38
|
+
SET = 6
|
|
39
|
+
|
|
40
|
+
def is_number(num):
|
|
41
|
+
return str(num).lstrip('-').replace('.', '', 1).isdigit()
|
pitybas/expression.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
from . import tokens
|
|
2
|
+
from .common import ExpressionError, Pri, is_number
|
|
3
|
+
|
|
4
|
+
class Base:
|
|
5
|
+
priority = Pri.NONE
|
|
6
|
+
|
|
7
|
+
can_run = False
|
|
8
|
+
can_set = False
|
|
9
|
+
can_get = True
|
|
10
|
+
can_fill_left = False
|
|
11
|
+
can_fill_right = False
|
|
12
|
+
absorbs = ()
|
|
13
|
+
|
|
14
|
+
end = None
|
|
15
|
+
|
|
16
|
+
def __init__(self, *elements):
|
|
17
|
+
self.contents = []
|
|
18
|
+
self.raw = []
|
|
19
|
+
self.finished = False
|
|
20
|
+
|
|
21
|
+
for e in elements:
|
|
22
|
+
self.append(e)
|
|
23
|
+
|
|
24
|
+
def append(self, token):
|
|
25
|
+
if self.contents:
|
|
26
|
+
prev = self.contents[-1]
|
|
27
|
+
|
|
28
|
+
# the minus sign implies a * -1 when used by itself
|
|
29
|
+
if isinstance(prev, tokens.Minus):
|
|
30
|
+
# TODO: fix this the rest of the way
|
|
31
|
+
if len(self.contents) == 1:
|
|
32
|
+
self.contents.pop()
|
|
33
|
+
self.contents += [tokens.Value(-1), tokens.Mult()]
|
|
34
|
+
|
|
35
|
+
# absorb: tokens can absorb the next token from the expression if it matches a list of types
|
|
36
|
+
elif isinstance(token, prev.absorbs):
|
|
37
|
+
if isinstance(token, Base):
|
|
38
|
+
token = token.flatten()
|
|
39
|
+
|
|
40
|
+
prev.absorb(token)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
# implied multiplication
|
|
44
|
+
elif prev.priority == token.priority == tokens.Pri.NONE:
|
|
45
|
+
|
|
46
|
+
# negative numbers actually have implied addition
|
|
47
|
+
if isinstance(token, tokens.Value)\
|
|
48
|
+
and is_number(token.value) and int(token.value) < 0:
|
|
49
|
+
self.contents.append(tokens.Plus())
|
|
50
|
+
else:
|
|
51
|
+
self.contents.append(tokens.Mult())
|
|
52
|
+
|
|
53
|
+
self.raw.append(token)
|
|
54
|
+
self.contents.append(token)
|
|
55
|
+
|
|
56
|
+
def extend(self, array):
|
|
57
|
+
for x in array:
|
|
58
|
+
self.append(x)
|
|
59
|
+
|
|
60
|
+
def flatten(self):
|
|
61
|
+
if len(self.contents) == 1:
|
|
62
|
+
first = self.contents[0]
|
|
63
|
+
if isinstance(first, Base):
|
|
64
|
+
return first.flatten()
|
|
65
|
+
elif first.can_get:
|
|
66
|
+
return first
|
|
67
|
+
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def fill(self):
|
|
71
|
+
# TODO: instead of this system, perhaps tokens should be able to specify whether they need/want left/right params
|
|
72
|
+
if not self.contents: return
|
|
73
|
+
|
|
74
|
+
# if we don't have a proper variable:token:variable pairing in the token list,
|
|
75
|
+
# this method will allow tokens to fill in an implied variable to their left or right
|
|
76
|
+
new = []
|
|
77
|
+
for i in range(len(self.contents)):
|
|
78
|
+
t = self.contents[i]
|
|
79
|
+
if (i % 2 == 0 and not t.can_get):
|
|
80
|
+
left = None
|
|
81
|
+
right = None
|
|
82
|
+
|
|
83
|
+
if i > 0:
|
|
84
|
+
left = self.contents[i-1]
|
|
85
|
+
if not left.can_fill_right:
|
|
86
|
+
left = None
|
|
87
|
+
|
|
88
|
+
right = self.contents[i]
|
|
89
|
+
if not right.can_fill_left:
|
|
90
|
+
right = None
|
|
91
|
+
|
|
92
|
+
if left is not None and right is not None:
|
|
93
|
+
if left < right:
|
|
94
|
+
left = None
|
|
95
|
+
else:
|
|
96
|
+
right = None
|
|
97
|
+
|
|
98
|
+
if left is not None:
|
|
99
|
+
new.append(left.fill_right())
|
|
100
|
+
elif right is not None:
|
|
101
|
+
new.append(right.fill_left())
|
|
102
|
+
|
|
103
|
+
new.append(t)
|
|
104
|
+
|
|
105
|
+
last = new[-1]
|
|
106
|
+
if not last.can_get:
|
|
107
|
+
if last.can_fill_right:
|
|
108
|
+
new.append(last.fill_right())
|
|
109
|
+
|
|
110
|
+
self.contents = new
|
|
111
|
+
|
|
112
|
+
def validate(self):
|
|
113
|
+
if not self.contents: return
|
|
114
|
+
|
|
115
|
+
# figure out how to handle in-place tokens like the symbol for ^3
|
|
116
|
+
# perhaps replace it with a ^3 so we can enforce (value, token, value)
|
|
117
|
+
# or we can pad "in-place" tokens with a null to be passed as right
|
|
118
|
+
|
|
119
|
+
# make sure expression is ordered (value, token, value, token, value)
|
|
120
|
+
for i in range(len(self.contents)):
|
|
121
|
+
t = self.contents[i]
|
|
122
|
+
|
|
123
|
+
if (i % 2 == 0 and not t.can_get) or ( i % 2 == 1 and not t.can_run):
|
|
124
|
+
raise ExpressionError('bad token order: %s' % self)
|
|
125
|
+
|
|
126
|
+
# determine whether we have any tokens after a ->
|
|
127
|
+
found_stor = False
|
|
128
|
+
for i in range(len(self.contents)):
|
|
129
|
+
t = self.contents[i]
|
|
130
|
+
odd = i % 2
|
|
131
|
+
|
|
132
|
+
if isinstance(t, tokens.Store):
|
|
133
|
+
found_stor = True
|
|
134
|
+
stor_odd = odd
|
|
135
|
+
elif found_stor and (odd == stor_odd):
|
|
136
|
+
raise ExpressionError('Store cannot be followed by non-Store tokens in expression: %s' % self)
|
|
137
|
+
|
|
138
|
+
def order(self):
|
|
139
|
+
# this step returns a list of ordered indicies
|
|
140
|
+
# to help reduce tokens to a single value
|
|
141
|
+
# see common.Pri for an ordering explanation
|
|
142
|
+
|
|
143
|
+
order = {}
|
|
144
|
+
|
|
145
|
+
for i in range(len(self.contents)):
|
|
146
|
+
token = self.contents[i]
|
|
147
|
+
p = token.priority
|
|
148
|
+
if p >= 0:
|
|
149
|
+
# anything below zero is to be ignored
|
|
150
|
+
if p in order:
|
|
151
|
+
order[p].append(i)
|
|
152
|
+
else:
|
|
153
|
+
order[p] = [i]
|
|
154
|
+
|
|
155
|
+
ret = []
|
|
156
|
+
for p in sorted(order):
|
|
157
|
+
ret += order[p]
|
|
158
|
+
|
|
159
|
+
return ret
|
|
160
|
+
|
|
161
|
+
def get(self, vm):
|
|
162
|
+
self.fill()
|
|
163
|
+
self.validate()
|
|
164
|
+
|
|
165
|
+
sub = []
|
|
166
|
+
expr = self.contents[:]
|
|
167
|
+
for i in self.order():
|
|
168
|
+
n = 0
|
|
169
|
+
for s in sub:
|
|
170
|
+
if s < i:
|
|
171
|
+
n += 1
|
|
172
|
+
|
|
173
|
+
sub += [i, i+1]
|
|
174
|
+
i -= n
|
|
175
|
+
|
|
176
|
+
right = expr.pop(i+1)
|
|
177
|
+
left = expr.pop(i-1)
|
|
178
|
+
|
|
179
|
+
token = expr[i-1]
|
|
180
|
+
expr[i-1] = tokens.Value(token.run(vm, left, right))
|
|
181
|
+
|
|
182
|
+
return vm.get(expr[0])
|
|
183
|
+
|
|
184
|
+
def finish(self):
|
|
185
|
+
self.finished = True
|
|
186
|
+
|
|
187
|
+
def close(self, char):
|
|
188
|
+
for stack in reversed(self.contents):
|
|
189
|
+
if isinstance(stack, Base):
|
|
190
|
+
if stack.close(char):
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
if char == self.end and not self.finished:
|
|
194
|
+
self.finish()
|
|
195
|
+
return True
|
|
196
|
+
|
|
197
|
+
def __str__(self):
|
|
198
|
+
return ''.join([a.token for a in self.raw])
|
|
199
|
+
|
|
200
|
+
def __len__(self):
|
|
201
|
+
return len(self.contents)
|
|
202
|
+
|
|
203
|
+
def __repr__(self):
|
|
204
|
+
return 'E(%s)' % (' '.join(repr(token) for token in self.contents))
|
|
205
|
+
|
|
206
|
+
bracket_map = {'(':')', '{':'}', '[':']'}
|
|
207
|
+
|
|
208
|
+
class Expression(Base):
|
|
209
|
+
def set(self, vm, value):
|
|
210
|
+
if len(self.contents) == 1:
|
|
211
|
+
self.contents[0].set(vm, value)
|
|
212
|
+
|
|
213
|
+
class Bracketed(Base):
|
|
214
|
+
def __init__(self, end):
|
|
215
|
+
self.end = bracket_map[end]
|
|
216
|
+
Base.__init__(self)
|
|
217
|
+
|
|
218
|
+
def __repr__(self):
|
|
219
|
+
return 'B(%s)' % (' '.join(repr(token) for token in self.contents))
|
|
220
|
+
|
|
221
|
+
class ParenExpr(Bracketed):
|
|
222
|
+
end = ')'
|
|
223
|
+
|
|
224
|
+
class Tuple(Base):
|
|
225
|
+
priority = Pri.INVALID
|
|
226
|
+
|
|
227
|
+
def __init__(self):
|
|
228
|
+
Base.__init__(self)
|
|
229
|
+
|
|
230
|
+
def append(self, expr):
|
|
231
|
+
if isinstance(expr, Base):
|
|
232
|
+
expr = expr.flatten()
|
|
233
|
+
|
|
234
|
+
if self.contents:
|
|
235
|
+
last = self.contents[-1]
|
|
236
|
+
if isinstance(last, Base) and not last.finished:
|
|
237
|
+
last.append(expr)
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
expr = Expression(expr)
|
|
241
|
+
self.contents.append(expr)
|
|
242
|
+
|
|
243
|
+
def sep(self):
|
|
244
|
+
if self.contents:
|
|
245
|
+
self.contents[-1].finish()
|
|
246
|
+
|
|
247
|
+
def get(self, vm):
|
|
248
|
+
return [vm.get(arg) for arg in self.contents]
|
|
249
|
+
|
|
250
|
+
def __len__(self):
|
|
251
|
+
return len(self.contents)
|
|
252
|
+
|
|
253
|
+
def __repr__(self):
|
|
254
|
+
def expr_repr(e):
|
|
255
|
+
if not isinstance(e, Expression):
|
|
256
|
+
return '({})'.format(e)
|
|
257
|
+
else:
|
|
258
|
+
return repr(e)
|
|
259
|
+
|
|
260
|
+
return 'T(%s)' % (', '.join(expr_repr(expr) for expr in self.contents))
|
|
261
|
+
|
|
262
|
+
class Arguments(Tuple, Bracketed):
|
|
263
|
+
def __init__(self, end):
|
|
264
|
+
Bracketed.__init__(self, end)
|
|
265
|
+
|
|
266
|
+
def __repr__(self):
|
|
267
|
+
return 'A(%s)' % (', '.join(repr(expr) for expr in self.contents))
|
|
268
|
+
|
|
269
|
+
class FunctionArgs(Arguments):
|
|
270
|
+
end = ')'
|
|
271
|
+
|
|
272
|
+
class ListExpr(Arguments):
|
|
273
|
+
priority = Pri.NONE
|
|
274
|
+
end = '}'
|
|
275
|
+
|
|
276
|
+
def __repr__(self):
|
|
277
|
+
return 'L{%s}' % (', '.join(repr(expr) for expr in self.contents))
|
|
278
|
+
|
|
279
|
+
class MatrixExpr(Arguments):
|
|
280
|
+
priority = Pri.NONE
|
|
281
|
+
end = ']'
|
|
282
|
+
|
|
283
|
+
def __repr__(self):
|
|
284
|
+
return 'M[%s]' % (', '.join(repr(expr) for expr in self.contents))
|
|
285
|
+
|
pitybas/interpret.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
import decimal
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
from .parse import Parser, ParseError
|
|
8
|
+
from .tokens import EOF, Value, REPL
|
|
9
|
+
from .common import ExecutionError, StopError, ReturnError
|
|
10
|
+
|
|
11
|
+
from pitybas.io.simple import IO
|
|
12
|
+
from .expression import Base
|
|
13
|
+
|
|
14
|
+
class Interpreter(object):
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_string(cls, string, *args, **kwargs):
|
|
17
|
+
code = Parser(string).parse()
|
|
18
|
+
return Interpreter(code, *args, **kwargs)
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_file(cls, filename, *args, **kwargs):
|
|
22
|
+
string = open(filename, 'r', encoding='utf8').read()
|
|
23
|
+
vm = Interpreter.from_string(string, *args, **kwargs)
|
|
24
|
+
vm.name = os.path.basename(filename)
|
|
25
|
+
return vm
|
|
26
|
+
|
|
27
|
+
def __init__(self, code, history=10, io=None, name=None):
|
|
28
|
+
if not io: io = IO
|
|
29
|
+
self.io = io(self)
|
|
30
|
+
|
|
31
|
+
self.name = name
|
|
32
|
+
self.code = code
|
|
33
|
+
self.code.append([EOF()])
|
|
34
|
+
self.line = 0
|
|
35
|
+
self.col = 0
|
|
36
|
+
self.expression = None
|
|
37
|
+
self.blocks = []
|
|
38
|
+
self.running = []
|
|
39
|
+
self.history = []
|
|
40
|
+
self.hist_len = history
|
|
41
|
+
|
|
42
|
+
self.vars = {}
|
|
43
|
+
self.lists = defaultdict(list)
|
|
44
|
+
self.matrix = {}
|
|
45
|
+
self.fixed = -1
|
|
46
|
+
|
|
47
|
+
self.serial = 0
|
|
48
|
+
self.repl_serial = 0
|
|
49
|
+
|
|
50
|
+
def cur(self):
|
|
51
|
+
return self.code[self.line][self.col]
|
|
52
|
+
|
|
53
|
+
def inc(self):
|
|
54
|
+
self.col += 1
|
|
55
|
+
if self.col >= len(self.code[self.line]):
|
|
56
|
+
self.col = 0
|
|
57
|
+
return self.inc_row()
|
|
58
|
+
|
|
59
|
+
return self.cur()
|
|
60
|
+
|
|
61
|
+
def inc_row(self):
|
|
62
|
+
self.line = min(self.line+1, len(self.code)-1)
|
|
63
|
+
self.expression = None
|
|
64
|
+
return self.cur()
|
|
65
|
+
|
|
66
|
+
def get_var(self, var, default=None):
|
|
67
|
+
if var not in self.vars and default is not None:
|
|
68
|
+
return default
|
|
69
|
+
return self.vars[var]
|
|
70
|
+
|
|
71
|
+
def set_var(self, var, value):
|
|
72
|
+
if isinstance(value, (Value, Base)):
|
|
73
|
+
value = value.get(self)
|
|
74
|
+
|
|
75
|
+
self.vars[var] = value
|
|
76
|
+
return value
|
|
77
|
+
|
|
78
|
+
def get_matrix(self, name):
|
|
79
|
+
return self.matrix[name]
|
|
80
|
+
|
|
81
|
+
def set_matrix(self, name, value):
|
|
82
|
+
self.matrix[name] = value
|
|
83
|
+
|
|
84
|
+
def get_list(self, name):
|
|
85
|
+
return self.lists[name]
|
|
86
|
+
|
|
87
|
+
def set_list(self, name, value):
|
|
88
|
+
self.lists[name] = value
|
|
89
|
+
|
|
90
|
+
def push_block(self, block=None):
|
|
91
|
+
if not block and self.running:
|
|
92
|
+
block = self.running[-1]
|
|
93
|
+
|
|
94
|
+
if block:
|
|
95
|
+
self.blocks.append(block)
|
|
96
|
+
else:
|
|
97
|
+
raise ExecutionError('tried to push an invalid block to the stack')
|
|
98
|
+
|
|
99
|
+
def pop_block(self):
|
|
100
|
+
if self.blocks:
|
|
101
|
+
return self.blocks.pop()
|
|
102
|
+
else:
|
|
103
|
+
raise ExecutionError('tried to pop an empty block stack')
|
|
104
|
+
|
|
105
|
+
def find(self, *types, **kwargs):
|
|
106
|
+
if 'wrap' in kwargs:
|
|
107
|
+
wrap = kwargs['wrap']
|
|
108
|
+
else:
|
|
109
|
+
wrap = False
|
|
110
|
+
|
|
111
|
+
if 'pos' in kwargs:
|
|
112
|
+
pos = kwargs['pos']
|
|
113
|
+
else:
|
|
114
|
+
pos = self.line
|
|
115
|
+
|
|
116
|
+
def y(i):
|
|
117
|
+
line = self.code[i]
|
|
118
|
+
if line:
|
|
119
|
+
cur = line[0]
|
|
120
|
+
if isinstance(cur, types):
|
|
121
|
+
return i, 0, cur
|
|
122
|
+
|
|
123
|
+
for i in range(pos, len(self.code)):
|
|
124
|
+
ret = y(i)
|
|
125
|
+
if ret: yield ret
|
|
126
|
+
|
|
127
|
+
if wrap:
|
|
128
|
+
for i in range(0, pos):
|
|
129
|
+
ret = y(i)
|
|
130
|
+
if ret: yield ret
|
|
131
|
+
|
|
132
|
+
def goto(self, row, col):
|
|
133
|
+
if row >= 0 and row < len(self.code)\
|
|
134
|
+
and col >= 0 and col < len(self.code[row]):
|
|
135
|
+
self.line = row
|
|
136
|
+
self.col = col
|
|
137
|
+
else:
|
|
138
|
+
raise ExecutionError('cannot goto (%i, %i)' % (row, col))
|
|
139
|
+
|
|
140
|
+
def get(self, *var):
|
|
141
|
+
ret = []
|
|
142
|
+
for v in var:
|
|
143
|
+
val = v.get(self)
|
|
144
|
+
if isinstance(val, complex):
|
|
145
|
+
if not val.imag:
|
|
146
|
+
val = val.real
|
|
147
|
+
|
|
148
|
+
if isinstance(val, (float)):
|
|
149
|
+
# TODO: perhaps limit precision here
|
|
150
|
+
i = int(val)
|
|
151
|
+
if val == i:
|
|
152
|
+
val = i
|
|
153
|
+
|
|
154
|
+
ret.append(val)
|
|
155
|
+
|
|
156
|
+
if len(ret) == 1:
|
|
157
|
+
return ret[0]
|
|
158
|
+
|
|
159
|
+
return ret
|
|
160
|
+
|
|
161
|
+
def disp_round(self, num):
|
|
162
|
+
if not isinstance(num, (decimal.Decimal, int, float, complex)):
|
|
163
|
+
return num
|
|
164
|
+
|
|
165
|
+
if self.fixed < 0:
|
|
166
|
+
return num
|
|
167
|
+
else:
|
|
168
|
+
return round(num, self.fixed)
|
|
169
|
+
|
|
170
|
+
def run(self, cur):
|
|
171
|
+
self.history.append((self.line, self.col, cur))
|
|
172
|
+
self.history = self.history[-self.hist_len:]
|
|
173
|
+
|
|
174
|
+
cur.line, cur.col = self.line, self.col
|
|
175
|
+
|
|
176
|
+
if cur.can_run:
|
|
177
|
+
self.running.append((self.line, self.col, cur))
|
|
178
|
+
self.inc()
|
|
179
|
+
cur.run(self)
|
|
180
|
+
self.running.pop()
|
|
181
|
+
elif cur.can_get:
|
|
182
|
+
self.inc()
|
|
183
|
+
self.set_var('Ans', cur.get(self))
|
|
184
|
+
self.serial = time.time()
|
|
185
|
+
else:
|
|
186
|
+
raise ExecutionError('cannot seem to run token: %s' % cur)
|
|
187
|
+
|
|
188
|
+
def execute(self):
|
|
189
|
+
with self.io:
|
|
190
|
+
try:
|
|
191
|
+
while not isinstance(self.cur(), EOF):
|
|
192
|
+
cur = self.cur()
|
|
193
|
+
self.run(cur)
|
|
194
|
+
except StopError as e:
|
|
195
|
+
if e.args:
|
|
196
|
+
print()
|
|
197
|
+
print('Stopped:', e.args[0])
|
|
198
|
+
except ReturnError as e:
|
|
199
|
+
if e.args:
|
|
200
|
+
print()
|
|
201
|
+
print('Returned:', e.args[0])
|
|
202
|
+
|
|
203
|
+
def print_tokens(self):
|
|
204
|
+
for line in self.code:
|
|
205
|
+
print((', '.join(repr(n) for n in line)).replace("u'", "'"))
|
|
206
|
+
|
|
207
|
+
def print_ast(self, start=0, end=None, highlight=None):
|
|
208
|
+
if end is None:
|
|
209
|
+
end = len(self.code)
|
|
210
|
+
|
|
211
|
+
for i in range(max(start, 0), min(end, len(self.code))):
|
|
212
|
+
line = self.code[i]
|
|
213
|
+
if highlight is not None and i == highlight - 1:
|
|
214
|
+
print('>>>> {}'.format(line))
|
|
215
|
+
else:
|
|
216
|
+
print('{:3}: {}'.format(i, line))
|
|
217
|
+
|
|
218
|
+
def print_stacktrace(self, num=None, vardump=False):
|
|
219
|
+
if not num:
|
|
220
|
+
num = self.hist_len
|
|
221
|
+
|
|
222
|
+
if self.name:
|
|
223
|
+
print('-===[ Dumping {} ]===-'.format(self.name))
|
|
224
|
+
|
|
225
|
+
if self.history:
|
|
226
|
+
print()
|
|
227
|
+
print('-===[ Stacktrace ]===-')
|
|
228
|
+
|
|
229
|
+
for row, col, cur in self.history[-num:]:
|
|
230
|
+
print(('[{}, {}]:'.format(row, col)).ljust(9), repr(cur).replace("u'", '').replace("'", ''))
|
|
231
|
+
|
|
232
|
+
if self.history:
|
|
233
|
+
print()
|
|
234
|
+
|
|
235
|
+
print('-===[ Code (row {}, col {}) ]===-'.format(self.line, self.col))
|
|
236
|
+
h = num // 2
|
|
237
|
+
self.print_ast(self.line - h, self.line + h, highlight=self.line)
|
|
238
|
+
print()
|
|
239
|
+
|
|
240
|
+
if vardump:
|
|
241
|
+
print()
|
|
242
|
+
print('-===[ Variable Dump ]===-')
|
|
243
|
+
import pprint
|
|
244
|
+
pprint.pprint(self.vars)
|
|
245
|
+
print()
|
|
246
|
+
|
|
247
|
+
def run_pgrm(self, name):
|
|
248
|
+
for ref in os.listdir('.'):
|
|
249
|
+
if ref.endswith('.bas'):
|
|
250
|
+
test = ref.rsplit('.', 1)[0]
|
|
251
|
+
if test.lower() == name.lower():
|
|
252
|
+
sub = Interpreter.from_file(ref)
|
|
253
|
+
sub.execute()
|
|
254
|
+
return
|
|
255
|
+
raise ExecutionError('pgrm{} not found'.format(name))
|
|
256
|
+
|
|
257
|
+
class Repl(Interpreter):
|
|
258
|
+
def __init__(self, code=[], **kwargs):
|
|
259
|
+
super(Repl, self).__init__(code, **kwargs)
|
|
260
|
+
self.code.insert(-2, [REPL()])
|
|
261
|
+
|
|
262
|
+
def execute(self):
|
|
263
|
+
while not isinstance(self.cur(), EOF):
|
|
264
|
+
try:
|
|
265
|
+
super(Repl, self).execute()
|
|
266
|
+
except ParseError as e:
|
|
267
|
+
print(e)
|
|
268
|
+
except:
|
|
269
|
+
print(traceback.format_exc())
|
pitybas/io/__init__.py
ADDED
|
File without changes
|