X-Git-Url: https://code.kerkeslager.com/?p=fur;a=blobdiff_plain;f=interpreter.py;fp=interpreter.py;h=5f71e3b0bf3e95c60f2f65f586bd7161328dfa8c;hp=0000000000000000000000000000000000000000;hb=d70666abc2db430300d48691046ada2395b2f0d5;hpb=732b4ed7f693a328509b94d7137f3210c9c4042f diff --git a/interpreter.py b/interpreter.py new file mode 100644 index 0000000..5f71e3b --- /dev/null +++ b/interpreter.py @@ -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