1 (* OCaml code generation *)
13 (* http://caml.inria.fr/pub/docs/manual-ocaml-4.07/manual049.html *)
74 let ident ~prefix name
=
75 assert (prefix
<> "");
79 if List.mem name
reserved then
82 let name = String.map
(function ('a'
..'z'
| 'A'
..'Z'
| '
0'
..'
9'
as c
) -> c
| _
-> '_'
) name in
84 | '
0'
..'
9'
| '_'
-> prefix ^
name
85 | _
-> String.uncapitalize_ascii
name
87 let idents ~prefix l
=
88 let rec choose acc base n
=
89 let name = sprintf
"%s%d" base n
in
90 if List.mem
name acc
then choose acc base
(n
+1) else name
92 let rec loop acc
= function
95 let x = ident ~prefix
x in
96 let x = if List.mem
x acc
then choose acc
x 0 else x in
103 let inline_values = String.concat
" "
105 let quote = String.replace_chars
(function '
\n'
-> "\\n\\\n" | '
\r'
-> "" | '
"' -> "\\\"" | c -> String.make 1 c)
106 let quote s = "\"" ^ quote s ^ "\""
108 let rec replace_all ~str ~sub ~by =
109 match String.replace ~str ~sub ~by with
110 | (true,s) -> replace_all ~str:s ~sub ~by
113 let quote_comment_inline str =
114 let str = replace_all ~str ~sub:"*)
" ~by:"* )" in
115 replace_all ~str ~sub:"(*" ~by:"( *"
117 let make_comment str = "(* " ^ (quote_comment_inline str) ^ " *)"
118 let comment () fmt = Printf.kprintf (indent_endline $ make_comment) fmt
120 let empty_line () = print_newline ()
123 let as_lang_type = function
124 | Type.Blob -> Type.to_string Type.Text
125 | t -> Type.to_string t
127 let as_api_type = as_lang_type
130 let get_column index attr =
131 sprintf "(T.get_column_%s%s stmt %u
)"
132 (L.as_lang_type attr.domain)
133 (if is_attr_nullable attr then "_nullable
" else "")
136 module T = Translate(L)
141 let output_schema_binder _ schema =
142 let name = "invoke_callback
" in
143 output "let %s stmt
=" name;
144 let args = Name.idents ~prefix:"r
" (List.map (fun a -> a.name) schema) in
145 let values = List.mapi get_column schema in
148 indented (fun () -> List.iter2 (output "~%s
:%s
") args values));
152 let output_select1_cb _ schema =
153 let name = "get_row
" in
154 output "let %s stmt
=" name;
156 List.mapi get_column schema |> String.concat ", " |> indent_endline);
160 let output_schema_binder index schema kind =
163 | _ -> match kind with
164 | Stmt.Select `Zero_one -> "select_one_maybe
", output_select1_cb index schema
165 | Select `One -> "select_one
", output_select1_cb index schema
166 | _ -> "select
",output_schema_binder index schema
168 let is_callback stmt =
169 match stmt.schema, stmt.kind with
171 | _, Stmt.Select (`Zero_one | `One) -> false
174 let list_separate f l =
177 List.iter (fun x -> match f x with `Left x -> tuck a x | `Right x -> tuck b x) l;
178 List.rev !a, List.rev !b
180 let make_variant_name i name =
181 "`
" ^ match name with
182 | None -> sprintf "V_%d
" i
183 | Some n -> String.capitalize_ascii n
185 let vname n = make_variant_name 0 (Some n)
187 let match_variant_wildcard i name args =
188 sprintf "%s%s
" (make_variant_name i name) (match args with Some [] | None -> "" | Some _ -> " _
")
190 let set_param index param =
191 let nullable = is_param_nullable param in
192 let pname = show_param_name param index in
193 let ptype = show_param_type param in
195 output "begin match %s
with None
-> T.set_param_null p
| Some v
-> T.set_param_%s p v
end;" pname ptype
197 output "T.set_param_%s p %s
;" ptype pname
199 let rec set_var index var =
201 | Single p -> set_param index p
203 | Choice (name,ctors) ->
204 output "begin match %s
with " (make_param_name index name);
205 ctors |> List.iteri begin fun i ctor ->
207 | Simple (param,args) ->
208 output "| %s%s
-> %s
"
209 (make_variant_name i param.label)
210 (match args with Some [] | None -> "" | Some l -> " ("^String.concat "," (names_of_vars l)^")")
211 (match args with Some [] | None -> "()" | Some _ -> "");
213 List.iter (set_var index) (Option.default [] args);
215 | Verbatim (n,_) -> output "| %s
-> ()" (vname n)
219 let rec eval_count_params vars =
220 let (static,choices) = list_separate (function Single _ -> `Left true | SingleIn _ -> `Left false | Choice (name,c) -> `Right (name, c)) vars in
221 string_of_int (List.length @@ List.filter (fun x -> x) static) ^
225 choices |> List.mapi begin fun i (name,ctors) ->
226 sprintf " + (match %s
with " (make_param_name i name) ^
227 (ctors |> List.mapi (fun i ctor ->
229 | Verbatim (n,_) -> sprintf "%s
-> 0" (vname n)
230 | Simple (param,args) -> sprintf "%s
-> %s
" (match_variant_wildcard i param.label args) (eval_count_params @@ Option.default [] args)) |> String.concat " | ")
232 end |> String.concat ""
234 let output_params_binder _ vars =
235 output "let set_params stmt
=";
237 output "let p = T.start_params stmt
(%s
) in" (eval_count_params vars);
238 List.iteri set_var vars;
239 output "T.finish_params
p";
244 let rec exclude_in_vars l =
248 | Single _ as v -> Some v
249 | Choice (param_id, ctors) ->
250 Some (Choice (param_id, List.map exclude_in_vars_in_constructors ctors)))
253 and exclude_in_vars_in_constructors = function
254 | Verbatim _ as ctor -> ctor
255 | Simple (param_id, vars) -> Simple (param_id, Option.map exclude_in_vars vars)
257 let output_params_binder index vars =
258 match exclude_in_vars vars with
259 | [] -> "T.no_params
"
260 | vars -> output_params_binder index vars
262 let prepend prefix = function s -> prefix ^ s
264 let in_var_module _label typ = Sql.Type.to_string typ
266 let gen_in_substitution var =
267 if Option.is_none var.id.label then failwith "empty label
in IN param
";
268 sprintf {code| "(" ^ String.concat ", " (List.map T.Types.%s.to_literal %s) ^ ")"|code}
269 (in_var_module (Option.get var.id.label) var.typ)
270 (Option.get var.id.label)
273 let b = Buffer.create 100 in
274 let rec loop app = function
276 | Static "" :: tl when app -> loop app tl
278 if app then bprintf b " ^
";
279 Buffer.add_string b (quote s);
281 | SubstIn param :: tl ->
282 if app then bprintf b " ^
";
283 Buffer.add_string b (gen_in_substitution param);
285 | Dynamic (name, ctors) :: tl ->
286 if app then bprintf b " ^
";
287 bprintf b "(match %s
with" (make_param_name 0 name);
288 ctors |> List.iteri (fun i (name,args,l) -> bprintf b " %s%s
-> " (if i = 0 then "" else "| ") (match_variant_wildcard i name.label args); loop false l);
292 Buffer.add_string b "(";
294 Buffer.add_string b ")";
297 let generate_stmt style index stmt =
298 let name = choose_name stmt.props stmt.kind index |> String.uncapitalize in
299 let subst = Props.get_all stmt.props "subst" in
300 let inputs = (subst @ names_of_vars stmt.vars) |> List.map (prepend "~
") |> inline_values in
301 match style, is_callback stmt with
302 | (`List | `Fold), false -> ()
304 let all_inputs = inputs ^ (if style = `List || is_callback stmt then " callback
" else "") ^ (if style = `Fold then " acc
" else "") in
305 output "let %s db %s
=" name all_inputs;
307 let sql = make_sql @@ get_sql stmt in
308 let sql = match subst with
311 output "let __sqlgg_sql =";
312 output " let replace_all ~
str ~sub ~by
=";
313 output " let rec loop str = match ExtString.String.replace ~
str ~sub ~by
with";
314 output " | true, str -> loop str";
315 output " | false, s
-> s
";
316 output " in loop str";
318 output " let sql = %s
in" sql;
319 List.iter (fun var -> output " let sql = replace_all ~
str:sql ~sub
:(\"%%%%%s
%%%%\") ~by
:%s
in" var var) vars;
324 let (func,callback) = output_schema_binder index stmt.schema stmt.kind in
325 let params_binder_name = output_params_binder index stmt.vars in
326 if style = `Fold then output "let r_acc = ref acc
in";
327 if style = `List then output "let r_acc = ref [] in";
328 let (bind, callback) =
330 | `Fold -> "IO.(>>=) (", sprintf "(fun x -> r_acc := %s
x !r_acc))" callback
331 | `List -> "IO.(>>=) (", sprintf "(fun x -> r_acc := %s
x :: !r_acc))" callback
332 | `Direct -> "", callback (* or empty string *)
334 output "%sT
.%s db %s %s %s
" bind func sql params_binder_name callback;
335 if style = `Fold then output "(fun () -> IO.return
!r_acc)";
336 if style = `List then output "(fun () -> IO.return
(List.rev
!r_acc))";
340 let generate ~gen_io name stmts =
343 String.concat " and " (List.map (fun s -> sprintf "%s
= T.%s
" s s) ["num
";"text
";"any
"])
348 | true -> "Sqlgg_traits.M_io
", "T.IO
"
349 | false -> "Sqlgg_traits.M
", "Sqlgg_io.Blocking
"
351 output "module %s
(T
: %s
) = struct" (String.capitalize name) traits;
354 output "module IO
= %s
" io;
356 List.iteri (generate_stmt `Direct) stmts;
357 output "module Fold
= struct";
359 List.iteri (generate_stmt `Fold) stmts;
361 output "end (* module Fold *)";
363 output "module List
= struct";
365 List.iteri (generate_stmt `List) stmts;
367 output "end (* module List *)";
369 output "end (* module %s *)" (String.capitalize name)
371 module Generator_base = struct
377 let comment = comment
379 let empty_line = empty_line
382 module Generator = struct
383 include Generator_base
384 let generate () name stmts = generate ~gen_io:false name stmts
387 module Generator_io = struct
388 include Generator_base
389 let generate () name stmts = generate ~gen_io:true name stmts