src/Tools/code/code_thingol.ML
author haftmann
Fri, 10 Aug 2007 17:04:34 +0200
changeset 24219 e558fe311376
child 24252 4eb5bc6af008
permissions -rw-r--r--
new structure for code generator modules

(*  Title:      Tools/code/code_thingol.ML
    ID:         $Id$
    Author:     Florian Haftmann, TU Muenchen

Intermediate language ("Thin-gol") representing executable code.
*)

infix 8 `%%;
infixr 6 `->;
infixr 6 `-->;
infix 4 `$;
infix 4 `$$;
infixr 3 `|->;
infixr 3 `|-->;

signature BASIC_CODE_THINGOL =
sig
  type vname = string;
  datatype dict =
      DictConst of string * dict list list
    | DictVar of string list * (vname * (int * int));
  datatype itype =
      `%% of string * itype list
    | ITyVar of vname;
  datatype iterm =
      IConst of string * (dict list list * itype list (*types of arguments*))
    | IVar of vname
    | `$ of iterm * iterm
    | `|-> of (vname * itype) * iterm
    | ICase of ((iterm * itype) * (iterm * iterm) list) * iterm;
        (*((term, type), [(selector pattern, body term )]), primitive term)*)
  val `-> : itype * itype -> itype;
  val `--> : itype list * itype -> itype;
  val `$$ : iterm * iterm list -> iterm;
  val `|--> : (vname * itype) list * iterm -> iterm;
  type typscheme = (vname * sort) list * itype;
end;

signature CODE_THINGOL =
sig
  include BASIC_CODE_THINGOL;
  val unfoldl: ('a -> ('a * 'b) option) -> 'a -> 'a * 'b list;
  val unfoldr: ('a -> ('b * 'a) option) -> 'a -> 'b list * 'a;
  val unfold_fun: itype -> itype list * itype;
  val unfold_app: iterm -> iterm * iterm list;
  val split_abs: iterm -> (((vname * iterm option) * itype) * iterm) option;
  val unfold_abs: iterm -> ((vname * iterm option) * itype) list * iterm;
  val split_let: iterm -> (((iterm * itype) * iterm) * iterm) option;
  val unfold_let: iterm -> ((iterm * itype) * iterm) list * iterm;
  val unfold_const_app: iterm ->
    ((string * (dict list list * itype list)) * iterm list) option;
  val collapse_let: ((vname * itype) * iterm) * iterm
    -> (iterm * itype) * (iterm * iterm) list;
  val eta_expand: (string * (dict list list * itype list)) * iterm list -> int -> iterm;
  val fold_constnames: (string -> 'a -> 'a) -> iterm -> 'a -> 'a;
  val fold_varnames: (string -> 'a -> 'a) -> iterm -> 'a -> 'a;
  val fold_unbound_varnames: (string -> 'a -> 'a) -> iterm -> 'a -> 'a;

  datatype def =
      Bot
    | Fun of (iterm list * iterm) list * typscheme
    | Datatype of (vname * sort) list * (string * itype list) list
    | Datatypecons of string
    | Class of (class * string) list * (vname * (string * itype) list)
    | Classop of class
    | Classrel of class * class
    | Classinst of (class * (string * (vname * sort) list))
          * ((class * (string * (string * dict list list))) list
        * (string * iterm) list);
  type code = def Graph.T;
  type transact;
  val empty_code: code;
  val merge_code: code * code -> code;
  val project_code: bool (*delete empty funs*)
    -> string list (*hidden*) -> string list option (*selected*)
    -> code -> code;
  val empty_funs: code -> string list;
  val is_cons: code -> string -> bool;
  val add_eval_def: string (*bind name*) * iterm -> code -> code;

  val ensure_def: (string -> string) -> (transact -> def * code) -> string
    -> string -> transact -> transact;
  val succeed: 'a -> transact -> 'a * code;
  val fail: string -> transact -> 'a * code;
  val message: string -> (transact -> 'a) -> transact -> 'a;
  val start_transact: (transact -> 'a * transact) -> code -> 'a * code;
end;

structure CodeThingol: CODE_THINGOL =
struct

(** auxiliary **)

fun unfoldl dest x =
  case dest x
   of NONE => (x, [])
    | SOME (x1, x2) =>
        let val (x', xs') = unfoldl dest x1 in (x', xs' @ [x2]) end;

fun unfoldr dest x =
  case dest x
   of NONE => ([], x)
    | SOME (x1, x2) =>
        let val (xs', x') = unfoldr dest x2 in (x1::xs', x') end;


(** language core - types, pattern, expressions **)

(* language representation *)

type vname = string;

datatype dict =
    DictConst of string * dict list list
  | DictVar of string list * (vname * (int * int));

datatype itype =
    `%% of string * itype list
  | ITyVar of vname;

datatype iterm =
    IConst of string * (dict list list * itype list)
  | IVar of vname
  | `$ of iterm * iterm
  | `|-> of (vname * itype) * iterm
  | ICase of ((iterm * itype) * (iterm * iterm) list) * iterm;
    (*see also signature*)

(*
  variable naming conventions

  bare names:
    variable names          v
    class names             class
    type constructor names  tyco
    datatype names          dtco
    const names (general)   c
    constructor names       co
    class operation names   clsop (op)
    arbitrary name          s

    v, c, co, clsop also annotated with types etc.

  constructs:
    sort                    sort
    type parameters         vs
    type                    ty
    type schemes            tysm
    term                    t
    (term as pattern)       p
    instance (class, tyco)  inst
 *)

fun ty1 `-> ty2 = "fun" `%% [ty1, ty2];
val op `--> = Library.foldr (op `->);
val op `$$ = Library.foldl (op `$);
val op `|--> = Library.foldr (op `|->);

val unfold_fun = unfoldr
  (fn "fun" `%% [ty1, ty2] => SOME (ty1, ty2)
    | _ => NONE);

val unfold_app = unfoldl
  (fn op `$ t => SOME t
    | _ => NONE);

val split_abs =
  (fn (v, ty) `|-> (t as ICase (((IVar w, _), [(p, t')]), _)) =>
        if v = w then SOME (((v, SOME p), ty), t') else SOME (((v, NONE), ty), t)
    | (v, ty) `|-> t => SOME (((v, NONE), ty), t)
    | _ => NONE);

val unfold_abs = unfoldr split_abs;

val split_let = 
  (fn ICase (((td, ty), [(p, t)]), _) => SOME (((p, ty), td), t)
    | _ => NONE);

val unfold_let = unfoldr split_let;

fun unfold_const_app t =
 case unfold_app t
  of (IConst c, ts) => SOME (c, ts)
   | _ => NONE;

fun fold_aiterms f (t as IConst _) = f t
  | fold_aiterms f (t as IVar _) = f t
  | fold_aiterms f (t1 `$ t2) = fold_aiterms f t1 #> fold_aiterms f t2
  | fold_aiterms f (t as _ `|-> t') = f t #> fold_aiterms f t'
  | fold_aiterms f (ICase (_, t)) = fold_aiterms f t;

fun fold_constnames f =
  let
    fun add (IConst (c, _)) = f c
      | add _ = I;
  in fold_aiterms add end;

fun fold_varnames f =
  let
    fun add (IVar v) = f v
      | add ((v, _) `|-> _) = f v
      | add _ = I;
  in fold_aiterms add end;

fun fold_unbound_varnames f =
  let
    fun add _ (IConst _) = I
      | add vs (IVar v) = if not (member (op =) vs v) then f v else I
      | add vs (t1 `$ t2) = add vs t1 #> add vs t2
      | add vs ((v, _) `|-> t) = add (insert (op =) v vs) t
      | add vs (ICase (_, t)) = add vs t;
  in add [] end;

fun collapse_let (((v, ty), se), be as ICase (((IVar w, _), ds), _)) =
      let
        fun exists_v t = fold_unbound_varnames (fn w => fn b =>
          b orelse v = w) t false;
      in if v = w andalso forall (fn (t1, t2) =>
        exists_v t1 orelse not (exists_v t2)) ds
        then ((se, ty), ds)
        else ((se, ty), [(IVar v, be)])
      end
  | collapse_let (((v, ty), se), be) =
      ((se, ty), [(IVar v, be)])

fun eta_expand (c as (_, (_, tys)), ts) k =
  let
    val j = length ts;
    val l = k - j;
    val ctxt = (fold o fold_varnames) Name.declare ts Name.context;
    val vs_tys = Name.names ctxt "a" ((curry Library.take l o curry Library.drop j) tys);
  in vs_tys `|--> IConst c `$$ ts @ map (fn (v, _) => IVar v) vs_tys end;


(** definitions, transactions **)

(* type definitions *)

type typscheme = (vname * sort) list * itype;
datatype def =
    Bot
  | Fun of (iterm list * iterm) list * typscheme
  | Datatype of (vname * sort) list * (string * itype list) list
  | Datatypecons of string
  | Class of (class * string) list * (vname * (string * itype) list)
  | Classop of class
  | Classrel of class * class
  | Classinst of (class * (string * (vname * sort) list))
        * ((class * (string * (string * dict list list))) list
      * (string * iterm) list);

type code = def Graph.T;
type transact = Graph.key option * code;
exception FAIL of string list;


(* abstract code *)

val empty_code = Graph.empty : code; (*read: "depends on"*)

fun ensure_bot name = Graph.default_node (name, Bot);

fun add_def_incr (name, Bot) code =
      (case the_default Bot (try (Graph.get_node code) name)
       of Bot => error "Attempted to add Bot to code"
        | _ => code)
  | add_def_incr (name, def) code =
      (case try (Graph.get_node code) name
       of NONE => Graph.new_node (name, def) code
        | SOME Bot => Graph.map_node name (K def) code
        | SOME _ => error ("Tried to overwrite definition " ^ quote name));

fun add_dep (dep as (name1, name2)) =
  if name1 = name2 then I else Graph.add_edge dep;

val merge_code : code * code -> code = Graph.merge (K true);

fun project_code delete_empty_funs hidden raw_selected code =
  let
    fun is_empty_fun name = case Graph.get_node code name
     of Fun ([], _) => true
      | _ => false;
    val names = subtract (op =) hidden (Graph.keys code);
    val deleted = Graph.all_preds code (filter is_empty_fun names);
    val selected = case raw_selected
     of NONE => names |> subtract (op =) deleted 
      | SOME sel => sel
          |> delete_empty_funs ? subtract (op =) deleted
          |> subtract (op =) hidden
          |> Graph.all_succs code
          |> delete_empty_funs ? subtract (op =) deleted
          |> subtract (op =) hidden;
  in
    code
    |> Graph.subgraph (member (op =) selected)
  end;

fun empty_funs code =
  Graph.fold (fn (name, (Fun ([], _), _)) => cons name
               | _ => I) code [];

fun is_cons code name = case Graph.get_node code name
 of Datatypecons _ => true
  | _ => false;

fun check_samemodule names =
  fold (fn name =>
    let
      val module_name = (NameSpace.qualifier o NameSpace.qualifier) name
    in
     fn NONE => SOME module_name
      | SOME module_name' => if module_name = module_name' then SOME module_name
          else error ("Inconsistent name prefix for simultanous names: " ^ commas_quote names)
    end
  ) names NONE;

fun check_funeqs eqs =
  (fold (fn (pats, _) =>
    let
      val l = length pats
    in
     fn NONE => SOME l
      | SOME l' => if l = l' then SOME l
          else error "Function definition with different number of arguments"
    end
  ) eqs NONE; eqs);

fun check_prep_def code Bot =
      Bot
  | check_prep_def code (Fun (eqs, d)) =
      Fun (check_funeqs eqs, d)
  | check_prep_def code (d as Datatype _) =
      d
  | check_prep_def code (Datatypecons dtco) =
      error "Attempted to add bare term constructor"
  | check_prep_def code (d as Class _) =
      d
  | check_prep_def code (Classop _) =
      error "Attempted to add bare class operation"
  | check_prep_def code (Classrel _) =
      error "Attempted to add bare class relation"
  | check_prep_def code (d as Classinst ((class, (tyco, arity)), (_, inst_classops))) =
      let
        val Class (_, (_, classops)) = Graph.get_node code class;
        val _ = if length inst_classops > length classops
          then error "Too many class operations given"
          else ();
        fun check_classop (f, _) =
          if AList.defined (op =) inst_classops f
          then () else error ("Missing definition for class operation " ^ quote f);
        val _ = map check_classop classops;
      in d end;

fun postprocess_def (name, Datatype (_, constrs)) =
      tap (fn _ => check_samemodule (name :: map fst constrs))
      #> fold (fn (co, _) =>
        add_def_incr (co, Datatypecons name)
        #> add_dep (co, name)
        #> add_dep (name, co)
      ) constrs
  | postprocess_def (name, Class (classrels, (_, classops))) =
      tap (fn _ => check_samemodule (name :: map fst classops @ map snd classrels))
      #> fold (fn (f, _) =>
        add_def_incr (f, Classop name)
        #> add_dep (f, name)
        #> add_dep (name, f)
      ) classops
      #> fold (fn (superclass, classrel) =>
        add_def_incr (classrel, Classrel (name, superclass))
        #> add_dep (classrel, name)
        #> add_dep (name, classrel)
      ) classrels
  | postprocess_def _ =
      I;


(* transaction protocol *)

fun ensure_def labelled_name defgen msg name (dep, code) =
  let
    val msg' = (case dep
     of NONE => msg
      | SOME dep => msg ^ ", required for " ^ labelled_name dep);
    fun add_dp NONE = I
      | add_dp (SOME dep) = add_dep (dep, name);
    fun prep_def def code =
      (check_prep_def code def, code);
    fun invoke_generator name defgen code =
      defgen (SOME name, code) handle FAIL msgs => raise FAIL (msg' :: msgs);
    fun add_def false =
          ensure_bot name
          #> add_dp dep
          #> invoke_generator name defgen
          #-> (fn def => prep_def def)
          #-> (fn def => add_def_incr (name, def)
          #> postprocess_def (name, def))
      | add_def true =
          add_dp dep;
  in
    code
    |> add_def (can (Graph.get_node code) name)
    |> pair dep
  end;

fun succeed some (_, code) = (some, code);

fun fail msg (_, code) = raise FAIL [msg];

fun message msg f trns =
  f trns handle FAIL msgs =>
    raise FAIL (msg :: msgs);

fun start_transact f code =
  let
    fun handle_fail f x =
      (f x
      handle FAIL msgs =>
        (error o cat_lines) ("Code generation failed, while:" :: msgs))
  in
    (NONE, code)
    |> handle_fail f
    |-> (fn x => fn (_, code) => (x, code))
  end;

fun add_eval_def (name, t) code =
  code
  |> Graph.new_node (name, Fun ([([], t)], ([("_", [])], ITyVar "_")))
  |> fold (curry Graph.add_edge name) (Graph.keys code);

end; (*struct*)


structure BasicCodeThingol: BASIC_CODE_THINGOL = CodeThingol;