2012-03-27 6 views
11

OCaml에 관해서는 완전히 초보자입니다. 필자는 최근에 언어 사용을 시작했으나 (약 2 주 전), 유감스럽게도 구문 분석기 (parser + lexer, 함수가 문장을 수락할지 여부를 결정하는 것) Menhir 사용. 자, OCaml과 Menhir에 관한 인터넷 자료를 찾았습니다 :OCaml + Menhir 컴파일/작성

Menhir 매뉴얼.

This webpage for some French University course.

소스 포지에서 토스의 홈페이지에 짧은 선돌 튜토리얼

.

github by derdon의 Menhir 예제.

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

SooHyoung 오에 의해 임의의 ocamllex 튜토리얼.

Menhir의 소스 코드와 함께 제공되는 예제입니다.

(I 두 개 이상의 하이퍼 링크를 넣을 수 없습니다, 그래서 내가 여기에 언급하고있어 웹 사이트의 일부로 바로 링크 할 수 없습니다. 죄송합니다!)

그래서, 당신이 볼 수 있듯이, 내가 ' 이 프로그램을 만들 때 나를 돕기 위해 점점 더 많은 자료를 필사적으로 찾고있었습니다. 불행히도, 나는 아직도 많은 개념을 이해할 수 없으며, 따라서 많은 어려움을 겪고 있습니다.

처음에는 프로그램을 올바르게 컴파일하는 방법을 알지 못합니다. 나는 다음과 같은 명령을 사용하고있다 :

내 프로그램은 네 개의 서로 다른 파일에 나누어
ocamlbuild -use-menhir -menhir "menhir --external-tokens Tokens" main.native 

: main.ml을; lexer.mll; parser.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에 관해 알아볼 수있는 의미있는 자료는 어디에서 찾을 수 있습니까?, ocamlbuild에 전달할 그런 다음, 나는 마법의 옵션을 모르는

%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 

%% 

을 나는 아주 잘 menhir를 모르는 :

답변

8

더 간단한 방법은 Parser/Tokens 분리를 제거하는 것입니다. Thomas가 지적한대로 이라는 선언은 menhir이 %token 지시문에서 자동으로 생성하므로 선언 할 필요가 없습니다.

그래서 당신은 정의 할 수 있습니다 parser.mly 등 :

{ 
    open Parser 
    exception Error of string 
} 

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

다음 tokens.mly을 제거하고

ocamlbuild -use-menhir main.native 

로 컴파일하고 모든 것이 잘 작동합니다 같은

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

+0

사실 'mly'를 하나만 사용하면 훨씬 간단 해집니다. 필자는 @Lopson이 menhir의 "구문 분석 단위의 개별 컴파일"기능을 사용하기를 원했기 때문에이 해답을 제안하지 않았습니다. – Thomas

+0

모든 도움을 주셔서 감사합니다, 얘들 아, 내게 얼마나 귀중한 게시물인지 모르 셨다! 마지막으로, 일들이 이해되기 시작합니다. –

7

그래서 첫째, 당신은 tokens.mly에 토큰을 repet 할 필요가 없습니다 당신이 lexer.mllParser BYT Token의 발생을 교체 할 경우 012, 그리고

menhir tokens.mly parser.mly -base parser 

:하지만, 내 이해에서 당신을 "팩"모든 .mly 하나 파서에 장치를 필요이 작동해야합니다. 그러나 그것을 할 수있는 영리한 방법이 있음을 유의하십시오.

1

파서가 현재 직접 외부 모듈을 필요로한다는 것을 제외하고는 동일한 문제가 발생했습니다. . 내가 그 파서를 지정 ocamlbuild 호출하는 방법을 알아낼 수 {ml의 MLI는} 3 개 MLY 파일에서 구축 할 수 있었다, 그래서 나는 단순히 메이크했다 :

  • 사본이 모듈은 _build에서 .cmi을 현재 디렉토리에
  • 다음

내가 만족하지 오전 ocamlbuild 호출

  • 을 ocamlbuild 만족 복사 모듈을 제거 선돌에게
  • 를 호출 (선돌 --infer 만족합니다), 그래서 관심 더 나은 고도에서 만약 당신이 정말로 최소한의 노력으로 프로젝트를 끝내야 만한다면, 나는 그 길을가는 것 같아.

    편집 : 사실, 컴파일 된 모듈을 복사하고 제거 할 필요는 없습니다. menhir에게 옵션을 전달하면됩니다. 두번째 단계 : 선돌 --ocamlc "-I \ ocamlc"../_ 빌드

    안타깝게도/모듈/\ ""--infer --base 파서이 스틸 파서 생성 이전 WRT가된다는 것을 의미 따라서 모듈의 컴파일은 불필요한 (그리고 실패한) 첫 번째 컴파일이 예상됩니다.