2016-06-01 7 views
1

J'écris un frontal pour une langue (par ocamllex et ocamlyacc).Manipuler fidèlement les espaces blancs dans une jolie imprimante

Ainsi, le frond-end peut créer un Abstract Syntax Tree (AST) à partir d'un programme. Ensuite, nous écrivons souvent une jolie imprimante, qui prend un AST et imprime un programme. Si plus tard nous voulons juste compiler ou analyser l'AST, la plupart du temps, nous n'avons pas besoin que le programme imprimé soit exactement le même que le programme original, en termes d'espacement des blancs. Cependant, cette fois, je veux écrire une jolie imprimante qui imprime exactement le même programme que l'original, en termes d'espacement des blancs. Par conséquent, ma question est de savoir quelles sont les meilleures pratiques pour gérer l'espacement des blancs tout en essayant de ne pas trop modifier les types d'ASAT. Je ne veux vraiment pas ajouter un nombre (d'espaces blancs) à chaque type dans l'AST.

Par exemple, voici comment je traite actuellement avec (c.-à-sauter) espacement blanc lexer.mll:

rule token = parse 
    ... 
    | [' ' '\t']  { token lexbuf }  (* skip blanks *) 
    | eof    { EOF } 

Est-ce que quelqu'un sait comment changer cela ainsi que d'autres parties du front-end prendre correctement en compte l'espacement des blancs pour une impression ultérieure?

+0

Si votre jolie imprimante n'altère en aucun cas les espaces, que fait-elle exactement qui justifie le mot "jolie"? :) En d'autres termes, pourquoi ne régurgitez-vous pas tout le texte d'entrée? – rici

+0

Je vois ... c'est parce que pour certaines parties d'un programme, je ne veux pas modifier les espaces blancs. Par exemple, pour un appel de fonction 'f (arg0, arg1, arg2, arg3)', je veux garder tel quel, plutôt que de le changer en joli 'f (arg0, arg1, arg2, arg3)'. – SoftTimur

Répondre

1

Il est assez fréquent de conserver les informations d'emplacement du fichier source pour chaque jeton. Cette information permet des erreurs plus précises, par exemple.

La manière la plus générale de le faire est de conserver le numéro de ligne de début et de fin et la position de colonne pour chaque jeton, soit un total de quatre nombres. S'il était facile de calculer la position finale d'un jeton à partir de sa valeur et de sa position de départ, cela pourrait être réduit à deux nombres, mais au prix d'une complexité de code supplémentaire.

Bison possède certaines caractéristiques qui simplifient le travail de tenue de livres pour se souvenir des objets de localisation; il est possible que ocamlyacc inclue des fonctionnalités similaires, mais je n'ai rien vu dans la documentation. Dans tous les cas, il est simple de conserver un objet de localisation associé à chaque jeton d'entrée. Avec cette information, il est facile de recréer l'espace entre deux jetons adjacents, tant que ce qui séparait les jetons était un espace. Les commentaires sont un autre problème.

C'est un appel de jugement si cela est plus simple que de simplement attacher des espaces (et même des commentaires) précédents à chaque jeton tel qu'il est lexé.

+0

OCaml possède un type [position] (http://caml.inria.fr/pub/docs/manual-ocaml/libref/Lexing.html) pour obtenir la position d'un jeton. Pourriez-vous en dire plus sur la façon de ** maintenir ** l'emplacement associé à chaque jeton? Dois-je stocker l'emplacement ou le nombre d'espaces blancs pour chaque élément dans AST, de sorte que j'utilise cette information dans pretty-printer? – SoftTimur

+0

@softtimur: je conserverais les informations de localisation dans le jeton. Mais il y a probablement d'autres alternatives. Augmenter le jeton est simple – rici

+0

Désolé, que voulez-vous dire par "conserver les informations de localisation dans le cadre du jeton"? Comment les types ressemblent-ils? – SoftTimur

0

Vous pouvez avoir des instructions de correspondance qui impriment un nombre différent d'espaces en fonction du jeton auquel vous avez affaire. J'imprimerais habituellement 1 espace avant si le jeton est un: id, num, define statement, assign (=)

Si le jeton est une expression arithmétique, j'imprimerais un espace avant et un espace après. Si vous traitez une instruction if ou while, je mettrai le corps en retrait de quatre espaces.

Je pense que le meilleur pari serait d'écrire une fonction pretty_print tels que:

let rec pretty_print pos ast = 
    match ast with 
    |Some_token -> String.make pos ' '; (* adds 'pos' number of spaces; pos will start off as zero. *) 
        print_string "Some_token"; 
    |Other_token... 

En somme je gérer les espaces blancs en faisant correspondre chaque jeton individuellement dans une fonction récursive et imprimer le nombre approprié de les espaces.

+1

Je suppose que cela ne recréerait pas exactement le format du programme original mais cela créerait un programme parfaitement indenté –