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