2012-03-27 7 views
11

Ich bin ein kompletter Neuling, wenn es um OCaml geht. Ich habe erst vor kurzem mit der Sprache angefangen (vor ungefähr 2 Wochen), aber leider wurde ich damit beauftragt, einen Syntaxanalysator (Parser + Lexer, dessen Funktion es ist, einen Satz zu akzeptieren oder nicht) für eine erfundene Sprache zu machen Menhir verwenden. Nun habe ich im Internet einige Materialien zu OCaml und Menhir gefunden:OCaml + Menhir Kompilieren/Schreiben

Das Menhir Manual.

This webpage for some French University course.

Ein kurzer Menhir Tutorial auf Toss der Homepage bei Source.

Ein Menhir-Beispiel auf GitHub von DerDon.

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

Eine zufällige ocamllex Tutorial von SooHyoung Oh.

Und die Beispiele, die mit Menhirs Quellcode kommen.

(Ich kann nicht mehr als zwei Hyperlinks setzen, so kann ich Ihnen nicht direkt verlinkt auf einige der Websites ich hier zu erwähnen. Es tut uns Leid!)

So, wie Sie sehen können, ich Ich habe verzweifelt nach mehr und mehr Material gesucht, um mir bei der Erstellung dieses Programms zu helfen. Leider kann ich immer noch nicht viele Konzepte erfassen, und als solche habe ich viele, viele Schwierigkeiten.

Für den Anfang habe ich keine Ahnung, wie ich mein Programm richtig kompiliere. Ich habe den folgenden Befehl unter Verwendung von:

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

Mein Programm in vier verschiedenen Dateien aufgeteilt: main.ml; lexer.mll; parser.mly; Token. main.ml ist der Teil, der Eingaben von einer Datei im Dateisystem erhält, die als Argument angegeben ist.

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) 

Die zweite Datei ist 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))) } 

Die dritte Datei ist parser.mly.

%start <bool> main 
%% 

main: 
| WRITEINT INT { true } 

Der vierte ist 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 
%} 

%% 

Nun, ich weiß, es gibt eine Menge von nicht verwendeten Symbole ist hier, aber ich beabsichtige, sie in meinem Parser zu verwenden. Egal wie viele Änderungen ich an den Dateien mache, der Compiler bläht sich immer wieder auf meinem Gesicht auf. Ich habe alles versucht, was mir einfällt, und nichts scheint zu funktionieren. Was macht ocamlbuild in einer Fülle von Fehlern von ungebundenen Konstruktoren und nicht definierten Startsymbolen explodieren? Welchen Befehl sollte ich verwenden, um das Programm richtig zu kompilieren? Wo kann ich aussagekräftige Materialien über Menhir finden?

Antwort

8

Eine einfachere Möglichkeit ist das Entfernen der Parser/Tokens Trennung. Wie Thomas anmerkte, gibt es keine Notwendigkeit für eine Deklaration type token = ..., weil es automatisch von Menhir aus %token Direktiven produziert wird.

%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 } 

und lexer.mll als:

So können Sie parser.mly wie definieren

{ 
    open Parser 
    exception Error of string 
} 

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

dann tokens.mly entfernen, und kompilieren mit

ocamlbuild -use-menhir main.native 

und es funktioniert alles gut.

+0

In der Tat ist es einfacher, nur einen 'mly' zu haben. Ich habe diese Lösung in meiner Antwort nicht vorgeschlagen, weil ich annahm, dass @Lopson die Funktion "separate Compilation of Parsing Units" von Menhir verwenden wollte. – Thomas

+0

Danke für all die Hilfe, Leute, du hast keine Ahnung, wie wertvoll deine Beiträge für mich waren! Schließlich fangen die Dinge an, einen Sinn zu ergeben. –

7

So zuerst, brauchen Sie nicht die Tokens in tokens.mly Repet:

%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 

%% 

Dann weiß ich nicht, die magische Option ocamlbuild passieren und ich weiß nicht, menhir sehr gut, aber, in meinem Verständnis müssen Sie "Pack" alle .mly in einer Parsereinheit:

menhir tokens.mly parser.mly -base parser 

Dann, wenn Sie alle Vorkommen von Token byt Parser in lexer.mll ersetzen, ocamlbuild -no-hygiene main.byte sollte funktionieren. Beachten Sie jedoch, dass es einen cleveren Weg gibt, dies zu tun.

1

Ich stieß auf das gleiche Problem, außer dass der Parser zusätzlich Module außerhalb der aktuellen direkten benötigt. . Ich konnte nicht herausfinden, wie ocamlbuild aufzurufen, die Parser angeben {ml, MLI} hatte von 3 mly Dateien gebaut werden, so dass ich einfach eine Make-Datei, die:

  • kopiert die Module von _build .cmi in das aktuelle Verzeichnis
  • aufrufen menhir
  • entfernen sie die kopierten Module (bis menhir --infer erfüllen), mit ihm zu befriedigen ocamlbuild
  • dann rufen ocamlbuild

ich bin nicht zufrieden, so dass ich bin interessiert in irgendeiner besseren alt ernative, aber wenn Sie wirklich Ihr Projekt mit minimalem Aufwand zu beenden, ich denke, das ist der Weg zu gehen

edit: Eigentlich ist es nicht notwendig, kopieren und entfernen Sie die kompilierten Module, übergeben Sie einfach die Option Menhir um der zweite Schritt: menhir --ocamlc "ocamlc -I \" ../_ build/modules/\ "" --infer --base Parser

Bedauerlicherweise bedeutet dies, dass Standbilder der Parser die vorherige Generation sein wird wrt Kompilierung der Module, daher ist eine unnötige (und gescheiterte) erste Kompilierung zu erwarten.