Plasma GitLab Archive
Projects Blog Knowledge

(*
 * <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)
;;

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml