College Online
0%

Arvores Sintaticas (ASTs)

Modulo 4 · Aula 1 ~25 min de leitura Nivel: Intermediario

Video da aula estara disponivel em breve

Parse Tree vs. AST

A parse tree (arvore de derivacao) reflete todas as producoes da gramatica, incluindo nao-terminais intermediarios. A AST (Abstract Syntax Tree) simplifica, mantendo apenas a informacao semanticamente relevante:

Diagrama
Expressao: 2 + 3 * 4

Parse Tree (concreta):             AST (abstrata):
        expr                            +
       / | \                           / \
    expr '+' term                     2    *
     |       / | \                        / \
   term   term '*' factor               3    4
     |      |        |
  factor  factor   NUMBER(4)
     |      |
  NUMBER(2) NUMBER(3)

A parse tree tem 11 nos. A AST tem 5.
A AST descarta nao-terminais intermediarios e parenteses —
mantem apenas a estrutura semantica.

Definindo Nos da AST em Python

Python
from dataclasses import dataclass
from typing import List, Optional

# Nos da AST para uma mini-linguagem
@dataclass
class NumberLit:
    value: float

@dataclass
class StringLit:
    value: str

@dataclass
class Identifier:
    name: str

@dataclass
class BinOp:
    op: str
    left: 'Expr'
    right: 'Expr'

@dataclass
class Assignment:
    target: Identifier
    value: 'Expr'

@dataclass
class FuncCall:
    name: str
    args: List['Expr']

@dataclass
class IfStmt:
    condition: 'Expr'
    then_body: List['Stmt']
    else_body: Optional[List['Stmt']]

# Construir a AST de: resultado = taxa * 100 + bonus
ast = Assignment(
    target=Identifier("resultado"),
    value=BinOp(
        op="+",
        left=BinOp(
            op="*",
            left=Identifier("taxa"),
            right=NumberLit(100)
        ),
        right=Identifier("bonus")
    )
)
print(ast)

Visitor Pattern

O Visitor pattern permite percorrer a AST e executar operacoes diferentes em cada tipo de no sem modificar as classes dos nos:

Python
class ASTVisitor:
    """Visitor base — despacha para visit_TipoDoNo."""
    def visit(self, node):
        method = f'visit_{type(node).__name__}'
        visitor = getattr(self, method, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        raise NotImplementedError(f"Sem visitor para {type(node).__name__}")

class Evaluator(ASTVisitor):
    """Avalia expressoes numericas."""
    def __init__(self):
        self.env = {}

    def visit_NumberLit(self, node):
        return node.value

    def visit_Identifier(self, node):
        return self.env.get(node.name, 0)

    def visit_BinOp(self, node):
        left = self.visit(node.left)
        right = self.visit(node.right)
        ops = {'+': lambda a,b: a+b, '-': lambda a,b: a-b,
               '*': lambda a,b: a*b, '/': lambda a,b: a/b}
        return ops[node.op](left, right)

    def visit_Assignment(self, node):
        value = self.visit(node.value)
        self.env[node.target.name] = value
        return value

# Uso
evaluator = Evaluator()
evaluator.env = {'taxa': 0.15, 'bonus': 50}
result = evaluator.visit(ast)
print(f"resultado = {result}")  # resultado = 65.0

class PrettyPrinter(ASTVisitor):
    """Imprime a AST como codigo."""
    def visit_NumberLit(self, node): return str(node.value)
    def visit_Identifier(self, node): return node.name
    def visit_BinOp(self, node):
        return f"({self.visit(node.left)} {node.op} {self.visit(node.right)})"
    def visit_Assignment(self, node):
        return f"{node.target.name} = {self.visit(node.value)}"

printer = PrettyPrinter()
print(printer.visit(ast))  # resultado = ((taxa * 100) + bonus)

Python ast Module Deep Dive

Python
import ast

# Python expoe sua propria AST via o modulo ast
code = 'resultado = taxa * 100 + bonus'
tree = ast.parse(code)

# NodeVisitor do Python — mesmo padrao Visitor
class NameCollector(ast.NodeVisitor):
    def __init__(self):
        self.names = set()
    def visit_Name(self, node):
        self.names.add(node.id)
        self.generic_visit(node)

collector = NameCollector()
collector.visit(tree)
print(f"Variaveis usadas: {collector.names}")
# Variaveis usadas: {'resultado', 'taxa', 'bonus'}

# NodeTransformer — modifica a AST
class ConstantFolder(ast.NodeTransformer):
    """Otimizacao: calcula expressoes constantes em compile-time."""
    def visit_BinOp(self, node):
        self.generic_visit(node)
        if isinstance(node.left, ast.Constant) and isinstance(node.right, ast.Constant):
            try:
                result = eval(compile(ast.Expression(node), '<ast>', 'eval'))
                return ast.Constant(value=result)
            except:
                pass
        return node

No harness.os

O knowledge graph do harness.os pode ser modelado como uma AST. As hierarquias de concerns, domains, e knowledge chunks formam uma arvore que pode ser percorrida com o Visitor pattern:

Python
@dataclass
class HarnessNode:
    pass

@dataclass
class Project(HarnessNode):
    slug: str
    domains: List['Domain']

@dataclass
class Domain(HarnessNode):
    name: str
    concerns: List['Concern']

@dataclass
class Concern(HarnessNode):
    name: str
    knowledge: List['Knowledge']

@dataclass
class Knowledge(HarnessNode):
    title: str
    content: str
    tokens: int  # custo em tokens

class TokenCounter(ASTVisitor):
    """Conta tokens totais por concern — para token economy."""
    def visit_Project(self, node):
        return sum(self.visit(d) for d in node.domains)
    def visit_Domain(self, node):
        return sum(self.visit(c) for c in node.concerns)
    def visit_Concern(self, node):
        return sum(self.visit(k) for k in node.knowledge)
    def visit_Knowledge(self, node):
        return node.tokens

Resumo

Exercicio

Construa uma representacao AST para regras do harness.os e implemente dois visitors: um que conta o total de tokens (para analise de token economy) e outro que gera uma representacao textual formatada da regra.

Verifique seu entendimento

Qual e a principal diferenca entre uma parse tree e uma AST?

  • Parse tree e gerada por parsers top-down; AST por parsers bottom-up
  • A AST omite nao-terminais intermediarios e mantem apenas a estrutura semantica
  • ASTs so existem em linguagens tipadas estaticamente
  • Parse trees sao mais eficientes em memoria