main.ml 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. open Cgen
  2. open Match
  3. let mgen ~verbose ~fuzz path lofs input oc =
  4. let info ?(level = 1) fmt =
  5. if level <= verbose then
  6. Printf.eprintf fmt
  7. else
  8. Printf.ifprintf stdout fmt
  9. in
  10. let rules =
  11. match Sexp.(run_parser ppats) input with
  12. | `Error (ps, err, loc) ->
  13. Printf.eprintf "%s:%d:%d %s\n"
  14. path (lofs + ps.Sexp.line) ps.Sexp.coln err;
  15. Printf.eprintf "%s" loc;
  16. exit 1
  17. | `Ok rules -> rules
  18. in
  19. info "adding ac variants...%!";
  20. let nparsed =
  21. List.fold_left
  22. (fun npats (_, _, ps) ->
  23. npats + List.length ps)
  24. 0 rules
  25. in
  26. let varsmap = Hashtbl.create 10 in
  27. let rules =
  28. List.concat_map (fun (name, vars, patterns) ->
  29. (try assert (Hashtbl.find varsmap name = vars)
  30. with Not_found -> ());
  31. Hashtbl.replace varsmap name vars;
  32. List.map
  33. (fun pattern -> {name; vars; pattern})
  34. (List.concat_map ac_equiv patterns)
  35. ) rules
  36. in
  37. info " %d -> %d patterns\n"
  38. nparsed (List.length rules);
  39. let rnames =
  40. setify (List.map (fun r -> r.name) rules) in
  41. info "generating match tables...%!";
  42. let sa, am, sm = generate_table rules in
  43. let numbr = make_numberer sa am sm in
  44. info " %d states, %d rules\n"
  45. (Array.length sa) (StateMap.cardinal sm);
  46. if verbose >= 2 then begin
  47. info "-------------\nstates:\n";
  48. Array.iteri (fun i s ->
  49. info " state %d: %s\n"
  50. i (show_pattern s.seen)) sa;
  51. info "-------------\nstatemap:\n";
  52. Test.print_sm stderr sm;
  53. info "-------------\n";
  54. end;
  55. info "generating matchers...\n";
  56. let matchers =
  57. List.map (fun rname ->
  58. info "+ %s...%!" rname;
  59. let m = lr_matcher sm sa rules rname in
  60. let vars = Hashtbl.find varsmap rname in
  61. info " %d nodes\n" (Action.size m);
  62. info ~level:2 " -------------\n";
  63. info ~level:2 " automaton:\n";
  64. info ~level:2 "%s\n"
  65. (Format.asprintf " @[%a@]" Action.pp m);
  66. info ~level:2 " ----------\n";
  67. (vars, rname, m)
  68. ) rnames
  69. in
  70. if fuzz then begin
  71. info ~level:0 "fuzzing statemap...\n";
  72. let tp = Fuzz.fuzz_numberer rules numbr in
  73. info ~level:0 "testing %d patterns...\n"
  74. (List.length rules);
  75. Fuzz.test_matchers tp numbr rules
  76. end;
  77. info "emitting C...\n";
  78. flush stderr;
  79. let cgopts =
  80. { pfx = ""; static = true; oc = oc } in
  81. emit_c cgopts numbr;
  82. emit_matchers cgopts matchers;
  83. ()
  84. let read_all ic =
  85. let bufsz = 4096 in
  86. let buf = Bytes.create bufsz in
  87. let data = Buffer.create bufsz in
  88. let read = ref 0 in
  89. while
  90. read := input ic buf 0 bufsz;
  91. !read <> 0
  92. do
  93. Buffer.add_subbytes data buf 0 !read
  94. done;
  95. Buffer.contents data
  96. let split_c src =
  97. let begin_re, eoc_re, end_re =
  98. let re = Str.regexp in
  99. ( re "mgen generated code"
  100. , re "\\*/"
  101. , re "end of generated code" )
  102. in
  103. let str_match regexp str =
  104. try
  105. let _: int =
  106. Str.search_forward regexp str 0
  107. in true
  108. with Not_found -> false
  109. in
  110. let rec go st lofs pfx rules lines =
  111. let line, lines =
  112. match lines with
  113. | [] ->
  114. failwith (
  115. match st with
  116. | `Prefix -> "could not find mgen section"
  117. | `Rules -> "mgen rules not terminated"
  118. | `Skip -> "mgen section not terminated"
  119. )
  120. | l :: ls -> (l, ls)
  121. in
  122. match st with
  123. | `Prefix ->
  124. let pfx = line :: pfx in
  125. if str_match begin_re line
  126. then
  127. let lofs = List.length pfx in
  128. go `Rules lofs pfx rules lines
  129. else go `Prefix 0 pfx rules lines
  130. | `Rules ->
  131. let pfx = line :: pfx in
  132. if str_match eoc_re line
  133. then go `Skip lofs pfx rules lines
  134. else go `Rules lofs pfx (line :: rules) lines
  135. | `Skip ->
  136. if str_match end_re line then
  137. let join = String.concat "\n" in
  138. let pfx = join (List.rev pfx) ^ "\n\n"
  139. and rules = join (List.rev rules)
  140. and sfx = join (line :: lines)
  141. in (lofs, pfx, rules, sfx)
  142. else go `Skip lofs pfx rules lines
  143. in
  144. let lines = String.split_on_char '\n' src in
  145. go `Prefix 0 [] [] lines
  146. let () =
  147. let usage_msg =
  148. "mgen [--fuzz] [--verbose <N>] <file>" in
  149. let fuzz_arg = ref false in
  150. let verbose_arg = ref 0 in
  151. let input_paths = ref [] in
  152. let anon_fun filename =
  153. input_paths := filename :: !input_paths in
  154. let speclist =
  155. [ ( "--fuzz", Arg.Set fuzz_arg
  156. , " Fuzz tables and matchers" )
  157. ; ( "--verbose", Arg.Set_int verbose_arg
  158. , "<N> Set verbosity level" )
  159. ; ( "--", Arg.Rest_all (List.iter anon_fun)
  160. , " Stop argument parsing" ) ]
  161. in
  162. Arg.parse speclist anon_fun usage_msg;
  163. let input_paths = !input_paths in
  164. let verbose = !verbose_arg in
  165. let fuzz = !fuzz_arg in
  166. let input_path, input =
  167. match input_paths with
  168. | ["-"] -> ("-", read_all stdin)
  169. | [path] -> (path, read_all (open_in path))
  170. | _ ->
  171. Printf.eprintf
  172. "%s: single input file expected\n"
  173. Sys.argv.(0);
  174. Arg.usage speclist usage_msg; exit 1
  175. in
  176. let mgen = mgen ~verbose ~fuzz in
  177. if Str.last_chars input_path 2 <> ".c"
  178. then mgen input_path 0 input stdout
  179. else
  180. let tmp_path = input_path ^ ".tmp" in
  181. Fun.protect
  182. ~finally:(fun () ->
  183. try Sys.remove tmp_path with _ -> ())
  184. (fun () ->
  185. let lofs, pfx, rules, sfx = split_c input in
  186. let oc = open_out tmp_path in
  187. output_string oc pfx;
  188. mgen input_path lofs rules oc;
  189. output_string oc sfx;
  190. close_out oc;
  191. Sys.rename tmp_path input_path;
  192. ());
  193. ()