Start working on the interpeter
[fur] / interpreter.py
1 import sys
2
3 import crossplatform_ir_generation
4
5 class Environment(object):
6     def __init__(self, parent):
7         self.symbols = {}
8         self.parent = parent
9
10     def __getitem__(self, symbol):
11         if symbol in self.symbols:
12             return self.symbols[symbol]
13
14         if self.parent is None:
15             raise Exception('Symbol "{}" not found'.format(symbol))
16
17         return self.parent[symbol]
18
19     def __setitem__(self, symbol, value):
20         self.symbols[symbol] = value
21
22 def builtin_print(*outputs):
23     outputs = list(outputs)
24
25     for i in range(len(outputs)):
26         output = outputs[i]
27
28         if isinstance(output, bool):
29             outputs[i] = str(output).lower()
30
31     print(*outputs, end='')
32
33 BUILTIN_ENVIRONMENT = Environment(None)
34 BUILTIN_ENVIRONMENT.symbols = {
35     'false': False,
36     'pow': lambda base, exponent: pow(base, exponent),
37     'print': builtin_print,
38     'true': True,
39 }
40
41 def interpret(program):
42     environment = Environment(BUILTIN_ENVIRONMENT)
43
44     label_indices = {}
45
46     for i in range(len(program.entry_list)):
47         if isinstance(program.entry_list[i], crossplatform_ir_generation.CIRLabel):
48             if program.entry_list[i].label in label_indices:
49                 raise Exception('Label already in labels')
50
51             label_indices[program.entry_list[i].label] = i
52
53     program_counter = label_indices['__main__'] + 1
54     stack = []
55
56     while True:
57         while isinstance(program.entry_list[program_counter], crossplatform_ir_generation.CIRLabel):
58             program_counter += 1
59
60         instruction = program.entry_list[program_counter].instruction
61         argument = program.entry_list[program_counter].argument
62
63         if instruction in ('add', 'idiv', 'mod', 'mul', 'sub'):
64             assert argument == 2
65             right = stack.pop()
66             left = stack.pop()
67             assert isinstance(left, int)
68             assert isinstance(right, int)
69
70             if instruction == 'add':
71                 result = left + right
72             elif instruction == 'idiv':
73                 result = left // right
74             elif instruction == 'mod':
75                 result = left % right
76             elif instruction == 'mul':
77                 result = left * right
78             elif instruction == 'sub':
79                 result = left - right
80
81             stack.append(result)
82
83         elif instruction in ('gt', 'gte', 'lt', 'lte'):
84             assert argument == 2
85             right = stack.pop()
86             left = stack.pop()
87             assert isinstance(left, int)
88             assert isinstance(right, int)
89
90             if instruction == 'gt':
91                 result = left > right
92             elif instruction == 'gte':
93                 result = left >= right
94             elif instruction == 'lt':
95                 result = left < right
96             elif instruction == 'lte':
97                 result = left <= right
98
99             stack.append(result)
100
101         elif instruction in ('eq', 'neq'):
102             assert argument == 2
103             right = stack.pop()
104             left = stack.pop()
105
106             if instruction == 'eq':
107                 result = left == right
108             elif instruction == 'neq':
109                 result = left != right
110
111             stack.append(result)
112
113         elif instruction == 'call':
114             assert isinstance(argument, int)
115             args = []
116
117             f = stack.pop()
118
119             for i in range(argument):
120                 args.append(stack.pop())
121
122             args = list(reversed(args))
123
124             stack.append(f(*args))
125
126         elif instruction == 'concat':
127             assert argument == 2
128             right = stack.pop()
129             left = stack.pop()
130             assert isinstance(left, str)
131             assert isinstance(right, str)
132
133             stack.append(left + right)
134
135         elif instruction == 'drop':
136             assert argument is None
137             stack.pop()
138
139         elif instruction == 'end':
140             assert argument is None
141             sys.exit(0)
142
143         elif instruction == 'get':
144             index = stack.pop()
145             assert isinstance(argument, int)
146             xs = stack.pop()
147             assert isinstance(xs, list)
148             stack.append(xs[index])
149
150         elif instruction == 'jump_if_false':
151             program_counter = label_indices[argument]
152
153         elif instruction == 'list':
154             assert isinstance(argument, int)
155
156             result = []
157
158             for i in range(argument):
159                 result.append(stack.pop())
160
161             stack.append(list(reversed(result)))
162
163         elif instruction == 'neg':
164             stack.append(-stack.pop())
165
166         elif instruction == 'pop':
167             assert argument.startswith('sym(')
168             assert argument.endswith(')')
169             environment[argument[4:-1]] = stack.pop()
170
171         elif instruction == 'push':
172             assert argument.startswith('sym(')
173             assert argument.endswith(')')
174             stack.append(environment[argument[4:-1]])
175
176         elif instruction == 'push_integer':
177             assert isinstance(argument, int)
178             stack.append(argument)
179
180         elif instruction == 'push_string':
181             assert argument.startswith('"')
182             assert argument.endswith('"')
183             stack.append(argument[1:-1].encode('utf-8').decode('unicode_escape'))
184
185         else:
186             raise Exception('Instruction "{}" not supported (argument {}).'.format(
187                 instruction,
188                 argument,
189             ))
190
191         program_counter += 1