front.ml 5.03 KB
Newer Older
1
(* The front-end. This module performs a series of toplevel side effects. *)
2

3 4 5 6 7 8 9
(* ------------------------------------------------------------------------- *)

(* Reading a grammar from a file. *)

let load_partial_grammar filename =
  let validExt = if Settings.coq then ".vy" else ".mly" in
  if not (Filename.check_suffix filename validExt) then
10
    Error.error []
11
      "argument file names should end in %s. \"%s\" is not accepted."
12
      validExt filename;
13 14 15 16 17
  Error.set_filename filename;
  try

    let contents = IO.read_whole_file filename in
    Error.file_contents := Some contents;
POTTIER Francois's avatar
POTTIER Francois committed
18
    let open Lexing in
19
    let lexbuf = Lexing.from_string contents in
POTTIER Francois's avatar
POTTIER Francois committed
20
    lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = filename };
21
    let grammar =
22
      { (Driver.grammar Lexer.main lexbuf) with ConcreteSyntax.pg_filename = filename }
23 24 25 26 27
    in
    Error.file_contents := None;
    grammar

  with Sys_error msg ->
28
    Error.error [] "%s" msg
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

(* ------------------------------------------------------------------------- *)

(* Read all of the grammar files that are named on the command line. *)

let partial_grammars = 
  List.map load_partial_grammar Settings.filenames

let () =
  Time.tick "Lexing and parsing"

(* ------------------------------------------------------------------------- *)

(* If several grammar files were specified, merge them. *)

let parameterized_grammar = 
  PartialGrammar.join_partial_grammars partial_grammars

(* ------------------------------------------------------------------------- *)

(* Expand away all applications of parameterized nonterminal symbols, so as to
   obtain a grammar without parameterized nonterminal symbols. *)

let grammar = 
  ParameterizedGrammar.expand parameterized_grammar

let () =
  Time.tick "Joining and expanding"

(* ------------------------------------------------------------------------- *)
59

60 61 62 63
(* If [--only-tokens] was specified on the command line, produce
   the definition of the [token] type and stop. *)

let () =
64
  TokenType.produce_tokentypes grammar
65

66 67
(* ------------------------------------------------------------------------- *)

68 69 70 71 72 73 74 75
(* Perform reachability analysis. *)

let grammar =
  Reachability.trim grammar

let () =
  Time.tick "Trimming"

76 77
(* ------------------------------------------------------------------------- *)

78 79
(* If [--depend] or [--raw-depend] was specified on the command line,
   perform dependency analysis and stop. *)
80 81 82 83 84 85 86 87 88

let () =
  match Settings.depend with
  | Settings.OMRaw
  | Settings.OMPostprocess ->
      Infer.depend grammar (* never returns *)
  | Settings.OMNone ->
      ()

89 90 91
(* The purpose of [--depend] and [--raw-depend] is to support [--infer].
   Indeed, [--infer] is implemented by producing a mock [.ml] file (which
   contains just the semantic actions) and invoking [ocamlc]. This requires
92 93 94 95 96
   certain [.cmi] files to exist. So, [--(raw-)depend] is a way for us to
   announce which [.cmi] files we need. It is implemented by producing the
   mock [.ml] file and running [ocamldep] on it. We also produce a mock
   [.mli] file, even though in principle it should be unnecessary -- see
   comment in [nonterminalType.mli]. *)
97

98 99
(* ------------------------------------------------------------------------- *)

100 101 102 103 104 105 106 107
(* If some flags imply that we will NOT produce an OCaml parser, then there
   is no need to perform type inference, so we act as if --infer was absent.
   This saves time and dependency nightmares. *)

let skipping_parser_generation =
  Settings.coq ||
  Settings.compile_errors <> None ||
  Settings.interpret_error ||
108 109
  Settings.list_errors ||
  Settings.compare_errors <> None
110 111 112 113
    (* maybe also: [preprocess_mode <> PMNormal] *)

(* ------------------------------------------------------------------------- *)

POTTIER Francois's avatar
POTTIER Francois committed
114 115
(* If [--infer] was specified on the command line, perform type inference.
   The OCaml type of every nonterminal is then known. *)
116 117

let grammar =
118
  if Settings.infer && not skipping_parser_generation then
119 120 121 122 123 124
    let grammar = Infer.infer grammar in
    Time.tick "Inferring types for nonterminals";
    grammar
  else
    grammar

125 126
(* ------------------------------------------------------------------------- *)

127 128 129 130 131 132 133 134
(* If [--no-inline] was specified on the command line, skip the
   inlining of non terminal definitions marked with %inline. *)

let grammar =
  if Settings.inline then begin
    let grammar, inlined = 
      NonTerminalDefinitionInlining.inline grammar
    in
135
    if not Settings.infer && inlined && not skipping_parser_generation then
136
      Error.warning []
137
	"you are using the standard library and/or the %%inline keyword. We\n\
138 139 140 141 142 143 144
	 recommend switching on --infer in order to avoid obscure type error messages.";
    Time.tick "Inlining";
    grammar
  end
  else 
    grammar

145 146
(* ------------------------------------------------------------------------- *)

147 148 149 150 151 152 153 154 155 156 157
(* If [--only-preprocess] or [--only-preprocess-drop] was specified on the
   command line, print the grammar and stop. Otherwise, continue. *)

let () =
  match Settings.preprocess_mode with
  | Settings.PMOnlyPreprocess mode ->
      UnparameterizedPrinter.print mode stdout grammar;
      exit 0
  | Settings.PMNormal ->
      ()