aboutsummaryrefslogtreecommitdiff
path: root/src/expressions/eval_ref.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/expressions/eval_ref.ml')
-rwxr-xr-xsrc/expressions/eval_ref.ml136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/expressions/eval_ref.ml b/src/expressions/eval_ref.ml
new file mode 100755
index 0000000..d367d2d
--- /dev/null
+++ b/src/expressions/eval_ref.ml
@@ -0,0 +1,136 @@
+type 'a range =
+ | Single of 'a
+ | Array1 of 'a list
+ | Array2 of 'a list list
+
+type content =
+ | Value: 'a ScTypes.DataFormat.t * 'a -> content
+ | List: 'a ScTypes.DataFormat.t * 'a list -> content
+ | Matrix: 'a ScTypes.DataFormat.t * 'a list list -> content
+
+(** Type for the mapper function.
+
+ This function should be able to read the cell from the spreadsheet from
+ it coordinates, and return the associated value.
+
+*)
+type mapper = (int * int -> ScTypes.Result.t option)
+
+type 'a t = mapper -> ScTypes.Result.t option range
+
+type 'a obs = mapper -> content
+
+let cell t mapper = begin
+ Single (mapper (Cell.to_pair t))
+end
+
+let range fst snd mapper = begin
+ let (x1, y1) = Cell.to_pair fst
+ and (x2, y2) = Cell.to_pair snd in
+ let min_x = min x1 x2
+ and max_x = max x1 x2
+ and min_y = min y1 y2
+ and max_y = max y1 y2 in
+ if (min_x = max_x) || (min_y = max_y) then (
+ (* There is only a one dimension array *)
+ let elms = ref [] in
+ for x = min_x to max_x do
+ for y = min_y to max_y do
+ elms := (mapper (x, y))::!elms
+ done
+ done;
+ Array1 (!elms)
+ ) else (
+ (* This a two-dimension array *)
+ let elmx = ref [] in
+ for x = min_x to max_x do
+ let elmy = ref [] in
+ for y = min_y to max_y do
+ elmy := (mapper (x, y))::!elmy
+ done;
+ elmx := !elmy::!elmx
+ done;
+ Array2 (!elmx)
+ )
+end
+
+module TypeContent = struct
+
+ type 'a t = 'a ScTypes.DataFormat.t * 'a
+
+ type value = Value: ('a ScTypes.DataFormat.t * 'a) -> value [@@unboxed]
+
+ type 'a obs = value
+
+ let str s = (ScTypes.DataFormat.String, s)
+
+ let bool b = (ScTypes.DataFormat.Bool, b)
+
+ let num n : DataType.Num.t t = (ScTypes.DataFormat.Number, n)
+
+ let date d : DataType.Num.t t = (ScTypes.DataFormat.Date, d)
+
+ let observe (f, t) = Value (f, t)
+
+end
+
+module M = ScTypes.Type.Eval(TypeContent)
+
+(** Add one element in a typed list.
+
+ The function will raise Error.TypeError if the elements does not match
+ with the list type.
+*)
+let add_elem: type a b. a ScTypes.DataFormat.t * a list -> ScTypes.Result.t option -> a ScTypes.DataFormat.t * a list =
+fun (format, elements) result ->
+ begin match result with
+ | None -> format, (ScTypes.DataFormat.default_value_for format)::elements
+ | Some (ScTypes.Result.Error x) -> raise x
+ | Some (ScTypes.Result.Ok r) ->
+
+ let TypeContent.Value (format', element) = M.eval r in
+ let ScTypes.DataFormat.Eq = ScTypes.DataFormat.compare_format format format' in
+ let new_format = if (ScTypes.DataFormat.priority format) > (ScTypes.DataFormat.priority format') then
+ format
+ else
+ format' in
+ new_format, element::elements
+ end
+
+(** extract the content from a range.
+
+ May raise Errors.TypeError if the range cannot be unified.
+ *)
+let get_content = begin function
+ | Single None -> raise Errors.TypeError
+ | Single (Some (ScTypes.Result.Error x)) -> raise x
+ | Single (Some (ScTypes.Result.Ok r)) ->
+ let TypeContent.Value (format, element) = M.eval r in
+ Value (format, element)
+ | Array1 l ->
+ (* Get the first element in the list in order to get the format *)
+ let TypeContent.Value (format, _) =
+ begin match (Tools.List.find_map (fun x -> x) l) with
+ | ScTypes.Result.Error x -> raise x
+ | ScTypes.Result.Ok r -> M.eval r
+ end in
+ (* Then build an unified list (if we can) *)
+ let format, values = List.fold_left add_elem (format, []) l in
+ List(format, List.rev values)
+ | Array2 l ->
+ (* Get the first element in the list *)
+ let TypeContent.Value (format, _) =
+ begin match (Tools.List.find_map2 (fun x -> x) l) with
+ | ScTypes.Result.Error x -> raise x
+ | ScTypes.Result.Ok r -> M.eval r
+ end in
+ (* Then build an unified list *)
+ let format, values = List.fold_left (fun (format, result) elems ->
+ let format, elems = List.fold_left add_elem (format, []) elems in
+ (format, List.rev (elems::result))
+ )(format, []) l in
+ Matrix(format, List.rev values)
+ end
+
+
+let observe t mapper = get_content (t mapper)