diff options
author | Sébastien Dailly <sebastien@chimrod.com> | 2018-01-31 13:20:20 +0100 |
---|---|---|
committer | Sébastien Dailly <sebastien@chimrod.com> | 2018-02-07 13:42:36 +0100 |
commit | 824f2987d47e87d58ee2a4a96d7be417aad6aeab (patch) | |
tree | bfbaca8d1c13a2eb1e5568f363cdcd6c94f1786f | |
parent | 112ab4b1c396fc2117191297227d8e411f9b9bb3 (diff) |
API refactoring : made the GADT abstract, provide contructor for each case, and deported the
expression with evaluation with module functors
31 files changed, 1143 insertions, 952 deletions
@@ -1,6 +1,6 @@ OCAMLBUILD ?= ocamlbuild
PACKAGES=dynlink,curses,camlzip,ezxmlm,text,str,menhirLib,zarith,base
-PATHS=src,src/odf,src/tree
+PATHS=src,src/odf,src/tree,src/expressions
MENHIR=-use-menhir
diff --git a/src/evaluator.ml b/src/evaluator.ml deleted file mode 100755 index 05b975f..0000000 --- a/src/evaluator.ml +++ /dev/null @@ -1,150 +0,0 @@ -module D = DataType
-module F = Functions
-
-module Data = struct
-
- (*** Values definitions *)
-
- type 'a value =
- | Bool: D.Bool.t -> D.Bool.t value
- | Num: D.Num.t ScTypes.dataFormat * D.Num.t -> D.Num.t value
- | String: UTF8.t -> UTF8.t value
- | List: 'a ScTypes.dataFormat * 'a list -> 'a list value
- | Matrix: 'a ScTypes.dataFormat * 'a list list -> 'a list list value
-
- (** Extract the type and the content from a value *)
- let get_argument: type a. a value -> a F.typ * a = function
- | Bool b -> F.t_bool, b
- | Num (_, n) -> F.t_int, n
- | String s -> F.t_string, s
- | List (t, l) -> F.t_list (F.typ_of_format t), l
- | Matrix (t, l) -> F.t_list (F.t_list (F.typ_of_format t)), l
-
-end
-
-(** Functions are stored as a mutable catalog. A setter is given *)
-let catalog = ref (F.C.compile F.C.empty)
-
-let set_catalog t = catalog := t
-
-type existencialResult =
- | Result : 'a Data.value -> existencialResult [@@unboxed]
-
-let inject:
-type a. a ScTypes.dataFormat -> a -> existencialResult = fun resultFormat res ->
- begin match resultFormat with
- | ScTypes.Bool -> Result (Data.Bool res)
- | ScTypes.String -> Result (Data.String res)
- | ScTypes.Number -> Result (Data.Num (resultFormat, res))
- | ScTypes.Date -> Result (Data.Num (resultFormat, res))
- end
-
-
-(** Extract the format from a list of results *)
-let build_format_list ll () =
-
- List.map (fun (Result x) ->
- begin match x with
- | Data.Bool _ -> ScTypes.DataFormat.F (ScTypes.Bool)
- | Data.Num (x, _) -> ScTypes.DataFormat.F x
- | Data.String _ -> ScTypes.DataFormat.F (ScTypes.String)
- | Data.List (f, _) -> ScTypes.DataFormat.F f
- | Data.Matrix (f, _) -> ScTypes.DataFormat.F f
- end
- ) ll
-
-(** Call the function with the arguments *)
-let call name args = begin
- let name' = UTF8.to_utf8string name in
- begin try match args with
- | [] ->
- let arg1 = (F.t_unit, ()) in
- let F.C.R(ret, res) = F.C.eval1 !catalog name' arg1 in
- let returnType = ScTypes.DataFormat.guess_format_result ret (fun () -> raise Errors.TypeError) in
- inject returnType res
-
- | (Result p1)::[] ->
- let arg1 = Data.get_argument p1 in
- let F.C.R(ret, res) = F.C.eval1 !catalog name' arg1 in
- let returnType = ScTypes.DataFormat.guess_format_result ret (build_format_list args) in
- inject returnType res
-
- | (Result p1)::(Result p2)::[] ->
- let arg1 = Data.get_argument p1
- and arg2 = Data.get_argument p2 in
- let F.C.R(ret, res) = F.C.eval2 !catalog name' arg1 arg2 in
- let returnType = ScTypes.DataFormat.guess_format_result ret (build_format_list args) in
- inject returnType res
-
- | (Result p1)::(Result p2)::(Result p3)::[] ->
- let arg1 = Data.get_argument p1
- and arg2 = Data.get_argument p2
- and arg3 = Data.get_argument p3 in
- let F.C.R(ret, res) = F.C.eval3 !catalog name' arg1 arg2 arg3 in
- let returnType = ScTypes.DataFormat.guess_format_result ret (build_format_list args) in
- inject returnType res
-
- | _ -> raise Not_found
- with Not_found ->
- let signature = List.map (fun (Result x) ->
- let formatter = Format.str_formatter in
- Functions.repr formatter (fst @@ Data.get_argument x);
- Format.flush_str_formatter ()) args in
-
- raise (Errors.Undefined (name, signature))
- end
-end
-
-let eval mapper value = begin
-
- (** Extract the value from a raw type.
- If the value is Undefined, raise an exception.
- *)
- let extract_value : ScTypes.result -> existencialResult = begin function
- | ScTypes.Result (ScTypes.Num (f, n)) -> Result (Data.Num (f, n))
- | ScTypes.Result (ScTypes.Bool b) -> Result (Data.Bool b)
- | ScTypes.Result (ScTypes.Str s) -> Result (Data.String s)
- | ScTypes.Error x -> raise x
- end in
-
- (** Extract the value from an expression.
- [extract expr] will evaluate the expression and return it. If the result
- cannot be evaluated (because of references pointing to missing values) a
- default value of type [typ] will be returned.
- *)
- let rec extract = begin function
- (* For a reference to an external we first extract the value pointed *)
- | ScTypes.Ref r -> ScTypes.Refs.(
- begin match ScTypes.Refs.get_content @@ mapper r with
- | Value (format, f) -> begin match format with
- | ScTypes.Date -> Result (Data.Num (format, f))
- | ScTypes.Number -> Result (Data.Num (format, f))
- | ScTypes.String -> Result (Data.String f)
- | ScTypes.Bool -> Result (Data.Bool f)
- end
- | List (format, l) -> Result (Data.List (format, l))
- | Matrix (format, l) -> Result (Data.Matrix (format, l))
- end)
-
- (* Evaluate the expression *)
- | ScTypes.Expression e -> extract e
- | ScTypes.Value v -> extract_value (ScTypes.Result v)
- | ScTypes.Call (name, args) ->
- (* The function is not tail recursive, but I don't think we will have
- more than 100 nested functions here... *)
- let args' = List.map extract args in
- call name args'
- end in
-
- let Result r = extract value in
- begin match r with
- | Data.Bool b -> ScTypes.Result (ScTypes.boolean b)
- | Data.String s -> ScTypes.Result (ScTypes.string s)
- | Data.Num (format, n) ->
- begin match ScTypes.get_numeric_type format with
- | ScTypes.Date -> ScTypes.Result (ScTypes.date n)
- | ScTypes.Number -> ScTypes.Result (ScTypes.number n)
- end
- | _ -> raise Errors.TypeError
- end
-end
diff --git a/src/evaluator.mli b/src/evaluator.mli deleted file mode 100755 index e338b8d..0000000 --- a/src/evaluator.mli +++ /dev/null @@ -1,4 +0,0 @@ -val eval: (ScTypes.refs -> ScTypes.result option ScTypes.Refs.range) -> ScTypes.expression -> ScTypes.result
-
-val set_catalog: Functions.C.t -> unit
-
diff --git a/src/expression.ml b/src/expression.ml index 20227ad..ae6c85f 100755 --- a/src/expression.ml +++ b/src/expression.ml @@ -3,13 +3,13 @@ module Tuple2 = Tools.Tuple2 let u = UTF8.from_utf8string
type t =
- | Basic: 'a ScTypes.types -> t (** A direct type *)
- | Formula: formula -> t (** A formula *)
- | Undefined: t (** The content is not defined *)
+ | Basic: 'a ScTypes.Type.t -> t (** A direct type *)
+ | Formula: formula -> t (** A formula *)
+ | Undefined: t (** The content is not defined *)
and formula =
- | Expression of ScTypes.expression (** A valid expression *)
- | Error of int * UTF8.t (** When the expression cannot be parsed *)
+ | Expression of ScTypes.Expr.t (** A valid expression *)
+ | Error of int * UTF8.t (** When the expression cannot be parsed *)
let is_defined = function
@@ -34,11 +34,11 @@ let load content = begin try String.sub content 0 (String.index content '\000')
with Not_found -> content in
try
- let ScTypes.Result r =
+ let ScTypes.Result.Ok r =
ExpressionParser.content ExpressionLexer.read
@@ Lexing.from_string content' in
Basic r
- with _ -> Basic (ScTypes.string (UTF8.from_utf8string content'))
+ with _ -> Basic (ScTypes.Type.string (UTF8.from_utf8string content'))
)
) else (
(* If the string in empty, build an undefined value *)
@@ -49,64 +49,66 @@ end let load_expr expr = expr
+module EvalExpr = ScTypes.Expr.Eval(Evaluate)
(** Extract the parameters to give to a function.
return an Error if one of them is an error
*)
-let eval expr sources = begin
-
- let eval_exp f = Evaluator.eval sources f in
+let eval expr catalog mapper = begin
begin try match expr with
- | Basic value -> ScTypes.Result value
- | Formula (Expression f) -> eval_exp f
- | Formula (Error (i, s)) -> ScTypes.Error ScTypes.Error
- | Undefined -> ScTypes.Error Not_found
- with ex -> ScTypes.Error ex
+ | Basic value -> ScTypes.Result.Ok value
+ | Formula (Expression e) -> EvalExpr.eval e (catalog, mapper)
+ | Formula (Error (i, s)) -> ScTypes.Result.Error ScTypes.Error
+ | Undefined -> ScTypes.Result.Error Not_found
+ with ex -> ScTypes.Result.Error ex
end
end
-let collect_sources expr = begin
- let rec collect refs = function
- | ScTypes.Ref r ->
- begin match ScTypes.Refs.collect r with
- | ScTypes.Refs.Single r -> Cell.Set.add r refs
- | ScTypes.Refs.Array1 a1 ->
- List.fold_left (fun set elt -> Cell.Set.add elt set) refs a1
- | ScTypes.Refs.Array2 a2 ->
- List.fold_left (List.fold_left (fun set elt -> Cell.Set.add elt set)) refs a2
- end
- | ScTypes.Call (ident, params) -> List.fold_left collect refs params
- | ScTypes.Expression f -> collect refs f
- | _ -> refs
- in match expr with
- | Formula (Expression f) -> collect Cell.Set.empty f
+
+module EvalSources = ScTypes.Expr.Eval(Collect_sources)
+
+let collect_sources = begin function
+ | Formula (Expression f) -> EvalSources.eval f () Cell.Set.empty
| _ -> Cell.Set.empty
end
+module Printer = ScTypes.Expr.Eval(Show_expr.Show_Expr(Show_ref)(Show_type))
+
+(** Inherit the default representation, but print the float with all decimals *)
+module LongPrinter = ScTypes.Type.Eval(struct
+
+ include Show_type
+
+ let num n buffer =
+ 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 ()
+
+end)
+
let show e =
let buffer = UTF8.Buffer.create 16 in
begin match e with
| Formula (Expression f) ->
UTF8.Buffer.add_char buffer '=';
- ScTypes.show_expr buffer f
- | Basic b -> ScTypes.Type.show_full buffer b
+ Printer.eval f () buffer
+ | Basic b -> LongPrinter.eval b buffer
| Formula (Error (i,s)) -> UTF8.Buffer.add_string buffer s
| Undefined -> ()
end;
UTF8.Buffer.contents buffer
-let shift vector =
-
- let rec shift_exp: ScTypes.expression -> ScTypes.expression = function
- | ScTypes.Value v -> ScTypes.Value v
- | ScTypes.Call (ident, params) -> ScTypes.Call (ident, List.map shift_exp params)
- | ScTypes.Ref r -> ScTypes.Ref (ScTypes.Refs.shift vector r)
- | ScTypes.Expression expr -> ScTypes.Expression (shift_exp expr)
-
- in function
- | Formula (Expression f) -> Formula (Expression (shift_exp f))
+let shift vector = function
+ | Formula (Expression f) -> Formula (Expression (ScTypes.Expr.shift_exp vector f))
| other -> other
let (=) t1 t2 = match t1, t2 with
diff --git a/src/expression.mli b/src/expression.mli index 8cab479..5867d48 100755 --- a/src/expression.mli +++ b/src/expression.mli @@ -1,10 +1,10 @@ type t =
- | Basic: 'a ScTypes.types -> t (** A direct type *)
- | Formula: formula -> t (** A formula *)
- | Undefined: t (** The content is not defined *)
+ | Basic: 'a ScTypes.Type.t -> t (** A direct type *)
+ | Formula: formula -> t (** A formula *)
+ | Undefined: t (** The content is not defined *)
and formula =
- | Expression of ScTypes.expression (** A valid expression *)
+ | Expression of ScTypes.Expr.t (** A valid Expr.t *)
| Error of int * UTF8.t (** When the expression cannot be parsed *)
@@ -16,7 +16,7 @@ val load_expr: t -> t val is_defined: t -> bool
(** Evaluate the expression *)
-val eval: t -> (ScTypes.refs -> ScTypes.result option ScTypes.Refs.range) -> ScTypes.result
+val eval: t -> Functions.C.t -> ((int * int) -> ScTypes.Result.t option) -> ScTypes.Result.t
(** Collect all the cell referenced in the expression *)
val collect_sources: t -> Cell.Set.t
diff --git a/src/expressionParser.mly b/src/expressionParser.mly index 473797f..1c2769c 100755 --- a/src/expressionParser.mly +++ b/src/expressionParser.mly @@ -1,11 +1,19 @@ %{ open ScTypes + open ScTypes.Result module S = Symbols let u = UTF8.from_utf8string let extractColumnNameFromNum (fixed, (str, value)) = (fixed, value) + let build_call ident = function + | [] -> Expr.call0 ident + | [p1] -> Expr.call1 ident p1 + | [p1;p2] -> Expr.call2 ident p1 p2 + | [p1;p2;p3] -> Expr.call3 ident p1 p2 p3 + | n -> Expr.callN ident n + %} %token <string> REAL @@ -34,8 +42,8 @@ %left TIMES DIVIDE %left POW -%start<ScTypes.expression> value -%start<ScTypes.result> content +%start<ScTypes.Expr.t> value +%start<ScTypes.Result.t> content %% @@ -46,18 +54,18 @@ content: | basic EOF {$1} basic: - | PLUS num {Result (number $2)} - | MINUS num {Result (number (DataType.Num.neg $2))} - | num {Result (number $1)} - | NUM DIVIDE NUM DIVIDE NUM {Result ( - date ( - DataType.Date.get_julian_day + | PLUS num {Ok (Type.number $2)} + | MINUS num {Ok (Type.number (DataType.Num.neg $2))} + | num {Ok (Type.number $1)} + | NUM DIVIDE NUM DIVIDE NUM {Ok ( + Type.date ( + DataType.Date.get_julian_day (int_of_string $1) (int_of_string $3) (int_of_string $5) ))} - | NUM COLON NUM COLON NUM {Result ( - date ( + | NUM COLON NUM COLON NUM {Ok ( + Type.date ( let nhour = DataType.Num.div (DataType.Num.of_int @@ int_of_string $1) (DataType.Num.of_int 24) and nmin = DataType.Num.div (DataType.Num.of_int @@ int_of_string $3) (DataType.Num.of_int 1440) and nsec = DataType.Num.div (DataType.Num.of_int @@ int_of_string $5) (DataType.Num.of_int 86400) @@ -66,34 +74,34 @@ basic: )} expr: - | num {Value (number ($1))} - | MINUS expr {Call (S.sub, [$2])} - | PLUS expr {Call (S.add, [$2])} + | num {Expr.value (Type.number ($1))} + | MINUS expr {Expr.call1 S.sub $2} + | PLUS expr {Expr.call1 S.add $2} - | LETTERS ident LPAREN separated_list(SEMICOLON, expr) RPAREN { Call (u($1 ^ $2), $4) } + | LETTERS ident LPAREN separated_list(SEMICOLON, expr) RPAREN { build_call (u($1 ^ $2)) $4 } - | cell {Ref (Cell $1)} - | cell COLON cell {Ref (Range ($1, $3))} + | cell {Expr.ref (Refs.cell $1)} + | cell COLON cell {Expr.ref (Refs.range $1 $3)} - | LPAREN expr RPAREN {Expression $2} - | STR {Value (string (u $1))} + | LPAREN expr RPAREN {Expr.expression $2} + | STR {Expr.value (Type.string (u $1))} (* Mathematical operators *) - | expr MINUS expr {Call (S.sub, [$1; $3])} - | expr DIVIDE expr {Call (S.div, [$1; $3])} - | expr TIMES expr {Call (S.mul, [$1; $3])} - | expr PLUS expr {Call (S.add, [$1; $3])} - | expr POW expr {Call (S.pow, [$1; $3])} + | expr MINUS expr {Expr.call2 S.sub $1 $3} + | expr DIVIDE expr {Expr.call2 S.div $1 $3} + | expr TIMES expr {Expr.call2 S.mul $1 $3} + | expr PLUS expr {Expr.call2 S.add $1 $3} + | expr POW expr {Expr.call2 S.pow $1 $3} (* Comparaison *) - | expr EQ expr {Call (S.eq, [$1; $3])} - | expr NEQ expr {Call (S.neq, [$1; $3])} - | expr LT expr {Call (S.lt, [$1; $3])} - | expr GT expr {Call (S.gt, [$1; $3])} - | expr LE expr {Call (S.le, [$1; $3])} - | expr GE expr {Call (S.ge, [$1; $3])} + | expr EQ expr {Expr.call2 S.eq $1 $3} + | expr NEQ expr {Expr.call2 S.neq $1 $3} + | expr LT expr {Expr.call2 S.lt $1 $3} + | expr GT expr {Expr.call2 S.gt $1 $3} + | expr LE expr {Expr.call2 S.le $1 $3} + | expr GE expr {Expr.call2 S.ge $1 $3} %inline cell: | LETTERS NUM { Cell.from_string (false, $1) (false, int_of_string $2) } diff --git a/src/expressions/collect_sources.ml b/src/expressions/collect_sources.ml new file mode 100755 index 0000000..d898b86 --- /dev/null +++ b/src/expressions/collect_sources.ml @@ -0,0 +1,69 @@ +module T = struct
+
+ type 'a t = unit
+
+ type 'a obs = ('a -> 'a)
+
+ let str s = ()
+
+ let num n = ()
+
+ let date d = ()
+
+ let bool b = ()
+
+ let observe () x = x
+
+end
+
+module R = struct
+
+ type 'a obs = Cell.Set.t -> Cell.Set.t
+
+ type 'a t = 'a obs
+
+ let cell (c:Cell.t) set = Cell.Set.add (Cell.to_pair c) set
+
+ let range c1 c2 set = begin
+
+ let x1, y1 = Cell.to_pair c1
+ and x2, y2 = Cell.to_pair c2 in
+
+ let f_x x acc = begin
+ let f_y y acc = begin
+ Cell.Set.add (x, y) acc
+ end in
+ Tools.fold_for f_y y1 y2 acc
+ end in
+ Tools.fold_for f_x x1 x2 set
+ end
+
+ let observe elem set = elem set
+
+
+end
+
+let observe f value = f value
+
+let value v () = T.observe v
+
+let ref r () = R.observe r
+
+let call0 ident () acc = acc
+
+let call1 ident p1 () acc = observe p1 acc
+
+let call2 ident p1 p2 () acc = observe p2 (observe p1 acc)
+
+let call3 ident p1 p2 p3 () acc = observe p3 (observe p2 (observe p1 acc))
+
+let callN ident params () acc = List.fold_left (fun acc p -> observe p acc) acc params
+
+let expression e () = e
+
+type obs = Cell.Set.t -> Cell.Set.t
+
+type t = unit
+
+type repr = obs
+
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)
diff --git a/src/expressions/evaluate.ml b/src/expressions/evaluate.ml new file mode 100755 index 0000000..e910c19 --- /dev/null +++ b/src/expressions/evaluate.ml @@ -0,0 +1,142 @@ +(** Internal representation for each type *)
+type 'a value =
+ | Bool: DataType.Bool.t -> DataType.Bool.t value
+ | Num: DataType.Num.t ScTypes.DataFormat.t * DataType.Num.t -> DataType.Num.t value
+ | String: DataType.String.t -> DataType.String.t value
+ | List: 'a ScTypes.DataFormat.t * 'a list -> 'a list value
+ | Matrix: 'a ScTypes.DataFormat.t * 'a list list -> 'a list list value
+
+type existencialResult =
+ | Result : 'a value -> existencialResult [@@unboxed]
+
+type t = (Functions.C.t * (int * int -> ScTypes.Result.t option))
+
+type repr = existencialResult
+
+type obs = ScTypes.Result.t
+
+module T:Sym_type.SYM_TYPE with type 'a obs = existencialResult = struct
+
+ type 'a t = 'a value
+
+ type 'a obs = existencialResult
+
+ let str s = String s
+
+ let num n = Num (ScTypes.DataFormat.Number, n)
+
+ let date d = Num (ScTypes.DataFormat.Date, d)
+
+ let bool b = Bool b
+
+ let observe x = Result x
+
+end
+
+module R = Eval_ref
+
+(** Extract the type and the content from a value *)
+let get_argument: type a. a value -> a Functions.typ * a = function
+ | Bool b -> Functions.t_bool, b
+ | Num (_, n) -> Functions.t_int, n
+ | String s -> Functions.t_string, s
+ | List (t, l) -> Functions.t_list (Functions.typ_of_format t), l
+ | Matrix (t, l) -> Functions.t_list (Functions.t_list (Functions.typ_of_format t)), l
+
+let wrap_call (Functions.C.R(ret, res)) type_builder = begin
+ let returnType = ScTypes.ReturnType.guess_format_result ret type_builder in
+ begin match returnType with
+ | ScTypes.DataFormat.Bool -> T.observe (T.bool res)
+ | ScTypes.DataFormat.String -> T.observe (T.str res)
+ | ScTypes.DataFormat.Number -> T.observe (T.num res)
+ | ScTypes.DataFormat.Date -> T.observe (T.date res)
+ end
+end
+
+(** Extract the format from a list of results *)
+let build_format_list ll =
+
+ List.map (fun (Result x) ->
+ begin match x with
+ | Bool _ -> ScTypes.DataFormat.F (ScTypes.DataFormat.Bool)
+ | Num (x, _) -> ScTypes.DataFormat.F x
+ | String _ -> ScTypes.DataFormat.F (ScTypes.DataFormat.String)
+ | List (f, _) -> ScTypes.DataFormat.F f
+ | Matrix (f, _) -> ScTypes.DataFormat.F f
+ end
+ ) ll
+
+let value v _ = T.observe v
+
+let ref r (_, mapper) = begin
+ match R.observe r mapper with
+ | R.Value (f, res) -> begin match f with
+ | ScTypes.DataFormat.Bool -> T.observe (T.bool res)
+ | ScTypes.DataFormat.String -> T.observe (T.str res)
+ | ScTypes.DataFormat.Number -> T.observe (T.num res)
+ | ScTypes.DataFormat.Date -> T.observe (T.date res)
+ end
+ | R.List (t, l) -> Result (List(t, l))
+ | R.Matrix (t, l) -> Result (Matrix(t, l))
+end
+
+let call0 ident (catalog, _) =
+ let name' = UTF8.to_utf8string ident in
+ let arg1 = (Functions.t_unit, ()) in
+ wrap_call
+ (Functions.C.eval1 catalog name' arg1)
+ (fun () -> raise Errors.TypeError)
+
+let call1 ident p1 (catalog, _) =
+ let name' = UTF8.to_utf8string ident in
+ let (Result r1) = p1 in
+ let arg1 = get_argument r1 in
+ wrap_call
+ (Functions.C.eval1 catalog name' arg1)
+ (fun () -> build_format_list [p1])
+
+let call2 ident p1 p2 (catalog, _) =
+ let name' = UTF8.to_utf8string ident in
+ let (Result r1) = p1 in
+ let (Result r2) = p2 in
+ let arg1 = get_argument r1
+ and arg2 = get_argument r2 in
+ wrap_call
+ (Functions.C.eval2 catalog name' arg1 arg2)
+ (fun () -> build_format_list [p1; p2])
+
+let call3 ident p1 p2 p3 (catalog, _) =
+ let name' = UTF8.to_utf8string ident in
+ let (Result r1) = p1 in
+ let (Result r2) = p2 in
+ let (Result r3) = p3 in
+ let arg1 = get_argument r1
+ and arg2 = get_argument r2
+ and arg3 = get_argument r3 in
+ wrap_call
+ (Functions.C.eval3 catalog name' arg1 arg2 arg3)
+ (fun () -> build_format_list [p1; p2 ; p3])
+
+let callN ident params (catalog, _) =
+ let signature = List.map (fun (Result r) ->
+ let formatter = Format.str_formatter in
+ Functions.repr formatter (fst @@ get_argument r);
+ Format.flush_str_formatter ()) params in
+ raise (Errors.Undefined (ident, signature))
+
+let expression e _ = e
+
+let observe repr = begin
+ let Result r = repr in match r with
+ | Bool b -> ScTypes.Result.Ok (ScTypes.Type.boolean b)
+ | String s -> ScTypes.Result.Ok (ScTypes.Type.string s)
+ | Num (format, n) ->
+ begin match format with
+ (* We can only match numeric formats here *)
+ | ScTypes.DataFormat.Date -> ScTypes.Result.Ok (ScTypes.Type.date n)
+ | ScTypes.DataFormat.Number -> ScTypes.Result.Ok (ScTypes.Type.number n)
+ end
+ | _ -> raise Errors.TypeError
+
+end
+
diff --git a/src/expressions/show_expr.ml b/src/expressions/show_expr.ml new file mode 100755 index 0000000..3a54929 --- /dev/null +++ b/src/expressions/show_expr.ml @@ -0,0 +1,62 @@ +let u = UTF8.from_utf8string
+
+module Show_Expr
+ (R:Sym_ref.SYM_REF with type 'a obs = (UTF8.Buffer.buffer -> unit))
+ (T:Sym_type.SYM_TYPE with type 'a obs = (UTF8.Buffer.buffer -> unit)) = struct
+
+ module T = T
+ module R = R
+
+ type t = unit
+ type repr = UTF8.Buffer.buffer -> unit
+ type obs = UTF8.Buffer.buffer -> unit
+
+ let observe buffer value = buffer value
+
+ let value v () buffer = T.observe v buffer
+
+ let ref r () buffer = R.observe r buffer
+
+ let call0 ident () buffer =
+ let utf8ident = UTF8.to_utf8string ident in
+ UTF8.Printf.bprintf buffer "%s()" utf8ident
+
+ let call1 ident p1 () buffer =
+ let utf8ident = UTF8.to_utf8string ident in
+ UTF8.Printf.bprintf buffer "%s(%a)"
+ utf8ident
+ (fun x b -> observe b x) p1
+
+ let call2 ident p1 p2 () buffer =
+ let utf8ident = UTF8.to_utf8string ident in
+ begin match utf8ident with
+ | "+" | "*" | "-" | "/" | "^" | "="
+ | "<>" | "<=" | ">=" | "<" | ">" ->
+ UTF8.Printf.bprintf buffer "%a%s%a"
+ (fun x b -> observe b x) p1
+ utf8ident
+ (fun x b -> observe b x) p2
+ | _ ->
+ UTF8.Printf.bprintf buffer "%s(%a;%a)"
+ utf8ident
+ (fun x b -> observe b x) p1
+ (fun x b -> observe b x) p2
+ end
+
+ let call3 ident p1 p2 p3 () buffer =
+ let utf8ident = UTF8.to_utf8string ident in
+ UTF8.Printf.bprintf buffer "%s(%a;%a;%a)"
+ utf8ident
+ (fun x b -> observe b x) p1
+ (fun x b -> observe b x) p2
+ (fun x b -> observe b x) p3
+
+ let callN ident (params: repr list) () buffer =
+ UTF8.Buffer.add_string buffer ident;
+ Tools.List.printb ~sep:(u";") (fun buffer value -> value buffer) buffer params
+
+ let expression e () buffer =
+ UTF8.Printf.bprintf buffer "(%a)"
+ (fun x b -> b x) e
+
+end
diff --git a/src/expressions/show_ref.ml b/src/expressions/show_ref.ml new file mode 100755 index 0000000..97d8022 --- /dev/null +++ b/src/expressions/show_ref.ml @@ -0,0 +1,11 @@ +type 'a t = UTF8.Buffer.buffer -> unit
+
+type 'a obs = UTF8.Buffer.buffer -> unit
+
+let cell t buffer =
+ UTF8.Buffer.add_string buffer @@ Cell.to_string t
+
+let range c1 c2 buffer =
+ Tools.Tuple2.printb ~first:"" ~last:"" ~sep:":" Cell.to_buffer Cell.to_buffer buffer (c1, c2)
+
+let observe elem buffer = elem buffer
diff --git a/src/expressions/show_type.ml b/src/expressions/show_type.ml new file mode 100755 index 0000000..c459dca --- /dev/null +++ b/src/expressions/show_type.ml @@ -0,0 +1,26 @@ +type 'a t = UTF8.Buffer.buffer -> unit
+type 'a obs = UTF8.Buffer.buffer -> unit
+
+let str s buffer =
+ UTF8.Buffer.add_string buffer s
+
+let num n buffer =
+ 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 ()
+
+let date n buffer =
+ let y, m, d = DataType.Date.date_from_julian_day n in
+ UTF8.Printf.bprintf buffer "%d/%d/%d" y m d
+
+let bool b buffer =
+ UTF8.Printf.bprintf buffer "%B" b
+
+let observe elem buffer = elem buffer
diff --git a/src/expressions/sym_expr.ml b/src/expressions/sym_expr.ml new file mode 100755 index 0000000..5ff828e --- /dev/null +++ b/src/expressions/sym_expr.ml @@ -0,0 +1,31 @@ +module type SYM_EXPR = sig
+
+ module T:Sym_type.SYM_TYPE
+
+ module R:Sym_ref.SYM_REF
+
+ type t
+
+ type repr
+
+ type obs
+
+ val value : 'a T.t -> t -> repr
+
+ val ref : 'a R.t -> t -> repr
+
+ val call0 : UTF8.t -> t -> repr
+
+ val call1 : UTF8.t -> repr -> t -> repr
+
+ val call2 : UTF8.t -> repr -> repr -> t -> repr
+
+ val call3 : UTF8.t -> repr -> repr -> repr -> t -> repr
+
+ val callN: UTF8.t -> repr list -> t -> repr
+
+ val expression : repr -> t -> repr
+
+ val observe : repr -> obs
+
+end
diff --git a/src/expressions/sym_ref.ml b/src/expressions/sym_ref.ml new file mode 100755 index 0000000..aba8053 --- /dev/null +++ b/src/expressions/sym_ref.ml @@ -0,0 +1,12 @@ +module type SYM_REF = sig
+
+ type 'a t
+
+ type 'a obs
+
+ val cell : Cell.t -> 'a t
+
+ val range : Cell.t -> Cell.t -> 'a t
+
+ val observe : 'a t -> 'a obs
+end
diff --git a/src/expressions/sym_type.ml b/src/expressions/sym_type.ml new file mode 100755 index 0000000..31c9534 --- /dev/null +++ b/src/expressions/sym_type.ml @@ -0,0 +1,18 @@ +module type SYM_TYPE = sig
+
+ type 'a t
+
+ type 'a obs
+
+ val str : DataType.String.t -> DataType.String.t t
+
+ val num : DataType.Num.t -> DataType.Num.t t
+
+ val date: DataType.Num.t -> DataType.Num.t t
+
+ val bool : DataType.Bool.t -> DataType.Bool.t t
+
+ val observe : 'a t -> 'a obs
+
+end
+
diff --git a/src/functions.ml b/src/functions.ml index 62426e9..309de6c 100755 --- a/src/functions.ml +++ b/src/functions.ml @@ -14,11 +14,11 @@ let t_int: DataType.Num.t typ = Num let t_string: UTF8.t typ = String
let t_list (t: 'a typ): 'a list typ = List t
-let typ_of_format: type a. a ScTypes.dataFormat -> a typ = function
- | ScTypes.Date -> Num
- | ScTypes.Number -> Num
- | ScTypes.String -> String
- | ScTypes.Bool -> Bool
+let typ_of_format: type a. a ScTypes.DataFormat.t -> a typ = function
+ | ScTypes.DataFormat.Date -> Num
+ | ScTypes.DataFormat.Number -> Num
+ | ScTypes.DataFormat.String -> String
+ | ScTypes.DataFormat.Bool -> Bool
let rec repr:
@@ -53,16 +53,16 @@ module C = Catalog.Make(struct type 'a t = 'a typ
- type 'a returnType = 'a ScTypes.returnType
+ type 'a returnType = 'a ScTypes.ReturnType.t
end)
-let f_num = ScTypes.f_num
-let f_date = ScTypes.f_date
-let f_number = ScTypes.f_number
-let f_string = ScTypes.f_string
-let f_bool = ScTypes.f_bool
+let f_num = ScTypes.ReturnType.f_num
+let f_date = ScTypes.ReturnType.f_date
+let f_number = ScTypes.ReturnType.f_number
+let f_string = ScTypes.ReturnType.f_string
+let f_bool = ScTypes.ReturnType.f_bool
module Make_Compare(Comp: D.COMPARABLE) = struct
diff --git a/src/functions.mli b/src/functions.mli index c6904b2..43a6fc2 100755 --- a/src/functions.mli +++ b/src/functions.mli @@ -9,13 +9,13 @@ val t_int: DataType.Num.t typ val t_string: UTF8.t typ
val t_list: 'a typ -> 'a list typ
-val typ_of_format: 'a ScTypes.dataFormat -> 'a typ
+val typ_of_format: 'a ScTypes.DataFormat.t -> 'a typ
val repr: Format.formatter -> 'a typ -> unit
module C : Catalog.CATALOG
with type 'a argument = 'a typ
- and type 'a returnType = 'a ScTypes.returnType
+ and type 'a returnType = 'a ScTypes.ReturnType.t
(** Load all the built_in functions *)
val built_in: C.catalog_builder -> C.catalog_builder
diff --git a/src/main.ml b/src/main.ml index 4491025..8e557ce 100755 --- a/src/main.ml +++ b/src/main.ml @@ -1,3 +1,5 @@ +module E:Sym_expr.SYM_EXPR = Evaluate + let u = UTF8.from_utf8string let redraw t screen = @@ -13,6 +15,8 @@ let action f msg (t, screen) = begin t', screen' end +let catalog = Functions.C.compile @@ Functions.built_in Functions.C.empty + let f screen = ActionParser.( begin match Screen.read_key screen with | "\027" -> ESC @@ -127,14 +131,17 @@ let rec normal_mode (t, screen) = begin normal_mode @@ redraw t' screen end +(* | Actions.Search -> let expr = Screen.search screen |> Expression.load in - let pattern = Expression.eval expr (fun _ -> ScTypes.Refs.Single None) in + (*let pattern = Expression.eval expr (fun _ -> ScTypes.Refs.Single None) in*) + let pattern = Expression.eval' expr catalog in begin match Sheet.search (`Pattern (Some pattern)) t with | Some t' -> normal_mode @@ redraw t' screen | None -> normal_mode (t, screen) end +*) | Actions.Button1_clicked coord -> begin match Screen.get_cell screen coord with @@ -227,7 +234,7 @@ and command (t, screen) action = begin normal_mode @@ redraw t screen *) | ("enew", _) -> (* Start a new spreadsheet *) - normal_mode @@ redraw (Sheet.create Sheet.Raw.empty) screen + normal_mode @@ redraw (Sheet.create catalog Sheet.Raw.empty) screen | ("q", _) -> (* Quit *) t | _ -> normal_mode @@ redraw t screen @@ -235,16 +242,12 @@ end let () = begin - let catalog = Functions.built_in Functions.C.empty in - ignore @@ Evaluator.set_catalog (Functions.C.compile catalog); - - let sheet = if Array.length Sys.argv = 1 then Sheet.Raw.empty else - Odf.load Sys.argv.(1) in + Odf.load catalog Sys.argv.(1) in Screen.run (fun window -> - ignore @@ normal_mode @@ redraw (Sheet.create sheet) window) + ignore @@ normal_mode @@ redraw (Sheet.create catalog sheet) window) end diff --git a/src/odf/odf.ml b/src/odf/odf.ml index 048be2e..176e70a 100755 --- a/src/odf/odf.ml +++ b/src/odf/odf.ml @@ -5,14 +5,14 @@ let u = UTF8.from_utf8string type t
-let load_xml input = begin
+let load_xml catalog input = begin
let source = Xmlm.make_input ~enc:(Some `UTF_8) (`Channel input) in
- let sheet = OdfLoader.load source in
+ let sheet = OdfLoader.load catalog source in
sheet
end
-let load file =
+let load catalog file =
let tmp_file = Filename.temp_file "content" ".xml" in
Unix.unlink tmp_file;
@@ -22,14 +22,13 @@ let load file = let input = open_in_bin tmp_file in
Tools.try_finally
- (fun () -> load_xml input)
+ (fun () -> load_xml catalog input)
(fun () ->
close_in input;
Unix.unlink tmp_file;
Zip.close_in zip
)
-
let write_type ovalue_type cvalue_type attrs output value = begin
let attrs =
(NS.ovalue_type_attr, ovalue_type)::
@@ -50,87 +49,86 @@ let write_bool = write_type "bool" "bool" let write_error = write_type "string" "error"
let write_date = write_type "date" "date"
-let write_basic: type a. 'b list -> Xmlm.output -> a ScTypes.types -> unit = fun attrs output types -> begin match types with
- | ScTypes.Str s -> write_str attrs output (UTF8.to_utf8string s)
- | ScTypes.Bool b -> write_bool attrs output (string_of_bool b)
- | ScTypes.Num (data_type, d) ->
- begin match ScTypes.get_numeric_type data_type with
- | ScTypes.Number ->
- let f = DataType.Num.to_float d in
- let value = string_of_float f in
- write_num ((NS.value_attr, value)::attrs) output value
- | ScTypes.Date ->
- let value = DataType.Date.to_string d in
- write_date ((NS.date_value_attr, value)::attrs) output value
- end
-end
+module BasicWriter = ScTypes.Type.Eval(struct
+
+ type 'a t = (Xmlm.attribute list -> Xmlm.output -> unit)
+ type 'a obs = 'a t
+
+ let str s attrs output = write_str attrs output (UTF8.to_utf8string s)
+
+ let bool b attrs output = write_bool attrs output (string_of_bool b)
+
+ let num n attrs output =
+ let f = DataType.Num.to_float n in
+ let value = string_of_float f in
+ write_num ((NS.value_attr, value)::attrs) output value
+
+ let date d attrs output =
+ let value = DataType.Date.to_string d in
+ write_date ((NS.date_value_attr, value)::attrs) output value
+
+ let observe value attrs output = value attrs output
+
+end)
+
+let write_basic: type a. 'b list -> Xmlm.output -> a ScTypes.Type.t -> unit = fun attrs output types ->
+ BasicWriter.eval types attrs output
let write_formula output attrs f = begin function
- | ScTypes.Result x -> write_basic attrs output x
- | ScTypes.Error exn -> write_str attrs output "#NAME?"
+ | ScTypes.Result.Ok x -> write_basic attrs output x
+ | ScTypes.Result.Error exn -> write_str attrs output "#NAME?"
end
-let print_ref buffer c =
- UTF8.Buffer.add_string buffer @@ u"[.";
- begin match c with
- | ScTypes.Cell c -> UTF8.Buffer.add_string buffer @@ Cell.to_string c;
- | ScTypes.Range (c1, c2) ->
+(** Print a reference *)
+module Show_ref = struct
+ type 'a t = UTF8.Buffer.buffer -> unit
+
+ type 'a obs = UTF8.Buffer.buffer -> unit
+
+ let cell t buffer =
+ UTF8.Buffer.add_string buffer @@ u"[.";
+ UTF8.Buffer.add_string buffer @@ Cell.to_string t;
+ UTF8.Buffer.add_string buffer @@ u"]"
+
+ let range c1 c2 buffer =
+ UTF8.Buffer.add_string buffer @@ u"[.";
UTF8.Buffer.add_string buffer @@ Cell.to_string c1;
UTF8.Buffer.add_string buffer @@ u":.";
UTF8.Buffer.add_string buffer @@ Cell.to_string c2;
- end;
- UTF8.Buffer.add_string buffer @@ u"]"
-
-let rec print_expr : UTF8.Buffer.buffer -> ScTypes.expression -> unit = fun buffer -> begin function
- | ScTypes.Value (ScTypes.Str s) ->
- UTF8.Printf.bprintf buffer "\"%s\"" (UTF8.to_utf8string s)
- | ScTypes.Value (ScTypes.Bool b) ->
- u(string_of_bool b)
+ UTF8.Buffer.add_string buffer @@ u"]"
+
+ let observe elem buffer = elem buffer
+end
+
+module Show_type = struct
+ type 'a t = UTF8.Buffer.buffer -> unit
+ type 'a obs = UTF8.Buffer.buffer -> unit
+
+ let str s buffer =
+ UTF8.Printf.bprintf buffer "\"%s\"" (UTF8.to_utf8string s)
+
+ let num n buffer =
+ let f = DataType.Num.to_float n in
+ UTF8.Buffer.add_string buffer @@ u(string_of_float f)
+
+ let date n buffer = DataType.Date.to_string n
+ |> u
|> UTF8.Buffer.add_string buffer
- | ScTypes.Value (ScTypes.Num (data_type, d)) ->
- begin match ScTypes.get_numeric_type data_type with
- | ScTypes.Number ->
- let f = DataType.Num.to_float d in
- UTF8.Buffer.add_string buffer @@ u(string_of_float f)
- | ScTypes.Date ->
- DataType.Date.to_string d
- |> u
- |> UTF8.Buffer.add_string buffer
- end
- | ScTypes.Ref r -> print_ref buffer r
- | ScTypes.Expression x ->
- UTF8.Buffer.add_char buffer '(';
- print_expr buffer x;
- UTF8.Buffer.add_char buffer ')';
- | ScTypes.Call (ident, params) ->
- begin match (UTF8.to_utf8string ident) with
- | "+" | "*" | "-" | "/" | "^" | "="
- | "<>" | "<=" | ">=" | "<" | ">" -> begin match params with
- | v1::[] ->
- UTF8.Printf.bprintf buffer "%s%a"
- (UTF8.to_utf8string ident)
- print_expr v1
- | v1::v2::[] ->
- UTF8.Printf.bprintf buffer "%a%s%a"
- print_expr v1
- (UTF8.to_utf8string ident)
- print_expr v2
- | _ ->
- UTF8.Buffer.add_string buffer ident;
- Tools.List.printb ~sep:(u";") print_expr buffer params
- end
- | _ ->
- UTF8.Buffer.add_string buffer ident;
- Tools.List.printb ~sep:(u";") print_expr buffer params
- end
+
+ let bool b buffer =
+ UTF8.Buffer.add_string buffer @@ u(string_of_bool b)
+
+ let observe elem buffer = elem buffer
end
+module ExpressionPrinter = ScTypes.Expr.Eval(Show_expr.Show_Expr(Show_ref)(Show_type))
+
let write_cell output value = begin function
| Expression.Undefined -> ()
| Expression.Basic b -> write_basic [] output b
| Expression.Formula (Expression.Expression f) ->
let buffer = UTF8.Buffer.create 10 in
- print_expr buffer f;
+ ExpressionPrinter.eval f () buffer;
let formula = UTF8.Buffer.contents buffer
|> UTF8.to_utf8string in
write_formula output [(NS.formula_attr, ("of:=" ^formula))] f value
diff --git a/src/odf/odfLoader.ml b/src/odf/odfLoader.ml index 9420fdd..93a6c62 100755 --- a/src/odf/odfLoader.ml +++ b/src/odf/odfLoader.ml @@ -16,18 +16,18 @@ end let load_content cache content = begin function
| "float" -> Expression.Basic (
- ScTypes.number (
+ ScTypes.Type.number (
DataType.Num.of_float (float_of_string content)
))
| "date" -> Expression.Basic (
- ScTypes.date (
+ ScTypes.Type.date (
DataType.Num.of_float (float_of_string content)
))
| _ ->
(* If the same text is present many times, use the same string instead of creating a new one *)
memoization cache content (fun content ->
Expression.Basic (
- ScTypes.string (
+ ScTypes.Type.string (
UTF8.from_utf8string content)))
end
@@ -83,7 +83,7 @@ let build_p (attributes:Xmlm.attribute list) = begin function end
-let build_row (sheet:Sheet.Raw.t ref) (row_num:int ref) (attributes:Xmlm.attribute list) (childs:tree list) = begin
+let build_row (sheet:Sheet.Raw.t ref) (row_num:int ref) catalog (attributes:Xmlm.attribute list) (childs:tree list) = begin
let repetition =
try int_of_string @@ List.assoc (NS.table, "number-rows-repeated") attributes
@@ -94,7 +94,7 @@ let build_row (sheet:Sheet.Raw.t ref) (row_num:int ref) (attributes:Xmlm.attribu List.iter (function
| Cell cell ->
for i = 1 to cell.repetition do
- sheet := snd @@ Sheet.Raw.add (!cell_num, !row_num) cell.expression !sheet;
+ sheet := snd @@ Sheet.Raw.add (!cell_num, !row_num) cell.expression catalog !sheet;
cell_num := !cell_num + cell.cell_width
done;
| _ -> ()
@@ -106,7 +106,7 @@ end let data str = Data str
-let load source = begin
+let load catalog source = begin
(* Mutable datas *)
let sheet = ref Sheet.Raw.empty in
@@ -115,7 +115,7 @@ let load source = begin let table = Base.String_dict.of_alist_exn [
((NS.text ^ "p"), build_p);
((NS.table ^ "table-cell"), build_cell cache);
- ((NS.table ^ "table-row"), build_row sheet (ref 1))
+ ((NS.table ^ "table-row"), build_row sheet (ref 1) catalog)
] in
let el (((ns, name), attributes):Xmlm.tag) childs = begin
diff --git a/src/odf/odf_ExpressionParser.mly b/src/odf/odf_ExpressionParser.mly index 54836cd..2acd1b8 100755 --- a/src/odf/odf_ExpressionParser.mly +++ b/src/odf/odf_ExpressionParser.mly @@ -6,6 +6,13 @@ let extractColumnNameFromNum (fixed, str) = (fixed, int_of_string str) + let build_call ident = function + | [] -> Expr.call0 ident + | [p1] -> Expr.call1 ident p1 + | [p1;p2] -> Expr.call2 ident p1 p2 + | [p1;p2;p3] -> Expr.call3 ident p1 p2 p3 + | n -> Expr.callN ident n + %} %token <string> REAL @@ -35,7 +42,7 @@ %left TIMES DIVIDE %left POW -%start<ScTypes.expression> value +%start<ScTypes.Expr.t> value %% @@ -43,37 +50,36 @@ value: | LETTERS COLON EQ expr EOF {$4} expr: - | num {Value (number ($1))} - | MINUS expr {Call (S.sub, [$2])} - | PLUS expr {Call (S.add, [$2])} + | num {Expr.value (Type.number ($1))} + | MINUS expr {Expr.call1 S.sub $2} + | PLUS expr {Expr.call1 S.add $2} | L_SQ_BRACKET ref R_SQ_BRACKET {$2} - | LPAREN expr RPAREN {Expression $2} - | STR {Value (string (u $1))} + | LPAREN expr RPAREN {Expr.expression $2} + | STR {Expr.value (Type.string (u $1))} (* Mathematical operators *) - | expr MINUS expr {Call (S.sub, [$1; $3])} - | expr DIVIDE expr {Call (S.div, [$1; $3])} - | expr TIMES expr {Call (S.mul, [$1; $3])} - | expr PLUS expr {Call (S.add, [$1; $3])} - | expr POW expr {Call (S.pow, [$1; $3])} + | expr MINUS expr {Expr.call2 S.sub $1 $3} + | expr DIVIDE expr {Expr.call2 S.div $1 $3} + | expr TIMES expr {Expr.call2 S.mul $1 $3} + | expr PLUS expr {Expr.call2 S.add $1 $3} + | expr POW expr {Expr.call2 S.pow $1 $3} (* Comparaison *) - | expr EQ expr {Call (S.eq, [$1; $3])} - | expr NEQ expr {Call (S.neq, [$1; $3])} - | expr LT expr {Call (S.lt, [$1; $3])} - | expr GT expr {Call (S.gt, [$1; $3])} - | expr LE expr {Call (S.le, [$1; $3])} - | expr GE expr {Call (S.ge, [$1; $3])} - - | ident LPAREN separated_list(SEMICOLON, expr) RPAREN { Call (u $1, $3) } + | expr EQ expr {Expr.call2 S.eq $1 $3} + | expr NEQ expr {Expr.call2 S.neq $1 $3} + | expr LT expr {Expr.call2 S.lt $1 $3} + | expr GT expr {Expr.call2 S.gt $1 $3} + | expr LE expr {Expr.call2 S.le $1 $3} + | expr GE expr {Expr.call2 S.ge $1 $3} + | ident LPAREN separated_list(SEMICOLON, expr) RPAREN { build_call (u $1) $3 } ref: - | cell {Ref (Cell $1)} - | cell COLON cell {Ref (Range ($1, $3))} + | cell {Expr.ref (Refs.cell $1)} + | cell COLON cell {Expr.ref (Refs.range $1 $3)} cell: | DOT fixed(LETTERS) fixed(NUM){Cell.from_string $2 (extractColumnNameFromNum $3)} diff --git a/src/scTypes.ml b/src/scTypes.ml index fc6dd1f..ef39af3 100755 --- a/src/scTypes.ml +++ b/src/scTypes.ml @@ -4,87 +4,29 @@ 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] + type _ t = + | Date: DataType.Num.t t (* Date *) + | Number: DataType.Num.t t (* Number *) + | String: DataType.String.t t(* String *) + | Bool: DataType.Bool.t t (* Boolean *) + + type formats = F : 'a t -> formats [@@unboxed] - let priority: type a. a dataFormat -> int = function + let priority: type a. a t -> int = function | Date -> 1 | Number -> 0 | String -> 0 | Bool -> 0 - let collect_format: DataType.Num.t dataFormat -> formats -> DataType.Num.t dataFormat = begin + let collect_format: DataType.Num.t t -> formats -> DataType.Num.t t = 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 + let default_value_for: type a. a t -> a = function | Date -> DataType.Num.zero | Number -> DataType.Num.zero | Bool -> false @@ -92,7 +34,7 @@ module DataFormat = struct type ('a, 'b) equality = Eq : ('a, 'a) equality - let compare_format: type a b. a dataFormat -> b dataFormat -> (a, b) equality = + let compare_format: type a b. a t -> b t -> (a, b) equality = fun a b -> begin match a, b with | Date, Date -> Eq | String, String -> Eq @@ -107,117 +49,44 @@ end module Type = struct - let (=) : type a b. a types -> b types -> bool = fun t1 t2 -> + type 'a t = + | Num : DataType.Num.t DataFormat.t * DataType.Num.t -> DataType.Num.t t (** A number *) + | Str : DataType.String.t -> DataType.String.t t (** A string *) + | Bool : DataType.Bool.t -> DataType.Bool.t t (** A boolean *) + + let number n = Num (Number, n) + let string s = Str s + let date d = Num (Date, d) + let boolean b = Bool b + + let (=) : type a b. a t -> b t -> 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 + module Eval(T:Sym_type.SYM_TYPE) = struct - 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 + let eval_type : type a. a t -> a T.t = function + | Str s -> T.str s + | Bool b -> T.bool b + | Num (f, n) -> + match f with + | DataFormat.Number -> T.num n + | DataFormat.Date -> T.date n - type t = - | Value: 'a dataFormat * 'a -> t + let eval t = T.observe (eval_type 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) + type t = + | Cell of Cell.t (** A cell *) + | Range of Cell.t * Cell.t (** An area of cells *) let shift (vector_x, vector_y) ref = let _shift ((x, y), (fixed_x, fixed_y)) = @@ -228,74 +97,90 @@ module Refs = struct | 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) + let cell c = Cell c + let range c1 c2 = Range (c1, c2) + + module Eval(R:Sym_ref.SYM_REF) = struct + + let eval_ref = function + | Cell c -> R.cell c + | Range(c1, c2) -> R.range c1 c2 + + let eval t = R.observe (eval_ref t) + end - type content = - | Value: 'a dataFormat * 'a -> content - | List: 'a dataFormat * 'a list -> content - | Matrix: 'a dataFormat * 'a list list -> content - - (** 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 + +module Expr = struct + + type ident = UTF8.t + + type t = + | Value : 'a Type.t -> t (** A direct value *) + | Ref : Refs.t -> t (** A reference to another cell *) + | Call0 : ident -> t (** A call to a 0 arg function *) + | Call1 : ident * t -> t (** A call to a 1 arg function *) + | Call2 : ident * t * t -> t (** A call to a 2 arg function *) + | Call3 : ident * t * t * t -> t (** A call to a 3 arg function *) + | CallN : ident * t list -> t (** A call to a function *) + | Expression : t -> t (** An expression *) + + let value v = Value v + let ref r = Ref r + let call0 ident = Call0 ident + let call1 ident expr = Call1 (ident, expr) + let call2 ident expr1 expr2 = Call2(ident, expr1, expr2) + let call3 ident expr1 expr2 expr3 = Call3(ident, expr1, expr2, expr3) + let callN ident params = CallN(ident, params) + let expression e = Expression e + + let rec shift_exp vector = function + | Value v -> Value v + | Call0 ident -> Call0 ident + | Call1 (ident, p1) -> Call1 (ident, shift_exp vector p1) + | Call2 (ident, p1, p2) -> Call2 (ident, shift_exp vector p1, shift_exp vector p2) + | Call3 (ident, p1, p2, p3) -> Call3 (ident, shift_exp vector p1, shift_exp vector p2, shift_exp vector p3) + | CallN (ident, params) -> CallN (ident, List.map (shift_exp vector) params) + | Ref r -> Ref (Refs.shift vector r) + | Expression expr -> Expression (shift_exp vector expr) + + module Eval(E:Sym_expr.SYM_EXPR) = struct + + module T = Type.Eval(E.T) + module R = Refs.Eval(E.R) + + let eval e t = begin + + let rec eval_expr : t -> E.repr = function + | Ref r -> E.ref (R.eval_ref r) t + | Value v -> E.value (T.eval_type v) t + | Call0 ident -> E.call0 ident t + | Call1 (ident, p1) -> E.call1 ident (eval_expr p1) t + | Call2 (ident, p1, p2) -> E.call2 ident (eval_expr p1) (eval_expr p2) t + | Call3 (ident, p1, p2, p3) -> E.call3 ident (eval_expr p1) (eval_expr p2) (eval_expr p3) t + | CallN (ident, exprs) -> E.callN ident (List.map (fun x -> eval_expr x) exprs) t + | Expression e -> E.expression (eval_expr e) t + in + E.observe (eval_expr e) 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 - 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 - 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 - Matrix(format, List.rev values) end end +module TypeRepr = Type.Eval(Show_type) + module Result = struct + + (** Result from a computation *) + type t = + | Ok : 'a Type.t -> t + | Error : exn -> t + let (=) t1 t2 = match t1, t2 with - | Result v1, Result v2 -> Type.(=) v1 v2 + | Ok v1, Ok v2 -> Type.(=) v1 v2 | _, _ -> t1 = t2 let show = begin function @@ -308,45 +193,33 @@ module Result = struct u(Buffer.contents buffer) *) u"#Error" - | Result v -> + | Ok v -> let buffer = UTF8.Buffer.create 16 in - Type.show buffer v; + TypeRepr.eval v buffer; 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 +module ReturnType = struct + + type 'a t = + | Num : DataType.Num.t DataFormat.t option -> DataType.Num.t t (** A number *) + | Str : DataType.String.t t (** A string *) + | Bool : DataType.Bool.t t (** A boolean *) + let f_num: DataType.Num.t t = Num None + let f_date: DataType.Num.t t = Num (Some Date) + let f_number: DataType.Num.t t = Num (Some Number) + let f_string: DataType.String.t t = Str + let f_bool: DataType.Bool.t t = Bool + + let guess_format_result: type a. a t -> (unit -> DataFormat.formats list) -> a DataFormat.t = + fun return params -> begin match return with + | Str -> DataFormat.String + | Bool -> DataFormat.Bool + | Num (Some x) -> x + | Num None -> List.fold_left DataFormat.collect_format DataFormat.Number (params ()) + end + +end diff --git a/src/scTypes.mli b/src/scTypes.mli index 46b48c6..f7bcc1b 100755 --- a/src/scTypes.mli +++ b/src/scTypes.mli @@ -1,123 +1,159 @@ (** All the types used in the spreadsheet. *)
-exception Error
+(** This module describe the most basic type use in the spreadsheet : every content is,
-type cell = (int * int) * (bool * bool)
+- either a ['a t]
+- either an expression which result to one ['a t]
-type ident = UTF8.t
+*)
+module Type : sig
-type 'a dataFormat =
- | Date: DataType.Num.t dataFormat (* A date in julian day *)
- | Number: DataType.Num.t dataFormat (* Number *)
- | String: DataType.String.t dataFormat (* String *)
- | Bool: DataType.Bool.t dataFormat (* Boolean *)
+ type 'a t
-type numericType =
- | Date
- | Number
+ (** Create a new number *)
+ val number: DataType.Num.t -> DataType.Num.t t
-val get_numeric_type: DataType.Num.t dataFormat -> numericType
+ (** Create a new string *)
+ val string: DataType.String.t -> DataType.String.t t
-type 'a types = private
- | 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 *)
+ (** Create a new boolean *)
+ val boolean: DataType.Bool.t -> DataType.Bool.t t
-val number: DataType.Num.t -> DataType.Num.t types
-val string: DataType.String.t -> DataType.String.t types
-val boolean: DataType.Bool.t -> DataType.Bool.t types
-val date: DataType.Num.t -> DataType.Num.t types
+ (** Create a new date *)
+ val date: DataType.Num.t -> DataType.Num.t t
-(** Private type for an internal representation of return format *)
-type 'a returnType
+ val (=) : 'a t -> 'b t -> bool
-(** Numeric (any format) *)
-val f_num: DataType.Num.t returnType
+ (** Evaluate a type and get the result *)
+ module Eval(T:Sym_type.SYM_TYPE): sig
-(** Date *)
-val f_date: DataType.Num.t returnType
+ val eval: 'a t -> 'a T.obs
-(** Number *)
-val f_number: DataType.Num.t returnType
+ end
-(** Boolean result *)
-val f_bool: DataType.Bool.t returnType
+end
-(** String *)
-val f_string: DataType.String.t returnType
+(** A reference to another cell.
-type refs =
- | Cell of cell (** A cell *)
- | Range of cell * cell (** An area of cells *)
+The reference can be a single cell (ie : A$1) or a range.
-(** This is the cell content *)
-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 *)
+*)
+module Refs : sig
-(** Result from a computation *)
-type result =
- | Result : 'a types -> result
- | Error : exn -> result
+ type t
-module DataFormat : sig
+ val cell : Cell.t -> t
+
+ val range : Cell.t -> Cell.t -> t
+
+ val shift: (int * int) -> t -> t
- type formats = F : 'a dataFormat -> formats [@@unboxed]
+ (** Evaluate a reference and get the result *)
+ module Eval(R:Sym_ref.SYM_REF): sig
- val guess_format_result: 'a returnType -> (unit -> formats list) -> 'a dataFormat
+ val eval: t -> 'a R.obs
+
+ end
end
-module Type : sig
+module Expr : sig
- type t = Value: 'a dataFormat * 'a -> t
+ type ident = UTF8.t
- val (=) : 'a types -> 'b types -> bool
+ (** This is the cell content *)
+ type t
- val show: UTF8.Buffer.buffer -> 'a types -> unit
+ (** Declare a direct value *)
+ val value: 'a Type.t -> t
- val show_full: UTF8.Buffer.buffer -> 'a types -> unit
+ (** Declare a reference to another cell *)
+ val ref: Refs.t -> t
-end
+ (** Declare a call to a 0 arg function *)
+ val call0: ident -> t
-module Refs : sig
+ (** Declare a call to a 1 arg function *)
+ val call1 : ident -> t -> t
- type 'a range =
- | Single of 'a
- | Array1 of 'a list
- | Array2 of 'a list list
+ (** Declare a call to a 2 arg function *)
+ val call2 : ident -> t -> t -> t
- (* Collect all the cells defined by a range. The cell are defined by their
- coordinates *)
- val collect: refs -> (int * int) range
+ (** Declare a call to a 3 arg function *)
+ val call3 : ident -> t -> t -> t -> t
- val map: ('a -> 'b) -> 'a range -> 'b range
-
- val shift: (int * int) -> refs -> refs
+ (** Declare a call to a function *)
+ val callN : ident -> t list -> t
- (** Each content from a reference contains a format and the appropriate value. *)
- type content =
- | Value: 'a dataFormat * 'a -> content
- | List: 'a dataFormat * 'a list -> content
- | Matrix: 'a dataFormat * 'a list list -> content
+ (** An expression *)
+ val expression : t -> t
- (** extract the content from a range.
+ val shift_exp: (int * int) -> t -> t
- May raise Errors.TypeError if the range cannot be unified.
- *)
- val get_content : result option range -> content
+ module Eval(E:Sym_expr.SYM_EXPR): sig
+
+ val eval: t -> E.t -> E.obs
+
+ end
end
-val show_expr: UTF8.Buffer.buffer -> expression -> unit
+module DataFormat : sig
+
+ type 'a t =
+ | Date: DataType.Num.t t (* A date in julian day *)
+ | Number: DataType.Num.t t (* Number *)
+ | String: DataType.String.t t (* String *)
+ | Bool: DataType.Bool.t t (* Boolean *)
+
+ type formats = F : 'a t -> formats [@@unboxed]
+
+ val default_value_for: 'a t -> 'a
+
+ type ('a, 'b) equality = Eq : ('a, 'a) equality
+
+ val compare_format: 'a t -> 'b t -> ('a, 'b) equality
+
+ val priority: 'a t -> int
+
+end
+
+module ReturnType : sig
+
+ (** Private type for an internal representation of return format *)
+ type 'a t
+
+ (** Numeric (any format) *)
+ val f_num: DataType.Num.t t
+
+ (** Date *)
+ val f_date: DataType.Num.t t
+
+ (** Number *)
+ val f_number: DataType.Num.t t
+
+ (** Boolean result *)
+ val f_bool: DataType.Bool.t t
+
+ (** String *)
+ val f_string: DataType.String.t t
+
+ val guess_format_result: 'a t -> (unit -> DataFormat.formats list) -> 'a DataFormat.t
+
+end
module Result : sig
- val (=) : result -> result -> bool
+ (** Result from a computation *)
+ type t =
+ | Ok : 'a Type.t -> t
+ | Error : exn -> t
- val show: result -> UTF8.t
+ val (=) : t -> t -> bool
+
+ val show: t -> UTF8.t
end
+exception Error
+
diff --git a/src/sheet.ml b/src/sheet.ml index 3dc83a0..6d3c34a 100755 --- a/src/sheet.ml +++ b/src/sheet.ml @@ -1,9 +1,7 @@ -module Option = Tools.Option
-
type cell = int * int
type search = [
- | `Pattern of ScTypes.result option
+ | `Pattern of ScTypes.Result.t option
| `Next
| `Previous
]
@@ -12,7 +10,7 @@ module Raw = struct type content = {
expr : Expression.t; (** The expression *)
- value : ScTypes.result option; (** The content evaluated *)
+ value : ScTypes.Result.t option; (** The content evaluated *)
sink : Cell.Set.t; (** All the cell which references this one *)
}
@@ -29,7 +27,6 @@ module Raw = struct type t = Map.t
-
(** The sheet is a map which always contains evaluated values. When a cell is
updated, all the cell which references this value are also updated.
*)
@@ -39,22 +36,12 @@ module Raw = struct let get_expr id t = (Map.find id t).expr
- (** Extract a value from a reference.
- This function is given to the evaluator for getting the values from a reference.
- *)
- let get_ref from t ref : ScTypes.result option ScTypes.Refs.range = begin
-
- ScTypes.Refs.collect ref
- |> ScTypes.Refs.map (fun coord -> get_value coord t)
-
- end
-
(** Update the value for the given cell.
Evaluate the new expression and compare it with the previous value.
@return Some map if the map has been updated
*)
- let update cell content t = begin
- let new_val = Expression.eval content.expr (get_ref cell t) in
+ let update catalog cell content t = begin
+ let new_val = Expression.eval content.expr catalog (fun id -> get_value id t) in
match content.value with
| None ->
(* If the previous value wasn't defined, update the map *)
@@ -68,54 +55,54 @@ module Raw = struct None
end
+ exception Cycle of Cell.Set.t * t
+
(** Parse all the successors from an element, apply a function to each of
them, and return them.
The function is too long and should be rewriten… *)
let rec traverse (f:(cell -> content -> t -> t option)) source (init, t) = begin
+ try Cell.Set.fold (successors init f init source) source.sink (init, t)
+ with Cycle (succ, t) -> (succ, t)
+ end
- let exception Cycle of Cell.Set.t * t in
-
- let rec successors parents element (succ, t) = begin
+ and successors init f parents source element (succ, t) = begin
- let content = Map.find element t in
+ let content = Map.find element t in
- if Cell.Set.mem element parents then (
+ if Cell.Set.mem element parents then (
- (* if the cell has already been visited, mark it in error, and all the
- descendant *)
- let cycle_error = Some (ScTypes.Error Errors.Cycle) in
+ (* if the cell has already been visited, mark it in error, and all the
+ descendant *)
+ let cycle_error = Some (ScTypes.Result.Error Errors.Cycle) in
- if content.value = cycle_error then (
- (* The content has already been updated, do not process it again *)
- (succ, t)
- ) else (
- let t = Map.add element { content with value = cycle_error } t
- and set_error cell content t =
- if content.value = cycle_error then
- None
- else
- Some (Map.add cell { content with value = cycle_error } t) in
- let succ, t = traverse set_error source (init, t) in
- raise (Cycle (succ, t))
- )
+ if content.value = cycle_error then (
+ (* The content has already been updated, do not process it again *)
+ (succ, t)
) else (
- begin match f element content t with
- | None ->
- (* The content does not change, we do not update the successors *)
- (succ, t)
- | Some t' ->
- let parents' = Cell.Set.add element parents
- and succ' = Cell.Set.add element succ in
- if (Cell.Set.is_empty content.sink) then
- (succ', t')
+ let t = Map.add element { content with value = cycle_error } t
+ and set_error cell content t =
+ if content.value = cycle_error then
+ None
else
- Cell.Set.fold (successors parents') content.sink (succ', t')
- end
+ Some (Map.add cell { content with value = cycle_error } t) in
+ let succ, t = traverse set_error source (init, t) in
+ raise (Cycle (succ, t))
)
- end in
- try Cell.Set.fold (successors init) source.sink (init, t)
- with Cycle (succ, t) -> (succ, t)
+ ) else (
+ begin match f element content t with
+ | None ->
+ (* The content does not change, we do not update the successors *)
+ (succ, t)
+ | Some t' ->
+ let parents' = Cell.Set.add element parents
+ and succ' = Cell.Set.add element succ in
+ if (Cell.Set.is_empty content.sink) then
+ (succ', t')
+ else
+ Cell.Set.fold (successors init f parents' source) content.sink (succ', t')
+ end
+ )
end
(** Remove the cell from the sheet *)
@@ -156,15 +143,15 @@ module Raw = struct end
end
- let remove id t = begin
+ let remove id catalog t = begin
match remove_element id t with
| t, None -> Cell.Set.empty, t
| t, Some content ->
(** Update all the successors *)
- traverse update content (Cell.Set.singleton id, t)
+ traverse (update catalog) content (Cell.Set.singleton id, t)
end
- let add_element id content_builder t = begin
+ let add_element catalog id content_builder t = begin
(** Add the references in each sources.
If the sources does not exists, create it.
@@ -191,29 +178,29 @@ module Raw = struct in
(** Update the value for each sink already evaluated *)
- traverse update content (Cell.Set.singleton id, updated)
+ traverse (update catalog) content (Cell.Set.singleton id, updated)
end
- let add id expression t = begin
+ let add id expression catalog t = begin
if not (Expression.is_defined expression) then
(Cell.Set.empty, t)
else
let f cell t = begin
{ cell with
expr = expression ;
- value = Some (Expression.eval expression (get_ref id t)) }
+ value = Some (Expression.eval expression catalog (fun id -> get_value id t)) }
end in
- add_element id f t
+ add_element catalog id f t
end
- let paste id shift content t = begin
+ let paste catalog id shift content t = begin
let expr = Expression.shift shift content.expr in
let f cell t =
{ cell with
expr = expr ;
- value = Some (Expression.eval expr (get_ref id t))
+ value = Some (Expression.eval expr catalog (fun id -> get_value id t))
} in
- add_element id f t
+ add_element catalog id f t
end
let search pattern t = begin
@@ -252,7 +239,8 @@ type t = { selected: Selection.t; (* The selected cell *)
data: Raw.t;
history: history; (* Unlimited history *)
- yank: yank list
+ yank: yank list;
+ catalog: Functions.C.t;
}
let undo t = begin
@@ -262,9 +250,9 @@ let undo t = begin let data = List.fold_left (
fun data (id, expression) ->
if Expression.is_defined expression then
- snd @@ Raw.add id expression data
+ snd @@ Raw.add id expression t.catalog data
else
- snd @@ Raw.remove id data
+ snd @@ Raw.remove id t.catalog data
) t.data hd in
Some { t with data = data; history = tl}
end
@@ -284,9 +272,10 @@ let move direction t = Some {t with selected = Selection.create position'}
let delete t = begin
+ let catalog = t.catalog in
let history = Selection.fold (fun acc id -> (id, Raw.get_expr id t.data)::acc) [] t.selected in
let count, data' = Selection.fold (fun (count, c) t ->
- (count + 1, snd @@ Raw.remove t c)) (0, t.data) t.selected in
+ (count + 1, snd @@ Raw.remove t catalog c)) (0, t.data) t.selected in
let t' = { t with
data = data';
history = history::t.history
@@ -313,6 +302,7 @@ let yank t = begin end
let paste t = begin
+ let catalog = t.catalog in
(* Origin of first cell *)
let (shift_x, shift_y) as shift = Selection.extract t.selected in
@@ -321,7 +311,7 @@ let paste t = begin id, Raw.get_expr id t.data) t.yank in
let _paste (count, t) ((x, y), content) = begin
- count + 1, snd @@ Raw.paste (shift_x + x, shift_y + y) shift content t
+ count + 1, snd @@ Raw.paste catalog (shift_x + x, shift_y + y) shift content t
end in
let count, data' = List.fold_left _paste (0, t.data) t.yank in
@@ -332,7 +322,7 @@ end let add expression t = begin
let id = Selection.extract t.selected in
let prev_expression = Raw.get_expr id t.data in
- let cells, data' = Raw.add id expression t.data in
+ let cells, data' = Raw.add id expression t.catalog t.data in
cells, { t with data = data'; history = [id, prev_expression]::t.history }
end
@@ -345,9 +335,10 @@ let search action t = begin match action with | _ -> None
end
-let create data = {
+let create catalog data = {
data = data;
selected = Selection.create (1, 1);
history = [];
yank = [];
+ catalog = catalog
}
diff --git a/src/sheet.mli b/src/sheet.mli index d768b8f..14856d4 100755 --- a/src/sheet.mli +++ b/src/sheet.mli @@ -12,21 +12,21 @@ module Raw: sig (** Add a new value in the sheet. The previous value is replaced @return All the successors to update and the new sheet. *) - val add: cell -> Expression.t -> t -> Cell.Set.t * t + val add: cell -> Expression.t -> Functions.C.t -> t -> Cell.Set.t * t - val remove: cell -> t -> Cell.Set.t * t + val remove: cell -> Functions.C.t -> t -> Cell.Set.t * t (** Get the value content. @return None if the cell is not defined *) - val get_value: cell -> t -> ScTypes.result option + val get_value: cell -> t -> ScTypes.Result.t option val get_expr: cell -> t -> Expression.t val get_sink: cell -> t -> Cell.Set.t (** Fold over all the defined values *) - val fold: ('a -> cell -> (Expression.t * ScTypes.result ) -> 'a) -> 'a -> t -> 'a + val fold: ('a -> cell -> (Expression.t * ScTypes.Result.t ) -> 'a) -> 'a -> t -> 'a end @@ -37,11 +37,12 @@ type t = { selected: Selection.t; (* The selected cell *) data: Raw.t; history: history; (* Unlimited history *) - yank: yank list (* All the selected cells *) + yank: yank list; (* All the selected cells *) + catalog: Functions.C.t } type search = [ - | `Pattern of ScTypes.result option + | `Pattern of ScTypes.Result.t option | `Next | `Previous ] @@ -75,5 +76,5 @@ val paste: t -> t * int val add: Expression.t -> t -> Cell.Set.t * t (** Create an empty sheet *) -val create: Raw.t -> t +val create: Functions.C.t -> Raw.t -> t diff --git a/src/tools.ml b/src/tools.ml index 7f500bf..8481d59 100755 --- a/src/tools.ml +++ b/src/tools.ml @@ -298,101 +298,11 @@ module ArrayMap(Ord: COMPARABLE_TYPE) = struct end -(** Map for any comparable value. - This map can bind 'a key -> 'a value as long as the key are comparable. - *) -module Map(Ord: COMPARABLE_TYPE) = struct - - type 'a key = 'a Ord.t - - type wrapper = Ex: 'a key * 'a -> wrapper - - type t = - | Empty : t - | Node : t * 'a key * 'a * t * int -> t - - let singleton x d = Node(Empty, x, d, Empty, 1) - - let empty = Empty - - let is_empty = function - | Empty -> true - | _ -> false - - let height = function - | Empty -> 0 - | Node(_,_,_,_,h) -> h - - let create l x d r = - let hl = height l and hr = height r in - Node(l, x, d, r, (if hl >= hr then hl + 1 else hr + 1)) - - let bal l x d r = - let hl = match l with Empty -> 0 | Node(_,_,_,_,h) -> h in - let hr = match r with Empty -> 0 | Node(_,_,_,_,h) -> h in - if hl > hr + 2 then begin - match l with - Empty -> invalid_arg "Map.bal" - | Node(ll, lv, ld, lr, _) -> - if height ll >= height lr then - create ll lv ld (create lr x d r) - else begin - match lr with - Empty -> invalid_arg "Map.bal" - | Node(lrl, lrv, lrd, lrr, _)-> - create (create ll lv ld lrl) lrv lrd (create lrr x d r) - end - end else if hr > hl + 2 then begin - match r with - Empty -> invalid_arg "Map.bal" - | Node(rl, rv, rd, rr, _) -> - if height rr >= height rl then - create (create l x d rl) rv rd rr - else begin - match rl with - Empty -> invalid_arg "Map.bal" - | Node(rll, rlv, rld, rlr, _) -> - create (create l x d rll) rlv rld (create rlr rv rd rr) - end - end else - Node(l, x, d, r, (if hl >= hr then hl + 1 else hr + 1)) - - - let rec add: type a. a key -> a -> t -> t = begin fun x data t -> match t with - | Empty -> Node(Empty, x, data, Empty, 1) - | Node(l, v, d, r, h) -> - match Ord.comp x v with - | Eq -> Node(l, x, data, r, h) - | Lt -> bal (add x data l) v d r - | Gt -> bal l v d (add x data r) - end - - let rec find: type a. a key -> t -> a = begin fun x t -> match t with - | Empty -> raise Not_found - | Node(l, k, v, r, _) -> - match Ord.comp x k with - | Eq -> v - | Lt -> find x l - | Gt -> find x r - end +let fold_for f a b init = + let rec _fold res i = begin + if i >= b then res + else + _fold (f i res) (i + 1) + end in + (_fold[@tailcall]) init a - let rec mem: type a. a key -> t -> bool = begin fun x t -> match t with - | Empty -> false - | Node(l, k, v, r, _) -> - match Ord.comp x k with - | Eq -> true - | Lt -> mem x l - | Gt -> mem x r - end - - (* - let rec fold: ('a -> wrapper -> 'a) -> 'a -> t -> 'a = - begin fun f init t -> match t with - | Empty -> init - | Node(l, k, v, r, _) -> - let res_left = fold f init l in - let result = f res_left @@ Ex (k, v) in - fold f result r - end - *) -end diff --git a/tests/expressionParser_test.ml b/tests/expressionParser_test.ml index 12ceeb0..481ed6d 100755 --- a/tests/expressionParser_test.ml +++ b/tests/expressionParser_test.ml @@ -19,7 +19,7 @@ let test_num ctx = begin let expected = Expression.Formula ( Expression.Expression ( - ScTypes.Value (ScTypes.number ( + ScTypes.Expr.value (ScTypes.Type.number ( DataType.Num.of_int 1 ) ))) in @@ -35,8 +35,7 @@ let test_call ctx = begin let expected = Expression.Formula ( Expression.Expression ( - ScTypes.Call ( - u"sum", []))) in + ScTypes.Expr.call0 (u"sum"))) in let result = load_expr "=sum()" in assert_equal @@ -52,10 +51,10 @@ let test_call2 ctx = begin let expected = Expression.Formula ( Expression.Expression ( - ScTypes.Call ( - u"foo2", [ScTypes.Value (ScTypes.number ( - DataType.Num.of_int 4 - ))]))) in + ScTypes.Expr.call1 + (u"foo2") (ScTypes.Expr.value (ScTypes.Type.number ( + DataType.Num.of_int 4) + )))) in let result = load_expr "=foo2(4)" in assert_equal @@ -68,8 +67,8 @@ let test_ref ctx = begin let expected = Expression.Formula ( Expression.Expression ( - ScTypes.Ref( - ScTypes.Cell ((1, 3), (false, false))))) in + ScTypes.Expr.ref( + ScTypes.Refs.cell ((1, 3), (false, false))))) in let result = load_expr "=A3" in assert_equal diff --git a/tests/expression_test.ml b/tests/expression_test.ml index 5def730..6743a09 100755 --- a/tests/expression_test.ml +++ b/tests/expression_test.ml @@ -5,18 +5,9 @@ let u = UTF8.from_utf8string let _msg ~expected ~result = - let get_type = function - | Expression.Basic ScTypes.Num (ScTypes.Number, _) -> "N" - | Expression.Basic ScTypes.Num (ScTypes.Date, _) -> "D" - | Expression.Basic ScTypes.Str _ -> "S" - | Expression.Basic ScTypes.Bool _ -> "B" - | Expression.Formula _ -> "F" in - - Printf.sprintf "Expected %s:%s but got %s:%s" + Printf.sprintf "Expected %s but got %s" (UTF8.raw_encode @@ Expression.show expected) - (get_type expected) (UTF8.raw_encode @@ Expression.show result) - (get_type result) let assert_equal expected result = OUnit2.assert_equal @@ -28,7 +19,7 @@ let assert_equal expected result = let test_str ctx = begin let result = Expression.load @@ u"cafe" in let expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.string (u"cafe")) in + ScTypes.Type.string (u"cafe")) in assert_equal expected result end @@ -36,7 +27,7 @@ end let test_str_space ctx = begin let result = Expression.load @@ u" =cafe" in let expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.string (u" =cafe")) in + ScTypes.Type.string (u" =cafe")) in assert_equal expected result end @@ -44,15 +35,15 @@ let test_formula_str ctx = begin let result = Expression.load @@ u"=\"cafe\"" in let expected = Expression.load_expr @@ Expression.Formula ( Expression.Expression ( - ScTypes.Value ( - ScTypes.string (u"cafe")))) in + ScTypes.Expr.value ( + ScTypes.Type.string (u"cafe")))) in assert_equal expected result end let test_num ctx = begin let result = Expression.load @@ u"123" in let expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.number ( + ScTypes.Type.number ( DataType.Num.of_int 123 )) in assert_equal expected result @@ -61,7 +52,7 @@ end let test_float ctx = begin let result = Expression.load @@ u"12.45" in let expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.number ( + ScTypes.Type.number ( DataType.Num.of_float @@ float_of_string "12.45" )) in assert_equal expected result @@ -70,7 +61,7 @@ end let test_relative ctx = begin let result = Expression.load @@ u"-123" in let expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.number ( + ScTypes.Type.number ( DataType.Num.of_int (-123) )) in assert_equal expected result @@ -79,7 +70,7 @@ end let test_date ctx = begin let result = Expression.load @@ u"1900/01/01" and expected = Expression.load_expr @@ Expression.Basic ( - ScTypes.date ( + ScTypes.Type.date ( DataType.Date.get_julian_day 1900 01 01 )) in assert_equal expected result @@ -96,6 +87,22 @@ let test_sources ctx = begin (UTF8.raw_encode @@ Tools.String.print_buffer Cell.Set.printb result) in OUnit2.assert_equal ~msg expected result +end + +let test_sources2 ctx = begin + let result = Expression.load @@ u"=if($A$1>0;rand()*10+1;0)" + |> Expression.collect_sources + and expected = Cell.Set.singleton (1, 1) in + + let msg_buffer = UTF8.Buffer.create 16 in + UTF8.Printf.bprintf msg_buffer "Expected %a but got %a" + Cell.Set.printb expected + Cell.Set.printb result; + + OUnit2.assert_equal + ~msg:(UTF8.to_utf8string @@ UTF8.Buffer.contents msg_buffer) + expected + result end @@ -114,5 +121,5 @@ let tests = "expression_test">::: [ "test_date" >:: test_date; "test_sources" >:: test_sources; - + "test_sources2" >:: test_sources2; ] diff --git a/tests/odf/odf_ExpressionParser_test.ml b/tests/odf/odf_ExpressionParser_test.ml index 18efe96..3d6c4fa 100755 --- a/tests/odf/odf_ExpressionParser_test.ml +++ b/tests/odf/odf_ExpressionParser_test.ml @@ -2,11 +2,13 @@ open OUnit2 let u = UTF8.from_utf8string
-let _msg ~(expected:ScTypes.expression) ~(result:ScTypes.expression) =
+module Show = ScTypes.Expr.Eval(Show_expr.Show_Expr(Show_ref)(Show_type))
+
+let _msg ~(expected:ScTypes.Expr.t) ~(result:ScTypes.Expr.t) =
let b1 = UTF8.Buffer.create 16
and b2 = UTF8.Buffer.create 16 in
- ScTypes.show_expr b1 expected;
- ScTypes.show_expr b2 result;
+ Show.eval expected () b1;
+ Show.eval result () b2;
Printf.sprintf "Expected \n\t%s but got \n\t%s"
(UTF8.raw_encode @@ UTF8.Buffer.contents b1)
@@ -14,7 +16,7 @@ let _msg ~(expected:ScTypes.expression) ~(result:ScTypes.expression) = -let build_num value = ScTypes.number (
+let build_num value = ScTypes.Type.number (
DataType.Num.of_int value
)
@@ -28,22 +30,22 @@ let test_formula ctx = begin let expected = ScTypes.(
- Call(u"CONCATENATE", [
- Call (u"SUM", [
- Ref (Range (((6, 16), (false, false)), (((36, 16), (false, false)))))]);
- Value (string (u"/"));
- Call(u"*", [
- Value (build_num 8);
- Call(u"NETWORKDAYS", [
- Ref (Cell ((6, 6), (false, false)));
- Call(u"+", [
- Ref (Cell ((6, 6), (false, false)));
- Expression (
- Call( u"-", [
- Call(u"ORG.OPENOFFICE.DAYSINMONTH", [
- Ref (Cell ((6, 6), (false, false)))]);
- Value (build_num 1);
- ]))])])])])) in
+ Expr.call3 (u"CONCATENATE")
+ (Expr.call1 (u"SUM")
+ (Expr.ref (Refs.range ((6, 16), (false, false)) (((36, 16), (false, false))))))
+ (Expr.value (Type.string (u"/")))
+ (Expr.call2 (u"*")
+ (Expr.value (build_num 8))
+ (Expr.call2 (u"NETWORKDAYS")
+ (Expr.ref (Refs.cell ((6, 6), (false, false))))
+ (Expr.call2 (u"+")
+ (Expr.ref (Refs.cell ((6, 6), (false, false))))
+ (Expr.expression
+ (Expr.call2 (u"-")
+ (Expr.call1 (u"ORG.OPENOFFICE.DAYSINMONTH")
+ (Expr.ref (Refs.cell ((6, 6), (false, false)))))
+ (Expr.value (build_num 1));
+ )))))) in
assert_equal
~msg:(_msg ~expected ~result)
@@ -59,11 +61,11 @@ let test_formula2 ctx = begin let expected = ScTypes.(
- Call (u"+", [
- Call(u"*", [
- Ref (Cell ((8, 51), (false, false)));
- Ref (Cell ((7, 52), (false, false)))
- ])])) in
+ Expr.call1 (u"+")
+ (Expr.call2 (u"*")
+ (Expr.ref (Refs.cell ((8, 51), (false, false))))
+ (Expr.ref (Refs.cell ((7, 52), (false, false))))
+ )) in
assert_equal
~msg:(_msg ~expected ~result)
@@ -72,7 +74,7 @@ let test_formula2 ctx = begin end
-let tests = "odf_ExpressionParser_test">::: [
+let tests = "odf_ExpressionParser_test" >::: [
"test_formula" >:: test_formula;
"test_formula2" >:: test_formula2;
diff --git a/tests/sheet_test.ml b/tests/sheet_test.ml index 3960c4b..dfa8da4 100755 --- a/tests/sheet_test.ml +++ b/tests/sheet_test.ml @@ -4,6 +4,8 @@ module Option = Tools.Option let u = UTF8.from_utf8string +let catalog = Functions.C.compile @@ Functions.built_in Functions.C.empty + let _msg ~expected ~result = begin let get_string v = match v with @@ -16,17 +18,17 @@ let _msg ~expected ~result = begin (get_string result) end -let build_num value = ScTypes.number @@ DataType.Num.of_int value +let build_num value = ScTypes.Type.number @@ DataType.Num.of_int value (** Test a simple references between two cells *) let test_create_ref_1 ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (3,3) @@ Expression.load @@ u"=-1" - |> snd |> Sheet.Raw.add (0,0) @@ Expression.load @@ u"=C3" + |> Sheet.Raw.add (3,3) (Expression.load @@ u"=-1") catalog + |> snd |> Sheet.Raw.add (0,0) (Expression.load @@ u"=C3") catalog |> snd in let result = (Sheet.Raw.get_value (0, 0) s) in - let expected = Some (ScTypes.Result (build_num (-1))) in + let expected = Some (ScTypes.Result.Ok (build_num (-1))) in assert_equal ~msg:(_msg ~expected ~result) @@ -37,14 +39,14 @@ end let test_create_ref_2 ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=C3" - |> snd |> Sheet.Raw.add (3,3) @@ Expression.load @@ u"=A1" - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"123" + |> Sheet.Raw.add (2,2) (Expression.load @@ u"=C3") catalog + |> snd |> Sheet.Raw.add (3,3) (Expression.load @@ u"=A1") catalog + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"123") catalog |> snd in let result = (Sheet.Raw.get_value (2, 2) s) in - let expected = Some (ScTypes.Result (build_num 123)) in + let expected = Some (ScTypes.Result.Ok (build_num 123)) in assert_equal ~msg:(_msg ~expected ~result) @@ -55,10 +57,10 @@ end let test_create_direct_cycle ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=B2 + 1" + |> Sheet.Raw.add (2,2) (Expression.load @@ u"=B2 + 1") catalog |> snd in let result = (Sheet.Raw.get_value (2, 2) s) in - let expected = Some (ScTypes.Error Errors.TypeError) in + let expected = Some (ScTypes.Result.Error Errors.TypeError) in assert_equal ~msg:(_msg ~expected ~result) @@ -70,11 +72,11 @@ end let test_recover_from_cycle ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=B2 + 1" - |> snd |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=6" + |> Sheet.Raw.add (2,2) (Expression.load @@ u"=B2 + 1") catalog + |> snd |> Sheet.Raw.add (2,2) (Expression.load @@ u"=6") catalog |> snd in let result = (Sheet.Raw.get_value (2, 2) s) in - let expected = Some (ScTypes.Result (build_num (6))) in + let expected = Some (ScTypes.Result.Ok (build_num (6))) in assert_equal ~msg:(_msg ~expected ~result) @@ -85,13 +87,13 @@ end let test_create_indirect_cycle ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=A1" - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=2" - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=B2+1" - |> snd |> Sheet.Raw.add (0,0) @@ Expression.load @@ u"=A1" + |> Sheet.Raw.add (2,2) (Expression.load @@ u"=A1") catalog + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"=2") catalog + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"=B2+1") catalog + |> snd |> Sheet.Raw.add (0,0) (Expression.load @@ u"=A1") catalog |> snd in let result = (Sheet.Raw.get_value (0, 0) s) in - let expected = Some (ScTypes.Error Errors.Cycle) in + let expected = Some (ScTypes.Result.Error Errors.Cycle) in assert_equal ~msg:(_msg ~expected ~result) @@ -103,18 +105,18 @@ let test_check_cycle3 ctx = begin let s = Sheet.Raw.empty (* First set A1 to 3 *) - |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=3" - |> snd |> Sheet.Raw.add (1,2) @@ Expression.load @@ u"=A1" - |> snd |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=A1" - |> snd |> Sheet.Raw.add (5,5) @@ Expression.load @@ u"=B2" + |> Sheet.Raw.add (1,1) (Expression.load @@ u"=3") catalog + |> snd |> Sheet.Raw.add (1,2) (Expression.load @@ u"=A1") catalog + |> snd |> Sheet.Raw.add (2,2) (Expression.load @@ u"=A1") catalog + |> snd |> Sheet.Raw.add (5,5) (Expression.load @@ u"=B2") catalog (* A3 = A1 + A1 = 6 *) - |> snd |> Sheet.Raw.add (1,3) @@ Expression.load @@ u"=A2 + E5" + |> snd |> Sheet.Raw.add (1,3) (Expression.load @@ u"=A2 + E5") catalog (* Then set A1 to 2 *) - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=2" + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"=2") catalog |> snd in let result = (Sheet.Raw.get_value (1, 3) s) in (* A3 = A1 + A1 = 4 *) - let expected = Some (ScTypes.Result (build_num 4)) in + let expected = Some (ScTypes.Result.Ok (build_num 4)) in assert_equal ~msg:(_msg ~expected ~result) @@ -125,10 +127,10 @@ end let test_delete ctx = begin let s = Sheet.Raw.empty - |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=C3" - |> snd |> Sheet.Raw.add (3,3) @@ Expression.load @@ u"=A1" - |> snd |> Sheet.Raw.remove (2,2) - |> snd |> Sheet.Raw.remove (3,3) + |> Sheet.Raw.add (2,2) (Expression.load @@ u"=C3") catalog + |> snd |> Sheet.Raw.add (3,3) (Expression.load @@ u"=A1") catalog + |> snd |> Sheet.Raw.remove (2,2) catalog + |> snd |> Sheet.Raw.remove (3,3) catalog |> snd in let result = (Sheet.Raw.get_value (3, 3) s) in let expected = None in @@ -142,10 +144,10 @@ end let test_update_succs1 ctx = begin let result = Sheet.Raw.empty - |> Sheet.Raw.add (1,1) @@ Expression.load @@ u" =1" - |> snd |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=A2" - |> snd |> Sheet.Raw.add (1,2) @@ Expression.load @@ u"=A1/1" - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=2" + |> Sheet.Raw.add (1,1) (Expression.load @@ u" =1") catalog + |> snd |> Sheet.Raw.add (2,2) (Expression.load @@ u"=A2") catalog + |> snd |> Sheet.Raw.add (1,2) (Expression.load @@ u"=A1/1") catalog + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"=2") catalog |> fst in (* All the cells are updated by the change *) @@ -163,10 +165,10 @@ end let test_update_succs2 ctx = begin let result = Sheet.Raw.empty - |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=1" - |> snd |> Sheet.Raw.add (2,2) @@ Expression.load @@ u"=A2" - |> snd |> Sheet.Raw.add (1,2) @@ Expression.load @@ u"=A1/0" - |> snd |> Sheet.Raw.add (1,1) @@ Expression.load @@ u"=2" + |> Sheet.Raw.add (1,1) (Expression.load @@ u"=1") catalog + |> snd |> Sheet.Raw.add (2,2) (Expression.load @@ u"=A2") catalog + |> snd |> Sheet.Raw.add (1,2) (Expression.load @@ u"=A1/0") catalog + |> snd |> Sheet.Raw.add (1,1) (Expression.load @@ u"=2") catalog |> fst in (* Only (1, 1) is updated ; (2, 2) does not change, neither (2, 2) *) let expected = Cell.Set.of_list [(1,1)] in @@ -178,10 +180,10 @@ end let test_paste_undo ctx = begin - let empty = Sheet.create Sheet.Raw.empty in + let empty = Sheet.create catalog Sheet.Raw.empty in (* The expected result for the whole test *) - let expected = Some (ScTypes.Result (ScTypes.number (DataType.Num.of_int 6))) in + let expected = Some (ScTypes.Result.Ok (ScTypes.Type.number (DataType.Num.of_int 6))) in let sheet = empty |> Tools.Option.test @@ Sheet.move (Actions.Absolute (2, 1)) diff --git a/tests/test.ml b/tests/test.ml index ee71cb0..0c5af82 100755 --- a/tests/test.ml +++ b/tests/test.ml @@ -1,6 +1,6 @@ let () = - Evaluator.set_catalog (Functions.C.compile @@ Functions.built_in @@ Functions.C.empty); + (*Evaluator.set_catalog (Functions.C.compile @@ Functions.built_in @@ Functions.C.empty);*) let tests = OUnit2.test_list [ Tools_test.tests; |