src/Tools/Compute_Oracle/am_ghc.ML
author haftmann
Sat, 15 Sep 2007 19:27:35 +0200
changeset 24584 01e83ffa6c54
parent 24134 6e69e0031f34
child 25217 3224db6415ae
permissions -rw-r--r--
fixed title
haftmann@24584
     1
(*  Title:      Tools/Compute_Oracle/am_ghc.ML
obua@23663
     2
    ID:         $Id$
obua@23663
     3
    Author:     Steven Obua
obua@23663
     4
*)
obua@23663
     5
obua@23663
     6
structure AM_GHC : ABSTRACT_MACHINE = struct
obua@23663
     7
obua@23663
     8
open AbstractMachine;
obua@23663
     9
obua@23663
    10
type program = string * string * (int Inttab.table)
obua@23663
    11
obua@23663
    12
obua@23663
    13
(*Returns true iff at most 0 .. (free-1) occur unbound. therefore
obua@23663
    14
  check_freevars 0 t iff t is closed*)
obua@23663
    15
fun check_freevars free (Var x) = x < free
obua@23663
    16
  | check_freevars free (Const c) = true
obua@23663
    17
  | check_freevars free (App (u, v)) = check_freevars free u andalso check_freevars free v
obua@23663
    18
  | check_freevars free (Abs m) = check_freevars (free+1) m
obua@23663
    19
obua@23663
    20
fun count_patternvars PVar = 1
obua@23663
    21
  | count_patternvars (PConst (_, ps)) =
obua@23663
    22
      List.foldl (fn (p, count) => (count_patternvars p)+count) 0 ps
obua@23663
    23
obua@23663
    24
fun update_arity arity code a = 
obua@23663
    25
    (case Inttab.lookup arity code of
obua@23663
    26
	 NONE => Inttab.update_new (code, a) arity
wenzelm@24134
    27
       | SOME (a': int) => if a > a' then Inttab.update (code, a) arity else arity)
obua@23663
    28
obua@23663
    29
(* We have to find out the maximal arity of each constant *)
obua@23663
    30
fun collect_pattern_arity PVar arity = arity
obua@23663
    31
  | collect_pattern_arity (PConst (c, args)) arity = fold collect_pattern_arity args (update_arity arity c (length args))
obua@23663
    32
 
obua@23663
    33
local
obua@23663
    34
fun collect applevel (Var _) arity = arity
obua@23663
    35
  | collect applevel (Const c) arity = update_arity arity c applevel
obua@23663
    36
  | collect applevel (Abs m) arity = collect 0 m arity
obua@23663
    37
  | collect applevel (App (a,b)) arity = collect 0 b (collect (applevel + 1) a arity)
obua@23663
    38
in
obua@23663
    39
fun collect_term_arity t arity = collect 0 t arity
obua@23663
    40
end
obua@23663
    41
obua@23663
    42
fun nlift level n (Var m) = if m < level then Var m else Var (m+n) 
obua@23663
    43
  | nlift level n (Const c) = Const c
obua@23663
    44
  | nlift level n (App (a,b)) = App (nlift level n a, nlift level n b)
obua@23663
    45
  | nlift level n (Abs b) = Abs (nlift (level+1) n b)
obua@23663
    46
obua@23663
    47
fun rep n x = if n = 0 then [] else x::(rep (n-1) x)
obua@23663
    48
obua@23663
    49
fun adjust_rules rules =
obua@23663
    50
    let
obua@23663
    51
	val arity = fold (fn (p, t) => fn arity => collect_term_arity t (collect_pattern_arity p arity)) rules Inttab.empty
obua@23663
    52
	fun arity_of c = the (Inttab.lookup arity c)
obua@23663
    53
	fun adjust_pattern PVar = PVar
obua@23663
    54
	  | adjust_pattern (C as PConst (c, args)) = if (length args <> arity_of c) then raise Compile ("Constant inside pattern must have maximal arity") else C
obua@23663
    55
	fun adjust_rule (PVar, t) = raise Compile ("pattern may not be a variable")
obua@23663
    56
	  | adjust_rule (rule as (p as PConst (c, args),t)) = 
obua@23663
    57
	    let
obua@23663
    58
		val _ = if not (check_freevars (count_patternvars p) t) then raise Compile ("unbound variables on right hand side") else () 
obua@23663
    59
		val args = map adjust_pattern args	        
obua@23663
    60
		val len = length args
obua@23663
    61
		val arity = arity_of c
obua@23663
    62
		fun lift level n (Var m) = if m < level then Var m else Var (m+n) 
obua@23663
    63
		  | lift level n (Const c) = Const c
obua@23663
    64
		  | lift level n (App (a,b)) = App (lift level n a, lift level n b)
obua@23663
    65
		  | lift level n (Abs b) = Abs (lift (level+1) n b)
obua@23663
    66
		val lift = lift 0
obua@23663
    67
		fun adjust_term n t = if n=0 then t else adjust_term (n-1) (App (t, Var (n-1))) 
obua@23663
    68
	    in
obua@23663
    69
		if len = arity then
obua@23663
    70
		    rule
obua@23663
    71
		else if arity >= len then  
obua@23663
    72
		    (PConst (c, args @ (rep (arity-len) PVar)), adjust_term (arity-len) (lift (arity-len) t))
obua@23663
    73
		else (raise Compile "internal error in adjust_rule")
obua@23663
    74
	    end
obua@23663
    75
    in
obua@23663
    76
	(arity, map adjust_rule rules)
obua@23663
    77
    end		    
obua@23663
    78
obua@23663
    79
fun print_term arity_of n =
obua@23663
    80
let
obua@23663
    81
    fun str x = string_of_int x
obua@23663
    82
    fun protect_blank s = if exists_string Symbol.is_ascii_blank s then "(" ^ s ^")" else s
obua@23663
    83
											  
obua@23663
    84
    fun print_apps d f [] = f
obua@23663
    85
      | print_apps d f (a::args) = print_apps d ("app "^(protect_blank f)^" "^(protect_blank (print_term d a))) args
obua@23663
    86
    and print_call d (App (a, b)) args = print_call d a (b::args) 
obua@23663
    87
      | print_call d (Const c) args = 
obua@23663
    88
	(case arity_of c of 
obua@23663
    89
	     NONE => print_apps d ("Const "^(str c)) args 
obua@23663
    90
	   | SOME a =>
obua@23663
    91
	     let
obua@23663
    92
		 val len = length args
obua@23663
    93
	     in
obua@23663
    94
		 if a <= len then 
obua@23663
    95
		     let
obua@23663
    96
			 val s = "c"^(str c)^(concat (map (fn t => " "^(protect_blank (print_term d t))) (List.take (args, a))))
obua@23663
    97
		     in
obua@23663
    98
			 print_apps d s (List.drop (args, a))
obua@23663
    99
		     end
obua@23663
   100
		 else 
obua@23663
   101
		     let
obua@23663
   102
			 fun mk_apps n t = if n = 0 then t else mk_apps (n-1) (App (t, Var (n-1)))
obua@23663
   103
			 fun mk_lambdas n t = if n = 0 then t else mk_lambdas (n-1) (Abs t)
obua@23663
   104
			 fun append_args [] t = t
obua@23663
   105
			   | append_args (c::cs) t = append_args cs (App (t, c))
obua@23663
   106
		     in
obua@23663
   107
			 print_term d (mk_lambdas (a-len) (mk_apps (a-len) (nlift 0 (a-len) (append_args args (Const c)))))
obua@23663
   108
		     end
obua@23663
   109
	     end)
obua@23663
   110
      | print_call d t args = print_apps d (print_term d t) args
obua@23663
   111
    and print_term d (Var x) = if x < d then "b"^(str (d-x-1)) else "x"^(str (n-(x-d)-1))
obua@23663
   112
      | print_term d (Abs c) = "Abs (\\b"^(str d)^" -> "^(print_term (d + 1) c)^")"
obua@23663
   113
      | print_term d t = print_call d t []
obua@23663
   114
in
obua@23663
   115
    print_term 0 
obua@23663
   116
end
obua@23663
   117
			 			
obua@23663
   118
fun print_rule arity_of (p, t) = 
obua@23663
   119
    let	
obua@23663
   120
	fun str x = Int.toString x		    
obua@23663
   121
	fun print_pattern top n PVar = (n+1, "x"^(str n))
obua@23663
   122
	  | print_pattern top n (PConst (c, [])) = (n, (if top then "c" else "C")^(str c))
obua@23663
   123
	  | print_pattern top n (PConst (c, args)) = 
obua@23663
   124
	    let
obua@23663
   125
		val (n,s) = print_pattern_list (n, (if top then "c" else "C")^(str c)) args
obua@23663
   126
	    in
obua@23663
   127
		(n, if top then s else "("^s^")")
obua@23663
   128
	    end
obua@23663
   129
	and print_pattern_list r [] = r
obua@23663
   130
	  | print_pattern_list (n, p) (t::ts) = 
obua@23663
   131
	    let
obua@23663
   132
		val (n, t) = print_pattern false n t
obua@23663
   133
	    in
obua@23663
   134
		print_pattern_list (n, p^" "^t) ts
obua@23663
   135
	    end
obua@23663
   136
	val (n, pattern) = print_pattern true 0 p
obua@23663
   137
    in
obua@23663
   138
	pattern^" = "^(print_term arity_of n t)	
obua@23663
   139
    end
obua@23663
   140
obua@23663
   141
fun group_rules rules =
obua@23663
   142
    let
obua@23663
   143
	fun add_rule (r as (PConst (c,_), _)) groups =
obua@23663
   144
	    let
obua@23663
   145
		val rs = (case Inttab.lookup groups c of NONE => [] | SOME rs => rs)
obua@23663
   146
	    in
obua@23663
   147
		Inttab.update (c, r::rs) groups
obua@23663
   148
	    end
obua@23663
   149
	  | add_rule _ _ = raise Compile "internal error group_rules"
obua@23663
   150
    in
obua@23663
   151
	fold_rev add_rule rules Inttab.empty
obua@23663
   152
    end
obua@23663
   153
obua@23663
   154
fun haskell_prog name rules = 
obua@23663
   155
    let
obua@23663
   156
	val buffer = ref ""
obua@23663
   157
	fun write s = (buffer := (!buffer)^s)
obua@23663
   158
	fun writeln s = (write s; write "\n")
obua@23663
   159
	fun writelist [] = ()
obua@23663
   160
	  | writelist (s::ss) = (writeln s; writelist ss)
obua@23663
   161
	fun str i = Int.toString i
obua@23663
   162
	val (arity, rules) = adjust_rules rules
obua@23663
   163
	val rules = group_rules rules
obua@23663
   164
	val constants = Inttab.keys arity
obua@23663
   165
	fun arity_of c = Inttab.lookup arity c
obua@23663
   166
	fun rep_str s n = concat (rep n s)
obua@23663
   167
	fun indexed s n = s^(str n)
obua@23663
   168
	fun section n = if n = 0 then [] else (section (n-1))@[n-1]
obua@23663
   169
	fun make_show c = 
obua@23663
   170
	    let
obua@23663
   171
		val args = section (the (arity_of c))
obua@23663
   172
	    in
obua@23663
   173
		"  show ("^(indexed "C" c)^(concat (map (indexed " a") args))^") = "
obua@23663
   174
		^"\""^(indexed "C" c)^"\""^(concat (map (fn a => "++(show "^(indexed "a" a)^")") args))
obua@23663
   175
	    end
obua@23663
   176
	fun default_case c = 
obua@23663
   177
	    let
obua@23663
   178
		val args = concat (map (indexed " x") (section (the (arity_of c))))
obua@23663
   179
	    in
obua@23663
   180
		(indexed "c" c)^args^" = "^(indexed "C" c)^args
obua@23663
   181
	    end
obua@23663
   182
	val _ = writelist [        
obua@23663
   183
		"module "^name^" where",
obua@23663
   184
		"",
obua@23663
   185
		"data Term = Const Integer | App Term Term | Abs (Term -> Term)",
obua@23663
   186
		"         "^(concat (map (fn c => " | C"^(str c)^(rep_str " Term" (the (arity_of c)))) constants)),
obua@23663
   187
		"",
obua@23663
   188
		"instance Show Term where"]
obua@23663
   189
	val _ = writelist (map make_show constants)
obua@23663
   190
	val _ = writelist [
obua@23663
   191
		"  show (Const c) = \"c\"++(show c)",
obua@23663
   192
		"  show (App a b) = \"A\"++(show a)++(show b)",
obua@23663
   193
		"  show (Abs _) = \"L\"",
obua@23663
   194
		""]
obua@23663
   195
	val _ = writelist [
obua@23663
   196
		"app (Abs a) b = a b",
obua@23663
   197
		"app a b = App a b",
obua@23663
   198
		"",
obua@23663
   199
		"calc s c = writeFile s (show c)",
obua@23663
   200
		""]
obua@23663
   201
	fun list_group c = (writelist (case Inttab.lookup rules c of 
obua@23663
   202
					   NONE => [default_case c, ""] 
obua@23663
   203
					 | SOME (rs as ((PConst (_, []), _)::rs')) => 
obua@23663
   204
					   if not (null rs') then raise Compile "multiple declaration of constant"
obua@23663
   205
					   else (map (print_rule arity_of) rs) @ [""]
obua@23663
   206
					 | SOME rs => (map (print_rule arity_of) rs) @ [default_case c, ""]))
obua@23663
   207
	val _ = map list_group constants
obua@23663
   208
    in
obua@23663
   209
	(arity, !buffer)
obua@23663
   210
    end
obua@23663
   211
obua@23663
   212
val guid_counter = ref 0
obua@23663
   213
fun get_guid () = 
obua@23663
   214
    let
obua@23663
   215
	val c = !guid_counter
obua@23663
   216
	val _ = guid_counter := !guid_counter + 1
obua@23663
   217
    in
obua@23663
   218
	(LargeInt.toString (Time.toMicroseconds (Time.now ()))) ^ (string_of_int c)
obua@23663
   219
    end
obua@23663
   220
obua@23663
   221
fun tmp_file s = Path.implode (Path.expand (File.tmp_path (Path.make [s])));
obua@23663
   222
fun wrap s = "\""^s^"\""
obua@23663
   223
obua@23663
   224
fun writeTextFile name s = File.write (Path.explode name) s
obua@23663
   225
    
obua@23663
   226
val ghc = ref (case getenv "GHC_PATH" of "" => "ghc" | s => s)
obua@23663
   227
obua@23663
   228
fun fileExists name = ((OS.FileSys.fileSize name; true) handle OS.SysErr _ => false)
obua@23663
   229
obua@23663
   230
fun compile eqs = 
obua@23663
   231
    let
obua@23663
   232
	val _ = if exists (fn (a,b,c) => not (null a)) eqs then raise Compile ("cannot deal with guards") else ()
obua@23663
   233
	val eqs = map (fn (a,b,c) => (b,c)) eqs
obua@23663
   234
	val guid = get_guid ()
obua@23663
   235
	val module = "AMGHC_Prog_"^guid
obua@23663
   236
	val (arity, source) = haskell_prog module eqs
obua@23663
   237
	val module_file = tmp_file (module^".hs")
obua@23663
   238
	val object_file = tmp_file (module^".o")
obua@23663
   239
	val _ = writeTextFile module_file source
obua@23663
   240
	val _ = system ((!ghc)^" -c "^module_file)
obua@23663
   241
	val _ = if not (fileExists object_file) then raise Compile ("Failure compiling haskell code (GHC_PATH = '"^(!ghc)^"')") else ()
obua@23663
   242
    in
obua@23663
   243
	(guid, module_file, arity)	
obua@23663
   244
    end
obua@23663
   245
obua@23663
   246
fun readResultFile name = File.read (Path.explode name) 
obua@23663
   247
			  			  			      			    
obua@23663
   248
fun parse_result arity_of result =
obua@23663
   249
    let
obua@23663
   250
	val result = String.explode result
obua@23663
   251
	fun shift NONE x = SOME x
obua@23663
   252
	  | shift (SOME y) x = SOME (y*10 + x)
obua@23663
   253
	fun parse_int' x (#"0"::rest) = parse_int' (shift x 0) rest
obua@23663
   254
	  | parse_int' x (#"1"::rest) = parse_int' (shift x 1) rest
obua@23663
   255
	  | parse_int' x (#"2"::rest) = parse_int' (shift x 2) rest
obua@23663
   256
	  | parse_int' x (#"3"::rest) = parse_int' (shift x 3) rest
obua@23663
   257
	  | parse_int' x (#"4"::rest) = parse_int' (shift x 4) rest
obua@23663
   258
	  | parse_int' x (#"5"::rest) = parse_int' (shift x 5) rest
obua@23663
   259
	  | parse_int' x (#"6"::rest) = parse_int' (shift x 6) rest
obua@23663
   260
	  | parse_int' x (#"7"::rest) = parse_int' (shift x 7) rest
obua@23663
   261
	  | parse_int' x (#"8"::rest) = parse_int' (shift x 8) rest
obua@23663
   262
	  | parse_int' x (#"9"::rest) = parse_int' (shift x 9) rest
obua@23663
   263
	  | parse_int' x rest = (x, rest)
obua@23663
   264
	fun parse_int rest = parse_int' NONE rest
obua@23663
   265
obua@23663
   266
	fun parse (#"C"::rest) = 
obua@23663
   267
	    (case parse_int rest of 
obua@23663
   268
		 (SOME c, rest) => 
obua@23663
   269
		 let
obua@23663
   270
		     val (args, rest) = parse_list (the (arity_of c)) rest
obua@23663
   271
		     fun app_args [] t = t
obua@23663
   272
		       | app_args (x::xs) t = app_args xs (App (t, x))
obua@23663
   273
		 in
obua@23663
   274
		     (app_args args (Const c), rest)
obua@23663
   275
		 end		     
obua@23663
   276
	       | (NONE, rest) => raise Run "parse C")
obua@23663
   277
	  | parse (#"c"::rest) = 
obua@23663
   278
	    (case parse_int rest of
obua@23663
   279
		 (SOME c, rest) => (Const c, rest)
obua@23663
   280
	       | _ => raise Run "parse c")
obua@23663
   281
	  | parse (#"A"::rest) = 
obua@23663
   282
	    let
obua@23663
   283
		val (a, rest) = parse rest
obua@23663
   284
		val (b, rest) = parse rest
obua@23663
   285
	    in
obua@23663
   286
		(App (a,b), rest)
obua@23663
   287
	    end
obua@23663
   288
	  | parse (#"L"::rest) = raise Run "there may be no abstraction in the result"
obua@23663
   289
	  | parse _ = raise Run "invalid result"
obua@23663
   290
	and parse_list n rest = 
obua@23663
   291
	    if n = 0 then 
obua@23663
   292
		([], rest) 
obua@23663
   293
	    else 
obua@23663
   294
		let 
obua@23663
   295
		    val (x, rest) = parse rest
obua@23663
   296
		    val (xs, rest) = parse_list (n-1) rest
obua@23663
   297
		in
obua@23663
   298
		    (x::xs, rest)
obua@23663
   299
		end
obua@23663
   300
	val (parsed, rest) = parse result
obua@23663
   301
	fun is_blank (#" "::rest) = is_blank rest
obua@23663
   302
	  | is_blank (#"\n"::rest) = is_blank rest
obua@23663
   303
	  | is_blank [] = true
obua@23663
   304
	  | is_blank _ = false
obua@23663
   305
    in
obua@23663
   306
	if is_blank rest then parsed else raise Run "non-blank suffix in result file"	
obua@23663
   307
    end
obua@23663
   308
obua@23663
   309
fun run (guid, module_file, arity) t = 
obua@23663
   310
    let
obua@23663
   311
	val _ = if check_freevars 0 t then () else raise Run ("can only compute closed terms")
obua@23663
   312
	fun arity_of c = Inttab.lookup arity c			 
obua@23663
   313
	val callguid = get_guid()
obua@23663
   314
	val module = "AMGHC_Prog_"^guid
obua@23663
   315
	val call = module^"_Call_"^callguid
obua@23663
   316
	val result_file = tmp_file (module^"_Result_"^callguid^".txt")
obua@23663
   317
	val call_file = tmp_file (call^".hs")
obua@23663
   318
	val term = print_term arity_of 0 t
obua@23663
   319
	val call_source = "module "^call^" where\n\nimport "^module^"\n\ncall = "^module^".calc \""^result_file^"\" ("^term^")"
obua@23663
   320
	val _ = writeTextFile call_file call_source
obua@23663
   321
	val _ = system ((!ghc)^" -e \""^call^".call\" "^module_file^" "^call_file)
obua@23663
   322
	val result = readResultFile result_file handle IO.Io _ => raise Run ("Failure running haskell compiler (GHC_PATH = '"^(!ghc)^"')")
obua@23663
   323
	val t' = parse_result arity_of result
obua@23663
   324
	val _ = OS.FileSys.remove call_file
obua@23663
   325
	val _ = OS.FileSys.remove result_file
obua@23663
   326
    in
obua@23663
   327
	t'
obua@23663
   328
    end
obua@23663
   329
obua@23663
   330
	
obua@23663
   331
fun discard _ = ()
obua@23663
   332
		 	  
obua@23663
   333
end
obua@23663
   334