2012-03-27 16 views
11

Soy un completo novato en lo que respecta a OCaml. Recientemente comencé a usar el lenguaje (hace aproximadamente 2 semanas), pero desafortunadamente me han encargado hacer un analizador de sintaxis (analizador + lector, cuya función es aceptar o no una oración) para un lenguaje compuesto usando Menhir. Ahora, he encontrado algunos materiales en Internet sobre OCaml y Menhir:OCaml + Menhir Compilación/Escritura

The Menhir Manual.

This webpage for some French University course.

Un breve tutorial Menhir en la página de Toss en Sourceforge.

Un ejemplo de Menhir en github por derdon.

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

Un tutorial ocamllex azar por SooHyoung Oh.

Y los ejemplos que vienen con el código fuente de Menhir.

(no puedo poner más de dos enlaces, por lo que no se puede vincular directamente a algunos de los sitios web que estoy mencionar aquí. Lo siento!)

Por lo tanto, como se puede ver, estoy he estado buscando desesperadamente más y más material para ayudarme en la realización de este programa. Desafortunadamente, todavía no puedo comprender muchos conceptos, y como tal, tengo muchas, muchas dificultades.

Para empezar, no tengo idea de cómo compilar correctamente mi programa. He estado usando el siguiente comando:

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

Mi programa se divide en cuatro archivos diferentes: main.ml; lexer.mll; parser.mly; tokens.mly. main.ml es la parte que obtiene entrada de un archivo en el sistema de archivos dado como argumento.

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) 

El segundo archivo es 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))) } 

El tercer archivo es parser.mly.

%start <bool> main 
%% 

main: 
| WRITEINT INT { true } 

El cuarto es 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 
%} 

%% 

Ahora, sé que hay una gran cantidad de símbolos utilizados aquí, pero tengo la intención de utilizarlos en mi analizador. No importa cuántos cambios haga en los archivos, el compilador sigue explotando en mi cara. He intentado todo lo que puedo pensar, y nada parece funcionar. ¿Qué es lo que está haciendo explotar ocamlbuild en una plétora de errores de constructores independientes y símbolos de inicio no definidos? ¿Qué comando debería usar para compilar el programa correctamente? ¿Dónde puedo encontrar materiales significativos para aprender sobre Menhir?

Respuesta

8

Una forma más sencilla de hacerlo es eliminar la separación Parser/Tokens. Como señaló Thomas, no hay necesidad de una declaración type token = ..., porque menhir la produce automáticamente a partir de las directivas %token.

Así se puede definir como parser.mly:

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

y lexer.mll como:

{ 
    open Parser 
    exception Error of string 
} 

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

luego retire tokens.mly, y compilar con

ocamlbuild -use-menhir main.native 

y todo funciona bien.

+0

De hecho, tener solo un 'mly' es mucho más simple. No he propuesto esa solución en mi respuesta porque suponía que @Lopson quería utilizar la función "compilación separada de unidades de análisis" de menhir. – Thomas

+0

Gracias por toda la ayuda, muchachos, ¡no tienen idea de lo valiosos que fueron sus mensajes para mí! Finalmente, las cosas están empezando a tener algún sentido. –

7

Así que, primero, no es necesario que RePet las fichas 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 

%% 

Entonces, no sé la opción mágica para pasar a ocamlbuild y no sé muy bien menhir, pero, en mi entendimiento es necesario "paquete" todo el .mly en un analizador unidad:

menhir tokens.mly parser.mly -base parser 

Entonces, si se reemplaza cualquier ocurrencia de Token byt Parser en lexer.mll, ocamlbuild -no-hygiene main.byte debería funcionar. Sin embargo, tenga en cuenta que tal vez haya una forma inteligente de hacerlo.

1

Me encontré con el mismo problema, excepto que además el analizador necesitaba módulos fuera de la corriente directa. . No podía encontrar la manera de invocar ocamlbuild para especificar que analizador {ml, MLI} tuvo que ser construido a partir de 3 archivos Mly, por lo que simplemente hizo un makefile que:

  • copias de los módulos .cmi _build en el directorio actual (para satisfacer menhir --infer)
  • invocan menhir
  • quitar los módulos copiados para satisfacer ocamlbuild
  • entonces invoco ocamlbuild

no estoy satisfecho con ella, por lo que me interesa en cualquier mejor alt ernative, pero si realmente tiene que terminar su proyecto con un mínimo esfuerzo, supongo que ese es el camino a seguir

edit: En realidad, no hay necesidad de copiar y eliminar los módulos compilados, solo pase la opción a menhir a el segundo paso: menhir --ocamlc "ocamlc -I \" ../_ acumulación/módulos/\ "" analizador --infer --base

Lamentablemente, esta alambiques significa que la generación analizador será WRT la anterior compilación de los módulos, por lo tanto, se espera una primera compilación innecesaria (y fallida).