class Runtime:
    
    def __init__(self, records={}, parent=None):
        self.parent = parent
        self.records = records
    
    def run(self, ast, records={}):
        self.records.update(records)
        if ast["type"] == "Program":
            return self.program(ast)
    
    def program(self, ast):
        return self.block(ast.get("body"))
    
    def block(self, ast):
        for statement in ast.get("body"):
            s = self.switch(statement)
            if s != None:
                return s
    
    def switch(self, ast):
        t = ast["type"]
        if t == "VariableDeclaration":
            self.variable_declaration(ast)
        if t == "AssignmentExpression":
            self.assignment_expression(ast)
        if t == "CallExpression":
            self.call_function(ast)
        if t == "ReturnStatement":
            return self.exec_return(ast)
    
    def assignment_expression(self, ast):
        id = ast.get("identifier").get("name")
        v = ast.get("value")
        self.set_identifier_value(id, self.unquote(v))
    
    def _is_call_function(self, ast):
        return ast["type"] == "CallExpression"

    def call_function(self,ast):
        id = ast.get("callee").get("name")
        args = ast.get("arguments")
        unquoted_args = []
        for arg in args:
            unquoted_args.append(self.unquote(arg))
        fu = self.get_identifier_value(id)
        if fu == None:
            raise Exception("Function not found: " + id)
        return fu(*unquoted_args)
    
    def get_identifier_value(self, key):
        v = self.records.get(key)
        if v != None:
            return v
        if self.parent != None:
            return self.parent.get_identifier_value(key)
        return None

    def set_identifier_value(self, key, value):
        v = self.records.get(key)
        if v != None:
            self.records[key] = value
            return
        if self.parent != None:
            self.parent.set_identifier_value(key, value)
            return
        raise Exception("Identifier not found: " + key)

    def unquote(self, ast):
        if self._is_identifier(ast):
            return self.get_identifier_value(ast.get("name"))
        if self._is_literal(ast):
            return self.literal(ast)
        if self._is_call_function(ast):
            return self.call_function(ast)

    def variable_declaration(self, ast):
        id = ast.get("identifier").get("name")
        v = ast.get("value")
        self.records[id] = self.unquote(v)
    
    def literal(self, ast):
        if ast.get("type") == "StringLiteral":
            return ast.get("value")
        elif ast.get("type") == "NumericLiteral":
            return int(ast.get("value"))
        elif ast.get("type") == "FloatLiteral":
            return float(ast.get("value"))
        elif ast.get("type") == "BooleanLiteral":
            return bool(ast.get("value"))

    def _is_identifier(self, ast):
        return ast["type"] == "Identifier"

    def _is_literal(self, ast):
        return ast["type"] in ["NumericLiteral", "StringLiteral", "FloatLiteral", "BooleanLiteral"]
    
    def exec_return(self, ast):
        v = ast.get("value")
        if self._is_literal(v):
            return self.literal(v)
        if self._is_identifier(v):
            return self.records.get(v.get("name"))
        if self._is_call_function(v):
            return self.call_function(v)
    
    def debug_print_records(self):
        print(self.records)