对于OCaml,我是一个完整的新手。我最近才开始使用这种语言(大约2周前),但不幸的是,我的任务是制作语法分析器(解析器+词法分析器,其功能是接受或不接受句子)以获得一种语言使用Menhir。现在,我在互联网上找到了一些关于OCaml和Menhir的材料:
The Menhir Manual。
This webpage for some French University course.
在Sourceforge的Toss主页上的简短Menhir教程。
derdon在github上的一个Menhir例子。
A book on OCaml (with a few things about ocamllex+ocamlyacc
SooHyoung的随机ocamllex教程哦。
Menhir的源代码附带的例子。
(我不能放两个以上的超链接,所以我不能直接链接到我在这里提到的一些网站。抱歉!)
所以,正如你所看到的,我一直在拼命寻找越来越多的资料来帮助我制定这个计划。不幸的是,我仍然无法掌握很多概念,因此,我遇到了很多困难。
对于初学者,我不知道如何正确编译我的程序。我一直在使用以下命令:
ocamlbuild -use-menhir -menhir "menhir --external-tokens Tokens" main.native
我的程序分为四个不同的文件:main.ml; lexer.mll; parser.mly; tokens.mly。 main.ml是从作为参数给出的文件系统中的文件获取输入的部分。
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)
第二个文件是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))) }
第三个文件是parser.mly。
%start <bool> main
%%
main:
| WRITEINT INT { true }
第四个是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
%}
%%
现在,我知道这里有很多未使用的符号,但我打算在我的解析器中使用它们。无论我对这些文件做了多少更改,编译器都会不停地炸毁我的脸。我已经尝试了所有我能想到的东西,但似乎没有任何效果。是什么让ocamlbuild在未绑定的构造函数和未定义的起始符号的过多错误中爆炸?我应该使用什么命令来正确编译程序?我在哪里可以找到有意义的材料来了解Menhir?
答案 0 :(得分:9)
更简单的方法是删除Parser
/ Tokens
分隔。正如托马斯所指出的那样,没有必要声明type token = ...
,因为它是由%token
指令中的menhir自动生成的。
因此,您可以将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 }
和lexer.mll
as:
{
open Parser
exception Error of string
}
[...] (* rest of the code not shown here *)
然后删除tokens.mly
,并使用
ocamlbuild -use-menhir main.native
这一切都运作良好。
答案 1 :(得分:7)
首先,您不需要在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
%%
然后,我不知道传递给ocamlbuild
的神奇选项而且我不太了解menhir
,但是,根据我的理解,你需要&#34 ;包&#34;所有.mly
到一个解析器单元:
menhir tokens.mly parser.mly -base parser
然后,如果您在Token
中替换任何Parser
字节lexer.mll
,ocamlbuild -no-hygiene main.byte
应该有效。但请注意,可能有一种聪明的方法可以做到。
答案 2 :(得分:1)
我遇到了同样的问题,除了另外解析器需要当前direct之外的模块。我无法弄清楚如何调用ocamlbuild来指定解析器。{ml,mli}必须从3个mly文件构建,所以我只创建了一个makefile:
我对它不满意,所以我对任何更好的选择感兴趣,但是如果你真的必须用最小的努力来完成你的项目,我想这是要走的路
编辑: 实际上,没有必要复制和删除已编译的模块,只需在第二步将选项传递给menhir: menhir --ocamlc“ocamlc -I \”../_ build / modules / \“”--infer --base解析器
可悲的是,这仍然意味着解析器生成将与之前的模块编译相同,因此可能会出现不必要的(并且失败的)第一次编译。