2013-04-11 05:21:02 -07:00
|
|
|
(* A -ppx rewriter which evaluates expressions at compile-time,
|
|
|
|
using the OCaml toplevel interpreter.
|
|
|
|
|
|
|
|
The following extensions are supported:
|
|
|
|
|
|
|
|
[%eval e] in expression context: the expression e will be evaluated
|
|
|
|
at compile time, and the resulting value will be inserted as a
|
|
|
|
constant literal.
|
|
|
|
|
|
|
|
[%%eval.start] as a structure item: forthcoming structure items
|
|
|
|
until the next [%%eval.stop] will be evaluated at compile time (the
|
|
|
|
result is ignored) only.
|
|
|
|
|
|
|
|
[%%eval.start both] as a structure item: forthcoming structure
|
|
|
|
items until the next [%%eval.stop] will be evaluated at compile
|
|
|
|
time (the result is ignored), but also kept in the compiled unit.
|
|
|
|
|
|
|
|
[%%eval.load "..."] as a structure item: load the specified
|
|
|
|
.cmo unit or .cma library, so that it can be used in the forthcoming
|
|
|
|
compile-time components.
|
|
|
|
*)
|
|
|
|
|
|
|
|
|
|
|
|
module Main : sig end = struct
|
|
|
|
|
2013-04-19 00:40:57 -07:00
|
|
|
open Location
|
2013-04-11 05:21:02 -07:00
|
|
|
open Parsetree
|
|
|
|
open Ast_helper
|
|
|
|
open Outcometree
|
2013-04-11 05:50:38 -07:00
|
|
|
open Ast_helper.Convenience
|
2013-04-11 05:21:02 -07:00
|
|
|
|
|
|
|
let rec lid_of_out_ident = function
|
|
|
|
| Oide_apply _ -> assert false
|
|
|
|
| Oide_dot (x, s) -> lid_of_out_ident x ^ "." ^ s
|
|
|
|
| Oide_ident s -> s
|
|
|
|
|
|
|
|
let rec exp_of_out_value = function
|
|
|
|
| Oval_string x -> str x
|
|
|
|
| Oval_int x -> int x
|
|
|
|
| Oval_char x -> char x
|
2013-07-22 07:58:15 -07:00
|
|
|
| Oval_float x -> Ast_helper.Convenience.float x
|
2013-04-11 05:21:02 -07:00
|
|
|
| Oval_list l -> list (List.map exp_of_out_value l)
|
|
|
|
| Oval_array l -> Exp.array (List.map exp_of_out_value l)
|
|
|
|
| Oval_constr (c, args) -> constr (lid_of_out_ident c) (List.map exp_of_out_value args)
|
|
|
|
| Oval_record l ->
|
2013-04-11 05:50:38 -07:00
|
|
|
record
|
|
|
|
(List.map
|
|
|
|
(fun (s, v) -> lid_of_out_ident s, exp_of_out_value v) l)
|
2013-04-11 05:21:02 -07:00
|
|
|
| v ->
|
|
|
|
Format.eprintf "[%%eval] cannot map value to expression:@.%a@."
|
|
|
|
!Toploop.print_out_value
|
|
|
|
v;
|
|
|
|
exit 2
|
|
|
|
|
|
|
|
let empty_str_item = Str.include_ (Mod.structure [])
|
|
|
|
|
|
|
|
let run phr =
|
|
|
|
try Toploop.execute_phrase true Format.err_formatter phr
|
|
|
|
with exn ->
|
|
|
|
Errors.report_error Format.err_formatter exn;
|
|
|
|
exit 2
|
|
|
|
|
2013-04-19 00:40:57 -07:00
|
|
|
let get_exp loc = function
|
2013-07-22 07:58:15 -07:00
|
|
|
| PStr [ {pstr_desc=Pstr_eval (e, _); _} ] -> e
|
2013-04-19 00:40:57 -07:00
|
|
|
| _ ->
|
2013-07-22 07:58:15 -07:00
|
|
|
Format.eprintf "%aExpression expected@."
|
2013-04-19 00:40:57 -07:00
|
|
|
Location.print_error loc;
|
|
|
|
exit 2
|
|
|
|
|
2013-09-26 01:34:06 -07:00
|
|
|
let eval _args =
|
|
|
|
let open Ast_mapper in
|
|
|
|
let eval_str_items = ref None in
|
|
|
|
let super = default_mapper in
|
|
|
|
let my_structure_item this i =
|
2013-04-11 05:21:02 -07:00
|
|
|
match i.pstr_desc with
|
2013-04-19 00:40:57 -07:00
|
|
|
| Pstr_extension(({txt="eval.load";loc}, e0), _) ->
|
|
|
|
let e0 = get_exp loc e0 in
|
2013-04-11 05:57:59 -07:00
|
|
|
let s =
|
|
|
|
match get_str e0 with
|
|
|
|
| Some s -> s
|
|
|
|
| None ->
|
|
|
|
Location.print_error Format.err_formatter e0.pexp_loc;
|
|
|
|
Format.eprintf "string literal expected";
|
|
|
|
exit 2
|
|
|
|
in
|
2013-04-11 05:21:02 -07:00
|
|
|
if not (Topdirs.load_file Format.err_formatter s) then begin
|
|
|
|
Location.print Format.err_formatter e0.pexp_loc;
|
|
|
|
exit 2;
|
|
|
|
end;
|
|
|
|
empty_str_item
|
2013-04-19 00:40:57 -07:00
|
|
|
| Pstr_extension(({txt="eval.start";_},
|
2013-07-22 07:58:15 -07:00
|
|
|
PStr [{pstr_desc=Pstr_eval (e, _);_}]
|
2013-04-19 00:40:57 -07:00
|
|
|
), _) when get_lid e = Some "both" ->
|
2013-09-26 01:34:06 -07:00
|
|
|
eval_str_items := Some true;
|
2013-04-11 05:21:02 -07:00
|
|
|
empty_str_item
|
2013-07-22 07:58:15 -07:00
|
|
|
| Pstr_extension(({txt="eval.start";_}, PStr []), _) ->
|
2013-09-26 01:34:06 -07:00
|
|
|
eval_str_items := Some false;
|
2013-04-11 05:21:02 -07:00
|
|
|
empty_str_item
|
2013-07-22 07:58:15 -07:00
|
|
|
| Pstr_extension(({txt="eval.stop";_}, PStr []), _) ->
|
2013-09-26 01:34:06 -07:00
|
|
|
eval_str_items := None;
|
2013-04-11 05:21:02 -07:00
|
|
|
empty_str_item
|
|
|
|
| _ ->
|
2013-09-26 01:34:06 -07:00
|
|
|
let s = super.structure_item this i in
|
|
|
|
match !eval_str_items with
|
2013-04-11 05:21:02 -07:00
|
|
|
| None -> s
|
|
|
|
| Some both ->
|
|
|
|
if not (run (Ptop_def [s])) then begin
|
|
|
|
Location.print_error Format.err_formatter s.pstr_loc;
|
|
|
|
Format.eprintf "this structure item raised an exception@.";
|
|
|
|
exit 2
|
|
|
|
end;
|
|
|
|
if both then s else empty_str_item
|
2013-09-26 01:34:06 -07:00
|
|
|
in
|
|
|
|
let my_expr this e =
|
2013-04-11 05:21:02 -07:00
|
|
|
match e.pexp_desc with
|
2013-04-19 00:40:57 -07:00
|
|
|
| Pexp_extension({txt="eval";loc}, e0) ->
|
|
|
|
let e0 = get_exp loc e0 in
|
2013-04-11 05:21:02 -07:00
|
|
|
let last_result = ref None in
|
|
|
|
let pop = !Toploop.print_out_phrase in
|
|
|
|
Toploop.print_out_phrase := begin fun _ppf -> function
|
|
|
|
| Ophr_eval (v, _) -> last_result := Some v
|
|
|
|
| r ->
|
|
|
|
Location.print_error Format.err_formatter e.pexp_loc;
|
|
|
|
Format.eprintf "error while evaluating expression:@.%a@."
|
|
|
|
pop
|
|
|
|
r;
|
|
|
|
exit 2
|
|
|
|
end;
|
2013-04-11 05:50:38 -07:00
|
|
|
assert (run (Ptop_def [Str.eval e0]));
|
2013-04-11 05:21:02 -07:00
|
|
|
Toploop.print_out_phrase := pop;
|
2013-04-11 05:50:38 -07:00
|
|
|
let v = match !last_result with None -> assert false | Some v -> v in
|
|
|
|
with_default_loc e0.pexp_loc (fun () -> exp_of_out_value v)
|
2013-04-11 05:21:02 -07:00
|
|
|
| _ ->
|
2013-09-26 01:34:06 -07:00
|
|
|
super.expr this e
|
|
|
|
in
|
|
|
|
Toploop.initialize_toplevel_env ();
|
|
|
|
{super with expr = my_expr; structure_item = my_structure_item}
|
2013-04-11 05:21:02 -07:00
|
|
|
|
|
|
|
|
2013-09-26 01:34:06 -07:00
|
|
|
let () = Ast_mapper.run_main eval
|
2013-04-11 05:21:02 -07:00
|
|
|
end
|