comparison interps/lambda/parser.py @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
comparison
equal deleted inserted replaced
995:6883f5911eb7 996:859f9b4339e6
1 class ParsingException(Exception):
2 pass
3
4 class Exp:
5 def show(self, indent_level):
6 return ' ' * indent_level + str(self.body)
7 def __str__(self):
8 # return self.show(0)
9 return str(self.body)
10
11 class StringExp(Exp):
12 def __init__(self, line):
13 self.line = line
14 def __str__(self):
15 return '"' + self.line + '"'
16
17 class SpecialExp(Exp):
18 def __init__(self, body):
19 self.body = body
20
21 class NameExp(Exp):
22 def __init__(self, body):
23 self.body = body
24
25 class ApplyExp(Exp):
26 def __init__(self, rator, rand):
27 self.rator = rator
28 self.rand = rand
29 def show(self, indent_level):
30 indent = ' ' * indent_level
31 strings = [indent + 'Apply:', self.rator.show(indent_level+1),
32 indent + 'To:', self.rand.show(indent_level+1)]
33 return '\n'.join(strings)
34 def __str__(self):
35 return str(self.rator) + ' ' + str(self.rand)
36
37
38 class LambdaExp(Exp):
39 def __init__(self, arg, body):
40 self.arg = arg
41 self.body = body
42 def show(self, indent_level):
43 indent = ' ' * indent_level
44 strings = [indent + 'Lambda ' + self.arg + ': ' , self.body.show(indent_level+1)]
45 return '\n'.join(strings)
46 def __str__(self):
47 # Perhaps we can combine this lambda with whatever is nested inside.
48 args = [self.arg]
49 curr = self
50 while isinstance(curr.body, LambdaExp):
51 curr = curr.body
52 args.append(curr.arg)
53 body_str = str(curr.body)
54 argstrings = ','.join(args)
55 if ' ' in body_str:
56 body_str = '('+body_str+')'
57 return '\\' + argstrings + '.' + body_str
58
59
60 def parse_exp(tokens, start_token, env, bound_vars):
61 def parse_one_exp(tokens, start_token, env, bound_vars):
62 """Parses a single expression NOT an application
63 of one expression to another, but possibly an
64 application inside (), starting from start_token. Returns a pair
65 (expression_object, last_token + 1)"""
66 index = start_token
67 current = tokens[index]
68 index += 1
69 if current[0] == '"':
70 # a string
71 exp = StringExp(current[1:-1])
72 elif current[0].isalpha():
73 # a name
74 if current in bound_vars:
75 exp = NameExp(current)
76 elif current in env:
77 exp = env[current]
78 else:
79 raise ParsingException("Unbound variable: " + current)
80 elif current[0] == '#':
81 exp = SpecialExp(current)
82 elif current == '(':
83 exp, index = parse_exp(tokens, index, env, bound_vars)
84 assert tokens[index] == ')'
85 index += 1
86 elif current == '\\':
87 # first read the names
88 names = []
89 while 1:
90 assert tokens[index].isalpha()
91 names.append(tokens[index])
92 index += 1
93 if tokens[index] == '.':
94 break
95 assert tokens[index] == ','
96 index += 1
97 # now index points at the last dot
98 index += 1
99 body, index = parse_one_exp(tokens, index, env, bound_vars + names)
100 # now create as many nested LambdaExps as we have names
101 while names:
102 body = LambdaExp(names[-1], body)
103 names = names[:-1]
104 exp = body
105 else:
106 raise ParsingException("Expression cannot start with " + current)
107 return exp, index
108
109 index = start_token
110 exps = []
111 while index < len(tokens) and tokens[index] != ')' and tokens[index] != ';':
112 exp, index = parse_one_exp(tokens, index, env, bound_vars)
113 exps.append(exp)
114 if exps == []:
115 raise ParsingException("Empty expression")
116 while len(exps) > 1:
117 app = ApplyExp(exps[0],exps[1])
118 exps = [app] + exps[2:]
119 return exps[0], index
120
121 def parse(tokens, env):
122 """Parses a list of tokens, and performs name substitution
123 from 'env' if any is necessary. Returns a pair (exp, env) with env a dict
124 containing any new definitions. Parsed expression will NOT
125 contain names of definitions."""
126 current = 0
127 env = env.copy()
128 while 1:
129 if len(tokens) - current < 2:
130 # Not enough room for a definition here
131 break
132 if tokens[current+1] == '=':
133 name = tokens[current]
134 exp, current = parse_exp(tokens, current+2, env, bound_vars=[])
135 env[name] = exp
136 assert tokens[current] == ';'
137 current += 1
138 else:
139 # not a definition. So we're done.
140 break
141 if current == len(tokens):
142 return None, env # useful in the REPL
143 exp, current = parse_exp(tokens, current, env, bound_vars=[] )
144 assert current == len(tokens)
145 return exp, env