├── .gitignore ├── COPYING ├── README.rst ├── _tags ├── calc.ml ├── lexer.mll ├── myocamlbuild.ml ├── parser.mly └── testfile /.gitignore: -------------------------------------------------------------------------------- 1 | # generated by ocamlbuild 2 | _build 3 | *.native 4 | 5 | # generated by menhir 6 | *.automaton 7 | 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | NOTE: This code is all *example* code. It is targeted to developers; read it 4 | and learn from it! 5 | 6 | This example should be seen as an extension to the already existing menhir 7 | examples (accessible through the directory "demo" in the source code of menhir). 8 | 9 | To write this code, I used the example demos/calc from menhir's source code. 10 | In the files lexer.mll and parser.mly I added comments for my own modifications 11 | so that the reader of the source code can distinguish between code from INRIA 12 | and code added by me. 13 | 14 | A testfile is attached to avoid misunderstandings with the required file format. 15 | 16 | Dependencies / Requirements 17 | =========================== 18 | - OCaml_, tested with version 3.11.2 19 | - Batteries_, tested with version 1.1.0-1 20 | - menhir_ , tested with version 20090505 21 | 22 | File format 23 | =========== 24 | Every expression must end with a semicolon (;). Whitespace, tabulators and 25 | newlines are ignored. 26 | 27 | How to compile 28 | ============== 29 | ``ocamlbuild calc.native`` 30 | 31 | How to use 32 | ========== 33 | ``./calc.native [filename]`` 34 | 35 | This command will print a list of results, represented as an OCaml list. 36 | 37 | .. _OCaml: http://caml.inria.fr 38 | .. _Batteries: http://batteries.forge.ocamlcore.org/ 39 | .. _menhir: http://cristal.inria.fr/~fpottier/menhir/ 40 | -------------------------------------------------------------------------------- /_tags: -------------------------------------------------------------------------------- 1 | <*>: pkg_batteries,batteries.pa_string.syntax,pkg_threads 2 | true: use_menhir 3 | -------------------------------------------------------------------------------- /calc.ml: -------------------------------------------------------------------------------- 1 | open Batteries 2 | 3 | (* the name of the file which contains the expressions *) 4 | let filename = Sys.argv.(1) 5 | 6 | let print_int_list = List.print Int.print stdout 7 | 8 | let main () = 9 | let input = open_in filename in 10 | let filebuf = Lexing.from_input input in 11 | try 12 | print_int_list (Parser.main Lexer.token filebuf) 13 | with 14 | | Lexer.Error msg -> 15 | Printf.eprintf "%s%!" msg 16 | | Parser.Error -> 17 | Printf.eprintf "At offset %d: syntax error.\n%!" (Lexing.lexeme_start filebuf) 18 | ; 19 | IO.close_in input 20 | 21 | let _ = main () 22 | -------------------------------------------------------------------------------- /lexer.mll: -------------------------------------------------------------------------------- 1 | (**************************************************************************) 2 | (* *) 3 | (* Menhir *) 4 | (* *) 5 | (* François Pottier, INRIA Rocquencourt *) 6 | (* Yann Régis-Gianas, PPS, Université Paris Diderot *) 7 | (* *) 8 | (* Copyright 2005-2008 Institut National de Recherche en Informatique *) 9 | (* et en Automatique. All rights reserved. This file is distributed *) 10 | (* under the terms of the Q Public License version 1.0, with the change *) 11 | (* described in file LICENSE. *) 12 | (* *) 13 | (**************************************************************************) 14 | 15 | { 16 | open Parser 17 | 18 | exception Error of string 19 | } 20 | 21 | rule token = parse 22 | | [' ' '\t' '\n'] (* also ignore newlines, not only whitespace and tabs *) 23 | { token lexbuf } 24 | (* add the semicolon as a new token *) 25 | | ';' 26 | { SEMICOLON } 27 | | ['0'-'9']+ as i 28 | { INT (int_of_string i) } 29 | | '+' 30 | { PLUS } 31 | | '-' 32 | { MINUS } 33 | | '*' 34 | { TIMES } 35 | | '/' 36 | { DIV } 37 | | '(' 38 | { LPAREN } 39 | | ')' 40 | { RPAREN } 41 | | eof 42 | { EOF } 43 | | _ 44 | { raise (Error (Printf.sprintf "At offset %d: unexpected character.\n" (Lexing.lexeme_start lexbuf))) } 45 | -------------------------------------------------------------------------------- /myocamlbuild.ml: -------------------------------------------------------------------------------- 1 | open Ocamlbuild_plugin 2 | open Command (* no longer needed for OCaml >= 3.10.2 *) 3 | 4 | (** 5 | Overview of tags: 6 | - [pkg_batteries] to use Batteries as a library, without syntax extensions 7 | - [use_batteries] and [use_batteries_r] to use both Batteries and all the non-destructive syntax extensions 8 | - [pkg_sexplib.syntax] with [syntax_camlp4o] or [syntax_camlp4r] for sexplib 9 | *) 10 | 11 | 12 | (** 13 | {1 OCamlFind} 14 | *) 15 | 16 | let run_and_read = Ocamlbuild_pack.My_unix.run_and_read 17 | 18 | let blank_sep_strings = Ocamlbuild_pack.Lexers.blank_sep_strings 19 | 20 | module OCamlFind = 21 | struct 22 | (* this lists all supported packages *) 23 | let find_packages () = 24 | blank_sep_strings & 25 | Lexing.from_string & 26 | run_and_read "ocamlfind list | cut -d' ' -f1" 27 | 28 | (* this is supposed to list available syntaxes, but I don't know how to do it. *) 29 | let find_syntaxes () = ["camlp4o"; "camlp4r"] 30 | 31 | (* ocamlfind command *) 32 | let ocamlfind x = S[A"ocamlfind"; x] 33 | 34 | let before_options () = 35 | (* by using Before_options one let command line options have an higher priority *) 36 | (* on the contrary using After_options will guarantee to have the higher priority *) 37 | 38 | (* override default commands by ocamlfind ones *) 39 | Options.ocamlc := ocamlfind & A"ocamlc"; 40 | Options.ocamlopt := ocamlfind & A"ocamlopt"; 41 | Options.ocamldep := ocamlfind & A"ocamldep"; 42 | Options.ocamldoc := ocamlfind & A"ocamldoc"; 43 | Options.ocamlmktop := ocamlfind & A"ocamlmktop" 44 | 45 | let get_ocamldoc_directory () = 46 | let ocamldoc_directory = run_and_read "ocamlfind ocamldoc -customdir" in 47 | let length = String.length ocamldoc_directory in 48 | assert (length != 0); 49 | let char = ocamldoc_directory.[length - 1] in 50 | if (char = '\n') || (char = '\r') then String.sub ocamldoc_directory 0 (length - 1) 51 | else ocamldoc_directory 52 | 53 | let after_rules () = 54 | (* When one link an OCaml library/binary/package, one should use -linkpkg *) 55 | flag ["ocaml"; "byte"; "link"; "program"] & A"-linkpkg"; 56 | flag ["ocaml"; "native"; "link"; "program"] & A"-linkpkg"; 57 | 58 | 59 | (* For each ocamlfind package one inject the -package option when 60 | * compiling, computing dependencies, generating documentation and 61 | * linking. *) 62 | List.iter begin fun pkg -> 63 | flag ["ocaml"; "compile"; "pkg_"^pkg] & S[A"-package"; A pkg]; 64 | flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S[A"-package"; A pkg]; 65 | flag ["ocaml"; "doc"; "pkg_"^pkg] & S[A"-package"; A pkg]; 66 | flag ["ocaml"; "link"; "pkg_"^pkg] & S[A"-package"; A pkg]; 67 | flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S[A"-package"; A pkg]; 68 | end (find_packages ()); 69 | 70 | (* Like -package but for extensions syntax. Morover -syntax is useless 71 | * when linking. *) 72 | List.iter begin fun syntax -> 73 | flag ["ocaml"; "compile"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 74 | flag ["ocaml"; "ocamldep"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 75 | flag ["ocaml"; "doc"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 76 | flag ["ocaml"; "infer_interface"; "syntax_"^syntax] & S[A"-syntax"; A syntax]; 77 | end (find_syntaxes ()); 78 | 79 | (* The default "thread" tag is not compatible with ocamlfind. 80 | Indeed, the default rules add the "threads.cma" or "threads.cmxa" 81 | options when using this tag. When using the "-linkpkg" option with 82 | ocamlfind, this module will then be added twice on the command line. 83 | 84 | To solve this, one approach is to add the "-thread" option when using 85 | the "threads" package using the previous plugin. 86 | *) 87 | flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]); 88 | flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]); 89 | flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"]) 90 | end 91 | 92 | (** 93 | {1 OCaml Batteries Included} 94 | *) 95 | 96 | module Batteries = 97 | struct 98 | let before_options () = () 99 | 100 | let after_rules () = 101 | flag ["ocaml"; "link"; "byte"; "use_ocamldoc_info"] (S[A "-I"; A "+ocamldoc"; A "odoc_info.cma"]); 102 | flag ["ocaml"; "link"; "native"; "use_ocamldoc_info"] (S[A "-I"; A "+ocamldoc"(*; A "odoc_info.cmxa"*)]); 103 | flag ["ocaml"; "docfile"; "use_ocamldoc_info"] (S[A "-I"; A "+ocamldoc"]); 104 | flag ["ocaml"; "docdir"; "use_ocamldoc_info"] (S[A "-I"; A "+ocamldoc"]); 105 | flag ["ocaml"; "doc"; "use_ocamldoc_info"] (S[A "-I"; A "+ocamldoc"]); 106 | 107 | (*The command-line for [use_batteries] and [use_batteries_r]*) 108 | 109 | let cl_use_boilerplate = [A"-package"; A "batteries.pa_type_conv.syntax,batteries,sexplib.syntax"] 110 | and cl_use_batteries = [A"-package"; A "batteries.pa_openin.syntax,batteries.pa_where.syntax,batteries.pa_batteries.syntax"; A "-package"; A "batteries"] 111 | and cl_use_batteries_o = [] 112 | (*[cl_use_batteries_o]: extensions which only make sense in original syntax*) 113 | and cl_camlp4o = [A"-syntax"; A "camlp4o"] 114 | and cl_camlp4r = [A"-syntax"; A "camlp4r"] in 115 | 116 | let cl_boilerplate_original = cl_use_boilerplate @ cl_camlp4o 117 | and cl_boilerplate_revised = cl_use_boilerplate @ cl_camlp4r 118 | and cl_batteries_original = cl_use_batteries @ cl_use_batteries_o @ cl_camlp4o 119 | and cl_batteries_revised = cl_use_batteries @ cl_camlp4r in 120 | 121 | (** Tag [use_boilerplate] provides boilerplate syntax extensions, 122 | in original syntax*) 123 | 124 | flag ["ocaml"; "compile"; "use_boilerplate"] & S cl_boilerplate_original ; 125 | flag ["ocaml"; "ocamldep"; "use_boilerplate"] & S cl_boilerplate_original ; 126 | flag ["ocaml"; "doc"; "use_boilerplate"] & S cl_boilerplate_original ; 127 | flag ["ocaml"; "link"; "use_boilerplate"] & S cl_boilerplate_original ; 128 | 129 | (** Tag [use_boilerplate_r] provides boilerplate syntax extensions, 130 | in original syntax*) 131 | 132 | flag ["ocaml"; "compile"; "use_boilerplate_r"] & S cl_boilerplate_revised ; 133 | flag ["ocaml"; "ocamldep"; "use_boilerplate_r"] & S cl_boilerplate_revised ; 134 | flag ["ocaml"; "doc"; "use_boilerplate_r"] & S cl_boilerplate_revised ; 135 | flag ["ocaml"; "link"; "use_boilerplate_r"] & S cl_boilerplate_revised ; 136 | 137 | (** Tag [use_batteries] provides both package [batteries] 138 | and all syntax extensions, in original syntax. *) 139 | 140 | flag ["ocaml"; "compile"; "use_batteries"] & S cl_batteries_original ; 141 | flag ["ocaml"; "ocamldep"; "use_batteries"] & S cl_batteries_original ; 142 | flag ["ocaml"; "doc"; "use_batteries"] & S cl_batteries_original ; 143 | flag ["ocaml"; "link"; "use_batteries"] & S cl_batteries_original ; 144 | 145 | (** Tag [use_batteries_r] provides both package [batteries] 146 | and all syntax extensions, in revised syntax. *) 147 | 148 | flag ["ocaml"; "compile"; "use_batteries_r"] & S cl_batteries_revised; 149 | flag ["ocaml"; "ocamldep"; "use_batteries_r"] & S cl_batteries_revised; 150 | flag ["ocaml"; "doc"; "use_batteries_r"] & S cl_batteries_revised; 151 | flag ["ocaml"; "link"; "use_batteries_r"] & S cl_batteries_revised 152 | 153 | 154 | (* flag ["ocaml"; "compile"; "use_batteries"] & S[A "-verbose"; 155 | A"-package"; A "batteries.syntax.full"; 156 | A"-syntax"; A "batteries.syntax.full"]; 157 | flag ["ocaml"; "ocamldep"; "use_batteries"] & S[A "-verbose"; 158 | A"-package"; A "batteries.syntax.full"; 159 | A"-syntax"; A "batteries.syntax.full"]; 160 | flag ["ocaml"; "doc"; "use_batteries"] & S[A "-verbose"; 161 | A"-package"; A "batteries.syntax.full"; 162 | A"-syntax"; A "batteries.syntax.full"]; 163 | flag ["ocaml"; "link"; "use_batteries"] & S[A "-verbose"; 164 | A"-package"; A "batteries.syntax.full"; 165 | A"-syntax"; A "batteries.syntax.full"];*) 166 | 167 | 168 | end 169 | 170 | module Ocamlviz = struct 171 | 172 | let before_options () = () 173 | 174 | let after_rules () = 175 | flag ["ocaml"; "compile"; "ocamlviz_auto"] (S[A"-ppopt"; A"camlp4 pa_o.cmo str.cma pa_ocamlviz.cmo pr_o.cmo"]); 176 | flag ["ocaml"; "ocamldep"; "ocamlviz_auto"] (S[A"-ppopt"; A"camlp4 pa_o.cmo str.cma pa_ocamlviz.cmo pr_o.cmo"]); 177 | flag ["ocaml"; "compile"; "byte"; "ocamlviz"] (S[A"libocamlviz.cma"]); 178 | flag ["ocaml"; "compile"; "native"; "ocamlviz"] (S[A"libocamlviz.cmxa"]); 179 | flag ["ocaml"; "link"; "byte"; "ocamlviz"] (S[A"libocamlviz.cma"]); 180 | flag ["ocaml"; "link"; "native"; "ocamlviz"] (S[A"libocamlviz.cmxa"]); 181 | 182 | end 183 | 184 | let _ = dispatch begin function 185 | | Before_options -> 186 | OCamlFind.before_options (); 187 | Batteries.before_options (); 188 | Ocamlviz.before_options (); 189 | | After_rules -> 190 | OCamlFind.after_rules (); 191 | Batteries.after_rules (); 192 | Ocamlviz.after_rules () 193 | | _ -> () 194 | end 195 | 196 | 197 | (** 198 | which ocamlrun -> header 199 | 200 | print_backtrace -> ajouter "-b" après le header 201 | **) 202 | -------------------------------------------------------------------------------- /parser.mly: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /* */ 3 | /* Menhir */ 4 | /* */ 5 | /* François Pottier, INRIA Rocquencourt */ 6 | /* Yann Régis-Gianas, PPS, Université Paris Diderot */ 7 | /* */ 8 | /* Copyright 2005-2008 Institut National de Recherche en Informatique */ 9 | /* et en Automatique. All rights reserved. This file is distributed */ 10 | /* under the terms of the Q Public License version 1.0, with the change */ 11 | /* described in file LICENSE. */ 12 | /* */ 13 | /**************************************************************************/ 14 | 15 | %token INT 16 | %token PLUS MINUS TIMES DIV 17 | %token LPAREN RPAREN 18 | %token SEMICOLON 19 | %token EOF 20 | 21 | %left PLUS MINUS /* lowest precedence */ 22 | %left TIMES DIV /* medium precedence */ 23 | %nonassoc UMINUS /* highest precedence */ 24 | 25 | /* changed the type, because the script does not return one value, but all 26 | * results which are calculated in the file */ 27 | %start main 28 | 29 | %% 30 | 31 | /* the calculated results are accumalted in an OCaml int list */ 32 | main: 33 | | stmt = statement EOF { [stmt] } 34 | | stmt = statement m = main { stmt :: m} 35 | 36 | /* expressions end with a semicolon, not with a newline character */ 37 | statement: 38 | | e = expr SEMICOLON { e } 39 | 40 | expr: 41 | | i = INT 42 | { i } 43 | | LPAREN e = expr RPAREN 44 | { e } 45 | | e1 = expr PLUS e2 = expr 46 | { e1 + e2 } 47 | | e1 = expr MINUS e2 = expr 48 | { e1 - e2 } 49 | | e1 = expr TIMES e2 = expr 50 | { e1 * e2 } 51 | | e1 = expr DIV e2 = expr 52 | { e1 / e2 } 53 | | MINUS e = expr %prec UMINUS 54 | { - e } 55 | -------------------------------------------------------------------------------- /testfile: -------------------------------------------------------------------------------- 1 | 3;4*(2+8);3-4; 2 | --------------------------------------------------------------------------------