2012-10-03 5 views
1

J'ai écrit un petit compilateur pour une machine à pile simple. Il peut assembler et gérer l'étendue/les fonctions via un nombre de hacks de machine virtuelle uniquement. C'est-à-dire que j'ai défini la portée et les définitions des variables de portée dans le bytecode lui-même.Comment gérer la portée lors de la génération de bytecode avec un compilateur manuscrit

Puis-je obtenir quelques conseils sur la façon dont je transportera plus étendue.

Les problèmes que je rencontre sont surtout, comment puis-je le faire savoir quand et quand ne pas écraser une variable en dehors d'une variable à l'intérieur, et similaire. Le bytecode est mutable et je préférerais le changer.

D'autres problèmes incluent comment conserver les variables à l'extérieur après le retour. Alors qu'une variable a encore sa valeur. Je peux le pousser à empiler, mais je pourrais avoir beaucoup de variables.

Je pense qu'il ya un travail de compilateur fait pour vérifier ces choses, mais je ne peux pas penser à ce qu'il faudrait faire pour le faire.

+0

Voulez-vous parler des fermetures? – delnan

+0

Sorta, mais non. Fondamentalement, comment générer bytecode pour gérer les variables avec la portée, ou plutôt des exemples de (qui pourrait être plus utile). – Chase

Répondre

2

Une façon serait de rename variables at compile time to ensure there is no masking. donc:

{ 
    declare foo; 
    foo = assignment; 
    { 
    declare foo; 

    foo = another_assignment; 
    another_use = foo; 
    } 
    use = foo; 
} 

équivaut à:

{ 
    declare foo_0; 
    foo_0 = assignment; 
    { 
    declare foo_1; 

    foo_1 = another_assignment; 
    another_use = foo_1; 
    } 
    use = foo_0; 
} 

Lors de la compilation, vous maintenez un 'renommer pile' par variable. Et:

  1. Chaque fois que vous voyez une déclaration, vous générez un nouveau nom et l'exposez à la pile de renommer de la variable correspondante.

  2. Quand vous voyez une cession/utilisation, vous remplacez le nom avec tout ce qui est sur le dessus de la pile.

  3. Lorsque vous quittez une portée, vous pop de la pile.

+0

Merci, c'est vraiment très utile. Je n'ai pas pensé à garder une pile de renommer séparée pour chaque variable. J'essayais de le faire sur une base de portée. De cette façon, c'est beaucoup plus élégant. Je vous remercie! – Chase

+0

Comment puis-je gérer les instructions récursives? C'est une question qui m'a un peu dérangé. – Chase

+1

Le masquage dû à la portée peut être traité de la manière que j'ai expliquée, * au moment de la compilation *, c'est-à-dire en renommant pour éviter le masquage. D'autre part, les appels récursifs (et même les appels réguliers) doivent être traités * lors de l'exécution *. c'est-à-dire dans la pile d'appels. C'est vraiment un problème tout à fait différent. Sauf qu'il y a une pile impliquée là aussi;) – ArjunShankar

Questions connexes