ÍNDICE DE CONTEÚDO
O Psyche-C é um frontend de compilador para a linguagem C. Uma diferença fundamental entre ele e o Clang é que no Psyche-C existe uma separação clara e modular entre as fases de análise sintática e análise semântica. Além disso, o Psyche-C conta com um notável mecanismo de inferência de tipos, sobre o qual já escrevi aqui no Embarcados.
Atualmente o projeto passa por uma grande reformulação; como parte dela, seu parser foi reescrito praticamente do zero (inclusive, na semana passada eu publiquei um programa de Bug Bounty, no valor de R$ 100,00, para quem encontrar erros nesse componente — até o momento, há 5 recompensas ainda disponíveis), de maneira a produzir uma AST que se assemelha àquela do Clang. A API geral do Psyche-C também foi reformulada, tendo como inspiração a API do Roslyn, o compilador de .NET.
Neste artigo, mostro como visualizar a AST do Psyche-C — irei também compará-la brevemente com a AST do Clang. Considere o programa test.c
abaixo.
1 2 3 4 5 6 7 8 |
int v, f(int d); int f(int p) { if (p == v) return(p - 1); return 0; } |
Para produzir e visualizar a AST de um programa com o Psyche-C, o driver cnippet deve ser invocado com a opção --C-dump-AST
.
Já a AST produzida pelo Clang para o mesmo programa test.c
é a seguinte.
Além das cores e da decoração extra, quais são as principais diferenças entre elas?
- A AST do Psyche-C é mais próxima da gramática de C. Por exemplo, na AST do Clang os declarators não aparecem, pois eles são absorvidos pela declaration que os contém. Se essa característica é boa ou ruim, dependerá de aplicação em questão. Para fins de análise estática, considero esse refinamento sintático da AST do Psyche-C como uma vantagem.
Isso não quer dizer que todo nodo da AST deve corresponder a um terminal da gramática. Senão, acabaríamos com uma árvore de sintaxe bastante concreta, ao invés uma árvore de sintaxe abstrata. Uma AST inteligível deve conter a “medida certa” de semântica da linguagem. (Por exemplo, a AST do antigo parser do Psyche-C era muito orientada/focada no aspecto gramatical.)
Indo um pouco além, considere este snippet de test.c
:
1 |
int v, f(int d); |
No AST do Clang, ele é representado por um VarDecl
e um FunctionDecl
; ambos os nodos herdam de DeclaratorDecl
(ou seja, uma declaration que vem de um declarator). Porém, sendo razoavelmente pedante, essa representação não é totalmente correta, dado que há uma única declaration — tal que se inicia com int
termina com ;
— neste trecho do programa.
No Psyche-C, eu optei por uma representação mais rigorosa na AST:
- Um nodo
VariableAndOrFunctionDeclaration
é criado (assim como no Clang, este nodo herda deDeclaratorDeclaration
), mas ele tem dois nodos filhos: umIdentifierDeclarator
, designando objeto de tipoint
, e umFunctionDeclarator
, designando uma função cujo tipo de retorno éint
. Outra vantagem dessa é que ela torna o AST do Psyche-C precisa/acurada para fins de reescrita (i.e., unparsing).
Em geral, o “espírito” de design do AST do Psyche-C é mais alinhado com o Roslyn. Embora, como Psyche-C é um frontend para C, seus nodos serão, obviamente, semelhantes àqueles da AST do Clang.
Para encerrar, deixo uma versão C# de test.c
(bem, na verdade test.cs
), junto com sua AST para que possam realizar uma comparação mais superficial.
Saiba mais
Programando em C com inferência de tipos usando PsycheC
Orientação a objeto em C: Encapsulamento com estruturas opacas