Análise Sintática¶
Status: Concluída
Consome a sequência de tokens produzida pelo lexer e constrói a Árvore Sintática Abstrata (AST).
A entrada é Vec<Token> e a saída é Program (uma lista de declarações globais) mais um vetor de diagnósticos.
Estrutura do Parser¶
struct Parser {
tokens: Vec<Token>,
pos: usize,
diagnostics: Vec<CompilerError>,
type_names: HashSet<String>, // aliases de typedef registrados
}
| Método | Comportamento |
|---|---|
peek() |
Token atual sem avançar |
peek_at(n) |
Token em pos + n (lookahead arbitrário) |
advance() |
Consome token atual |
expect(kind, msg) |
Consome ou emite SyntaxError |
starts_type() |
Retorna true se o token atual inicia um tipo |
Recuperação de Erros¶
Quando parse_global_item() falha, o erro é acumulado em diagnostics e synchronize() é chamado.
A sincronização descarta tokens até encontrar ; ou }, depois continua o próximo item global.
Isso permite que um arquivo com múltiplos erros produza todos os diagnósticos de uma vez.
Hierarquia de Parse¶
parse_program()
└── parse_global_item() ← loop até EOF
├── parse_struct_decl() ← struct Nome {
├── parse_enum_decl() ← enum Nome {
├── parse_typedef_decl() ← typedef tipo Alias;
├── parse_function_decl() ← tipo nome (
└── parse_global_var_decl() ← variável global
parse_function_decl()
└── parse_block()
└── parse_stmt() ← loop até }
├── parse_var_decl()
├── parse_block() ← recursivo
├── if / while / for / do-while / switch
├── return / break / continue
└── parse_expr() + ;
parse_expr(min_bp) ← Pratt parser
├── parse_prefix_expr()
└── loop:
├── try_parse_postfix()
└── infix com binding power
Declarações Globais¶
O dispatcher usa lookahead para decidir o tipo de declaração sem consumir tokens:
peek[0] == Struct && peek[1] == Ident && peek[2] == { → parse_struct_decl()
peek[0] == Enum && peek[1] == Ident && peek[2] == { → parse_enum_decl()
peek[0] == Typedef → parse_typedef_decl()
senão: parse_type() + nome
peek == ( → parse_function_decl()
senão → parse_global_var_decl()
Struct¶
struct Ponto {
int x;
int y;
};
Produz Decl::StructDecl(nome, Vec<(QualifierType, String)>, span).
Enum¶
enum Cor { VERMELHO, VERDE = 10, AZUL };
Produz Decl::EnumDecl(nome, Vec<(String, Option<Expr>)>, span).
Typedef¶
typedef unsigned int uint32_t;
Produz Decl::Typedef(QualifierType, alias_nome, span).
Após o parse, o alias é registrado em type_names para que expressões como (uint32_t)x sejam reconhecidas como cast.
Função¶
int soma(int a, int b) { return a + b; }
Produz Decl::Function(retorno, nome, params, stmts, span).
Tipos (parse_type)¶
O parse de tipos segue três etapas:
- Qualificadores —
const,unsignedem qualquer ordem - Tipo base —
int,long,short,char,float,double,void,struct Nome,enum Nome, ou um alias de typedef - Ponteiros — loop que consome
*, envolvendo o tipo emPointer(T)
const unsigned int ** → QualifierType {
ty: Pointer(Pointer(Int)),
is_const: true, is_unsigned: true
}
Sufixo de array (parse_array_suffix) — chamado após o identificador, não após o tipo:
int arr[10][20]
// ^^^^^^^^^ envolve em Array(Array(Int))
Statements¶
| Token | Statement produzido |
|---|---|
{ |
Stmt::Block(stmts) |
return |
Stmt::Return(Option<Expr>) |
break |
Stmt::Break |
continue |
Stmt::Continue |
if |
Stmt::If(cond, then, Option<else>) |
while |
Stmt::While(cond, body) |
do |
Stmt::DoWhile(cond, body) |
for |
Stmt::For(Option<init>, Option<cond>, Option<inc>, body) |
switch |
Stmt::Switch(expr, Vec<SwitchCase>) |
starts_type() |
Stmt::VarDecl(qty, nome, Option<init>) |
| qualquer outro | Stmt::ExprStmt(expr) |
O for aceita declaração de variável no init:
for (int i = 0; i < n; i++) { ... }
Expressões — Pratt Parser¶
O Pratt parser controla precedência via binding powers sem criar regras gramaticais por nível.
Tabela de binding powers¶
| Operadores | lbp / rbp |
|---|---|
= += -= *= /= %= &= \|= ^= <<= >>= |
1 / 1 (direita) |
\|\| |
4 / 5 |
&& |
6 / 7 |
\| |
8 / 9 |
^ |
10 / 11 |
& |
12 / 13 |
== != |
14 / 15 |
< <= > >= |
16 / 17 |
<< >> |
18 / 19 |
+ - |
20 / 21 |
* / % |
22 / 23 |
| prefix unário, cast | — / 30 |
postfix ++ -- [] () . -> |
> 30 |
Algoritmo¶
parse_expr(min_bp):
lhs = parse_prefix_expr()
loop:
se try_parse_postfix(lhs) → continua
op = peek()
(lbp, rbp) = infix_binding_power(op)
se lbp < min_bp → para
consome op
rhs = parse_expr(rbp)
lhs = Binary/Assign/CompoundAssign(lhs, op, rhs)
retorna lhs
Exemplo: 1 + 2 * 3 produz Binary(1, Add, Binary(2, Mul, 3)) porque * tem lbp=22 > rbp=19 de +.
Expressões prefixas¶
| Token | Nó AST |
|---|---|
IntLiteral(v) |
Literal::Int(v) |
FloatLiteral(v) |
Literal::Double(v) |
Identifier(n) |
Expr::Ident(n) |
- ! ~ * & |
Expr::Unary(op, inner) |
++ -- |
Expr::Prefix(op, inner) |
(tipo) expr |
Expr::Cast(qty, inner) |
sizeof(tipo) |
Expr::SizeofType(qty) |
sizeof expr |
Expr::Sizeof(inner) |
(expr) |
agrupamento |
A distinção cast vs. agrupamento é feita por lookahead: ( seguido de starts_type() → cast.
Expressões postfixas¶
| Token | Nó AST |
|---|---|
++ -- |
Expr::Postfix(op, lhs) |
[expr] |
Expr::Index(lhs, idx) |
(args) |
Expr::Call(lhs, args) |
.nome |
Expr::Member(lhs, Direct, campo) |
->nome |
Expr::Member(lhs, Pointer, campo) |
AST — Nós Produzidos¶
Decl¶
Decl::Function(retorno, nome, params, stmts, span)
Decl::GlobalVar(qty, nome, Option<init>, span)
Decl::StructDecl(nome, campos, span)
Decl::EnumDecl(nome, Vec<(String, Option<Expr>)>, span)
Decl::Typedef(qty, alias_nome, span)
Stmt¶
Stmt::Block(stmts, span)
Stmt::VarDecl(qty, nome, Option<init>, span)
Stmt::ExprStmt(expr, span)
Stmt::Return(Option<expr>, span)
Stmt::If(cond, then, Option<else>, span)
Stmt::While(cond, body, span)
Stmt::DoWhile(cond, body, span)
Stmt::For(Option<init>, Option<cond>, Option<inc>, body, span)
Stmt::Switch(expr, Vec<SwitchCase>, span)
Stmt::Break(span)
Stmt::Continue(span)
Expr¶
Expr::Literal(Literal, span) // Int(i64), Double(f64), Char(char), String(String)
Expr::Ident(String, span)
Expr::Binary(lhs, BinOp, rhs, span)
Expr::Unary(UnOp, inner, span) // -, !, ~, *, &
Expr::Prefix(PrefixOp, inner, span) // ++, -- prefixo
Expr::Postfix(PostfixOp, inner, span) // ++, -- sufixo
Expr::Assign(lhs, rhs, span)
Expr::CompoundAssign(BinOp, lhs, rhs, span) // +=, -=, ...
Expr::Call(callee, args, span)
Expr::Index(arr, idx, span)
Expr::Cast(QualifierType, inner, span)
Expr::Sizeof(inner, span)
Expr::SizeofType(QualifierType, span)
Expr::Ternary(cond, then, else, span)
Expr::Member(obj, MemberAccess, campo, span) // Direct (.) | Pointer (->)
Type¶
Type::Int | Long | Short | Char | Float | Double | Void
Type::Pointer(Box<Type>)
Type::Array(Box<Type>) ← tamanho não armazenado
Type::Struct(String) ← referência por nome
Type::Enum(String) ← referência por nome
Type::Alias(String) ← nome de typedef; resolvido pelo analisador semântico
QualifierType { ty: Type, is_const: bool, is_unsigned: bool }