From 098ac444e731d7674d8910264ae58fb876618a5a Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Fri, 24 Nov 2017 13:46:00 +0100 Subject: Move function in their own modules --- Makefile | 4 +- readme.rst | 2 +- src/catalog.ml | 151 +++++++++++++----- src/catalog.mli | 66 +++++--- src/evaluator.ml | 323 ++++++--------------------------------- src/evaluator.mli | 64 +------- src/expressionParser.mly | 28 ++-- src/functions.ml | 208 +++++++++++++++++++++++-- src/functions.mli | 21 +++ src/main.ml | 11 +- src/odf/odf_ExpressionParser.mly | 28 ++-- src/splay.ml | 7 +- src/symbols.ml | 14 ++ src/symbols.mli | 16 ++ tests/test.ml | 3 + 15 files changed, 506 insertions(+), 440 deletions(-) create mode 100755 src/functions.mli create mode 100755 src/symbols.ml create mode 100755 src/symbols.mli diff --git a/Makefile b/Makefile index 4be3178..f6cb2b5 100755 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ OCAMLBUILD ?= ocamlbuild -PACKAGES=dynlink,curses,camlzip,ezxmlm,text,str,menhirLib,zarith +PACKAGES=dynlink,curses,camlzip,ezxmlm,text,str,menhirLib,zarith,base PATHS=src,src/odf MENHIR=-use-menhir @@ -18,7 +18,7 @@ stub: $(MAKE) -C stub LIB=$(LIB) deps: - opam install ocamlbuild curses camlzip ezxmlm ounit text menhir zarith + opam install ocamlbuild curses camlzip ezxmlm ounit text menhir zarith base byte: stub $(OCAMLBUILD) -pkgs $(PACKAGES) $(STUB_OPTIONS) $(MENHIR) -Is $(PATHS) main.byte diff --git a/readme.rst b/readme.rst index 5d213c2..755b289 100755 --- a/readme.rst +++ b/readme.rst @@ -16,7 +16,7 @@ licht requires ocaml 4.04 and ncurses .. code-block:: console # sudo aptitude install opam libncures-dev libiconv-dev - $ opam install ocamlbuild curses camlzip ezxmlm ounit text menhir zarith + $ opam install ocamlbuild curses camlzip ezxmlm ounit text menhir zarith base $ make ===== diff --git a/src/catalog.ml b/src/catalog.ml index e4cd34b..95f13ce 100755 --- a/src/catalog.ml +++ b/src/catalog.ml @@ -1,13 +1,63 @@ module T = Tools module type DATA_SIG = sig - type 'a typ + type 'a t type 'a returnType - val compare_typ: 'a typ -> 'b typ -> ('a, 'b) T.cmp + val compare_typ: 'a t -> 'b t -> ('a, 'b) T.cmp - val repr: Format.formatter -> 'a typ -> unit + val repr: Format.formatter -> 'a t -> unit + +end + +module type CATALOG = sig + + type 'a argument + type 'a returnType + + type t + + (** Create a new catalog builder used for registering all the functions *) + type catalog_builder + + (** Empty catalog *) + val empty: catalog_builder + + val register1: + string -> (* The function name *) + 'a argument -> (* The signature *) + 'b returnType -> (* The return type *) + ('a -> 'b) -> (* The function to call *) + catalog_builder -> catalog_builder + + val register2: + string -> (* The function name *) + ('a argument * 'b argument) ->(* The signature *) + 'c returnType -> (* The return type *) + ( 'a -> 'b -> 'c) -> (* The function to call*) + catalog_builder -> catalog_builder + + val register3: + string -> (* The function name *) + ('a argument * 'b argument * 'c argument) -> (* The signature *) + 'd returnType -> (* The return type *) + ( 'a -> 'b -> 'c -> 'd) -> (* The function to call*) + catalog_builder -> catalog_builder + + + (** Compile the catalog *) + val compile: catalog_builder -> t + + + type result = + | R : 'a returnType * 'a -> result + + val eval1: t -> string -> ('a argument * 'a) -> result + + val eval2: t -> string -> ('a argument * 'a) -> ('b argument * 'b) -> result + + val eval3: t -> string -> ('a argument * 'a) -> ('b argument * 'b) -> ('c argument * 'c) -> result end @@ -19,6 +69,9 @@ exception RegisteredFunction (** Catalog for all functions *) module Make(Data:DATA_SIG) = struct + type 'a argument = 'a Data.t + type 'a returnType = 'a Data.returnType + (** This is the way the function is store in the map. We just the return type, and the function itself. *) type _ t_function = @@ -28,39 +81,29 @@ module Make(Data:DATA_SIG) = struct (** This is the key for storing functions in the map. *) type _ sig_typ = - | T1: 'a Data.typ -> 'a t_function sig_typ - | T2: 'a Data.typ * 'b Data.typ -> ('a * 'b) t_function sig_typ - | T3: 'a Data.typ * 'b Data.typ * 'c Data.typ -> ('a * 'b * 'c) t_function sig_typ - + | T1: 'a Data.t -> 'a t_function sig_typ + | T2: 'a Data.t * 'b Data.t -> ('a * 'b) t_function sig_typ + | T3: 'a Data.t * 'b Data.t * 'c Data.t -> ('a * 'b * 'c) t_function sig_typ - let repr: type a. Format.formatter -> a sig_typ -> unit = fun formatter -> function - | T1 t -> Format.fprintf formatter "(%a)" Data.repr t - | T2 (t1, t2) -> Format.fprintf formatter "(%a,%a)" Data.repr t1 Data.repr t2 - | T3 (t1, t2, t3) -> Format.fprintf formatter "(%a,%a,%a)" Data.repr t1 Data.repr t2 Data.repr t3 module ComparableSignature = struct - type 'a t = string * 'a sig_typ + type 'a t = 'a sig_typ (* Type for pure equality *) type (_, _) eq = Eq : ('a, 'a) eq (** Compare two signature *) - let comp: type a b. string * a sig_typ -> string * b sig_typ -> (a, b) T.cmp = begin fun (namea, a) (nameb, b) -> + let comp: type a b. a sig_typ -> b sig_typ -> (a, b) T.cmp = begin fun a b -> - let cmp: type c d. c Data.typ -> d Data.typ -> ((c, d) eq -> (a, b) T.cmp) -> (a, b) T.cmp = + let cmp: type c d. c Data.t -> d Data.t -> ((c, d) eq -> (a, b) T.cmp) -> (a, b) T.cmp = begin fun a b f -> match Data.compare_typ a b with | T.Eq -> f Eq | T.Lt -> T.Lt | T.Gt -> T.Gt end in - if namea < nameb then - T.Lt - else if namea > nameb then - T.Gt - else match a, b with - + match a, b with | T1(a), T1(b) -> cmp a b (fun Eq -> T.Eq) | T1(_), _ -> T.Lt | _, T1(_) -> T.Gt @@ -78,27 +121,27 @@ module Make(Data:DATA_SIG) = struct cmp c f (fun Eq -> T.Eq) ) ) - end - - let repr : type a. Format.formatter -> a t -> unit = begin fun formatter (str, typ) -> - Format.fprintf formatter "%s:%a" - str - repr typ - end + let repr : type a. Format.formatter -> a t -> unit = begin fun formatter -> function + | T1 t -> Format.fprintf formatter "(%a)" Data.repr t + | T2 (t1, t2) -> Format.fprintf formatter "(%a,%a)" Data.repr t1 Data.repr t2 + | T3 (t1, t2, t3) -> Format.fprintf formatter "(%a,%a,%a)" Data.repr t1 Data.repr t2 Data.repr t3 + end end + module Catalog = Map.Make(String) module Functions = Splay.Make(ComparableSignature) (* This is the map which contains all the registered functions. Each name is binded with another map with contains the function for each signature. *) - type t = Functions.t + type t = Functions.t Base.String_dict.t + type catalog_builder = Functions.t Catalog.t - let empty = Functions.empty + let empty = Catalog.empty (** Register a function in the catalog. If the function is already defined, @@ -107,19 +150,57 @@ module Make(Data:DATA_SIG) = struct let register t name signature f = begin let name' = String.uppercase_ascii name in - if Functions.member (name', signature) t then - raise RegisteredFunction - else - Functions.add (name', signature) f t + let map = begin match Catalog.find name' t with + | exception Not_found -> + Functions.add signature f Functions.empty + | x -> + if Functions.member signature x then + raise RegisteredFunction + else + Functions.add signature f x + end in + Catalog.add name' map t end + let register1 name typ1 returnType f catalog = + register catalog name (T1(typ1)) (Fn1 (returnType, f)) + + let register2 name (typ1, typ2) result f catalog = + register catalog name (T2(typ1, typ2)) (Fn2 (result, f)) + + let register3 name (typ1, typ2, typ3) result f catalog = + register catalog name (T3(typ1, typ2, typ3)) (Fn3 (result, f)) + (** Look in the catalog for a function with the given name and signature *) let find_function: type a. t -> string -> a t_function sig_typ -> a t_function = begin fun t name signature -> - Functions.find ((String.uppercase_ascii name), signature) t + Base.String_dict.find_exn t (String.uppercase_ascii name) + |> Functions.find signature + end + + let compile t = + (* Use efficient Base.String_dict. + The requirement to have a unique key is garantee by the Map structure. + *) + Base.String_dict.of_alist_exn (Catalog.bindings t) + + + type result = + | R : 'a returnType * 'a -> result + + let eval1 catalog name (t1, arg1) = begin + let Fn1(ret, f) = find_function catalog name (T1 t1) in + R (ret, f arg1) end - let repr = Functions.repr + let eval2 catalog name (t1, arg1) (t2, arg2) = begin + let Fn2(ret, f) = find_function catalog name (T2 (t1, t2)) in + R (ret, f arg1 arg2) + end + let eval3 catalog name (t1, arg1) (t2, arg2) (t3, arg3) = begin + let Fn3(ret, f) = find_function catalog name (T3 (t1, t2, t3)) in + R (ret, f arg1 arg2 arg3) + end end diff --git a/src/catalog.mli b/src/catalog.mli index e871378..f39e87b 100644 --- a/src/catalog.mli +++ b/src/catalog.mli @@ -1,38 +1,66 @@ module type DATA_SIG = sig - type 'a typ + type 'a t type 'a returnType - val compare_typ: 'a typ -> 'b typ -> ('a, 'b) Tools.cmp + val compare_typ: 'a t -> 'b t -> ('a, 'b) Tools.cmp - val repr: Format.formatter -> 'a typ -> unit + val repr: Format.formatter -> 'a t -> unit end -module Make(D:DATA_SIG): sig +module type CATALOG = sig + + type 'a argument + type 'a returnType type t - type 'a t_function = - | Fn1: 'b D.returnType * ('a -> 'b) -> 'a t_function - | Fn2: 'c D.returnType * ('a -> 'b -> 'c) -> ('a * 'b) t_function - | Fn3: 'd D.returnType * ('a -> 'b -> 'c -> 'd) -> ('a * 'b * 'c) t_function + (** Create a new catalog builder used for registering all the functions *) + type catalog_builder + + (** Empty catalog *) + val empty: catalog_builder - type 'a sig_typ = - | T1: 'a D.typ -> 'a t_function sig_typ - | T2: 'a D.typ * 'b D.typ -> ('a * 'b) t_function sig_typ - | T3: 'a D.typ * 'b D.typ * 'c D.typ -> ('a * 'b * 'c) t_function sig_typ + val register1: + string -> (* The function name *) + 'a argument -> (* The signature *) + 'b returnType -> (* The return type *) + ('a -> 'b) -> (* The function to call *) + catalog_builder -> catalog_builder + + val register2: + string -> (* The function name *) + ('a argument * 'b argument) ->(* The signature *) + 'c returnType -> (* The return type *) + ( 'a -> 'b -> 'c) -> (* The function to call*) + catalog_builder -> catalog_builder + + val register3: + string -> (* The function name *) + ('a argument * 'b argument * 'c argument) -> (* The signature *) + 'd returnType -> (* The return type *) + ( 'a -> 'b -> 'c -> 'd) -> (* The function to call*) + catalog_builder -> catalog_builder - (** Empty catalog *) - val empty: t - (** Register a new function in the catalog *) - val register : t -> string -> 'a t_function sig_typ -> 'a t_function -> t + (** Compile the catalog *) + val compile: catalog_builder -> t - (** Find a function with the given name and signature *) - val find_function: t -> string -> 'a t_function sig_typ -> 'a t_function - val repr: Format.formatter -> t -> unit + type result = + | R : 'a returnType * 'a -> result + + val eval1: t -> string -> ('a argument * 'a) -> result + + val eval2: t -> string -> ('a argument * 'a) -> ('b argument * 'b) -> result + + val eval3: t -> string -> ('a argument * 'a) -> ('b argument * 'b) -> ('c argument * 'c) -> result end + +module Make(D:DATA_SIG) : CATALOG + with type 'a argument = 'a D.t + and type 'a returnType = 'a D.returnType + diff --git a/src/evaluator.ml b/src/evaluator.ml index f718e1f..ed384e6 100755 --- a/src/evaluator.ml +++ b/src/evaluator.ml @@ -1,103 +1,42 @@ module D = DataType -module T = Tools +module F = Functions module Data = struct -(** Data format *) + (*** Values definitions *) -type 'a dataFormat = 'a ScTypes.dataFormat + 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 -(*** Type definitions *) - -type _ typ = - | Unit: unit typ - | Bool: D.Bool.t typ - | Num: D.Num.t typ - | String: UTF8.t typ - | List: 'a typ -> 'a list typ - -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 rec compare_typ: type a b. a typ -> b typ -> (a, b) T.cmp = -begin fun a b -> - match a, b with - | Unit, Unit -> T.Eq - | Bool, Bool -> T.Eq - | Num, Num -> T.Eq - | String, String -> T.Eq - | List l1, List l2 -> - begin match compare_typ l1 l2 with - | T.Lt -> T.Lt - | T.Eq -> T.Eq - | T.Gt -> T.Gt - end - | x, y -> if (T.Ex x) > (T.Ex y) then T.Gt else T.Lt -end - -let rec repr: -type a. Format.formatter -> a typ -> unit = -fun printer typ -> match typ with - | Unit -> Format.fprintf printer "Unit" - | Bool -> Format.fprintf printer "Bool" - | Num -> Format.fprintf printer "Num" - | String -> Format.fprintf printer "String" - | List t -> Format.fprintf printer "List[%a]" - repr t - -type 'a returnType = 'a ScTypes.returnType - -(*** 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 - -(** Get the value out of the box *) -let get_value_content: type a. a value -> a = function - | Bool b -> b - | Num (_, n) -> n - | String s -> s - | List (t, l) -> l - | Matrix (t, l) -> l - -(* Extract the type from a boxed value *) -let type_of_value: type a. a value -> a typ = function - | Bool b -> Bool - | Num (n, _) -> Num - | String s -> String - | List (t, l) -> List (typ_of_format t) - | Matrix (t, l) -> List (List (typ_of_format t)) + (** 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 -module C = Catalog.Make(Data) - - -type t = C.t - -let catalog = ref C.empty - -let get_catalog () = !catalog +(** Functions are stored as a mutable catalog. A setter is given *) +let catalog = ref (F.C.compile F.C.empty) -let repr = C.repr +let set_catalog t = catalog := t type existencialResult = | Result : 'a Data.value -> existencialResult [@@unboxed] let inject: -type a. a Data.dataFormat -> a -> existencialResult = fun resultFormat res -> +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)) + | 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 @@ -106,58 +45,50 @@ 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 + | 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 - -let register0 name returnType f = - catalog := C.register !catalog name (C.T1(Data.Unit)) (C.Fn1 (returnType, f)) - -let register1 name typ1 returnType f = - catalog := C.register !catalog name (C.T1(typ1)) (C.Fn1 (returnType, f)) - -let register2 name (typ1, typ2) result f = - catalog := C.register !catalog name (C.T2(typ1, typ2)) (C.Fn2 (result, f)) - -let register3 name (typ1, typ2, typ3) result f = - catalog := C.register !catalog name (C.T3(typ1, typ2, typ3)) (C.Fn3 (result, f)) - +(** Call the function with the arguments *) let call name args = begin let name' = UTF8.to_utf8string name in begin try match args with | [] -> - let C.Fn1(ret, f) = C.find_function !catalog name' (C.T1 Data.Unit) in + 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 (f ()) + inject returnType res | (Result p1)::[] -> - let C.Fn1(ret, f) = - C.find_function !catalog name' (C.T1 (Data.type_of_value p1)) in + 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 (f (Data.get_value_content p1)) + inject returnType res | (Result p1)::(Result p2)::[] -> - let C.Fn2(ret, f) = - C.find_function !catalog name' (C.T2 (Data.type_of_value p1, Data.type_of_value p2)) in + 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 (f (Data.get_value_content p1) (Data.get_value_content p2)) + inject returnType res | (Result p1)::(Result p2)::(Result p3)::[] -> - let C.Fn3(ret, f) = - C.find_function !catalog name' (C.T3 (Data.type_of_value p1, Data.type_of_value p2, Data.type_of_value p3)) in + 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 (f (Data.get_value_content p1) (Data.get_value_content p2) (Data.get_value_content p3)) + inject returnType res | _ -> raise Not_found with Not_found -> let signature = List.map (fun (Result x) -> let formatter = Format.str_formatter in - Data.repr formatter (Data.type_of_value x); + Functions.repr formatter (fst @@ Data.get_argument x); Format.flush_str_formatter ()) args in raise (Errors.Undefined (name, signature)) @@ -201,173 +132,17 @@ let eval mapper value = begin | ScTypes.Call (name, args) -> let args' = List.map extract args in call name args' - end - in + end in + let Result r = ((extract[@tailrec]) 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 + | 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 - -let wrap f = - let old_catalog = !catalog in - Tools.try_finally - (fun () -> catalog := C.empty; f ()) - (fun () -> catalog := old_catalog) - - -(* Register the standard functions *) -type 'a returnType = 'a ScTypes.returnType - -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 - -module Make_Compare(C: D.COMPARABLE) = struct - - let register t = begin - register2 "=" (t, t) f_bool C.eq; - register2 "<>" (t, t) f_bool C.neq; - register2 ">" (t, t) f_bool C.gt; - register2 ">=" (t, t) f_bool C.ge; - register2 "<" (t, t) f_bool C.lt; - register2 "<=" (t, t) f_bool C.le; - end - -end - -type 'a typ = 'a Data.typ -let t_bool: DataType.Bool.t typ = Data.Bool -let t_int: DataType.Num.t typ = Data.Num -let t_string: UTF8.t typ = Data.String -let t_list (t: 'a typ): 'a list typ = Data.List t - -(* Helper for list functions : reduce over a list of elements *) -let reduce name typ res f = begin - register1 name (t_list typ) res (fun x -> - List.fold_left f (List.hd x) x); - register1 name (t_list (t_list typ)) res (fun x -> - List.fold_left (List.fold_left f) (List.hd (List.hd x)) x); -end - -(* Helper for list functions : fold over a list of elements *) -let fold name t_in t_out f init = begin - register1 name (t_list t_in) t_out (fun x -> - List.fold_left f init x); - register1 name (t_list (t_list t_in)) t_out (fun x -> - List.fold_left (List.fold_left f) init x); -end - -let if_: type a. bool -> a -> a -> a = fun a b c -> if a then b else c - - -let () = begin - - (* Build a date *) - register3 "date" (t_int, t_int, t_int) f_date ( - fun year month day -> - D.Date.get_julian_day - (D.Num.to_int year) - (D.Num.to_int month) - (D.Num.to_int day) - ); - - let module CompareNum = Make_Compare(D.Num) in - Data.( - CompareNum.register t_int; - register0 "rand" f_number D.Num.rnd; - - register0 "pi" f_number (fun () -> D.Num.of_float (4. *. atan 1.)); - register1 "sin" t_int f_number (fun x -> D.Num.of_float (sin @@ D.Num.to_float x)); - register1 "cos" t_int f_number (fun x -> D.Num.of_float (cos @@ D.Num.to_float x)); - register1 "tan" t_int f_number (fun x -> D.Num.of_float (tan @@ D.Num.to_float x)); - register1 "atan" t_int f_number (fun x -> D.Num.of_float (atan @@ D.Num.to_float x)); - register1 "asin" t_int f_number (fun x -> D.Num.of_float (asin @@ D.Num.to_float x)); - register1 "acos" t_int f_number (fun x -> D.Num.of_float (acos @@ D.Num.to_float x)); - register1 "sinh" t_int f_number (fun x -> D.Num.of_float (sinh @@ D.Num.to_float x)); - register1 "cosh" t_int f_number (fun x -> D.Num.of_float (cosh @@ D.Num.to_float x)); - register1 "tanh" t_int f_number (fun x -> D.Num.of_float (tanh @@ D.Num.to_float x)); - register2 "atan2" (t_int, t_int)f_number (fun x y -> - D.Num.of_float (atan2 (D.Num.to_float x) (D.Num.to_float y)) - ); - - register1 "sqrt" t_int f_number (fun x -> D.Num.of_float (sqrt @@ D.Num.to_float x)); - register1 "exp" t_int f_number (fun x -> D.Num.of_float (exp @@ D.Num.to_float x)); - register1 "ln" t_int f_number (fun x -> D.Num.of_float (log @@ D.Num.to_float x)); - - register3 "if" (t_bool, t_int, t_int) f_number if_; - register3 "if" (t_bool, t_bool, t_bool) f_bool if_; - register3 "if" (t_bool, t_string, t_string) f_string if_; - - register1 "abs" t_int f_number D.Num.abs; - register1 "int" t_int f_number D.Num.floor; - register1 "rounddown" t_int f_number D.Num.round_down; - register1 "round" t_int f_number D.Num.round; - - register1 "trim" t_string f_string UTF8.trim; - register1 "right" t_string f_string (fun x -> UTF8.get x (-1)); - register2 "right" (t_string, t_int) f_string ( - fun t n -> - let n' = D.Num.to_int n in - UTF8.sub t (-(n')) n' - ); - register1 "left" t_string f_string (fun x -> UTF8.get x 0); - register2 "left" (t_string, t_int) f_string ( - fun t n -> - let n' = D.Num.to_int n in - UTF8.sub t 0 n' - ); - register1 "len" t_string f_number (fun x -> D.Num.of_int @@ UTF8.length x); - register1 "lenb" t_string f_number (fun x -> D.Num.of_int @@ String.length @@ UTF8.to_utf8string x); - register1 "lower" t_string f_string UTF8.lower; - register1 "unicode" t_string f_number (fun x -> D.Num.of_int @@ UTF8.code x); - register1 "unichar" t_int f_string (fun x -> UTF8.char @@ D.Num.to_int x); - register1 "upper" t_string f_string UTF8.upper; - register3 "substitute" (t_string, t_string, t_string) f_string UTF8.replace; - register2 "rept" (t_string, t_int) f_string (fun t n -> UTF8.repeat (D.Num.to_int n) t); - - let module CompareBool = Make_Compare(D.Bool) in - CompareBool.register t_bool; - register0 "true" f_bool (fun () -> D.Bool.true_); - register0 "false" f_bool (fun () -> D.Bool.false_); - register1 "not" t_bool f_bool D.Bool.not; - register2 "and" (t_bool, t_bool) f_bool D.Bool.and_; -(* fold "and" t_bool f_bool D.Bool.and_ (D.Bool.true_); *) - register2 "or" (t_bool, t_bool) f_bool D.Bool.or_; -(* fold "or" t_bool f_bool D.Bool.or_ (D.Bool.false_); *) - register2 "xor" (t_bool, t_bool) f_bool D.Bool.neq; -(* fold "xor" t_bool f_bool D.Bool.neq (D.Bool.false_); *) - - let module CompareString = Make_Compare(D.String) in - CompareString.register t_string; - - reduce "min" t_int f_num D.Num.min; (* Minimum value from a list *) - reduce "max" t_int f_num D.Num.max; (* Maximum value from a list *) - - fold "sum" t_int f_number D.Num.add (D.Num.zero); - fold "product" t_int f_number D.Num.mult (D.Num.one); - - register2 "^" (t_int, t_int) f_number D.Num.pow; - register2 "power" (t_int, t_int) f_number D.Num.pow; - - register2 "gcd"(t_int, t_int) f_number D.Num.gcd; - register2 "lcm"(t_int, t_int) f_number D.Num.lcm; - register1 "+" t_int f_num (fun x -> x); - register1 "-" t_int f_num D.Num.neg; (* Unary negation *) - register2 "+" (t_int, t_int) f_num D.Num.add; - register2 "-" (t_int, t_int) f_num D.Num.sub; - register2 "*" (t_int, t_int) f_number D.Num.mult; - register2 "/" (t_int, t_int) f_number D.Num.div; - - ) - -end - diff --git a/src/evaluator.mli b/src/evaluator.mli index b296b90..e338b8d 100755 --- a/src/evaluator.mli +++ b/src/evaluator.mli @@ -1,66 +1,4 @@ -type t - val eval: (ScTypes.refs -> ScTypes.result option ScTypes.Refs.range) -> ScTypes.expression -> ScTypes.result -val repr: Format.formatter -> t -> unit - -val get_catalog: unit -> t - -(** Type definitions *) - -type 'a typ -val t_bool: DataType.Bool.t typ -val t_int: DataType.Num.t typ -val t_string: UTF8.t typ -val t_list: 'a typ -> 'a list typ - -(** Result formats *) - -type 'a returnType - -(** Numeric (any format) *) -val f_num: DataType.Num.t returnType - -(** Date *) -val f_date: DataType.Num.t returnType - -(** Number *) -val f_number: DataType.Num.t returnType - -(** Boolean result *) -val f_bool: DataType.Bool.t returnType - -(** String *) -val f_string: DataType.String.t returnType - -(** Catalog *) - -val register0: - string -> (* The function name *) - 'a returnType -> (* The return type *) - (unit -> 'a) (* The function to call *) - -> unit - -val register1: - string -> (* The function name *) - 'a typ -> (* The signature *) - 'b returnType -> (* The return type *) - ('a -> 'b) (* The function to call *) - -> unit - -val register2: - string -> (* The function name *) - ('a typ * 'b typ) ->(* The signature *) - 'c returnType -> (* The return type *) - ( 'a -> 'b -> 'c) (* The function to call*) - -> unit - -val register3: - string -> (* The function name *) - ('a typ * 'b typ * 'c typ) ->(* The signature *) - 'd returnType -> (* The return type *) - ( 'a -> 'b -> 'c -> 'd) (* The function to call*) - -> unit +val set_catalog: Functions.C.t -> unit -(** [wrap f] run [f] inside a context where there is no functions *) -val wrap: (unit -> 'a) -> 'a diff --git a/src/expressionParser.mly b/src/expressionParser.mly index b7f77ae..473797f 100755 --- a/src/expressionParser.mly +++ b/src/expressionParser.mly @@ -1,6 +1,6 @@ %{ open ScTypes - module F = Functions + module S = Symbols let u = UTF8.from_utf8string @@ -67,8 +67,8 @@ basic: expr: | num {Value (number ($1))} - | MINUS expr {Call (F.sub, [$2])} - | PLUS expr {Call (F.add, [$2])} + | MINUS expr {Call (S.sub, [$2])} + | PLUS expr {Call (S.add, [$2])} | LETTERS ident LPAREN separated_list(SEMICOLON, expr) RPAREN { Call (u($1 ^ $2), $4) } @@ -81,19 +81,19 @@ expr: | STR {Value (string (u $1))} (* Mathematical operators *) - | expr MINUS expr {Call (F.sub, [$1; $3])} - | expr DIVIDE expr {Call (F.div, [$1; $3])} - | expr TIMES expr {Call (F.mul, [$1; $3])} - | expr PLUS expr {Call (F.add, [$1; $3])} - | expr POW expr {Call (F.pow, [$1; $3])} + | 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])} (* Comparaison *) - | expr EQ expr {Call (F.eq, [$1; $3])} - | expr NEQ expr {Call (F.neq, [$1; $3])} - | expr LT expr {Call (F.lt, [$1; $3])} - | expr GT expr {Call (F.gt, [$1; $3])} - | expr LE expr {Call (F.le, [$1; $3])} - | expr GE expr {Call (F.ge, [$1; $3])} + | 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])} %inline cell: | LETTERS NUM { Cell.from_string (false, $1) (false, int_of_string $2) } diff --git a/src/functions.ml b/src/functions.ml index 56d7530..62426e9 100755 --- a/src/functions.ml +++ b/src/functions.ml @@ -1,14 +1,194 @@ -let u = UTF8.from_utf8string - -let eq = u"=" -let neq = u"<>" -let lt = u"<" -let le = u"<=" -let gt = u">" -let ge = u">=" - -let add = u"+" -let mul = u"*" -let pow = u"^" -let div = u"/" -let sub = u"-" +module D = DataType +module T = Tools + +type _ typ = + | Unit: unit typ + | Bool: D.Bool.t typ + | Num: D.Num.t typ + | String: UTF8.t typ + | List: 'a typ -> 'a list typ + +let t_unit = Unit +let t_bool: DataType.Bool.t typ = Bool +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 rec repr: +type a. Format.formatter -> a typ -> unit = +fun printer typ -> match typ with + | Unit -> Format.fprintf printer "Unit" + | Bool -> Format.fprintf printer "Bool" + | Num -> Format.fprintf printer "Num" + | String -> Format.fprintf printer "String" + | List t -> Format.fprintf printer "List[%a]" + repr t + +module C = Catalog.Make(struct + + let repr = repr + + let rec compare_typ: type a b. a typ -> b typ -> (a, b) T.cmp = + begin fun a b -> + match a, b with + | Unit, Unit -> T.Eq + | Bool, Bool -> T.Eq + | Num, Num -> T.Eq + | String, String -> T.Eq + | List l1, List l2 -> + begin match compare_typ l1 l2 with + | T.Lt -> T.Lt + | T.Eq -> T.Eq + | T.Gt -> T.Gt + end + | x, y -> if (T.Ex x) > (T.Ex y) then T.Gt else T.Lt + end + + type 'a t = 'a typ + + type 'a returnType = 'a ScTypes.returnType + + +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 + +module Make_Compare(Comp: D.COMPARABLE) = struct + + let register t catalog = begin catalog + |> C.register2 "=" (t, t) f_bool Comp.eq + |> C.register2 "<>" (t, t) f_bool Comp.neq + |> C.register2 ">" (t, t) f_bool Comp.gt + |> C.register2 ">=" (t, t) f_bool Comp.ge + |> C.register2 "<" (t, t) f_bool Comp.lt + |> C.register2 "<=" (t, t) f_bool Comp.le + end + +end + +let built_in catalog = begin + + let module CompareNum = Make_Compare(D.Num) in + let module CompareString = Make_Compare(D.String) in + let module CompareBool = Make_Compare(D.Bool) in + + (* Helper for list functions : reduce over a list of elements *) + let reduce name typ res f c = begin + C.register1 name (t_list typ) res (fun x -> + List.fold_left f (List.hd x) x) c + |> C.register1 name (t_list (t_list typ)) res (fun x -> + List.fold_left (List.fold_left f) (List.hd (List.hd x)) x); + end in + + (* Helper for list functions : fold over a list of elements *) + let fold name t_in t_out f init c = begin + C.register1 name (t_list t_in) t_out (fun x -> + List.fold_left f init x) c + |> C.register1 name (t_list (t_list t_in)) t_out (fun x -> + List.fold_left (List.fold_left f) init x) + end in + + + let if_: type a. bool -> a -> a -> a = fun a b c -> if a then b else c in + + (* Build a date *) + C.register3 "date" (t_int, t_int, t_int) f_date ( + fun year month day -> + D.Date.get_julian_day + (D.Num.to_int year) + (D.Num.to_int month) + (D.Num.to_int day) + ) catalog + |> CompareNum.register t_int + + |> C.register1 "rand" t_unit f_number D.Num.rnd + + |> C.register1 "pi" t_unit f_number (fun () -> D.Num.of_float (4. *. atan 1.)) + |> C.register1 "sin" t_int f_number (fun x -> D.Num.of_float (sin (D.Num.to_float x))) + |> C.register1 "cos" t_int f_number (fun x -> D.Num.of_float (cos (D.Num.to_float x))) + |> C.register1 "tan" t_int f_number (fun x -> D.Num.of_float (tan (D.Num.to_float x))) + |> C.register1 "atan" t_int f_number (fun x -> D.Num.of_float (atan (D.Num.to_float x))) + |> C.register1 "asin" t_int f_number (fun x -> D.Num.of_float (asin (D.Num.to_float x))) + |> C.register1 "acos" t_int f_number (fun x -> D.Num.of_float (acos (D.Num.to_float x))) + |> C.register1 "sinh" t_int f_number (fun x -> D.Num.of_float (sinh (D.Num.to_float x))) + |> C.register1 "cosh" t_int f_number (fun x -> D.Num.of_float (cosh (D.Num.to_float x))) + |> C.register1 "tanh" t_int f_number (fun x -> D.Num.of_float (tanh (D.Num.to_float x))) + |> C.register2 "atan2" (t_int, t_int)f_number (fun x y -> + D.Num.of_float (atan2 (D.Num.to_float x) (D.Num.to_float y)) + ) + + |> C.register1 "sqrt" t_int f_number (fun x -> D.Num.of_float (sqrt(D.Num.to_float x))) + |> C.register1 "exp" t_int f_number (fun x -> D.Num.of_float (exp (D.Num.to_float x))) + |> C.register1 "ln" t_int f_number (fun x -> D.Num.of_float (log (D.Num.to_float x))) + + |> C.register3 "if" (t_bool, t_int, t_int) f_number if_ + |> C.register3 "if" (t_bool, t_bool, t_bool) f_bool if_ + |> C.register3 "if" (t_bool, t_string, t_string) f_string if_ + + |> C.register1 "abs" t_int f_number D.Num.abs + |> C.register1 "int" t_int f_number D.Num.floor + |> C.register1 "rounddown" t_int f_number D.Num.round_down + |> C.register1 "round" t_int f_number D.Num.round + + |> C.register1 "trim" t_string f_string UTF8.trim + |> C.register1 "right" t_string f_string (fun x -> UTF8.get x (-1)) + |> C.register2 "right" (t_string, t_int) f_string ( + fun t n -> + let n' = D.Num.to_int n in + UTF8.sub t (-(n')) n' + ) + |> C.register1 "left" t_string f_string (fun x -> UTF8.get x 0) + |> C.register2 "left" (t_string, t_int) f_string ( + fun t n -> + let n' = D.Num.to_int n in + UTF8.sub t 0 n' + ) + |> C.register1 "len" t_string f_number (fun x -> D.Num.of_int (UTF8.length x)) + |> C.register1 "lenb" t_string f_number (fun x -> D.Num.of_int (String.length (UTF8.to_utf8string x))) + |> C.register1 "lower" t_string f_string UTF8.lower + |> C.register1 "unicode" t_string f_number (fun x -> D.Num.of_int (UTF8.code x)) + |> C.register1 "unichar" t_int f_string (fun x -> UTF8.char (D.Num.to_int x)) + |> C.register1 "upper" t_string f_string UTF8.upper + |> C.register3 "substitute" (t_string, t_string, t_string) f_string UTF8.replace + |> C.register2 "rept" (t_string, t_int) f_string (fun t n -> UTF8.repeat (D.Num.to_int n) t) + + |> CompareBool.register t_bool + |> C.register1 "true" t_unit f_bool (fun () -> D.Bool.true_) + |> C.register1 "false" t_unit f_bool (fun () -> D.Bool.false_) + |> C.register1 "not" t_bool f_bool D.Bool.not + |> C.register2 "and" (t_bool, t_bool) f_bool D.Bool.and_ + |> C.register2 "or" (t_bool, t_bool) f_bool D.Bool.or_ + |> C.register2 "xor" (t_bool, t_bool) f_bool D.Bool.neq + + |> CompareString.register t_string + + |> reduce "min" t_int f_num D.Num.min (* Minimum value from a list *) + |> reduce "max" t_int f_num D.Num.max (* Maximum value from a list *) + + |> fold "sum" t_int f_number D.Num.add (D.Num.zero) + |> fold "product" t_int f_number D.Num.mult (D.Num.one) + + |> C.register2 "^" (t_int, t_int) f_number D.Num.pow + |> C.register2 "power" (t_int, t_int) f_number D.Num.pow + + |> C.register2 "gcd"(t_int, t_int) f_number D.Num.gcd + |> C.register2 "lcm"(t_int, t_int) f_number D.Num.lcm + |> C.register1 "+" t_int f_num (fun x -> x) + |> C.register1 "-" t_int f_num D.Num.neg (* Unary negation *) + |> C.register2 "+" (t_int, t_int) f_num D.Num.add + |> C.register2 "-" (t_int, t_int) f_num D.Num.sub + |> C.register2 "*" (t_int, t_int) f_number D.Num.mult + |> C.register2 "/" (t_int, t_int) f_number D.Num.div + +end diff --git a/src/functions.mli b/src/functions.mli new file mode 100755 index 0000000..c6904b2 --- /dev/null +++ b/src/functions.mli @@ -0,0 +1,21 @@ + +(** Function signature *) + +type 'a typ + +val t_unit: unit typ +val t_bool: DataType.Bool.t typ +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 repr: Format.formatter -> 'a typ -> unit + +module C : Catalog.CATALOG + with type 'a argument = 'a typ + and type 'a returnType = 'a ScTypes.returnType + +(** 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 3b83e85..4491025 100755 --- a/src/main.ml +++ b/src/main.ml @@ -55,9 +55,12 @@ let f screen = ActionParser.( | _ -> raise Not_found end) +let menhirParser = + MenhirLib.Convert.Simplified.traditional2revised ActionParser.normal + let parser screen = begin let get_value () = f screen, Lexing.dummy_pos, Lexing.dummy_pos in - MenhirLib.Convert.Simplified.traditional2revised ActionParser.normal get_value + menhirParser get_value end let rec normal_mode (t, screen) = begin @@ -215,12 +218,14 @@ and command (t, screen) action = begin | ("w", file) -> (* Save the file *) Odf.save t.Sheet.data file; normal_mode @@ redraw t screen +(* | ("repr", file) -> (* Save the file *) let out_gv = open_out_bin file in let form = Format.formatter_of_out_channel out_gv in Evaluator.repr form (Evaluator.get_catalog ()); close_out out_gv; normal_mode @@ redraw t screen +*) | ("enew", _) -> (* Start a new spreadsheet *) normal_mode @@ redraw (Sheet.create Sheet.Raw.empty) screen | ("q", _) -> (* Quit *) @@ -230,6 +235,10 @@ 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 diff --git a/src/odf/odf_ExpressionParser.mly b/src/odf/odf_ExpressionParser.mly index 6b571a9..54836cd 100755 --- a/src/odf/odf_ExpressionParser.mly +++ b/src/odf/odf_ExpressionParser.mly @@ -1,6 +1,6 @@ %{ open ScTypes - module F = Functions + module S = Symbols let u = UTF8.from_utf8string @@ -44,8 +44,8 @@ value: expr: | num {Value (number ($1))} - | MINUS expr {Call (F.sub, [$2])} - | PLUS expr {Call (F.add, [$2])} + | MINUS expr {Call (S.sub, [$2])} + | PLUS expr {Call (S.add, [$2])} | L_SQ_BRACKET ref R_SQ_BRACKET {$2} @@ -54,19 +54,19 @@ expr: | STR {Value (string (u $1))} (* Mathematical operators *) - | expr MINUS expr {Call (F.sub, [$1; $3])} - | expr DIVIDE expr {Call (F.div, [$1; $3])} - | expr TIMES expr {Call (F.mul, [$1; $3])} - | expr PLUS expr {Call (F.add, [$1; $3])} - | expr POW expr {Call (F.pow, [$1; $3])} + | 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])} (* Comparaison *) - | expr EQ expr {Call (F.eq, [$1; $3])} - | expr NEQ expr {Call (F.neq, [$1; $3])} - | expr LT expr {Call (F.lt, [$1; $3])} - | expr GT expr {Call (F.gt, [$1; $3])} - | expr LE expr {Call (F.le, [$1; $3])} - | expr GE expr {Call (F.ge, [$1; $3])} + | 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) } diff --git a/src/splay.ml b/src/splay.ml index ec5750c..4bbc3dd 100644 --- a/src/splay.ml +++ b/src/splay.ml @@ -1,10 +1,11 @@ module type KEY = sig - type 'a t + type 'a t - val comp: 'a t -> 'b t -> ('a, 'b) Tools.cmp + (** Parametrized comparator *) + val comp: 'a t -> 'b t -> ('a, 'b) Tools.cmp - val repr: Format.formatter -> 'a t -> unit + val repr: Format.formatter -> 'a t -> unit end diff --git a/src/symbols.ml b/src/symbols.ml new file mode 100755 index 0000000..56d7530 --- /dev/null +++ b/src/symbols.ml @@ -0,0 +1,14 @@ +let u = UTF8.from_utf8string + +let eq = u"=" +let neq = u"<>" +let lt = u"<" +let le = u"<=" +let gt = u">" +let ge = u">=" + +let add = u"+" +let mul = u"*" +let pow = u"^" +let div = u"/" +let sub = u"-" diff --git a/src/symbols.mli b/src/symbols.mli new file mode 100755 index 0000000..764b539 --- /dev/null +++ b/src/symbols.mli @@ -0,0 +1,16 @@ + +(** Symbols *) + +val eq : UTF8.t +val neq : UTF8.t +val lt : UTF8.t +val le : UTF8.t +val gt : UTF8.t +val ge : UTF8.t +val add : UTF8.t +val mul : UTF8.t +val pow : UTF8.t +val div : UTF8.t +val sub : UTF8.t + + diff --git a/tests/test.ml b/tests/test.ml index b9672ab..2d881ca 100755 --- a/tests/test.ml +++ b/tests/test.ml @@ -1,4 +1,7 @@ let () = + + Evaluator.set_catalog (Functions.C.compile @@ Functions.built_in @@ Functions.C.empty); + let tests = OUnit2.test_list [ Tools_test.tests; DataType_test.num_tests; -- cgit v1.2.3