-
Notifications
You must be signed in to change notification settings - Fork 0
/
lispyctomo.py
191 lines (152 loc) · 4.44 KB
/
lispyctomo.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
### 環境
Symbol = str
class Env(dict):
def __init__(self, key_value_pair={}, outer=None):
self.update(key_value_pair)
self.outer = outer
def find(self, key):
if key in self:
return self
elif self.outer:
return self.outer.find(key)
else:
raise Exception(f"{key} is not found. @Env.find()")
import math, operator as op
global_env = Env(
key_value_pair={
# システム関数
"print": print,
"exit": exit,
# 数値,論理演算
"+": op.add,
"-": op.sub,
"*": op.mul,
"/": op.truediv,
"!": op.not_,
">": op.gt,
"<": op.lt,
">=": op.ge,
"<=": op.le,
"==": op.eq,
# list操作
"length": len,
"cons": lambda x, y: [x] + y,
"car": lambda x: x[0],
"cdr": lambda x: x[1:],
"append": op.add,
"list": lambda *x: list(x),
"list?": lambda x: isinstance(x, list),
"null?": lambda x: x == [],
# atom操作
"symbol?": lambda x: isinstance(x, Symbol),
}
)
### 評価(実行)
# 型判定ができていない e.g. defineのkey
def eval(exp, env=global_env):
if isinstance(exp, Symbol): # 変数解決
return env.find(exp)[exp]
elif not isinstance(exp, list): # リテラル
return exp
else:
child_env = Env(outer=env)
if len(exp) == 0: # nullリスト
return # これでいい?
# 組み込み構文
elif exp[0] == "quote":
_, *cdr = exp
return cdr
elif exp[0] == "if":
_, cond, true_exp, false_exp = exp
if eval(cond, env):
return eval(true_exp, env)
else:
return eval(false_exp, env)
elif exp[0] == "while":
_, cond, body = exp
while eval(cond, env):
eval(body, env)
return
elif exp[0] == "define":
_, key, value = exp
if not key in env:
env[key] = eval(value, env)
else:
raise Exception(f"{key} is already defined. @eval()")
return
elif exp[0] == "set!":
_, key, value = exp
set_env = env.find(key)
set_env[key] = eval(value, env)
return
elif exp[0] == "lambda":
_, params, body = exp
def arrow_func(*args):
if len(args) != len(params):
raise Exception("Invalid number of arguments. @eval()")
return eval(body, child_env.update(zip(params, args)))
return arrow_func
elif exp[0] == "begin":
_, *child_exps = exp
for child_exp in child_exps:
val = eval(child_exp, child_env)
return val
# elif exp[0] == "":
# return
# カスタム関数
else:
exp_evaluated = [eval(elem, env) for elem in exp]
proc = exp_evaluated.pop(0)
return proc(*exp_evaluated)
### 構文解析以下
# atom
# リテラル, シンボルの分割
def atom(token):
try:
return int(token)
except:
try:
return float(token)
except:
return str(token)
# parser
# 不十分
def parse(tokens):
if len(tokens) == 0:
return
# raise SyntaxError("Invalid tokens. @parse()")
token = tokens.pop(0)
if token == "(":
L = []
while tokens[0] != ")":
L.append(parse(tokens))
tokens.pop(0)
return L
elif token == ")":
raise SyntaxError("Unexpected ')'. @parse()")
else:
return atom(token)
# tokenizer
def tokenize(chars):
return chars.replace("(", " ( ").replace(")", " ) ").split()
### 対話システム
# read-eval-print loop
def repl():
tokens = []
while True:
val = input("lispyctomo> ")
tokens.extend(tokenize(val))
bracket_num = 0
for index, token in enumerate(tokens):
if token == "(":
bracket_num += 1
elif token == ")":
bracket_num -= 1
if bracket_num == 0:
syntax_tree = parse(tokens[: index + 1])
tokens = tokens[index + 1 :]
# print(syntax_tree)
eval(syntax_tree, global_env)
break
if __name__ == "__main__":
repl()