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.
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:
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







