import re

class Yoctium:
    
    def __init__(self):
        self.command_patterns = {
            "func_call": r'^([A-Za-z_][A-Za-z0-9_.]*)\s*\((.*?)\)$',
            
            "var_dec" : r'var ([a-zA-Z][a-zA-Z0-9_]*?)\s*=\s*(.*?)$'
        }
        self.funcs = {
            "Console.display" : print,
        }
        
        self.builtins = {
            "String" : str,
            "read" : input,
            "int" : int,
        }
        
        self.variables = {}
        
        self.cln = 0
        
    
    def eval_expr(self, expr):
        try:
            return eval(expr, {"__builtins__" : __builtins__}, {**self.variables, **self.builtins})
        except NameError as e:
            name = str(e).split("'")[1]
            print(f"\n!!!\nError in line {self.cln+1}: Name \"{name}\" is not defined.\n")
    
    
    def exec_line(self, line, l_type):
        if l_type == "func_call":
            match = re.search(self.command_patterns["func_call"], line)
            func_name = match.group(1)
            func_args = match.group(2)
            
            if func_name in self.funcs.keys():
                if func_args:
                    arg_val = self.eval_expr(func_args)
                    return self.funcs[func_name](arg_val)
                return self.funcs[func_name]()
            print("\n!!!\nError in line {self.cln+1}: Function \"{func_name}\" is not defined.\n")
                
        elif l_type == "var_dec":
            match = re.search(self.command_patterns["var_dec"], line)
            var_name = match.group(1)
            var_val = match.group(2)
            self.variables[var_name] = self.eval_expr(var_val)
            
        
    
    def classify_command(self, cmd):
        if re.fullmatch(self.command_patterns["func_call"], cmd):
            return "func_call"
        elif re.fullmatch(self.command_patterns["var_dec"], cmd):
            return "var_dec"
        
        
    
    
    def run_code(self, code):
        
        self.cln = 0 #current line number
        lines = code.splitlines()
        
        while (self.cln < len(lines)):
            cl = lines[self.cln]
            
            if cl.strip() != "":
                
                l_type = self.classify_command(cl)
                
                self.exec_line(cl, l_type)
                
            self.cln += 1
        
        
