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 предыдущая компиляция модулей, поэтому следует ожидать ненужной (и неудачной) первой компиляции.