More TODO.
[whenjobs.git] / lib / whenutils.ml
index d53ac7e..aa59f18 100644 (file)
@@ -52,6 +52,7 @@ let rec filter_map f = function
     | None -> filter_map f xs
 
 type whenexpr =
+  | Expr_unit
   | Expr_bool of bool
   | Expr_str of string
   | Expr_int of Big_int.big_int
@@ -74,11 +75,13 @@ type whenexpr =
   | Expr_increases of string
   | Expr_decreases of string
   | Expr_prev of string
+  | Expr_reloaded
 
 (* This internal type is used during conversion of the OCaml AST
  * to the whenexpr type.
  *)
 type whenexpr_int =
+  | IExpr_unit
   | IExpr_bool of bool
   | IExpr_str of string
   | IExpr_int of Big_int.big_int
@@ -104,18 +107,21 @@ type shell_script = {
 }
 
 type variable =
+  | T_unit
   | T_bool of bool
   | T_string of string
   | T_int of big_int
   | T_float of float
 
 let variable_of_rpc = function
+  | `unit_t -> T_unit
   | `bool_t b -> T_bool b
   | `string_t s -> T_string s
   | `int_t i -> T_int (big_int_of_string i)
   | `float_t f -> T_float f
 
 let rpc_of_variable = function
+  | T_unit -> `unit_t
   | T_bool b -> `bool_t b
   | T_string s -> `string_t s
   | T_int i -> `int_t (string_of_big_int i)
@@ -127,18 +133,19 @@ type job_private = {
   (* The result of the previous evaluation.  This is used for
    * implementing edge-triggering, since we only trigger the job to run
    * when the state changes from false -> true.
+   *
+   * [None] means there has been no previous evaluation.
    *)
-  job_prev_eval_state : bool;
+  job_prev_eval_state : bool option;
 
   (* When the job {i ran} last time, we take a copy of the variables.
    * This allows us to implement the 'changes' operator.
+   *
+   * [None] means there has been no previous run.
    *)
-  job_prev_variables : variables;
+  job_prev_variables : variables option;
 }
 
-let no_job_private =
-  { job_prev_eval_state = false; job_prev_variables = StringMap.empty }
-
 type job_cond =
   | When_job of whenexpr
   | Every_job of periodexpr
@@ -151,10 +158,23 @@ type job = {
   job_private : job_private;
 }
 
+let make_when_job _loc name e sh =
+  { job_loc = _loc; job_name = name;
+    job_cond = When_job e; job_script = sh;
+    job_private = { job_prev_eval_state = None;
+                    job_prev_variables = None } }
+
+let make_every_job _loc name e sh =
+  { job_loc = _loc; job_name = name;
+    job_cond = Every_job e; job_script = sh;
+    job_private = { job_prev_eval_state = None;
+                    job_prev_variables = None } }
+
 let rec expr_of_ast _loc ast =
   expr_of_iexpr _loc (iexpr_of_ast _loc ast)
 
 and iexpr_of_ast _loc = function
+  | ExId (_, IdUid (_, "()")) -> IExpr_unit
   | ExId (_, IdUid (_, "True")) -> IExpr_bool true
   | ExId (_, IdUid (_, "False")) -> IExpr_bool false
   | ExStr (_, str) -> IExpr_str str
@@ -190,6 +210,7 @@ and uncurry_app_tree _loc = function
     invalid_arg (sprintf "%s: invalid expression" (Loc.to_string _loc))
 
 and expr_of_iexpr _loc = function
+  | IExpr_unit -> Expr_unit
   | IExpr_bool b -> Expr_bool b
   | IExpr_str s -> Expr_str s
   | IExpr_int i -> Expr_int i
@@ -251,6 +272,12 @@ and expr_of_iexpr _loc = function
     invalid_arg (sprintf "%s: '%s' operator must be followed by a variable name"
                    (Loc.to_string _loc) op)
 
+  | IExpr_app ("reloaded", [IExpr_unit]) ->
+    Expr_reloaded
+
+  | IExpr_app ("reloaded", _) ->
+    invalid_arg (sprintf "%s: you must use 'reloaded ()'" (Loc.to_string _loc))
+
   | IExpr_app (op, _) ->
     invalid_arg (sprintf "%s: unknown operator in expression: %s"
                    (Loc.to_string _loc) op)
@@ -270,6 +297,7 @@ and one_param _loc op exprs f =
                    op (Loc.to_string _loc))
 
 let rec string_of_whenexpr = function
+  | Expr_unit -> "()"
   | Expr_bool b -> sprintf "%b" b
   | Expr_str s -> sprintf "%S" s
   | Expr_int i -> sprintf "%s" (string_of_big_int i)
@@ -304,6 +332,7 @@ let rec string_of_whenexpr = function
   | Expr_increases v -> sprintf "increases %s" v
   | Expr_decreases v -> sprintf "decreases %s" v
   | Expr_prev v -> sprintf "prev %s" v
+  | Expr_reloaded -> "reloaded ()"
 
 let string_of_periodexpr = function
   | Every_seconds 1 -> "1 second"
@@ -316,6 +345,7 @@ let string_of_periodexpr = function
   | Every_years i -> sprintf "%d years" i
 
 let rec dependencies_of_whenexpr = function
+  | Expr_unit -> []
   | Expr_bool _ -> []
   | Expr_str _ -> []
   | Expr_int _ -> []
@@ -340,103 +370,105 @@ let rec dependencies_of_whenexpr = function
   | Expr_increases v
   | Expr_decreases v
   | Expr_prev v -> [v]
+  | Expr_reloaded -> []
 
 let dependencies_of_job = function
   | { job_cond = When_job whenexpr } -> dependencies_of_whenexpr whenexpr
   | { job_cond = Every_job _ } -> []
 
-let rec eval_whenexpr job variables = function
+let rec eval_whenexpr job variables onload = function
+  | Expr_unit -> T_unit
   | Expr_bool b -> T_bool b
   | Expr_str s -> T_string s
   | Expr_int i -> T_int i
   | Expr_float f -> T_float f
 
   | Expr_var v ->
-    (try StringMap.find v variables with Not_found -> T_string "")
+    get_variable variables v
 
   | Expr_and (e1, e2) ->
-    if eval_whenexpr_as_bool job variables e1 &&
-       eval_whenexpr_as_bool job variables e2 then
+    if eval_whenexpr_as_bool job variables onload e1 &&
+       eval_whenexpr_as_bool job variables onload e2 then
       T_bool true
     else
       T_bool false
 
   | Expr_or (e1, e2) ->
-    if eval_whenexpr_as_bool job variables e1 ||
-       eval_whenexpr_as_bool job variables e2 then
+    if eval_whenexpr_as_bool job variables onload e1 ||
+       eval_whenexpr_as_bool job variables onload e2 then
       T_bool true
     else
       T_bool false
 
   | Expr_lt (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     if compare_values e1 e2 < 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_le (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     if compare_values e1 e2 <= 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_eq (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     if compare_values e1 e2 = 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_ge (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     if compare_values e1 e2 >= 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_gt (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     if compare_values e1 e2 > 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_not e ->
-    if not (eval_whenexpr_as_bool job variables e) then
+    if not (eval_whenexpr_as_bool job variables onload e) then
       T_bool true
     else
       T_bool false
 
   | Expr_add (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     add_values e1 e2
 
   | Expr_sub (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     sub_values e1 e2
 
   | Expr_mul (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     mul_values e1 e2
 
   | Expr_div (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     div_values e1 e2
 
   | Expr_mod (e1, e2) ->
-    let e1 = eval_whenexpr job variables e1
-    and e2 = eval_whenexpr job variables e2 in
+    let e1 = eval_whenexpr job variables onload e1
+    and e2 = eval_whenexpr job variables onload e2 in
     mod_values e1 e2
 
   | Expr_changes v ->
@@ -448,34 +480,44 @@ let rec eval_whenexpr job variables = function
 
   | Expr_increases v ->
     let prev_value, curr_value = get_prev_curr_value job variables v in
-    if compare_values prev_value curr_value > 0 then
+    if compare_values prev_value curr_value < 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_decreases v ->
     let prev_value, curr_value = get_prev_curr_value job variables v in
-    if compare_values prev_value curr_value < 0 then
+    if compare_values prev_value curr_value > 0 then
       T_bool true
     else
       T_bool false
 
   | Expr_prev v ->
-    try StringMap.find v job.job_private.job_prev_variables
-    with Not_found -> T_string ""
+    get_prev_variable job v
+
+  | Expr_reloaded ->
+    T_bool onload
 
 and get_prev_curr_value job variables v =
-  let prev_value =
-    try StringMap.find v job.job_private.job_prev_variables
-    with Not_found -> T_string "" in
-  let curr_value =
-    try StringMap.find v variables
-    with Not_found -> T_string "" in
+  let prev_value = get_prev_variable job v in
+  let curr_value = get_variable variables v in
   prev_value, curr_value
 
+and get_variable variables v =
+  try StringMap.find v variables with Not_found -> T_string ""
+
+and get_prev_variable job v =
+  match job.job_private.job_prev_variables with
+  | None ->
+    (* Job has never run.  XXX Should do better here. *)
+    T_string ""
+  | Some prev_variables ->
+    get_variable prev_variables v
+
 (* Call {!eval_whenexpr} and cast the result to a boolean. *)
-and eval_whenexpr_as_bool job variables expr =
-  match eval_whenexpr job variables expr with
+and eval_whenexpr_as_bool job variables onload expr =
+  match eval_whenexpr job variables onload expr with
+  | T_unit -> false
   | T_bool r -> r
   | T_string s -> s <> ""
   | T_int i -> sign_big_int i <> 0
@@ -491,6 +533,7 @@ and compare_values value1 value2 =
   | T_string s1, T_string s2 -> compare s1 s2
   | T_int i1, T_int i2 -> compare_big_int i1 i2
   | T_float f1, T_float f2 -> compare f1 f2
+    (* XXX BUG: int should be promoted to float in mixed numeric comparison *)
   | _ ->
     let value1 = string_of_variable value1
     and value2 = string_of_variable value2 in
@@ -559,37 +602,41 @@ and mod_values value1 value2 =
          (printable_string_of_variable value2))
 
 and string_of_variable = function
+  | T_unit -> "" (* for string_of_variable, we don't want () here *)
   | T_bool b -> string_of_bool b
   | T_string s -> s
   | T_int i -> string_of_big_int i
   | T_float f -> string_of_float f
 
 and printable_string_of_variable = function
+  | T_unit -> "()"
   | T_bool b -> string_of_bool b
   | T_string s -> sprintf "%S" s
   | T_int i -> string_of_big_int i
   | T_float f -> string_of_float f
 
-let job_evaluate job variables =
+let job_evaluate job variables onload =
   match job with
   | { job_cond = Every_job _ } -> false, job
   | { job_cond = When_job whenexpr } ->
-    let state = eval_whenexpr_as_bool job variables whenexpr in
+    let state = eval_whenexpr_as_bool job variables onload whenexpr in
 
     (* Because jobs are edge-triggered, we're only interested in the
      * case where the evaluation state changes from false -> true.
      *)
     match job.job_private.job_prev_eval_state, state with
-    | false, false
-    | true, true
-    | true, false ->
-      let jobp = { job.job_private with job_prev_eval_state = state } in
+    | None, false
+    | Some false, false
+    | Some true, true
+    | Some true, false ->
+      let jobp = { job.job_private with job_prev_eval_state = Some state } in
       let job = { job with job_private = jobp } in
       false, job
 
-    | false, true ->
-      let jobp = { job_prev_eval_state = true;
-                   job_prev_variables = variables } in
+    | None, true
+    | Some false, true ->
+      let jobp = { job_prev_eval_state = Some true;
+                   job_prev_variables = Some variables } in
       let job = { job with job_private = jobp } in
       true, job