# SPDX-License-Identifier: LGPL-2.1-only # Copyright 2021 Jookia <contact@jookia.org> from src import ast_types import sys class Bool: def __init__(self, value): self.value = value def __repr__(self): return "Bool(%s)" % (self.value) def verb_ToText(self, args): return Text(self._value) class Text: def __init__(self, value): self.value = value def __repr__(self): return "Text('%s')" % (self.value) def verb_Append(self, args): args_count = len(args) if args_count != 1: raise InterpreterError( "Invalid argument count %i, expected 1" % (args_count) ) appendix = args[0] if appendix.__class__ != Text: raise InterpreterError( "Invalid argument type %s, expected Text" % (appendix.__class__.__name__) ) return Text(self.value + " " + appendix.value) def verb_Equals(self, args): args_count = len(args) if args_count != 1: raise InterpreterError( "Invalid argument count %i, expected 1" % (args_count) ) compare = args[0] if compare.__class__ != Text: raise InterpreterError( "Invalid argument type %s, expected Text" % (compare.__class__.__name__) ) return Bool(self.value == compare.value) class Module_System: def verb_Print(self, args): args_count = len(args) if args_count != 1: raise InterpreterError( "Invalid argument count %i, expected 1" % (args_count) ) line = args[0] if line.__class__ != Text: raise InterpreterError( "Invalid argument type %s, expected Text" % (line.__class__.__name__) ) print(line.value) def verb_Read(self, args): try: return Text(input()) except KeyboardInterrupt: return Text("") except EOFError: return Text("") def verb_Exit(self, args): sys.exit(0) class InterpreterError(BaseException): def __init__(self, error): self.error = error def __repr__(self): return "InterpreterError(error '%s')" % (self.error) class Interpreter: def __init__(self, env): self.env = env def resolve_value(self, value): if value.__class__ == ast_types.Reference: if value.value in self.env: return self.env[value.value] else: raise InterpreterError("Unknown environment value %s" % (value.value)) elif value.__class__ == ast_types.Text: return Text(value.value) elif value.__class__ == ast_types.Bool: return Bool(value.value) else: raise InterpreterError("Unknown value type %s" % (value.__class__.__name__)) def run_statement(self, ast): subject = self.resolve_value(ast.subject) if not ast.verb: return subject args = [] for arg in ast.arguments: args.append(self.resolve_value(arg)) verb = getattr(subject, "verb_" + ast.verb, None) if verb: return verb(args) else: raise InterpreterError( "Unknown verb %s for subject %s" % (ast.verb, subject.__class__.__name__) ) def run_set(self, ast): self.env[ast.subject] = self.run_statement(ast.statement) return self.env[ast.subject] def run_conditional(self, ast): test = self.run_statement(ast.test) if test.__class__ != Bool: raise InterpreterError("Test condition didn't return a boolean") if test.value: ret = self.run_statement(ast.success) else: ret = self.run_statement(ast.failure) return ret def run_command(self, ast): if ast.__class__ == ast_types.Statement: return self.run_statement(ast) elif ast.__class__ == ast_types.Set: return self.run_set(ast) elif ast.__class__ == ast_types.Conditional: return self.run_conditional(ast) else: raise InterpreterError("Unknown command type %s" % (ast.__class__.__name__)) def run(self, ast): while True: for command in ast: self.run_command(command) def run_ast(ast): env = { "System": Module_System(), } try: return Interpreter(env).run(ast) except InterpreterError as e: print("Interpreter error: %s" % (e)) return None