package gobba

  1. Overview
  2. Docs

Source file types.ml

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
module T = ANSITerminal
module D = Util.Dict

(** A value identifier*)
type ide = string
[@@deriving show, eq, ord]

(** A type wrapper for complex numbers where equality, ordering
    and showing are defined *)
type complext = Complex.t [@polyprinter fun fmt (n: Complex.t) -> fprintf fmt
                    "%f:+%f" n.re n.im] [@equal (=)] [@compare compare]
[@@deriving show { with_path = false }, eq, ord]

(** An environment type containing identifier - purity couples *)
type purityenv_type = (ide, puret) Util.Dict.t [@@deriving show,eq, ord]
(** A type representing if a computation is pure or not  *)
and puret =  Impure | Uncertain | PurityModule of purityenv_type | Pure | Numerical
[@@deriving show { with_path = false }, eq, ord]


(** Contains a primitive's name, number of arguments and pureness *)
type primitiveinfo = (ide * int * puret) [@@deriving show { with_path = false }, eq, ord]

(** Represents a binary operation kind *)
type binop =
  | Getkey
  | Eq | Gt | Lt | Ge | Le | And | Or
  | Plus | Sub | Div | Mult
  | Cons | Concat | Compose  [@@deriving show { with_path = false }, eq, ord]

(** The type representing Abstract Syntax Tree expressions *)
type expr =
  | Unit
  | Purity of puret * expr
  | NumInt of int
  | NumFloat of float
  | NumComplex of complext
  | Boolean of bool
  | String of string
  | Symbol of ide
  | List of expr list
  | Dict of assignment_type list
  (* Binary Operation *)
  | Binop of binop * expr * expr
  | Not of expr
  (* Control flow and functions *)
  | IfThenElse of expr * expr * expr
  | Let of assignment_type list * expr
  | Lambda of ide * expr
  | Apply of expr * expr
  | ApplyPrimitive of primitiveinfo * expr list
  | Sequence of expr * expr
[@@deriving show { with_path = false }, eq, ord]

(* Defines an assignment: laziness, name and value *)
and assignment_type = (bool * ide * expr) [@@deriving show { with_path = false }, eq, ord]

(** Function that finds a nested lambda body *)
let rec findbody l = match l with
  | Lambda(_, b) ->  findbody b
  | other -> other
(** Function that finds and replaces a (nested) lambda body *)
let rec replacebody l newbody = match l with
  | Lambda(p, b) -> Lambda(p, replacebody b newbody)
  | _ -> newbody
(** Function that creates a list with the params of a nested lambda*)
let rec findparams l = match l with
  | Lambda(p, b) -> p::(findparams b)
  | _ -> []

(** Show a short representation of an expression (useful for stack traces) *)
let rec simple_show_expr e = match e with
  | NumInt i -> string_of_int i
  | NumFloat i -> string_of_float i
  | NumComplex i -> show_complext i
  | Boolean i -> string_of_bool i
  | String i -> "\"" ^ i ^ "\""
  | Symbol s -> s
  | Apply(Symbol f, b) -> f ^ " (" ^ simple_show_expr b ^ ")"
  | Lambda(p, b) -> "(fun " ^ (String.concat " " (p::(findparams b))) ^ " -> ... )"
  | Let(l, _) -> "let " ^ (String.concat " and" (List.map (fun x -> Util.snd3 x ^ " = ... ") l))
  | Binop(kind, a, b) -> simple_show_expr a ^ " " ^ (show_binop kind) ^ " " ^ simple_show_expr b
  | _ -> "<code>"


(** Creates a nested Lambda from a list of params*)
let lambda_from_paramlist l body = List.fold_right (fun p e -> Lambda (p, e)) l body

(** Creates a nested Apply from a list of expressions*)
let apply_from_exprlist l f = List.fold_left (fun e p -> Apply (e, p)) f l

(** Creates a list of Symbol from a list of string*)
let symbols_from_strings l = List.map (fun x -> Symbol x) l

(** A type containing directives information *)
type directive =
  | Dumpenv
  | Dumppurityenv
  | Includefile of string
  | Includefileasmodule of string * ide option 
  | Setpurity of puret
  | Setverbose of int
[@@deriving show,eq,ord]


(** A type useful for evaluating files, stating if a command is
    an expression or simply a "global" declaration (appended to environment) *)
type command =
  | Directive of directive
  | Expr of expr
  | Def of assignment_type list
[@@deriving show { with_path = false }, eq, ord]


(** A type that represents an evaluated (result of a computation) value *)
type evt =
  | EvtUnit
  | EvtInt of int         [@compare compare]
  | EvtFloat of float     [@compare compare]
  | EvtComplex of complext [@compare compare]
  | EvtBool of bool       [@equal (=)] [@compare compare]
  | EvtString of string   [@equal (=)] [@compare compare]
  | EvtList of evt list   [@equal (=)]
  | EvtDict of (ide * evt) list [@equal (=)]
  (** Recursion is achieved by keeping an optional function name in the constructor *)
  | Closure of ide option * ide * expr * env_type  [@equal (=)]
  | LazyExpression of expr
[@@deriving show { with_path = false }, eq, ord]

(* An environment of already evaluated values  *)
and env_type = (ide, evt) D.t [@@deriving show { with_path = false }, eq, ord]

(** A type containing information about types *)
and typeinfo =
  | TUnit
  | TBool
  | TNumber
  | TInt
  | TFloat
  | TComplex
  | TString
  | TList
  | TDict
  | TLambda

let show_tinfo t = match t with
  | TUnit   -> "unit"
  | TBool   -> "bool"
  | TNumber -> "number"
  | TInt    -> "int"
  | TFloat  -> "float"
  | TComplex -> "complex"
  | TString -> "string"
  | TList -> "list"
  | TDict -> "dict"
  | TLambda -> "fun"

(* Generate a list of parameter names to use in the primitive abstraction *)
let generate_prim_params n =
  if n = 0 then ["..."] else
    Array.to_list(Array.make n 'a' |> Array.mapi (fun i c -> int_of_char c + i |> char_of_int |> Printf.sprintf "%c"))


let rec show_unpacked_evt e = match e with
  | EvtUnit -> "()"
  | EvtInt v -> string_of_int v
  | EvtFloat v -> Printf.sprintf "%f" v
  | EvtComplex n -> show_complext n
  | EvtBool v -> string_of_bool v
  | EvtString v -> "\"" ^ (String.escaped v) ^ "\""
  | EvtList l -> "[" ^ (String.concat "; " (List.map show_unpacked_evt l)) ^ "]"
  | EvtDict d -> "{" ^
                 (String.concat ", " 
                    (List.map (fun (x,y) -> x ^ " = " ^ show_unpacked_evt y) d))
                 ^ "}"
  | Closure (name, param, body, _) ->
    (match name with | Some x -> x | None -> "") ^ "(fun " ^ (String.concat " " (param::(findparams body))) ^ " -> ... )"
  | LazyExpression e -> "<lazy>: " ^ (simple_show_expr e)

(** Function that creates a list with the params of a nested lambda in a Closure *)
let findevtparams l = match l with
  | Closure(_, p, b, _) -> p::(findparams b)
  | _ -> []

(** A type representing a primitive *)
type primitive = Primitive of (evt list -> evt) * primitiveinfo

(** Get the purity of a primitive *)
let get_primitive_purity x = match x with
  Primitive (_, (_, _, p)) -> p

(** Get the actual function from a primitive type *)
let get_primitive_function x = match x with
  Primitive (f, _) -> f

(** Get the information from a primitive type *)
let get_primitive_info x = match x with
  Primitive (_, i) -> i

(** Generate a lambda from a primitive *)
let lambda_from_primitive prim =
    let name, numparams, purity = get_primitive_info prim in
    (* Generate a closure abstraction from a primitive *)
    let primargs = generate_prim_params numparams in
    let symprimargs = symbols_from_strings primargs in
    let lambdas = lambda_from_paramlist primargs (ApplyPrimitive((name, numparams, purity), symprimargs)) in
    lambdas


(** A recursive type representing a stacktrace frame *)
type stackframe =
  | StackValue of int * expr * stackframe
  | EmptyStack
[@@deriving show { with_path = false }]

(** Push an AST expression into a stack
    @param s The stack where to push the expression
    @param e The expression to push
*)
let push_stack (s: stackframe) (e: expr) =
  match s with
  | StackValue(d, ee, ss) -> StackValue(d+1, e, StackValue(d, ee, ss))
  | EmptyStack -> StackValue(1, e, EmptyStack)

(** Pop an AST expression from a stack *)
let pop_stack (s: stackframe) = match s with
  | StackValue(_, _, ss) -> ss
  | EmptyStack -> failwith "Stack underflow"

let depth_of_stack (s: stackframe) = match s with
  | StackValue(d, _, _) -> d
  | EmptyStack -> 0

let rec string_of_stack maxdepth (s: stackframe) =
  match s with
  | EmptyStack -> "----- : toplevel"
  | StackValue(d, e, ss) ->
    if maxdepth = 0 then "... " ^ (string_of_int d) ^ " stack frames omitted ..." else
    Printf.sprintf "%05i : %s in\n%s" d (simple_show_expr e) (string_of_stack (maxdepth - 1)  ss)

(** Options for the eval function *)
type evalstate = {
  env: env_type;
  purityenv: purityenv_type;
  verbosity: int;
  stack: stackframe;
  mutable printresult: bool;
  purity: puret;
}