let rec eval ~env tm =
let mk v = { V.t = tm.t ; V.value = v } in
match tm.term with
| Unit -> mk (V.Unit)
| Bool x -> mk (V.Bool x)
| Int x -> mk (V.Int x)
| String x -> mk (V.String x)
| Float x -> mk (V.Float x)
| Encoder x -> mk (V.Encoder x)
| List l -> mk (V.List (List.map (eval ~env) l))
| Product (a,b) -> mk (V.Product (eval ~env a, eval ~env b))
| Ref v -> mk (V.Ref (ref (eval ~env v)))
| Get r ->
begin match (eval ~env r).V.value with
| V.Ref r -> !r
| _ -> assert false
end
| Set (r,v) ->
begin match (eval ~env r).V.value with
| V.Ref r ->
r := eval ~env v ;
mk V.Unit
| _ -> assert false
end
| Let {gen=generalized;var=x;def=v;body=b} ->
(* It should be the case that generalizable variables don't
* get instantiated in any way when evaluating the definition.
* But we don't double-check it. *)
let v = eval ~env v in
eval ~env:((x,(generalized,v))::env) b
| Fun (fv,p,body) ->
(* Unlike OCaml we always evaluate default values,
* and we do that early.
* I think the only reason is homogeneity with FFI,
* which are declared with values as defaults. *)
let p =
List.map
(function
| (lbl,var,_,Some v) -> lbl,var,Some (eval ~env v)
| (lbl,var,_,None) -> lbl,var,None)
p
in
let env = List.filter (fun (x,_) -> Vars.mem x fv) env in
mk (V.Fun (p,[],env,body))
| Var var ->
lookup env var tm.t
| Seq (a,b) ->
ignore (eval ~env a) ;
eval ~env b
| App (f,l) ->
apply ~t:tm.t
(eval ~env f)
(List.map (fun (l,t) -> l, eval ~env t) l)
and apply ~t f l =
let mk v = { V.t = t ; V.value = v } in
(* Extract the components of the function, whether it's explicit
* or foreign, together with a rewrapping function for creating
* a closure in case of partial application. *)
let p,pe,f,rewrap =
match f.V.value with
| V.Fun (p,pe,e,body) ->
p,pe,
(fun pe _ -> eval ~env:(List.rev_append pe e) body),
(fun p pe -> mk (V.Fun (p,pe,e,body)))
| V.FFI (p,pe,f) ->
p,pe,
(fun pe t -> f (List.rev pe) t),
(fun p pe -> mk (V.FFI (p,pe,f)))
| _ -> assert false
in
let pe,p =
List.fold_left
(fun (pe,p) (lbl,v) ->
let (_,var,_),p =
remove_first (fun (l,_,_) -> l=lbl) p
in
(var,([],v))::pe, p)
(pe,p) l
in
if List.exists (fun (_,_,x) -> x=None) p then
(* Partial application. *)
rewrap p pe
else
(* XXX Contrary to older implementation of eval,
* we do not assign location-based IDs to sources
* (e.g. add@L13C4). *)
let pe =
List.fold_left
(fun pe (_,var,v) ->
(var,
(* Set the position information on FFI's default values.
* Cf. r5008: if an Invalid_value is raised on a default value,
* which happens with the mount/name params of output.icecast.*,
* the printing of the error should succeed at getting a position
* information. *)
let v = Utils.get_some v in
[],
{ v with V.t = T.make ~pos:t.T.pos (T.Link v.V.t) })::pe)
pe p
in
let v = f pe t in
(* Similarly here, the result of an FFI call should have some position
* information. For example, if we build a fallible source and pass
* it to an operator that expects an infallible one, an error
* is issued about that FFI-made value and a position is needed. *)
{ v with V.t = T.make ~pos:t.T.pos (T.Link v.V.t) }