OCaml + Menhir Компиляция / Написание
Я полный новичок, когда дело доходит до программирования OCaml Я только недавно начал использовать язык (около 2 недель назад), но, к сожалению, мне было поручено сделать синтаксический анализатор (parser + lexer, функция которого состоит в том, чтобы либо принять, либо не предложение) для составленного языка с использованием Menhir. Теперь я нашел некоторые материалы в интернете, касающиеся OCaml и Menhir:
Руководство Менгир.
эта веб-страница для некоторых французских университетов курс.
короткий учебник менгира на домашней странице Toss в Sourceforge.
пример менгира на github от derdon.
книга об OCaml (с несколькими вещами об ocamllex+ocamlyacc
случайный учебник ocamllex по SooHyoung Oh.
и примеры, которые поставляются с исходным кодом Menhir.
(Я не могу поместить более двух гиперссылок, поэтому я не могу напрямую связать вас с некоторыми из сайтов, которые я упоминаю здесь. Прости!)
Итак, как вы можете видеть, я отчаянно искал все больше и больше материала, чтобы помочь мне в создании этой программы. К сожалению, я все еще не могу понять многие концепции, и поэтому у меня много, много трудностей.
для начала, я понятия не имею, как правильно компилировать свою программу. Я использую следующую команду:
ocamlbuild -use-menhir -menhir "menhir --external-tokens Tokens" main.native
моя программа разделена на четыре разных файла: main.мл; лексер.млл; парсер.мли; жетоны.млй. главная.мл это часть, которая получает входные данные из файла в файловой системе, приведенный в качестве аргумента.
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))) }
третий файл-парсер.млй.
%start <bool> main
%%
main:
| WRITEINT INT { true }
четвертый жетоны.мли
%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 взрываться во множестве ошибок несвязанных конструкторов и неопределенных символов запуска? Какую команду я должен использовать для правильной компиляции программы? Где я могу найти значимые материалы, чтобы узнать о Менгире?
3 ответов
более простой способ сделать это, чтобы удалить Parser
/Tokens
разделение. Как отметил Томас, нет необходимости в декларации type token = ...
, потому что он автоматически производится менгиром из %token
директивы.
таким образом, вы можете определить parser.mly
as:
%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
и все работает хорошо.
Итак, во-первых, вам не нужно повторять токены в 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
очень хорошо, но, в моем понимании нужно "упаковать" все .mly
в один блок парсера:
menhir tokens.mly parser.mly -base parser
тогда, если вы замените любое вхождение Token
быт Parser
на lexer.mll
, ocamlbuild -no-hygiene main.byte
должны работать. Обратите внимание, однако, что есть, возможно, умный способ сделать это.
Я столкнулся с той же проблемой, за исключением того, что, кроме того, парсер нуждался в модулях вне текущего прямого. Я не мог понять, как вызвать ocamlbuild, чтобы указать этот парсер.{ml, mli} должен был быть построен из 3 mly файлов, поэтому я просто сделал makefile, который:
- копии модули .cmi из _build в текущий каталог (для удовлетворения menhir --infer)
- вызвать менгир
- удалите скопированные модули, чтобы удовлетворить ocamlbuild
- затем вызвать ocamlbuild
Я не доволен этим, поэтому меня интересует любая лучшая альтернатива, но если вам действительно нужно закончить свой проект с минимальными усилиями, я думаю, что это путь
изменить: На самом деле, нет необходимости копировать и удалять скомпилированные модули, просто передайте опцию menhir на втором шаге: menhir --ocamlc "ocamlc-I"../_build / modules / \ "" --infer --base parser
к сожалению, это неподвижно означает, что поколение парсера будет wrt предыдущая компиляция модулей, поэтому следует ожидать ненужной (и неудачной) первой компиляции.