New normalization-by-evaluation package
authornipkow
Tue, 21 Feb 2006 16:18:50 +0100
changeset 19116 d065ec558092
parent 19115 bc8da9b4a81c
child 19117 5208677a394c
New normalization-by-evaluation package
src/Pure/Tools/nbe_codegen.ML
src/Pure/Tools/nbe_eval.ML
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Pure/Tools/nbe_codegen.ML	Tue Feb 21 16:18:50 2006 +0100
@@ -0,0 +1,372 @@
+(*  ID:         $Id$
+    Author:     Klaus Aehlig, Tobias Nipkow
+*)
+
+(* Global asssumptions:
+   For each function: there is at least one defining eqns and
+   all defining equations have same number of arguments.
+
+FIXME
+fun MLname s = "nbe_" ^ s;
+val quote = quote;
+
+FIXME xtab in theory
+*)
+(*
+structure NBE_Codegen =
+struct
+
+FIXME CodegenThingol names! which "error"?
+*)
+
+val is_constr = NBE_Eval.is_constr;
+val Eval = "NBE_Eval";
+val Eval_register= Eval ^ ".register";
+val Eval_lookup  = Eval ^ ".lookup";
+val Eval_apply   = Eval ^ ".apply";
+val Eval_Constr  = Eval ^ ".Constr";
+val Eval_C       = Eval ^ ".C";
+val Eval_AbsN    = Eval ^ ".AbsN";
+val Eval_Fun     = Eval ^ ".Fun";
+val Eval_BVar    = Eval ^ ".BVar";
+val Eval_new_name = Eval ^ ".new_name";
+val Eval_to_term = Eval ^ ".to_term";
+
+fun MLname s = "nbe_" ^ s;
+fun MLvname s  = "v_" ^ MLname s;
+fun MLparam n  = "p_" ^ string_of_int n;
+fun MLintern s = "i_" ^ MLname s;
+
+fun MLparams n = map MLparam (1 upto n);
+
+structure S =
+struct
+
+val quote = quote; (* FIXME *)
+
+fun app e1 e2 = "(" ^ e1 ^ " " ^ e2 ^ ")";
+fun abs v e = "(fn" ^ v ^ " => " ^ e ^ ")";
+fun tup es = "(" ^ commas es ^ ")";
+fun list es = "[" ^ commas es ^ "]";
+
+fun apps s ss = Library.foldl (uncurry app) (s, ss);
+fun nbe_apps s ss =
+  Library.foldr (fn (s,e) => app (app Eval_apply e) s) (ss,s);
+
+fun eqns name ees =
+  let fun eqn (es,e) = name ^ " " ^ space_implode " " es ^ " = " ^ e
+  in "fun " ^ space_implode "\n  | " (map eqn ees) ^ ";\n" end;
+
+
+fun Val v s = "val " ^ v ^ " = " ^ s;
+fun Let ds e = "let\n" ^ (space_implode "\n" ds) ^ " in " ^ e ^ " end"
+
+end
+
+fun mk_nbeFUN(nm,e) =
+  S.app Eval_Fun (S.tup [S.abs(S.list [MLvname nm])e,S.list [],"1",
+      S.abs(S.tup [])(S.Let 
+        [S.Val (MLintern "var") (S.app Eval_new_name (S.tup [])),
+         S.Val (MLvname nm) (S.app Eval_BVar (S.tup [(MLintern "var"), S.list []]))]
+	(S.app Eval_AbsN(S.tup[MLintern "var",(S.app Eval_to_term e)])))]);
+
+fun take_last n xs = rev (Library.take(n, rev xs));
+fun drop_last n xs = rev (Library.drop(n, rev xs));
+
+fun selfcall nm ar args =
+	if (ar <= length args) then 
+	  S.nbe_apps (S.app (MLname nm) (S.list (take_last ar args)))
+	             (drop_last ar args)
+        else S.app Eval_Fun (S.tup [MLname nm,S.list args,
+	           string_of_int(ar - (length args)),
+		   S.abs (S.tup []) (S.app Eval_C
+	(S.quote nm))]);
+
+
+fun mk_rexpr nm ar =
+  let fun mk args t = case t of
+    CodegenThingol.IConst((s,_),_) => 
+    if s=nm then selfcall nm ar args
+    else if is_constr s then S.app Eval_Constr 
+                                   (S.tup [S.quote s,S.list args ])
+    else S.nbe_apps (MLname s) args
+  | CodegenThingol.IVarE(s,_) => S.nbe_apps (MLvname s) args
+  | CodegenThingol.IApp(e1,e2) => mk (args @ [mk [] e2]) e1
+  | CodegenThingol.IAbs(CodegenThingol.IVarE(nm,_), e) =>
+      S.nbe_apps (mk_nbeFUN(nm, mk [] e)) args
+  | _ => sys_error "NBE mkexpr"
+  in mk [] end;
+
+val mk_lexpr =
+  let fun mk args t = case t of
+    CodegenThingol.IConst((s,_),_) =>
+      S.app Eval_Constr (S.tup [S.quote s, S.list args])
+  | CodegenThingol.IVarE(s,_) => if args = [] then MLvname s else 
+      sys_error "NBE mk_lexpr illegal higher order pattern"
+  | CodegenThingol.IApp(e1,e2) => mk (args @ [mk [] e2]) e1
+  | _ => sys_error "NBE mk_lexpr illegal pattern"
+  in mk [] end;
+
+fun mk_eqn nm ar (lhs,e) = ([S.list(map mk_lexpr (rev lhs))], mk_rexpr nm ar e);
+
+fun funs_of_expr(CodegenThingol.IConst((s,_),_),fs) = s::fs
+  | funs_of_expr(CodegenThingol.IApp(e1,e2),fs) =
+      funs_of_expr(e1, funs_of_expr(e2, fs))
+  | funs_of_expr(CodegenThingol.IAbs(_, e),fs) = funs_of_expr(e,fs)
+  | funs_of_expr(_,fs) = fs;
+
+fun lookup nm =
+  S.Val (MLname nm) (S.app Eval_lookup (S.quote nm));
+
+fun export thy (nm,CodegenThingol.Fun(eqns,_)) =
+  let
+    val ar = length(fst(hd eqns));
+    val params = S.list (rev (MLparams ar));
+    val funs = Library.flat(map (fn (_,e) => funs_of_expr(e,[])) eqns) \ nm;
+    val globals = map lookup (filter_out is_constr funs);
+    val default_eqn = ([params], S.app Eval_Constr (S.tup[S.quote nm,params]));
+    val code = S.eqns (MLname nm) (map (mk_eqn nm ar) eqns @ [default_eqn])
+    val register = S.app Eval_register
+                     (S.tup[S.quote nm, MLname nm, string_of_int ar])
+  in
+    S.Let (globals @ [code]) register
+  end
+  | export _ _ = "\"NBE no op\"";
+
+val use_show = fn s => (writeln ("\n---generated code:\n"^ s);
+     use_text(writeln o enclose "\n---compiler echo:\n" "\n---\n",
+              writeln o enclose "\n--- compiler echo (with error!):\n" 
+                                "\n---\n")
+      true s);
+
+val dummyIT = CodegenThingol.IType("DUMMY",[]);
+
+use_show(export "" ("append",CodegenThingol.Fun(
+  [([CodegenThingol.IConst(("Nil",dummyIT),[]),
+     CodegenThingol.IVarE("ys",dummyIT)],
+    CodegenThingol.IVarE("ys",dummyIT)),
+   ([CodegenThingol.IApp(
+       CodegenThingol.IApp(
+         CodegenThingol.IConst(("Cons",dummyIT),[]),
+         CodegenThingol.IVarE("x",dummyIT)),
+       CodegenThingol.IVarE("xs",dummyIT)),
+     CodegenThingol.IVarE("ys",dummyIT)],
+    CodegenThingol.IApp(
+       CodegenThingol.IApp(
+         CodegenThingol.IConst(("Cons",dummyIT),[]),
+         CodegenThingol.IVarE("x",dummyIT)),
+       CodegenThingol.IApp(
+         CodegenThingol.IApp(
+           CodegenThingol.IConst(("append",dummyIT),[]),
+           CodegenThingol.IVarE("xs",dummyIT)),
+         CodegenThingol.IVarE("ys",dummyIT))))]
+  ,([],dummyIT))));
+
+
+let
+fun test a b = if a=b then () else error "oops!";
+
+val CNil = Const("Nil",dummyT);
+val CCons = Const("Cons",dummyT);
+val Cappend = Const("append",dummyT);
+val Fx = Free("x",dummyT);
+val Fy = Free("y",dummyT);
+val Fxs = Free("xs",dummyT);
+val Fys = Free("ys",dummyT);
+open NBE_Eval
+in
+test (nbe(CNil)) (C "Nil");
+test (nbe(CCons)) (C "Cons");
+test (nbe(Cappend)) (C "append");
+test (nbe(Cappend $ CNil $ CNil)) (C "Nil");
+test (nbe(Cappend $ Fxs)) (A (C "append", V "xs"));
+test (nbe(Cappend $ Fxs $ Fys)) (A (A (C "append", V "xs"), V "ys"));
+test (nbe(Cappend $ CNil $ Fxs)) (V "xs");
+test (nbe(Cappend $ (CCons $ Fx $ Fxs) $ Fys))
+     (A (A (C "Cons", V "x"), A (A (C "append", V "xs"), V "ys")));
+test (nbe(Cappend $ (CCons $ Fx $ Fxs) $ (CCons $ Fy $ Fys)))
+     (A (A (C "Cons", V "x"), A (A (C "append", V "xs"), A (A (C "Cons", V "y"), V "ys"))));
+test (nbe(Cappend $ (CCons $ Fx $ (CCons $ Fy $ Fxs)) $ (CCons$Fy$Fys)))
+ (A
+      (A (C "Cons", V "x"),
+         A
+            (A (C "Cons", V "y"),
+               A (A (C "append", V "xs"), A (A (C "Cons", V "y"), V "ys")))))
+
+end;
+
+
+use_show(export "" ("app",CodegenThingol.Fun(
+  [
+  ([CodegenThingol.IConst(("Nil",dummyIT),[])],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("ys",dummyIT),
+                        CodegenThingol.IVarE("ys",dummyIT))),
+
+   ([CodegenThingol.IApp(
+       CodegenThingol.IApp(
+         CodegenThingol.IConst(("Cons",dummyIT),[]),
+         CodegenThingol.IVarE("x",dummyIT)),
+       CodegenThingol.IVarE("xs",dummyIT))],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("ys",dummyIT),
+    CodegenThingol.IApp(
+       CodegenThingol.IApp(
+         CodegenThingol.IConst(("Cons",dummyIT),[]),
+         CodegenThingol.IVarE("x",dummyIT)),
+       CodegenThingol.IApp(
+         CodegenThingol.IApp(
+           CodegenThingol.IConst(("app",dummyIT),[]),
+           CodegenThingol.IVarE("xs",dummyIT)),
+         CodegenThingol.IVarE("ys",dummyIT)))))]
+  ,([],dummyIT))));
+
+
+let
+fun test a b = if a=b then () else error "oops!";
+
+val CNil = Const("Nil",dummyT);
+val CCons = Const("Cons",dummyT);
+val Cappend = Const("app",dummyT);
+val Fx = Free("x",dummyT);
+val Fy = Free("y",dummyT);
+val Fxs = Free("xs",dummyT);
+val Fys = Free("ys",dummyT);
+open NBE_Eval
+in
+test (nbe(CNil)) (C "Nil");
+test (nbe(CCons)) (C "Cons");
+test (nbe(Cappend)) (C "app");
+test (nbe(Cappend $ CNil)) (AbsN (1, B 1));
+test (nbe(Cappend $ CNil $ CNil)) (C "Nil");
+test (nbe(Cappend $ Fxs)) (A (C "app", V "xs"));
+test (nbe(Cappend $ Fxs $ Fys)) (A (A (C "app", V "xs"), V "ys"));
+test (nbe(Cappend $ CNil $ Fxs)) (V "xs");
+test (nbe(Cappend $ (CCons $ Fx $ Fxs) $ Fys))
+     (A (A (C "Cons", V "x"), A (A (C "app", V "xs"), V "ys")));
+test (nbe(Cappend $ (CCons $ Fx $ Fxs))) 
+   (AbsN (1, A (A (C "Cons", V "x"), A (A (C "app", V "xs"), B 1))));
+test (nbe(Cappend $ (CCons $ Fx $ Fxs) $ (CCons $ Fy $ Fys)))
+     (A (A (C "Cons", V "x"), A (A (C "app", V "xs"), A (A (C "Cons", V "y"), V "ys"))));
+test (nbe(Cappend $ (CCons $ Fx $ (CCons $ Fy $ Fxs)) $ (CCons$Fy$Fys)))
+ (A
+      (A (C "Cons", V "x"),
+         A
+            (A (C "Cons", V "y"),
+               A (A (C "app", V "xs"), A (A (C "Cons", V "y"), V "ys")))));
+()
+end;
+
+
+use_show(export "" ("add",CodegenThingol.Fun(
+  [
+  ([CodegenThingol.IConst(("0",dummyIT),[])],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("n",dummyIT),
+                        CodegenThingol.IVarE("n",dummyIT))),
+   ([CodegenThingol.IApp(
+       CodegenThingol.IConst(("S",dummyIT),[]),
+       CodegenThingol.IVarE("n",dummyIT))],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("m",dummyIT),
+      CodegenThingol.IApp(
+         CodegenThingol.IConst(("S",dummyIT),[]),
+           CodegenThingol.IApp(
+             CodegenThingol.IApp(
+               CodegenThingol.IConst(("add",dummyIT),[]),
+               CodegenThingol.IVarE("n",dummyIT)),
+             CodegenThingol.IVarE("m",dummyIT)))))]
+  ,([],dummyIT))));
+
+
+let
+fun test a b = if a=b then () else error "oops!";
+
+val C0 = Const("0",dummyT);
+val CS = Const("S",dummyT);
+val Cadd = Const("add",dummyT);
+val Fx = Free("x",dummyT);
+val Fy = Free("y",dummyT);
+open NBE_Eval
+in
+test (nbe(Cadd $ C0)) (AbsN (1, B 1));
+test (nbe(Cadd $ C0 $ C0)) (C "0");
+test (nbe(Cadd $ (CS $ (CS $ (CS $ Fx))) $ (CS $ (CS $ (CS $ Fy)))))
+(A (C "S", A (C "S", A (C "S", A (A (C "add", V "x"), A (C "S", A (C "S", A (C "S", V "y")))))))
+)
+end;
+
+
+
+use_show(export "" ("mul",CodegenThingol.Fun(
+  [
+  ([CodegenThingol.IConst(("0",dummyIT),[])],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("n",dummyIT),
+                        CodegenThingol.IConst(("0",dummyIT),[]))),
+   ([CodegenThingol.IApp(
+       CodegenThingol.IConst(("S",dummyIT),[]),
+       CodegenThingol.IVarE("n",dummyIT))],
+    CodegenThingol.IAbs(CodegenThingol.IVarE("m",dummyIT),
+      CodegenThingol.IApp(
+       CodegenThingol.IApp(
+         CodegenThingol.IConst(("add",dummyIT),[]),
+        CodegenThingol.IVarE("m",dummyIT)),
+           CodegenThingol.IApp(
+             CodegenThingol.IApp(
+               CodegenThingol.IConst(("mul",dummyIT),[]),
+               CodegenThingol.IVarE("n",dummyIT)),
+             CodegenThingol.IVarE("m",dummyIT)))))]
+  ,([],dummyIT))));
+
+
+let
+fun test a b = if a=b then () else error "oops!";
+
+val C0 = Const("0",dummyT);
+val CS = Const("S",dummyT);
+val Cmul = Const("mul",dummyT);
+val Fx = Free("x",dummyT);
+val Fy = Free("y",dummyT);
+open NBE_Eval
+in
+test (nbe(Cmul $ C0)) (AbsN (1, C "0"));
+test (nbe(Cmul $ C0 $ Fx)) (C "0");
+test (nbe(Cmul $ (CS $ (CS $ (CS $ C0)))))
+(AbsN (1, A (A (C "add", B 1), A (A (C "add", B 1), A (A (C "add", B 1), C "0")))));
+test (nbe(Cmul $ (CS $ (CS $ (CS $ Fx)))))
+ (AbsN (1, A (A (C "add", B 1), A (A (C "add", B 1),
+                     A (A (C "add", B 1), A (A (C "mul", V "x"), B 1))))));
+test (nbe(Cmul $ (CS $ (CS $ Fx)) $ (CS $ (CS $ (CS $ Fy)))))
+(A (C "S", A(C "S",A(C "S",A(A (C "add", V "y"),A(C "S",A(C "S",A(C "S",A
+ (A (C "add", V "y"),A(A (C "mul", V "x"),A(C "S",A(C "S",A(C "S", V"y")))))))))))));
+()
+end;
+
+
+
+
+
+(*
+  | export(nm,Fun(cls,(_,ty)))
+  | export _ = ""
+
+fun top_eval st thy =
+let val t = Sign.read_term thy st
+    val tabs = CodegenPackage.mk_tabs thy;
+    val thy') =
+      CodegenPackage.expand_module NONE (CodegenPackage.codegen_term
+         thy tabs [t]) thy
+    val s = map export diff
+in use s; (* FIXME val thy'' = thy' |> *)
+   Pretty.writeln (Sign.pretty_term thy t');
+   thy'
+end
+
+structure P = OuterParse;
+
+val nbeP =
+  OuterSyntax.command "norm_by_eval" "norm by eval" K.thy_decl
+    (P.term >> (Toplevel.theory o top_eval));
+
+val _ = OuterSyntax.add_parsers [nbeP];
+
+ProofGeneral.write_keywords "nbe";
+(* isar-keywords-nbe.el -> isabelle/etc/
+   Isabelle -k nbe *)
+*)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Pure/Tools/nbe_eval.ML	Tue Feb 21 16:18:50 2006 +0100
@@ -0,0 +1,151 @@
+(*  ID:         $Id$
+    Author:     Klaus Aehlig, Tobias Nipkow
+*)
+
+(* ------------------------------ Evaluator  ------------------------------ *)
+
+(* Optimizations:
+ int instead of string in Constr
+ xtab as real table
+ treat int via ML ints
+*)
+
+signature NBE_EVAL =
+sig
+(* named terms used for output only *)
+datatype nterm =
+    C of string (* Named Constants *)
+  | V of string (* Free Variable *)
+  | B of int (* Named(!!) Bound Variables *)
+  | A of nterm*nterm (* Application *)
+  | AbsN of int*nterm ; (* Binding of named Variables *)
+
+val nbe: term -> nterm
+
+datatype Univ = 
+    Constr of string*(Univ list)       (* Named Constructors *)
+  | Var of string*(Univ list)          (* Free variables *)
+  | BVar of int*(Univ list)            (* Bound named variables *)
+  | Fun of (Univ list -> Univ) * (Univ list) * int * (unit -> nterm);
+                                            (* Functions *)
+val apply: Univ -> Univ -> Univ;
+val to_term: Univ -> nterm;
+
+val lookup: string -> Univ;
+val is_constr: string -> bool;
+val register: string * (Univ list -> Univ) * int -> unit
+val new_name: unit -> int;
+
+(* For testing
+val eval: term -> (Univ list) -> Univ
+*)
+
+end;
+
+(* FIXME add_funs and relatives should NOT be exported. Eventually it
+should work like for quickcheck: Eval generates code which leaves the
+function values in a public reference variable, the code is executed,
+and the value of the reference variable is added to the internal
+tables, all in one go, w/o interruption. *)
+
+
+structure NBE_Eval:NBE_EVAL =
+struct
+
+datatype nterm =
+    C of string (* Named Constants *)
+  | V of string (* Free Variable *)
+  | B of int (* Named(!!) Variables *)
+  | A of nterm * nterm (* Application *)
+  | AbsN of int * nterm (* Binding of named Variables *);
+
+fun apps t args = foldr (fn (y,x) => A(x,y)) t args;
+
+(* ------------------------------ The semantical universe --------------------- *)
+
+(*
+   Functions are given by their semantical function value. To avoid
+   trouble with the ML-type system, these functions have the most
+   generic type, that is "Univ list -> Univ". The calling convention is
+   that the arguments come as a list, the last argument first. In
+   other words, a function call that usually would look like
+
+   f x_1 x_2 ... x_n   or   f(x_1,x_2, ..., x_n)
+
+   would be in our convention called as
+
+              f [x_n,..,x_2,x_1]
+
+   Moreover, to handle functions that are still waiting for some
+   arguments we have additionally a list of arguments collected to far
+   and the number of arguments we're still waiting for.
+
+   Finally, it might happen, that a function does not get all the
+   arguments it needs. In this case the function must provide means to
+   present itself as a string. As this might be a heavy-wight
+   operation, we delay it.
+*)
+
+datatype Univ = 
+    Constr of string*(Univ list)       (* Named Constructors *)
+  | Var of string*(Univ list)          (* Free variables *)
+  | BVar of int*(Univ list)         (* Bound named variables *)
+  | Fun of (Univ list -> Univ) * (Univ list) * int * (unit -> nterm);
+
+fun to_term (Constr(name, args))    = apps (C name) (map to_term args)
+  |	to_term (Var   (name, args))    = apps (V name) (map to_term args)
+  |	to_term (BVar  (name, args))    = apps (B name) (map to_term args)
+  |	to_term (Fun(_,args,_,name_thunk)) = apps (name_thunk ()) (map to_term args);
+
+(*
+   A typical operation, why functions might be good for, is
+   application. Moreover, functions are the only values that can
+   reasonably we applied in a semantical way.
+*)
+
+fun  apply (Fun(f,xs,1,name))  x = f (x::xs)
+   | apply (Fun(f,xs,n,name))  x = Fun(f,x::xs,n-1,name)
+   | apply (Constr(name,args)) x = Constr(name,x::args)
+   | apply (Var(name,args))    x = Var(name,x::args)
+   | apply (BVar(name,args))   x = BVar(name,x::args);
+
+(* ---------------- table with the meaning of constants -------------------- *)
+
+val xfun_tab: (string * Univ)list ref = ref [];
+fun do_register sx = (xfun_tab := sx :: !xfun_tab);
+fun register(name,v,0) = do_register(name,v [])
+  | register(name,v,n) = do_register(name,Fun(v,[],n, fn () => C name));
+
+fun do_lookup [] s          = NONE
+  |	do_lookup ((n,v)::xs) s = if (s=n) then SOME v else do_lookup xs s;
+
+fun lookup s = case do_lookup (!xfun_tab) s of
+    SOME x => x
+  | NONE   => Constr(s,[]);
+
+fun is_constr s = is_none (do_lookup (!xfun_tab) s);
+
+(* ------------------ evaluation with greetings to Tarski ------------------ *)
+
+(* generation of fresh names *)
+
+val counter = ref 0;
+
+fun new_name() = (counter := !counter +1; !counter);
+
+(* greetings to Tarski *)
+
+fun eval(Const(f,_)) xs = lookup f
+  | eval(Free(x,_)) xs = Var(x,[])
+  | eval(Bound n) xs = List.nth(xs,n)
+  | eval(s $ t) xs = apply (eval s xs) (eval t xs)
+  | eval(Abs(_,_, t)) xs = Fun ((fn [x] => eval t (x::xs)),[],1,
+                           fn () => let val var = new_name() in
+                         AbsN(var, to_term (eval t ((BVar(var,[]) :: xs)))) end);
+
+
+(* finally... *)
+
+fun nbe t = (counter :=0; to_term(eval t []));
+
+end;