-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrepl.py
More file actions
96 lines (70 loc) · 2.06 KB
/
repl.py
File metadata and controls
96 lines (70 loc) · 2.06 KB
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
"""
The Romnomnom Tutorial!
See the README.md file for instructions.
"""
from functools import singledispatch
from collections import namedtuple
import ast
#
# The Romnomnom REPL!
#
def repl():
"""
Runs a Roman Numeral Read-Eval-Print-Loop.
"""
while True:
print(evaluate(input("tutorial> ")))
def evaluate(source):
"""
The "Eval" step in the REPL. Evaluation happens in two phases:
* Compile: lex, parse, generate bytecode
* Interpret: execute bytecode with Python's builtin `eval()`
"""
return eval(generate(parse(lex(source))))
#
# The Romnomnom Compiler!
#
Numeral = namedtuple("Numeral", "value")
Add = namedtuple("Add", "left right")
def lex(string) -> "tokens":
"""
Lex the string and output a generator of tokens for `parse()`.
"""
values = ("I", ...)
for value in string:
if value in values:
yield Numeral(value)
def parse(tokens) -> "tree":
"""
Parse the tokens and output a tree which represents the meaning of the token stream for `generate()`.
"""
token, *tokens = tokens
return Add(token, parse(tokens)) if tokens else token
def generate(tree) -> "bytecode":
"""
With the help of `translate()` below, generate Python bytecode for the Python's `eval()`.
https://docs.python.org/3/library/functions.html#compile
"""
return compile(
source=ast.fix_missing_locations(ast.Expression(body=translate(tree))),
filename="<input>",
mode="eval"
)
@singledispatch
def translate(node):
"""
Recursively translate a Romnomnom Abstract Syntax Tree into a Python Abstract Syntax Tree for Python's `compile()`.
"""
raise NotImplementedError('translate(%r)' % node)
@translate.register(Add)
def _(node):
return ast.BinOp(left=translate(node.left), op=ast.Add(), right=translate(node.right))
@translate.register(Numeral)
def _(node):
values = {"I": 1, ...: ...}
return ast.Num(n=values[node.value])
#
# This module may be run as a Python 3 script to start a REPL.
#
if __name__ == "__main__":
repl()