from lark import Lark, Transformer

# ==========================================
# 1. DEFINIÇÃO DA GRAMÁTICA (GLC)
# ==========================================
# Aqui aplicamos a Hierarquia de Chomsky. 
# A ordem das regras define a precedência matemática.
gramatica_fmu_v7 = """
    ?start: atribuicao

    # Uma atribuição exige um ID, o sinal de '=' e uma expressão à direita.
    atribuicao: ID "=" expressao

    # ?expressao: Nível mais baixo de precedência (Soma/Subtração).
    # O '?' diz ao Lark: "Se houver apenas um termo, não crie um nó extra na árvore".
    ?expressao: termo (SOMA termo)*
    
    # ?termo: Nível médio de precedência (Multiplicação/Divisão).
    ?termo: fator (MULT fator)*
    
    # ?fator: Maior precedência. Resolve números, variáveis ou parênteses primeiro.
    ?fator: NUMBER           -> numero
          | "(" expressao ")" 
          | ID               -> variavel

    # Definição dos Terminais (Tokens)
    SOMA: "+" | "-"
    MULT: "*" | "/"

    # Importações de padrões comuns: Identificadores e Números
    %import common.CNAME -> ID
    %import common.NUMBER
    %import common.WS
    %ignore WS
"""

# ==========================================
# 2. TRANSFORMER (ANÁLISE SEMÂNTICA)
# ==========================================
# O Transformer percorre a árvore de baixo para cima (Bottom-Up).
class CalculadoraFMU(Transformer):
    
    # Executada quando o Lark encontra um número (Terminal)
    def numero(self, tokens):
        # Converte o texto (string) em um número real (float)
        return float(tokens[0])
    
    # Executada para nomes de variáveis
    def variavel(self, tokens):
        return str(tokens[0])

    # Executada para resolver Multiplicações e Divisões
    def termo(self, tokens):
        # O acumulador 'res' inicia com o primeiro valor do termo
        res = tokens[0]
        i = 1
        # Percorre os tokens em pares: [operador, valor]
        while i < len(tokens):
            op = tokens[i]
            val = tokens[i+1]
            if op == '*': 
                res *= val
            elif op == '/': 
                res /= val
            i += 2
        return res

    # Executada para resolver Somas e Subtrações
    def expressao(self, tokens):
        res = tokens[0]
        i = 1
        while i < len(tokens):
            op = tokens[i]
            val = tokens[i+1]
            if op == '+': 
                res += val
            else: 
                res -= val
            i += 2
        return res

    # Regra final: monta o dicionário de saída
    def atribuicao(self, tokens):
        return {str(tokens[0]): tokens[1]}


# ==========================================
# 3. EXECUÇÃO E TESTE
# ==========================================
# Criamos o objeto analisador com base na nossa gramática
parser = Lark(gramatica_fmu_v7, start='atribuicao')

# Nossa sentença de teste (O "Código Fonte")
equacao = "x = (5 * 12) + 6"

try:
    # FASE SINTÁTICA: Gera a Árvore de Derivação
    arvore = parser.parse(equacao)
    print("--- 1. ÁRVORE SINTÁTICA (ESTRUTURA) ---")
    print(arvore.pretty())

    # FASE SEMÂNTICA/SÍNTESE: Percorre a árvore e gera o valor
    resultado = CalculadoraFMU().transform(arvore)
    print("--- 2. RESULTADO DO PROCESSAMENTO ---")
    print(resultado)

except Exception as e:
    print(f"Erro no processo de compilação: {e}")