Start working on the interpeter
[fur] / interpreter.py
diff --git a/interpreter.py b/interpreter.py
new file mode 100644 (file)
index 0000000..5f71e3b
--- /dev/null
@@ -0,0 +1,191 @@
+import sys
+
+import crossplatform_ir_generation
+
+class Environment(object):
+    def __init__(self, parent):
+        self.symbols = {}
+        self.parent = parent
+
+    def __getitem__(self, symbol):
+        if symbol in self.symbols:
+            return self.symbols[symbol]
+
+        if self.parent is None:
+            raise Exception('Symbol "{}" not found'.format(symbol))
+
+        return self.parent[symbol]
+
+    def __setitem__(self, symbol, value):
+        self.symbols[symbol] = value
+
+def builtin_print(*outputs):
+    outputs = list(outputs)
+
+    for i in range(len(outputs)):
+        output = outputs[i]
+
+        if isinstance(output, bool):
+            outputs[i] = str(output).lower()
+
+    print(*outputs, end='')
+
+BUILTIN_ENVIRONMENT = Environment(None)
+BUILTIN_ENVIRONMENT.symbols = {
+    'false': False,
+    'pow': lambda base, exponent: pow(base, exponent),
+    'print': builtin_print,
+    'true': True,
+}
+
+def interpret(program):
+    environment = Environment(BUILTIN_ENVIRONMENT)
+
+    label_indices = {}
+
+    for i in range(len(program.entry_list)):
+        if isinstance(program.entry_list[i], crossplatform_ir_generation.CIRLabel):
+            if program.entry_list[i].label in label_indices:
+                raise Exception('Label already in labels')
+
+            label_indices[program.entry_list[i].label] = i
+
+    program_counter = label_indices['__main__'] + 1
+    stack = []
+
+    while True:
+        while isinstance(program.entry_list[program_counter], crossplatform_ir_generation.CIRLabel):
+            program_counter += 1
+
+        instruction = program.entry_list[program_counter].instruction
+        argument = program.entry_list[program_counter].argument
+
+        if instruction in ('add', 'idiv', 'mod', 'mul', 'sub'):
+            assert argument == 2
+            right = stack.pop()
+            left = stack.pop()
+            assert isinstance(left, int)
+            assert isinstance(right, int)
+
+            if instruction == 'add':
+                result = left + right
+            elif instruction == 'idiv':
+                result = left // right
+            elif instruction == 'mod':
+                result = left % right
+            elif instruction == 'mul':
+                result = left * right
+            elif instruction == 'sub':
+                result = left - right
+
+            stack.append(result)
+
+        elif instruction in ('gt', 'gte', 'lt', 'lte'):
+            assert argument == 2
+            right = stack.pop()
+            left = stack.pop()
+            assert isinstance(left, int)
+            assert isinstance(right, int)
+
+            if instruction == 'gt':
+                result = left > right
+            elif instruction == 'gte':
+                result = left >= right
+            elif instruction == 'lt':
+                result = left < right
+            elif instruction == 'lte':
+                result = left <= right
+
+            stack.append(result)
+
+        elif instruction in ('eq', 'neq'):
+            assert argument == 2
+            right = stack.pop()
+            left = stack.pop()
+
+            if instruction == 'eq':
+                result = left == right
+            elif instruction == 'neq':
+                result = left != right
+
+            stack.append(result)
+
+        elif instruction == 'call':
+            assert isinstance(argument, int)
+            args = []
+
+            f = stack.pop()
+
+            for i in range(argument):
+                args.append(stack.pop())
+
+            args = list(reversed(args))
+
+            stack.append(f(*args))
+
+        elif instruction == 'concat':
+            assert argument == 2
+            right = stack.pop()
+            left = stack.pop()
+            assert isinstance(left, str)
+            assert isinstance(right, str)
+
+            stack.append(left + right)
+
+        elif instruction == 'drop':
+            assert argument is None
+            stack.pop()
+
+        elif instruction == 'end':
+            assert argument is None
+            sys.exit(0)
+
+        elif instruction == 'get':
+            index = stack.pop()
+            assert isinstance(argument, int)
+            xs = stack.pop()
+            assert isinstance(xs, list)
+            stack.append(xs[index])
+
+        elif instruction == 'jump_if_false':
+            program_counter = label_indices[argument]
+
+        elif instruction == 'list':
+            assert isinstance(argument, int)
+
+            result = []
+
+            for i in range(argument):
+                result.append(stack.pop())
+
+            stack.append(list(reversed(result)))
+
+        elif instruction == 'neg':
+            stack.append(-stack.pop())
+
+        elif instruction == 'pop':
+            assert argument.startswith('sym(')
+            assert argument.endswith(')')
+            environment[argument[4:-1]] = stack.pop()
+
+        elif instruction == 'push':
+            assert argument.startswith('sym(')
+            assert argument.endswith(')')
+            stack.append(environment[argument[4:-1]])
+
+        elif instruction == 'push_integer':
+            assert isinstance(argument, int)
+            stack.append(argument)
+
+        elif instruction == 'push_string':
+            assert argument.startswith('"')
+            assert argument.endswith('"')
+            stack.append(argument[1:-1].encode('utf-8').decode('unicode_escape'))
+
+        else:
+            raise Exception('Instruction "{}" not supported (argument {}).'.format(
+                instruction,
+                argument,
+            ))
+
+        program_counter += 1