(** All the types used in the spreadsheet. *) module Calendar = CalendarLib.Calendar.Precise let u = UTF8.from_utf8string exception Error type cell = Cell.t type ident = UTF8.t type types = | Num : Num.num * (UTF8.t option) -> types (** A number *) | Str : UTF8.t -> types (** A string *) | Date : Num.num -> types (** A date in julian day *) | Undefined : types (** The content is not defined *) | Bool : bool -> types (** A boolean *) | List : types list -> types (** List with heterogenous datas *) type refs = | Cell of cell (** A cell *) | Range of cell * cell (** An area of cells *) type expression = | Value of types (** A direct value *) | Ref of refs (** A reference to another cell *) | Call of ident * expression list (** A call to a function *) | Expression of expression (** An expression *) (** Result from a computation *) type result = | Result of types | Error of exn module Type = struct (* Required because Num.Big_int cannot be compared with Pervasives.(=) *) let (=) t1 t2 = match t1, t2 with | Num (n1,_), Num (n2,_) -> Num.eq_num n1 n2 | Date n1, Date n2 -> Num.eq_num n1 n2 | Num _, Date n2 -> false | Date n1, Num _ -> false | _, _ -> t1 = t2 (** 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 and show buffer = begin function | Undefined -> () | Num (n,x) -> begin match x with | Some value -> UTF8.Buffer.add_string buffer value | None -> if Num.is_integer_num n then UTF8.Buffer.add_string buffer @@ u(Num.string_of_num n) else UTF8.Printf.bprintf buffer "%.*f" 2 (Num.float_of_num n) end | Str x -> UTF8.Buffer.add_string buffer x | Bool b -> UTF8.Printf.bprintf buffer "%B" b | List l -> UTF8.Printf.bprintf buffer "[%a]" (show_list show) l | Date n -> Num.float_of_num n |> Calendar.from_jd |> CalendarLib.Printer.Precise_Calendar.to_string |> u |> UTF8.Buffer.add_string buffer end end module Refs = struct let collect = function | Cell x -> [Pervasives.fst x] | Range (first, snd) -> let (x1, y1) = Pervasives.fst first 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 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; List.rev (!elms) 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 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 _ -> 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::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