2009-11-05 4 views
3

Quelqu'un pourrait-il me donner quelques conseils/idées sur la façon de gérer les situations lorsqu'il est nécessaire de regarder d'autres déclarations pour pouvoir faire des actions sémantiques correctes sur le moment actuel? Par exemple, il s'agit d'une occurrence bien connue lorsque quelqu'un écrit un interpréteur/compilateur d'un langage de programmation qui ne supporte pas les "déclarations anticipées". Prenons un exemple:Stimuler l'esprit et transmettre les problèmes de déclarations

foo(123);//<-- our parser targets here. we estimate we have a function 
     // invocation, but we have no idea about foo declaration/prototype, 
     //  so we can't be sure that "foo" takes one integer argument. 


void foo(int i){ 
//... 
} 

Il est assez clair que nous devons avoir au moins deux passes. Premièrement nous analysons toutes les déclarations de fonction et obtenons toutes les informations nécessaires telles que: le nombre d'arguments que la fonction prend, leurs types et ensuite nous sommes capables de traiter les invocations de fonction et de résoudre les difficultés comme ci-dessus. Si nous allons de cette façon, nous devrons faire toutes ces passes en utilisant certains AST traversant les mécanismes/visiteurs. Dans ce cas, nous devons faire face à AST traversant/application des visiteurs et nous devons dire "au revoir" à toute la beauté des constructions de phénix intégrées directement dans nos parseurs.

Comment gérez-vous cela?

Répondre

3

[2ème réponse, sur la sémantique] Cet exemple particulier s'avère être simple. Ce que vous pouvez faire est d'enregistrer les appels de fonction faits à des fonctions non encore déclarées, et les types d'arguments réels. Lorsque vous rencontrez une déclaration de fonction plus tard, vous vérifiez s'il y a des appels de fonction précédents qui correspondent (mieux) à cette nouvelle fonction. Vous ne détecterez évidemment les erreurs qu'à la fin de l'analyse, car la toute dernière ligne pourrait introduire une fonction manquante. Mais après cette ligne, tout appel de fonction qui n'a pas été trouvé du tout est une erreur.

Maintenant, le problème est que cela fonctionne pour la sémantique simple. Si vous regardez des langues plus complexes - par ex. avec la fonction de type C++ templates - il n'est plus possible de faire de telles recherches dans un tableau simple. Vous auriez besoin de tabes spécialisés qui correspondent structurellement à vos constructions linguistiques. Un AST n'est pas la meilleure structure pour ceux-là, et encore moins l'AST partiel pendant l'analyse.

+0

bon point. MSalters, ne connaissez-vous pas de façons de faire de l'esprit-boost pour faire ces "multi-passes" en restant au boost? il n'y a pas tellement de plaisir à parcourir/coder ces passes manuellement. Existe-t-il des façons de dire à boost-spirit d'effectuer deux passes d'analyse: la première ne traiterait que les déclarations de fonction, ignorant tout le reste, et la seconde s'occuperait d'analyser tous les autres, faisant les vérifications sémantiques sur place. – varnie

+0

En fait, mon but était d'utiliser une seule passe d'analyse de l'arbre pour construire des tables de tout ce que vous rencontrez, puis extraire la sémantique des tables. Et le but d'une syntaxe correcte est de garder ces tables simples. – MSalters

1

Je pense que vous faites des hypothèses non fondées. Par exemple, "il est clair que nous devons avoir au moins deux passes". Non ce n'est pas. Si la syntaxe est telle que foo(123) ne peut être analysé que sous la forme function-name "(" expression ")", une passe suffit.

Par conséquent je conseillerais de concevoir votre syntaxe pour l'analyse non ambiguë. Évitez les constructions qui ne peuvent pas être analysées isolément, par ex. éviter les dépendances sur les déclarations ailleurs.

+0

Merci pour la réponse. Oui, j'évite la syntaxe ambiguë. vous avez raison, j'ai le nom de fonction "(" expression ")" comme syntaxe pour les invocations de fonctions. mon problème, comme je l'ai décrit ci-dessus, est un ** sémantique **: la fonction "foo" obtient-elle un argument int, ou une chaîne, un booléen, etc etc? au moment où nous venons d'analyser "foo (123)", nous ne pouvons pas être sûrs qu'il s'agit d'une invocation de fonction sémantiquement correcte. par conséquent, nous avons besoin d'analyser à l'avance pour effectuer des vérifications sémantiques correctes sur cette invocation, donc il y a au moins deux passages dont j'ai parlé. J'espère que ma question est claire. – varnie

+0

en bref, je parle de la question sémantique pas l'analyse. – varnie

2

Si vous voulez faire deux passes, au lieu de vérifier sémantique à la fin de la première passe, vous pouvez avoir des fonctions te appelées par vos actions savoir qui passent, ils sont. Donc, si vous aviez des actions

[functionCall(name, args)] 
[functionDef(name, args, body)] 

Ils seraient définis quelque chose comme ça (pas la syntaxe correcte de l'esprit, mais vous obtenez le point)

functionCall(string name, vector<string> args) 
{ 
    if (!first_pass) { 
    // check args for validity 
    // whatever else you need to do 
    } 
} 

functionDef(string name, vector<string> args, ... body) 
{ 
    if (first_pass) 
    // Add function decleration to symbol table 
    else 
    // define function 
} 
Questions connexes