Newer
Older
NewLang / src / interp.py
# SPDX-License-Identifier: LGPL-2.1-or-later
# 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