2012-03-27 2 views
11

Je suis un débutant complet quand il s'agit de OCaml. J'ai récemment commencé à utiliser la langue (il y a environ 2 semaines), mais malheureusement, j'ai été chargé de faire un analyseur de syntaxe (parser + lexer, dont la fonction est d'accepter ou non une phrase) pour un langage inventé en utilisant Menhir. Maintenant, j'ai trouvé quelques documents sur Internet concernant OCaml et Menhir:OCaml + Menhir Compilation/écriture

Le Manuel Menhir.

This webpage for some French University course.

Un court tutoriel Menhir sur la page d'accueil de Toss à Sourceforge.

Un exemple de Menhir sur github par derdon.

A book on OCaml (with a few things about ocamllex+ocamlyacc

Un tutoriel ocamllex par hasard SooHyoung Oh.

Et les exemples fournis avec le code source de Menhir.

(je ne peux pas mettre plus de deux liens hypertexte, donc je ne peux pas vous relier directement à certains des sites Web je mentionner ici. Désolé!)

Donc, comme vous pouvez le voir, je J'ai cherché désespérément de plus en plus de matériel pour m'aider dans la réalisation de ce programme. Malheureusement, je n'arrive toujours pas à saisir de nombreux concepts et, en tant que tel, j'ai beaucoup, beaucoup de difficultés.

Pour commencer, je ne sais pas comment compiler correctement mon programme. J'ai utilisé la commande suivante:

ocamlbuild -use-menhir -menhir "menhir --external-tokens Tokens" main.native 

Mon programme est divisé en quatre fichiers différents: main.ml; lexer.mll; parser.mly; tokens.mly. main.ml est la partie qui reçoit l'entrée d'un fichier dans le système de fichiers donné en argument.

let filename = Sys.argv.(1) 

let() = 
    let inBuffer = open_in filename in 
    let lineBuffer = Lexing.from_channel inBuffer in 
    try 
     let acceptance = Parser.main Lexer.main lineBuffer in 
     match acceptance with 
      | true -> print_string "Accepted!\n" 
      | false -> print_string "Not accepted!\n" 
    with 
     | Lexer.Error msg -> Printf.fprintf stderr "%s%!\n" msg 
     | Parser.Error -> Printf.fprintf stderr "At offset %d: syntax error.\n%!" (Lexing.lexeme_start lineBuffer) 

Le deuxième fichier est lexer.mll.

{ 
    open Tokens 
    exception Error of string 
} 

rule main = parse 
    | [' ' '\t']+ 
     { main lexbuf } 
    | ['0'-'9']+ as integer 
     { INT (int_of_string integer) } 
    | "True" 
     { BOOL true } 
    | "False" 
     { BOOL false } 
    | '+' 
     { PLUS } 
    | '-' 
     { MINUS } 
    | '*' 
     { TIMES } 
    | '/' 
     { DIVIDE } 
    | "def" 
     { DEF } 
    | "int" 
     { INTTYPE } 
    | ['A'-'Z' 'a'-'z' '_']['0'-'9' 'A'-'Z' 'a'-'z' '_']* as s 
     { ID (s) } 
    | '(' 
     { LPAREN } 
    | ')' 
     { RPAREN } 
    | '>' 
     { LARGER } 
    | '<' 
     { SMALLER } 
    | ">=" 
     { EQLARGER } 
    | "<=" 
     { EQSMALLER } 
    | "=" 
     { EQUAL } 
    | "!=" 
     { NOTEQUAL } 
    | '~' 
     { NOT } 
    | "&&" 
     { AND } 
    | "||" 
     { OR } 
    | '(' 
     { LPAREN } 
    | ')' 
     { RPAREN } 
    | "writeint" 
     { WRITEINT } 
    | '\n' 
     { EOL } 
    | eof 
     { EOF } 
    | _ 
     { raise (Error (Printf.sprintf "At offset %d: unexpected character.\n" (Lexing.lexeme_start lexbuf))) } 

Le troisième fichier est parser.mly.

%start <bool> main 
%% 

main: 
| WRITEINT INT { true } 

Le quatrième est tokens.mly

%token <string> ID 
%token <int> INT 
%token <bool> BOOL 
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT 
%token PLUS MINUS TIMES DIVIDE 
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%token NOT AND OR 

%left OR 
%left AND 
%nonassoc NOT 
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%left PLUS MINUS 
%left TIMES DIVIDE 
%nonassoc LPAREN 
%nonassoc ATTRIB 

%{ 
type token = 
    | ID of (string) 
    | INT 
    | BOOL 
    | DEF 
    | INTTYPE 
    | LPAREN 
    | RPAREN 
    | WRITEINT 
    | PLUS 
    | MINUS 
    | TIMES 
    | DIVIDE 
    | LARGER 
    | SMALLER 
    | EQLARGER 
    | EQSMALLER 
    | EQUAL 
    | NOTEQUAL 
    | NOT 
    | AND 
    | OR 
    | EOF 
    | EOL 
%} 

%% 

Maintenant, je sais qu'il ya beaucoup de symboles inutilisés ici, mais je l'intention de les utiliser dans mon analyseur. Peu importe le nombre de modifications que je fais pour les fichiers, le compilateur continue à exploser sur mon visage. J'ai essayé tout ce que je peux penser, et rien ne semble fonctionner. Qu'est-ce qui fait qu'ocamlbuild explose dans une pléthore d'erreurs de constructeurs non liés et de symboles de départ non définis? Quelle commande dois-je utiliser pour compiler correctement le programme? Où puis-je trouver des documents significatifs sur Menhir?

Répondre

8

Une façon plus simple de procéder consiste à supprimer la séparation Parser/Tokens. Comme Thomas l'a noté, il n'y a pas besoin de déclaration type token = ..., car il est automatiquement produit par menhir à partir des directives %token.

Vous pouvez définir parser.mly comme:

%start <bool> main 

%token <string> ID 
%token <int> INT 
%token <bool> BOOL 
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT 
%token PLUS MINUS TIMES DIVIDE 
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%token NOT AND OR 

%left OR 
%left AND 
%nonassoc NOT 
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%left PLUS MINUS 
%left TIMES DIVIDE 
%nonassoc LPAREN 
%nonassoc ATTRIB 
%% 

main: 
| WRITEINT INT { true } 

et lexer.mll comme:

{ 
    open Parser 
    exception Error of string 
} 

[...] (* rest of the code not shown here *) 

puis retirez tokens.mly et compilez avec

ocamlbuild -use-menhir main.native 

et tout fonctionne bien.

+0

En effet, avoir un seul 'mly' est beaucoup plus simple. Je n'ai pas proposé cette solution dans ma réponse parce que je supposais que @Lopson voulait utiliser la fonction «compilation séparée des unités d'analyse» du menhir. – Thomas

+0

Merci pour toute l'aide, les gars, vous n'avez aucune idée de la valeur de vos messages pour moi! Finalement, les choses commencent à avoir du sens. –

7

Alors d'abord, vous n'avez pas besoin de répet les jetons en tokens.mly:

%token <string> ID 
%token <int> INT 
%token <bool> BOOL 
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT 
%token PLUS MINUS TIMES DIVIDE 
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%token NOT AND OR 

%left OR 
%left AND 
%nonassoc NOT 
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL 
%left PLUS MINUS 
%left TIMES DIVIDE 
%nonassoc LPAREN 
%nonassoc ATTRIB 

%% 

Alors, je ne sais pas l'option magique pour passer à ocamlbuild et je ne sais pas très bien menhir, mais, dans ma compréhension dont vous avez besoin de « pack » tous les .mly en une seule unité analyseur:

menhir tokens.mly parser.mly -base parser 

Ensuite, si vous remplacez toute occurrence de Token byt Parser dans lexer.mll, ocamlbuild -no-hygiene main.byte devrait fonctionner. Notez cependant qu'il existe peut-être une manière intelligente de le faire.

1

J'ai rencontré le même problème, sauf que, de plus, l'analyseur avait besoin de modules en dehors du courant direct. . Je ne pouvais pas comprendre comment appeler ocamlbuild pour spécifier que l'analyseur {ml, MLI} a dû être construit à partir de 3 fichiers MLY, donc je simplement fait une makefile que:

  • copie les modules .cmi de _build dans le répertoire courant (pour satisfaire menhir --infer)
  • invoquez menhir
  • supprimer les modules copiés pour satisfaire ocamlbuild
  • invoquez ensuite ocamlbuild

Je ne suis pas satisfait, je suis intéressé en tout meilleur alt ernative, mais si vous devez vraiment finir votre projet avec un minimum d'effort, je suppose que c'est la voie à suivre

éditer: En fait, il n'y a pas besoin de copier et supprimer les modules compilés, il suffit de passer l'option à menhir à la deuxième étape: menhir --ocamlc "ocamlc -I \" ../_ build/modules/\ "" --infer --base parser

Malheureusement, cela signifie que la génération de l'analyseur sera effectuée par rapport à la précédente compilation des modules, d'où une première compilation inutile (et échouée) est à prévoir.