def add_example_interpreter_output_test(filename):
def test(self):
- try:
- p = subprocess.Popen(
- 'python main.py interpret {}'.format(
- os.path.join('examples', filename),
- ),
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
-
- actual_stdout, actual_stderr = p.communicate()
+ p = subprocess.Popen(
+ (
+ 'python',
+ 'main.py',
+ 'interpret',
+ os.path.join('examples', filename),
+ ),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
- expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
-
- if os.path.isfile(expected_stdout_path):
- with open(expected_stdout_path, 'rb') as f:
- expected_stdout = f.read()
- else:
- expected_stdout = b''
+ actual_stdout, actual_stderr = p.communicate()
- expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
+ expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
- if os.path.isfile(expected_stderr_path):
- with open(expected_stderr_path, 'rb') as f:
- expected_stderr = f.read()
- else:
- expected_stderr = b''
+ if os.path.isfile(expected_stdout_path):
+ with open(expected_stdout_path, 'rb') as f:
+ expected_stdout = f.read()
+ else:
+ expected_stdout = b''
- self.assertEqual(expected_stderr, actual_stderr)
+ expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
- # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
- os.remove(os.path.join('examples', filename + '.c'))
+ if os.path.isfile(expected_stderr_path):
+ with open(expected_stderr_path, 'rb') as f:
+ expected_stderr = f.read()
+ else:
+ expected_stderr = b''
- finally:
- try:
- os.remove('a.out')
- except OSError:
- pass
+ self.assertEqual(expected_stderr, actual_stderr)
- setattr(InterpreterOutputTests, 'test_' + filename, test)
+ setattr(InterpreterOutputTests, 'test_' + filename[:-4], test)
class CompilerOutputTests(unittest.TestCase):
pass
except OSError:
pass
- setattr(CompilerOutputTests, 'test_' + filename, test)
+ setattr(CompilerOutputTests, 'test_' + filename[:-4], test)
class MemoryLeakTests(unittest.TestCase):
pass
except OSError:
pass
- setattr(MemoryLeakTests, 'test_' + filename, test)
+ setattr(MemoryLeakTests, 'test_' + filename[:-4], test)
filenames = (
entry.name
--- /dev/null
+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