aboutsummaryrefslogtreecommitdiff
path: root/src/expression.ml
blob: c2e4ec89a580923f8080f1112a41e1c2a9f1e526 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
(*
This file is part of licht.

licht is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

licht is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with licht.  If not, see <http://www.gnu.org/licenses/>.
*)

module Tuple2 = Tools.Tuple2

let u = UTF8.from_utf8string

type t =
  | 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.Expr.t  (** A valid expression *)
  | Error of int * UTF8.t         (** When the expression cannot be parsed *)


let is_defined = function
  | Undefined -> false
  | _ -> true

let load content = begin
  let content = UTF8.to_utf8string content in
  if String.length content > 0 then (
    if content.[0] = '=' then (
      (* If the string start with a '=', load it as a formula *)
      Formula (
        try
          Expression (
               Lexing.from_string content
            |> ExpressionParser.value ExpressionLexer.read)
        with _ -> Error (1, UTF8.from_utf8string content)
      )
    ) else (
      (* First try to load the data with basic types, and fallback with string *)
      let content' =
        try String.sub content 0 (String.index content '\000')
        with Not_found -> content in
        try
          let ScTypes.Result.Ok r =
               ExpressionParser.content ExpressionLexer.read
            @@ Lexing.from_string content' in
          Basic r
        with _ -> Basic (ScTypes.Type.string (UTF8.from_utf8string content'))
    )
  ) else (
    (* If the string in empty, build an undefined value *)
    Undefined
  )
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 catalog mapper = begin

  begin try match expr with
    | 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


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 Shifter = ScTypes.Expr.Eval(Shift_expr)
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 '=';
      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 = function
  | Formula (Expression f) -> Formula (Expression (Shifter.eval f vector))
  | other -> other

let (=) t1 t2 = match t1, t2 with
  | Basic b1, Basic b2 -> ScTypes.Type.(=) b1 b2
  | o1, o2 -> Pervasives.(=) o1 o2