-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlisp_parser.py
More file actions
116 lines (106 loc) · 3.44 KB
/
lisp_parser.py
File metadata and controls
116 lines (106 loc) · 3.44 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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
from lisp_exceptions import LispParsingException
from ast import *
import re
class Parser:
"""Parses string into abstract syntax tree"""
@staticmethod
def tokenize(stx):
# Pad parentheses for convenient tokenizing by whitespace
stx = re.sub(r"([\(\)])", r" \1 ", stx)
parts = re.split(r"\s+", stx)
if parts[0] == "":
parts = parts[1:]
if parts[-1] == "":
parts = parts[:-1]
return parts
@staticmethod
def lex(tkns):
if len(tkns) == 1:
return tkns[0]
elif tkns[0] != "(" or tkns[-1] != ")":
raise LispParsingException(
"lex",
"expected atomic or s-expression; got " + str(tkons)
)
else:
stack = [[]]
topExpr = stack[0]
for token in tkns[1:]:
currExpr = stack[-1]
if token == "(":
currExpr = []
stack.append(currExpr)
elif token == ")":
if currExpr == topExpr:
break
else:
stack[-2].append(currExpr)
stack = stack[:-1]
else:
currExpr.append(token)
return topExpr
@staticmethod
def interpret(expr):
if type(expr) is list:
ast = map(Parser.interpret, expr)
if expr[0] == "if":
return ASTIf(
ast[1],
ast[2],
ast[3]
)
elif expr[0] == "cond":
return ASTCond(
map(lambda b: map(Parser.interpret, b), expr[1:])
)
elif expr[0] == "cons":
return ASTCons(ast[1], ast[2])
elif expr[0] == "car":
return ASTCar(ast[1])
elif expr[0] == "cdr":
return ASTCdr(ast[1])
elif expr[0] == "list":
return ASTList(ast[1:])
elif expr[0] == "lambda":
return ASTFun(
map(Parser.interpret, expr[1]),
Parser.interpret(expr[2])
)
elif expr[0] == "let":
bindings = expr[1]
values, names = \
map(list, zip(*[
[Parser.interpret(str(e))
for e in bind]
for bind in bindings
]))
return ASTWith(
values,
names,
Parser.interpret(expr[2])
)
else:
return ASTCall(
ast[0],
ast[1:]
)
else:
if expr == "t":
return ASTBool(True)
elif expr == "nil":
return ASTBool(False)
elif expr == "nil":
return ASTBool(False)
elif expr[0] == "'":
return ASTSym(expr[1:])
else:
try:
return ASTNum(int(expr))
except ValueError:
try:
return ASTNum(float(expr))
except ValueError:
return ASTId(expr)
@staticmethod
def parse(stx):
return Parser.interpret(Parser.lex(Parser.tokenize(stx)))