-
Notifications
You must be signed in to change notification settings - Fork 7
/
repl.py
117 lines (94 loc) · 3.34 KB
/
repl.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
from typing import Optional
from sys import argv, stdin as _stdin, stdout as _stdout
from std.stdlibrary import stdnamespace
from pycalc.interpreter import interpret
from pycalc.tokentypes.types import PyCalcError, NoCodeError
PROMPT = ">> "
def _format_exc(
code: str,
exc: PyCalcError,
file: str = "<anonymous>") -> str:
lineno, pos = exc.pos
line = code.split("\n")[lineno]
return f"{line}\n" + \
" " * pos + "^\n" \
f"{file}:{lineno+1}:{pos+1}: " \
f"{exc.__class__.__name__}: {exc}"
class InteractiveShell:
def __init__(self,
prompt: str = PROMPT,
interpreter: Optional[interpret.ABCInterpreter] = None
):
self.prompt = prompt
self.interpreter = interpreter or interpret.Interpreter()
def session(self, stdin=_stdin, stdout=_stdout):
while True:
print(end=self.prompt, file=stdout)
try:
expression = stdin.readline().strip()
except KeyboardInterrupt:
return
if not expression:
continue
try:
print(self.interpreter.interpret(expression, stdnamespace),
file=stdout)
except PyCalcError as exc:
print(_format_exc(expression, exc, file="<repl>"),
file=stdout)
except Exception as exc:
print(f"<repl>:1:?: internal interpreter error: {exc.__class__.__name__}: {exc}",
file=stdout)
def interactive_mode():
interpreter = interpret.Interpreter()
shell = InteractiveShell(
prompt=PROMPT,
interpreter=interpreter
)
shell.session()
def expr_exec_mode(expr: str):
try:
print(interpret.Interpreter().interpret(expr, stdnamespace))
except PyCalcError as exc:
print(_format_exc(expr, exc, file="<cli>"))
except NoCodeError:
pass
except Exception as exc:
print(f"<cli>:1:?: internal interpreter error: {exc.__class__.__name__}({repr(exc)})")
def script_exec_mode(filename: str):
if not filename.endswith(".calc"):
print("unsupported file extension:", filename)
return
try:
with open(filename) as fd:
code = fd.read()
except FileNotFoundError:
print("file not found:", filename)
return
interpreter = interpret.Interpreter()
try:
interpreter.interpret(code, stdnamespace)
except PyCalcError as exc:
print(_format_exc(code, exc, file=fd.name))
except NoCodeError:
pass
except Exception as exc:
print(f"{fd.name}:?:?: internal interpreter error:")
raise exc
if __name__ == '__main__':
options = {
"-e": expr_exec_mode,
"--execute": expr_exec_mode,
"-s": script_exec_mode,
"--script": script_exec_mode,
}
if len(argv) > 1 and (argv[1] not in options or len(argv) != 3):
print("Invalid options:", " ".join(argv[1:]))
print("Available options:")
print("\t-e, --execute <expression>: execute expression right from a command line")
print("\t-s, --script <filename>.calc: execute program from a file")
elif len(argv) == 3:
option, value = argv[1:]
options[option](value)
else:
interactive_mode()