aboutsummaryrefslogtreecommitdiff
path: root/src/scTypes.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/scTypes.ml')
-rwxr-xr-xsrc/scTypes.ml354
1 files changed, 354 insertions, 0 deletions
diff --git a/src/scTypes.ml b/src/scTypes.ml
new file mode 100755
index 0000000..48e4d3c
--- /dev/null
+++ b/src/scTypes.ml
@@ -0,0 +1,354 @@
+(** All the types used in the spreadsheet. *)
+
+let u = UTF8.from_utf8string
+
+exception Error
+
+type cell = Cell.t
+
+type ident = UTF8.t
+
+type _ dataFormat =
+ | Date: DataType.Num.t dataFormat (* Date *)
+ | Number: DataType.Num.t dataFormat (* Number *)
+ | String: DataType.String.t dataFormat(* String *)
+ | Bool: DataType.Bool.t dataFormat (* Boolean *)
+
+type numericType =
+ | Date
+ | Number
+
+let get_numeric_type: DataType.Num.t dataFormat -> numericType = function
+ | Date -> Date
+ | Number -> Number
+
+type 'a types =
+ | Num : DataType.Num.t dataFormat * DataType.Num.t -> DataType.Num.t types (** A number *)
+ | Str : DataType.String.t -> DataType.String.t types (** A string *)
+ | Bool : DataType.Bool.t -> DataType.Bool.t types (** A boolean *)
+
+let number n = Num (Number, n)
+let string s = Str s
+let date d = Num (Date, d)
+let boolean b = Bool b
+
+
+type 'a returnType =
+ | Num : DataType.Num.t dataFormat option -> DataType.Num.t returnType (** A number *)
+ | Str : DataType.String.t returnType (** A string *)
+ | Bool : DataType.Bool.t returnType (** A boolean *)
+
+
+let f_num: DataType.Num.t returnType = Num None
+let f_date: DataType.Num.t returnType = Num (Some Date)
+let f_number: DataType.Num.t returnType = Num (Some Number)
+let f_string: DataType.String.t returnType = Str
+let f_bool: DataType.Bool.t returnType = Bool
+
+type refs =
+ | Cell of cell (** A cell *)
+ | Range of cell * cell (** An area of cells *)
+
+type expression =
+ | Value : 'a types -> expression (** A direct value *)
+ | Ref : refs -> expression (** A reference to another cell *)
+ | Call : ident * expression list -> expression (** A call to a function *)
+ | Expression : expression -> expression (** An expression *)
+
+(** Result from a computation *)
+type result =
+ | Result : 'a types -> result
+ | Error : exn -> result
+
+module DataFormat = struct
+
+ type formats = F : 'a dataFormat -> formats [@@unboxed]
+
+ let priority: type a. a dataFormat -> int = function
+ | Date -> 1
+ | Number -> 0
+ | String -> 0
+ | Bool -> 0
+
+ let collect_format: DataType.Num.t dataFormat -> formats -> DataType.Num.t dataFormat = begin
+ fun dataFormat -> function
+ | F Date -> Date
+ | _ -> dataFormat
+ end
+
+ let guess_format_result: type a. a returnType -> (unit -> formats list) -> a dataFormat =
+ fun return params -> begin match return with
+ | Str -> String
+ | Bool -> Bool
+ | Num (Some x) -> x
+ | Num None -> List.fold_left collect_format Number (params ())
+ end
+
+ let default_value_for: type a. a dataFormat -> a = function
+ | Date -> DataType.Num.zero
+ | Number -> DataType.Num.zero
+ | Bool -> false
+ | String -> UTF8.empty
+
+ type ('a, 'b) equality = Eq : ('a, 'a) equality
+
+ let compare_format: type a b. a dataFormat -> b dataFormat -> (a, b) equality =
+ fun a b -> begin match a, b with
+ | Date, Date -> Eq
+ | String, String -> Eq
+ | Number, Number -> Eq
+ | Date, Number -> Eq
+ | Number, Date -> Eq
+ | Bool, Bool -> Eq
+ | _, _ -> raise Errors.TypeError
+ end
+
+end
+
+module Type = struct
+
+ let (=) : type a b. a types -> b types -> bool = fun t1 t2 ->
+ match t1, t2 with
+ | Num (_, n1), Num (_, n2) -> DataType.Num.eq n1 n2
+ | Bool b1, Bool b2 -> b1 = b2
+ | Str s1, Str s2 -> s1 = s2
+ | _, _ -> false
+
+ (** Show a list of elements
+ *)
+ let rec show_list printer buffer = begin function
+ | [] -> ()
+ | hd::[] ->
+ UTF8.Printf.bprintf buffer "%a"
+ printer hd
+ | hd::tl ->
+ UTF8.Printf.bprintf buffer "%a, "
+ printer hd;
+ show_list printer buffer tl
+ end
+
+ let show: type a. UTF8.Buffer.buffer -> a types -> unit = fun buffer -> begin function
+ | Str x -> UTF8.Buffer.add_string buffer x
+ | Bool b -> UTF8.Printf.bprintf buffer "%B" b
+ | Num (Number, n) ->
+ if DataType.Num.is_integer n then
+ DataType.Num.to_int n
+ |> string_of_int
+ |> UTF8.from_utf8string
+ |> UTF8.Buffer.add_string buffer
+ else
+ let f = DataType.Num.to_float n
+ and to_b = UTF8.Format.formatter_of_buffer buffer in
+ ignore @@ UTF8.Format.fprintf to_b "%.2f" f;
+ Format.pp_print_flush to_b ()
+ | Num (Date, n) ->
+ let y, m, d = DataType.Date.date_from_julian_day n in
+ UTF8.Printf.bprintf buffer "%d/%d/%d" y m d
+ end
+
+ let show_full: type a. UTF8.Buffer.buffer -> a types -> unit = fun buffer -> begin function
+ | Str x -> UTF8.Buffer.add_string buffer x
+ | Bool b -> UTF8.Printf.bprintf buffer "%B" b
+ | Num (Number, n) ->
+ if DataType.Num.is_integer n then
+ DataType.Num.to_int n
+ |> string_of_int
+ |> UTF8.from_utf8string
+ |> UTF8.Buffer.add_string buffer
+ else
+ let f = DataType.Num.to_float n
+ and to_b = UTF8.Format.formatter_of_buffer buffer in
+ ignore @@ UTF8.Format.fprintf to_b "%f" f;
+ Format.pp_print_flush to_b ()
+ | Num (Date, n) ->
+ let y, m, d = DataType.Date.date_from_julian_day n in
+ UTF8.Printf.bprintf buffer "%d/%d/%d" y m d
+ end
+
+ type t =
+ | Value: 'a dataFormat * 'a -> t
+
+ let get_content : type a. a types -> t = begin function
+ | Num (format, data) -> Value (format, data)
+ | Str s -> Value (String, s)
+ | Bool b -> Value (Bool, b)
+ end
+
+end
+
+module Refs = struct
+
+ type 'a range =
+ | Single of 'a
+ | Array1 of 'a list
+ | Array2 of 'a list list
+
+ let collect = function
+ | Cell x -> Single (Pervasives.fst x)
+ | Range (fst, snd) ->
+ let (x1, y1) = Pervasives.fst fst
+ and (x2, y2) = Pervasives.fst 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 := (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 := (x, y)::!elmy
+ done;
+ elmx := !elmy::!elmx
+ done;
+ Array2 (!elmx)
+ )
+
+ let map f = function
+ | Single coord -> Single (f coord)
+ | Array1 values -> Array1 (List.map f values)
+ | Array2 values -> Array2 (List.map (List.map f) values)
+
+ let shift (vector_x, vector_y) ref =
+ let _shift ((x, y), (fixed_x, fixed_y)) =
+ let x' = if fixed_x then x else x + vector_x
+ and y' = if fixed_y then y else y + vector_y in
+ (x', y'), (fixed_x, fixed_y)
+ in match ref with
+ | Cell x -> Cell (_shift x)
+ | Range (fst, snd) -> Range (_shift fst, _shift snd)
+
+ let show buffer = begin function
+ | Cell r -> UTF8.Buffer.add_string buffer @@ Cell.to_string r
+ | Range (f,t) ->
+ Tools.Tuple2.printb ~first:"" ~last:"" ~sep:":" Cell.to_buffer Cell.to_buffer buffer (f,t)
+ end
+
+ type 'a content =
+ | Value: 'a dataFormat * 'a -> 'a content
+ | List: 'a dataFormat * 'a list -> 'a list content
+ | Matrix: 'a dataFormat * 'a list list -> 'a list list content
+
+ type refContent =
+ | C: 'a content -> refContent [@@unboxed]
+
+ (** 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 dataFormat * a list -> result option -> a dataFormat * a list =
+ fun (format, elements) result ->
+ begin match result with
+ | None -> format, (DataFormat.default_value_for format)::elements
+ | Some (Error x) -> raise x
+ | Some (Result r) ->
+ let Type.Value (format', element) = Type.get_content r in
+ let DataFormat.Eq = DataFormat.compare_format format format' in
+ let new_format = if (DataFormat.priority format) > (DataFormat.priority format') then
+ format
+ else
+ format' in
+ new_format, element::elements
+ end
+
+ let get_content = begin function
+ | Single None -> raise Errors.TypeError
+ | Single (Some (Error x)) -> raise x
+ | Single (Some (Result r)) ->
+ let Type.Value (format, c) = Type.get_content r in C (Value (format, c))
+ | Array1 l ->
+ (* Get the first element in the list in order to get the format *)
+ let Type.Value (format, _) =
+ begin match (Tools.List.find_map (fun x -> x) l) with
+ | Error x -> raise x
+ | Result r -> Type.get_content r
+ end in
+ (* Then build an unified list (if we can) *)
+ let format, values = List.fold_left add_elem (format, []) l in
+ C (List(format, List.rev values))
+ | Array2 l ->
+ (* Get the first element in the list *)
+ let Type.Value (format, _) =
+ begin match (Tools.List.find_map2 (fun x -> x) l) with
+ | Error x -> raise x
+ | Result r -> Type.get_content 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
+ C (Matrix(format, List.rev values))
+ end
+
+end
+
+module Result = struct
+ let (=) t1 t2 =
+ match t1, t2 with
+ | Result v1, Result v2 -> Type.(=) v1 v2
+ | _, _ -> t1 = t2
+
+ let show = begin function
+ | Error x ->
+ (*
+ let buffer = Buffer.create 16 in
+ let b = Format.formatter_of_buffer buffer in
+ Errors.printf b x;
+ Format.pp_print_flush b ();
+ u(Buffer.contents buffer)
+ *)
+ u"#Error"
+ | Result v ->
+ let buffer = UTF8.Buffer.create 16 in
+ Type.show buffer v;
+ UTF8.Buffer.contents buffer
+ end
+
+end
+
+(** Represent an expression.
+ *)
+let rec show_expr buffer : expression -> unit = begin function
+ | Value (Str x) ->
+ (** Print the value with quotes *)
+ UTF8.Printf.bprintf buffer "\"%s\"" (UTF8.to_utf8string x)
+ | Value v -> Type.show buffer v
+ | Ref r -> Refs.show buffer r
+ | Call (ident, params) ->
+ let utf8ident = UTF8.to_utf8string ident in
+ begin match utf8ident with
+ | "+" | "*" | "-" | "/" | "^" | "="
+ | "<>" | "<=" | ">=" | "<" | ">" -> begin match params with
+ | v1::[] ->
+ UTF8.Printf.bprintf buffer "%s%a"
+ utf8ident
+ show_expr v1
+ | v1::v2::[] ->
+ UTF8.Printf.bprintf buffer "%a%s%a"
+ show_expr v1
+ utf8ident
+ show_expr v2
+ | _ ->
+ UTF8.Buffer.add_string buffer ident;
+ Tools.List.printb ~sep:(u";") show_expr buffer params
+ end
+ | _ ->
+ UTF8.Buffer.add_string buffer ident;
+ Tools.List.printb ~sep:(u";") show_expr buffer params
+ end
+ | Expression expr ->
+ UTF8.Printf.bprintf buffer "(%a)" show_expr expr
+end
+