(*
* <COPYRIGHT>
* Copyright 2006 Gerd Stolpmann
*
* <GPL>
* This file is part of WDialog.
*
* WDialog 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 2 of the License, or
* (at your option) any later version.
*
* WDialog 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 WDialog; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* </>
*)
(* $Id: wd_brexpr_eval.ml,v 3.2 2006-03-08 16:05:02 stolpmann Exp $ *)
open Wd_types
open Wd_brexpr
open Wd_util
let rec apply_oe app s enclist =
(* Apply the encodings [enclist] to [s], one after the other *)
match enclist with
[] -> s
| enc :: enclist' ->
let f = app#output_encoding enc in
apply_oe app (f s) enclist'
;;
let extract_head m =
match m with
| [] ->
[| |]
| first_row :: _ ->
let h = List.map fst first_row in
let h_dups = duplicates h in
if h_dups <> [] then
failwith ("Matrix has duplicate columns: " ^
String.concat ", " h_dups);
Array.of_list h
;;
let eval_expr ?(local_bindings = []) (dlg : dialog_type) (e : expr) =
let rec eval var_bindings (e : expr) =
match e with
| `Expr_var var_name ->
(* If bound locally, return the local value, else the value of the
* dialog variable
*)
( try
Lazy.force(Wd_dictionary.find var_name var_bindings)
with
| Not_found ->
dlg # variable var_name
)
| `Expr_strconst s ->
String_value s
| `Expr_matrix m ->
let head = extract_head m in
(* Possible optimization: Extract only once, not in every eval *)
let rows =
Array.map
(fun row ->
Array.mapi
(fun k (col_name, cell) ->
if k >= Array.length head || head.(k) <> col_name then
failwith "Badly formed matrix: column mismatch";
( match eval var_bindings cell with
| String_value s -> s
| _ ->
failwith "Matrices can only contain strings"
)
)
(Array.of_list row)
)
(Array.of_list m) in
Matrix_value(head, rows)
| `Expr_apply ( `Expr_fun_name "type", args) ->
(* CHECK: We ignore here that there may be locally bound
* variables!
*)
( match args with
[ `Expr_var var_name ] ->
let d = dlg # declaration # variable var_name in
( match d.var_type with
String_type -> String_value "string"
| Enum_type e -> String_value e.enum_name
| Dialog_type -> String_value "dialog"
| Dyn_enum_type -> String_value "dynamic-enumerator"
| Matrix_type -> String_value "matrix"
| Table_type -> String_value "table"
)
| _ ->
failwith "Function `type' must be applied to a variable"
)
| `Expr_apply ( `Expr_fun_name ("is_associative" | "is-associative"), args) ->
(* CHECK: We ignore here that there may be locally bound
* variables!
*)
( match args with
[ `Expr_var var_name ] ->
let d = dlg # declaration # variable var_name in
String_value(if d.var_associative then "yes" else "no")
| _ ->
failwith "Function `is_associative' must be applied to a variable"
)
| `Expr_apply ( `Expr_fun_name "default", args) ->
(* CHECK: We ignore here that there may be locally bound
* variables!
*)
( match args with
[ `Expr_var var_name ] ->
dlg # initial_variable_value var_name
| _ ->
failwith "Function `default' must be applied to a variable"
)
| `Expr_apply ( `Expr_fun_name "enum", args) ->
( match args with
[ `Expr_var var_name ] ->
( try
let e = dlg # declaration # enumeration var_name in
Dyn_enum_value e.enum_definition
with
Not_found ->
failwith("Function `enum': enumeration not found: " ^ var_name)
)
| _ ->
failwith "Function `enum': bad usage"
)
| `Expr_apply ( `Expr_fun_name "words", args) ->
let s =
String.concat " "
(List.map
(function
| `Expr_var var_name ->
var_name
| _ ->
failwith "Function `words': bad usage"
)
args) in
String_value s
| `Expr_apply ( `Expr_fun_name "m", args) ->
let s =
match args with
| [ `Expr_var var_name ] ->
var_name
| _ ->
failwith "Function `m': bad usage"
in
let fn =
try dlg # application # var_function "cat-translate"
with Not_found -> failwith "function cat-translate not found" in
fn dlg [ String_value s ]
| `Expr_apply ( `Expr_fun_name fn_name, args) ->
let args' = List.map (fun arg -> lazy(eval var_bindings arg)) args in
let fn =
try
let fn_val =
Lazy.force(Wd_dictionary.find fn_name var_bindings) in
( match fn_val with
| Function_value fn -> fn
| _ ->
failwith("Only functions can be applied to arguments")
)
with
| Not_found ->
( try
dlg # application # lazy_var_function fn_name
with
Not_found ->
failwith("No such function: " ^ fn_name)
)
in
fn dlg args'
| `Expr_apply ( (#expr as fn_expr), args ) ->
let fn_val = eval var_bindings fn_expr in
(* always eager eval here *)
( match fn_val with
| Function_value fn ->
let args' =
List.map (fun arg -> lazy(eval var_bindings arg)) args in
fn dlg args'
| _ ->
failwith "Only functions can be applied to arguments"
)
| `Expr_fun (var_names, bound_expr) ->
let var_dups = duplicates var_names in
if var_dups <> [] then
failwith("Duplicate variables in function definition: " ^
String.concat ", " var_dups);
let n_names = List.length var_names in
let fn dlg args =
if List.length args <> n_names then
failwith "Called function with wrong number of arguments";
let args' = List.combine var_names args in
let var_bindings' =
List.fold_left
(fun b (var_name, arg) ->
Wd_dictionary.add var_name arg b
)
var_bindings
args' in
eval var_bindings' bound_expr
in
Function_value fn
| `Expr_let (let_bindings, bound_expr) ->
let let_names = List.map fst let_bindings in
let let_dups = duplicates let_names in
if let_dups <> [] then
failwith("Duplicate variables in 'let' binding: " ^
String.concat ", " let_dups);
let var_bindings' =
List.fold_left
(fun b (let_name, let_expr) ->
let arg = lazy (eval var_bindings let_expr) in
Wd_dictionary.add let_name arg b
)
var_bindings
let_bindings in
eval var_bindings' bound_expr
| `Expr_param(_,_) ->
assert false
in
try
eval (Wd_dictionary.of_alist local_bindings) e
with
| Failure msg ->
raise(Eval_error_noloc msg)
| No_such_variable msg ->
raise(Eval_error_noloc("No such variable: " ^ msg))
;;
let eval_expr_s dlg e =
let v = eval_expr dlg e in
( match v with
| String_value s -> s
| _ ->
failwith("The final result of an expression must be a string")
)
;;
let eval_expr_oe dlg expr =
match expr with
| `Expr_oe (e, oelist) ->
let s = eval_expr_s dlg e in
apply_oe dlg#application s oelist
| #expr as e ->
eval_expr_s dlg e
;;
let eval_string_expr dlg str_expr =
String.concat ""
(List.map
(fun (item, s) ->
try
( match item with
| `Literal s -> s
| (#expr_oe as e) -> eval_expr_oe dlg e
)
with
| Eval_error_noloc msg ->
raise(Eval_error("In expression " ^ s ^ ": " ^ msg))
)
str_expr)
;;