ocaml/tools/scrapelabels.ml

291 lines
10 KiB
OCaml

(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Jacques Garrigue, Kyoto University RIMS *)
(* *)
(* Copyright 2001 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
open StdLabels
open Lexer301
let input_buffer = Buffer.create 16383
let input_function ic buf len =
let len = input ic buf 0 len in
Buffer.add_substring input_buffer buf 0 len;
len
let output_buffer = Buffer.create 16383
let modified = ref false
let modules =
ref [ "Arg"; "BigArray"; "Buffer"; "Condition"; "Dbm"; "Digest"; "Dynlink";
"Event"; "Filename"; "Format"; "Gc"; "Genlex"; "Graphics";
"Lexing"; "Marshal"; "Mutex"; "Parsing"; "Pervasives"; "Queue";
"Sort"; "Stack"; "Str"; "Stream"; "Sys";
"Thread"; "ThreadUnix"; "Weak" ]
let stdlabels = ["Array"; "List"; "String"]
let morelabels = ["Hashtbl"; "Map"; "Set"]
let alllabels = ref false
let noopen = ref false
exception Closing of token
let convert_impl buffer =
let input_pos = ref 0 in
let copy_input stop =
Buffer.add_substring output_buffer (Buffer.contents input_buffer)
!input_pos (stop - !input_pos);
input_pos := stop
in
let next_token () =
let token = Lexer301.token buffer
and start = Lexing.lexeme_start buffer
and stop = Lexing.lexeme_end buffer in
match token with
RPAREN | RBRACKET |BARRBRACKET | GREATERRBRACKET | END
| RBRACE | GREATERRBRACE ->
raise (Closing token)
| EOF ->
raise End_of_file
| _ ->
(token, start, stop)
in
let openunix = ref None and openstd = ref None and openmore = ref None in
let rec may_start (token, s, e) =
match token with
LIDENT _ -> search_start (dropext (next_token ()))
| UIDENT m when List.mem m !modules ->
may_discard (dropext (next_token ()))
| UIDENT m ->
List.iter ~f:
(fun (set,r) ->
if !r = None && List.mem m ~set then r := Some true)
[stdlabels, openstd; ["Unix"], openunix; morelabels, openmore];
search_start (next_token ())
| _ -> search_start (token, s, e)
and dropext (token, s, e) =
match token with
DOT ->
let (token, s, e) = next_token () in
begin match token with
LPAREN | LBRACKET | LBRACE ->
process_paren (token, s, e);
dropext (next_token ())
| UIDENT _ | LIDENT _ ->
dropext (next_token ())
| _ ->
prerr_endline ("bad index at position " ^ string_of_int s);
(token, s, e)
end
| _ ->
(token, s, e)
and may_discard (token, s, e) =
match token with
TILDE | LABEL _ ->
modified := true;
copy_input s; input_pos := e;
may_discard (next_token ())
| _ when !alllabels ->
may_discard (next_token ())
| LPAREN | LBRACKET | LBRACKETBAR | LBRACKETLESS | BEGIN
| LBRACE | LBRACELESS | STRUCT | SIG | OBJECT->
process_paren (token, s, e);
may_discard (next_token ())
| PREFIXOP _ ->
may_discard (next_token ())
| LIDENT _ | UIDENT _ ->
may_discard (dropext (next_token ()))
| BACKQUOTE ->
ignore (next_token ());
may_discard (next_token ())
| INT _ | CHAR _ | STRING _ | FLOAT _ | FALSE | TRUE ->
may_discard (next_token ())
| _ ->
search_start (token, s, e)
and search_start (token, s, e) =
match token with
LPAREN | LBRACKET | LBRACKETBAR | LBRACKETLESS | BEGIN
| LBRACE | LBRACELESS | STRUCT | SIG | OBJECT ->
process_paren (token, s, e);
search_start (next_token ())
| EQUAL | SEMI | SEMISEMI | MINUSGREATER | LESSMINUS | COMMA
| IF | THEN | ELSE | WHILE | TO | DOWNTO | DO | IN | MATCH | TRY
| INFIXOP0 _ | INFIXOP1 _ | INFIXOP2 _ | INFIXOP3 _ | INFIXOP4 _
| PLUS | MINUS | MINUSDOT | STAR | LESS | GREATER
| OR | BARBAR | AMPERSAND | AMPERAMPER | COLONEQUAL ->
may_start (next_token ())
| OPEN ->
begin match next_token () with
| UIDENT m, _, _ ->
List.iter
~f:(fun (set,r) -> if List.mem m ~set then r := Some false)
[stdlabels, openstd; ["Unix"], openunix; morelabels, openmore]
| _ -> ()
end;
search_start (next_token ())
| _ ->
search_start (next_token ())
and process_paren (token, s, e) =
try match token with
LPAREN | LBRACKET | LBRACKETBAR | LBRACKETLESS | BEGIN ->
may_start (next_token ())
| LBRACE | LBRACELESS | STRUCT | SIG | OBJECT ->
search_start (next_token ())
| _ ->
assert false
with Closing last ->
match token, last with
LPAREN, RPAREN
| (LBRACKET|LBRACKETBAR|LBRACKETLESS),
(RBRACKET|BARRBRACKET|GREATERRBRACKET)
| (BEGIN|STRUCT|SIG|OBJECT), END
| LBRACE, RBRACE
| LBRACELESS, GREATERRBRACE -> ()
| _ -> raise (Closing last)
in
let first = next_token () in
try
if !alllabels then may_discard first else may_start first
with End_of_file ->
copy_input (Buffer.length input_buffer);
if not !alllabels
&& List.exists (fun r -> !r = Some true) [openstd; openunix; openmore]
then begin
modified := true;
let text = Buffer.contents output_buffer in
Buffer.clear output_buffer;
let (token, s, _) = first in
Buffer.add_substring output_buffer text 0 s;
List.iter ~f:
(fun (r, s) ->
if !r = Some true then Buffer.add_string output_buffer s)
[ openstd, "open StdLabels\n"; openmore, "open MoreLabels\n";
openunix, "module Unix = UnixLabels\n" ];
let sep =
if List.mem token [CLASS; EXTERNAL; EXCEPTION; FUNCTOR; LET;
MODULE; FUNCTOR; TYPE; VAL]
then "\n"
else if token = OPEN then "" else ";;\n\n"
in
Buffer.add_string output_buffer sep;
Buffer.add_substring output_buffer text s (String.length text - s)
end
| Closing _ ->
prerr_endline ("bad closing token at position " ^
string_of_int (Lexing.lexeme_start buffer));
modified := false
type state = Out | Enter | In | Escape
let convert_intf buffer =
let input_pos = ref 0 in
let copy_input stop =
Buffer.add_substring output_buffer (Buffer.contents input_buffer)
!input_pos (stop - !input_pos);
input_pos := stop
in
let last = ref (EOF, 0, 0) in
let state = ref Out in
try while true do
let token = Lexer301.token buffer
and start = Lexing.lexeme_start buffer
and stop = Lexing.lexeme_end buffer
and last_token, last_start, last_stop = !last in
begin match token with
| EXCEPTION | CONSTRAINT ->
state := In
| VAL | EXTERNAL | CLASS | METHOD | TYPE | AND ->
state := Enter
| EQUAL when !state = Enter ->
state := In
| COLON ->
begin match !state, last_token with
| In, LIDENT _ ->
modified := true;
copy_input last_start;
input_pos := stop
| Enter, _ ->
state := In
| Escape, _ ->
state := In
| _ ->
state := Out
end
| LBRACE | SEMI | QUESTION when !state = In ->
state := Escape
| SEMISEMI | SIG | STRUCT | END | OBJECT | OPEN | INCLUDE | MODULE ->
state := Out
| EOF -> raise End_of_file
| _ -> ()
end;
last := (token, start, stop)
done with
End_of_file ->
copy_input (Buffer.length input_buffer)
let convert_file ~intf name =
let ic = open_in name in
Buffer.clear input_buffer;
Buffer.clear output_buffer;
modified := false;
begin
let convert = if intf then convert_intf else convert_impl in
try convert (Lexing.from_function (input_function ic)); close_in ic
with exn -> close_in ic; raise exn
end;
if !modified then begin
let backup = name ^ ".bak" in
if Sys.file_exists backup then Sys.remove name
else Sys.rename name backup;
let oc = open_out name in
Buffer.output_buffer oc output_buffer;
close_out oc
end
else prerr_endline ("No changes in " ^ name)
let _ =
let files = ref [] and intf = ref false
and keepstd = ref false and keepmore = ref false in
Arg.parse
[ "-intf", Arg.Set intf,
" remove all non-optional labels from an interface;\n" ^
" other options are ignored";
"-all", Arg.Set alllabels,
" remove all labels, possibly including optional ones!";
"-keepstd", Arg.Set keepstd,
" keep labels for Array, List, String and Unix";
"-keepmore", Arg.Set keepmore,
" keep also labels for Hashtbl, Map and Set; implies -keepstd";
"-m", Arg.String (fun s -> modules := s :: !modules),
"<module> remove also labels for <module>";
"-noopen", Arg.Set noopen,
" do not insert `open' statements for -keepstd/-keepmore" ]
(fun s -> files := s :: !files)
("Usage: scrapelabels <options> <source files>\n" ^
" Remove labels from function arguments in standard library modules.\n" ^
" With -intf option below, can also process interfaces.\n" ^
" Old files are renamed to <file>.bak if there is no backup yet.\n" ^
"Options are:");
if !keepmore then keepstd := true;
if not !keepstd then modules := "Unix" :: stdlabels @ !modules;
if not !keepmore then modules := morelabels @ !modules;
List.iter (List.rev !files) ~f:
begin fun name ->
prerr_endline ("Processing " ^ name);
Printexc.catch (convert_file ~intf:!intf) name
end