3 (* Compile a list of variant tags into CPP defines *)
5 (* hash_variant, from ctype.ml *)
9 for i = 0 to String.length s - 1 do
10 accu := 223 * !accu + Char.code s.[i]
12 (* reduce to 31 bits *)
13 accu := !accu land (1 lsl 31 - 1);
14 (* make it signed for 64 bits architectures *)
15 if !accu > 0x3FFFFFFF then !accu - (1 lsl 31) else !accu
19 let lexer = make_lexer ["type"; "public"; "="; "["; "]"; "`"; "|"]
21 let may_string = parser
29 let rec ident_list = parser
30 [< ' Kwd "`"; ' Ident x; trans = may_string; _ = may_bar; s >] ->
31 (x, trans) :: ident_list s
34 let static = ref false
35 let may_public = parser
36 [< ' Kwd "public" >] -> true
37 | [< ' Kwd "private" >] -> false
38 | [< >] -> not !static
42 let hashes = Hashtbl.create 57
44 let declaration ~hc ~cc = parser
45 [< ' Kwd "type"; public = may_public; ' Ident name; ' Kwd "=";
46 prefix = may_string; ' Kwd "["; _ = may_bar;
47 tags = ident_list; ' Kwd "]"; suffix = may_string >] ->
48 let oh x = fprintf hc x and oc x = fprintf cc x in
49 (* Output tag values to headers *)
50 let first = ref true in
53 let hash = hash_variant tag in
55 let tag' = Hashtbl.find hashes hash in
57 failwith (String.concat ~sep:" " ["Doublon tag:";tag;"and";tag'])
59 Hashtbl.add ~key:hash ~data:tag hashes;
61 oh "/* %s : tags and macros */\n" name; first := false
63 oh "#define MLTAG_%s\tVal_int(%d)\n" tag hash;
67 if trans <> "" then trans else
70 String.sub tag ~pos:1 ~len:(String.length tag -1)
74 if prefix = "" then None, ""
76 Some (prefix.[String.length prefix - 1]),
77 String.sub prefix ~pos:0 ~len:(String.length prefix - 1)
80 prefix ^ String.uncapitalize tag ^ suffix
82 prefix ^ String.uppercase tag ^ suffix
86 String.capitalize name
90 ~order:(fun (tag1,_) (tag2,_) -> hash_variant tag1 < hash_variant tag2)
92 (* Output table to code file *)
93 oc "/* %s : conversion table */\n" name;
94 let static = if not public then "static " else "" in
95 oc "%slookup_info ml_table_%s[] = {\n" static name;
96 oc " { 0, %d },\n" (List.length tags);
98 begin fun (tag,trans) ->
99 oc " { MLTAG_%s, %s },\n" tag (ctag tag trans)
102 (* Output macros to headers *)
103 if not !first then oh "\n";
104 if public then oh "extern lookup_info ml_table_%s[];\n" name;
105 oh "#define Val_%s(data) ml_lookup_from_c (ml_table_%s, data)\n"
107 oh "#define %s_val(key) ml_lookup_to_c (ml_table_%s, key)\n\n"
109 | [< >] -> raise End_of_file
112 let process ic ~hc ~cc =
113 let chars = Stream.of_channel ic in
114 let s = lexer chars in
116 while true do declaration s ~hc ~cc done
117 with End_of_file -> ()
118 | Stream.Error err ->
120 (Printf.sprintf "Parsing error \"%s\" at character %d on input stream"
121 err (Stream.count chars))
124 let inputs = ref [] in
125 let header = ref "" in
127 Arg.parse ~errmsg:"usage: varcc [options] file.var" ~keywords:
128 [ "-h", Arg.String ((:=) header), "file to output macros (file.h)";
129 "-c", Arg.String ((:=) code),
130 "file to output conversion tables (file.c)";
131 "-static", Arg.Set static, "do not export conversion tables" ]
132 ~others:(fun s -> inputs := s :: !inputs);
133 let inputs = List.rev !inputs in
134 begin match inputs with
136 if !header = "" then header := "a.h";
137 if !code = "" then code := "a.c"
140 if Filename.check_suffix ip ".var" then Filename.chop_extension ip
142 if !header = "" then header := rad ^ ".h";
143 if !code = "" then code := rad ^ ".c"
145 let hc = open_out !header and cc = open_out !code in
146 let chars = Stream.of_channel stdin in
147 if inputs = [] then process stdin ~hc ~cc else begin
150 let ic = open_in file in
151 try process ic ~hc ~cc; close_in ic
152 with exn -> close_in ic; prerr_endline ("Error in " ^ file); raise exn
155 close_out hc; close_out cc
157 let _ = Printexc.print main ()