Home My Page Projects Code Snippets Project Openings 3D graphics for Standard ML
Summary Activity SCM

SCM Repository

[sml3d] View of /trunk/sml3d/gen/gen-from-xml/gldb/db.sml
ViewVC logotype

View of /trunk/sml3d/gen/gen-from-xml/gldb/db.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1413 - (download) (annotate)
Mon Apr 14 22:48:52 2014 UTC (4 years, 11 months ago) by jhr
File size: 17077 byte(s)
  added more ML type info; constrain GL_TRUE and GL_FALSE to have GLboolean type.
(* db.sml
 *
 * COPYRIGHT (c) 2014 The SML3d Project (http://sml3d.cs.uchicago.edu)
 * All rights reserved.
 *
 * We should use the SpecLoader.registry representation for the database.  Perhaps it will
 * need to be made closer to the DBXML representation, but there is no reason for yet another
 * representation of basically the same information!
 *)

structure DB : sig

    type db

    val toXML : db -> DBXMLRep.db
    val fromXML : DBXMLRep.db -> db

(* NOTE: probably don't need this function *)
  (* load the specifications for the database into the database.  If the database is not
   * empty, then the Fail exception is raised.
   *)
    val load : {
	  regFile : string,
	  api : string,		(* api to filter the registry *)
	  profile : string,	(* profile to filter the registry *)
	  extensions : string	(* string that is matched against "supported" attribute *)
	} -> db

  (* extend the first database with any additional definitions, etc. that are provided by
   * the second database.
   *)
    val extend : db * db -> unit

  end = struct

    structure X = DBXMLRep
    structure ATbl = AtomTable
    structure AMap = AtomMap
    structure ASet = AtomSet

    datatype db = DB of {
        registry : string,       	(* specification file pathname *)
	api : string,
	profile : string,
	extensions : string,
	types : X.ty AtomTable.hash_table,
	enums : X.enum AtomTable.hash_table,
	groups : X.enum_grp AtomTable.hash_table,
	commands : X.cmd AtomTable.hash_table,
	features : X.feature AtomTable.hash_table
      }

  (* check to see if a database is empty *)
    fun isEmpty (DB{types, enums, commands, ...}) =
          (ATbl.numItems types = 0) andalso (ATbl.numItems enums = 0)
            andalso (ATbl.numItems commands = 0)

  (* comparison function on atoms that we can use with ListMergeSort.sort to sort in ascending
   * lexical order.
   *)
    fun atomGT (a, b) = (case Atom.lexCompare(a, b)
           of GREATER => true
            | _ => false
          (* end case *))

    fun sortItems keyItemPairList = let
          fun gt ((key1, _), (key2, _)) = atomGT(key1, key2)
          in
            List.map #2 (ListMergeSort.sort gt keyItemPairList)
          end

    fun sortTableItems tbl = sortItems (ATbl.listItemsi tbl)

    fun toXML (DB content) = X.DB{
	    registry = #registry content,
	    api = #api content,
	    profile = #profile content,
	    extensions = #extensions content,
	    types = sortTableItems(#types content),
	    enums = sortTableItems(#enums content),
	    commands = sortTableItems(#commands content),
	    groups = sortTableItems(#groups content),
	    features = sortTableItems(#features content)
          }

    fun fromXML (X.DB content) = let
          val enums = ATbl.mkTable (List.length(#enums content), Fail "enums")
          val insEnum = ATbl.insert enums
          val types = ATbl.mkTable (List.length(#types content), Fail "types")
          val insTy = ATbl.insert types
          val commands = ATbl.mkTable (List.length(#commands content), Fail "functions")
          val insCmd = ATbl.insert commands
	  val groups = ATbl.mkTable (List.length(#groups content), Fail "groups")
	  val insGrp = ATbl.insert groups
	  val features = ATbl.mkTable (List.length(#features content), Fail "features")
	  val insFeature = ATbl.insert features
          in
          (* initialize tables *)
            List.app (fn (ty as {name, def}) => insTy(name, ty)) (#types content);
            List.app (fn (enum as {name, ty, value}) => insEnum(name, enum)) (#enums content);
	    List.app (fn (cmd as X.Cmd{name, ...}) => insCmd(name, cmd)) (#commands content);
	    List.app (fn (grp as X.EnumGrp{name, ...}) => insGrp(name, grp)) (#groups content);
	    List.app (fn (f as X.Feature{name, ...}) => insFeature(name, f)) (#features content);
            DB{
		registry = #registry content,
		api = #api content,
		profile = #profile content,
		extensions = #extensions content,
		types = types,
		enums = enums,
		commands = commands,
		groups = groups,
		features = features
              }
          end

(* obsolete
  (* create a new empty database by cloning the meta information from another database *)
    fun clone (DB content) = DB{
	    registry = #registry content,
	    api = #api content,
	    profile = #profile content,
	    extensions = #extensions content,
            types = ATbl.mkTable (ATbl.numItems(#types content), Fail "types"),
            enums = ATbl.mkTable (ATbl.numItems(#enums content), Fail "enums"),
            commands = ATbl.mkTable (ATbl.numItems(#commands content), Fail "commands"),
            groups = ATbl.mkTable (ATbl.numItems(#groups content), Fail "groups"),
	    features = ATbl.mkTable (ATbl.numItems(#features content), Fail "features")
          }
*)

  (* load the specifications for the database into the database. *)
    fun load {regFile, api, profile, extensions} = let
	(* load and check specification *)
	  val registry = SpecLoader.load{
		  regFile = regFile,
		  api = SOME api,
		  profile = SOME profile,
		  extensions = SOME extensions
		}
	  in
	    DB{
		registry = regFile,
		api = api,
		profile = profile,
		extensions = extensions,
		types = #types registry,
		enums = #enums registry,
		groups = #groups registry,
		commands = #commands registry,
		features = let
		  val tbl = ATbl.mkTable (List.length (#features registry), Fail "features")
		  val ins = ATbl.insert tbl
		  in
		    List.app (fn (f as X.Feature{name, ...}) => ins(name, f)) (#features registry);
		    tbl
		  end
	      }
	  end

    fun conflict msg = raise Fail(concat msg)

 (* extend the type table by the constants in ext *)
    fun extendTypes (base, ext) = let
          val findInBase = ATbl.find base
          val insertInBase = ATbl.insert base
          fun extend (ty as {name, def}) = (case findInBase name
                 of SOME ty' => if not (CType.same(def, #def ty'))
                      then conflict[
                          "type ", Atom.toString name, " definition conflict"
                        ]
                      else ()
                  | NONE => insertInBase (name, ty)
                (* end case *))
          in
            ATbl.app extend ext
          end

 (* extend the enum table base by the enums in ext *)
    fun extendEnums (base : X.enum AtomTable.hash_table, ext) = let
          val findInBase = ATbl.find base
          val insertInBase = ATbl.insert base
	  fun extend (enum as {name, ty, value}) = (case findInBase name
		 of NONE => insertInBase(name, enum)
		  | SOME{ty=ty', value=value', ...} => let
		      val tyConflict = (case (ty, ty')
			     of (SOME ty, SOME ty') => not(CType.same(ty, ty'))
			      | (SOME ty, NONE) => ((* update type info *)
				  insertInBase(name, {name=name, ty=SOME ty, value=value});
				  false)
			      | (NONE, NONE) => false
			      | _ => true
			    (* end case *))
		      in
			if tyConflict orelse (value <> value')
			  then conflict[
			      "enum ", Atom.toString name, " value conflict"
			    ]
			  else ()
		      end
		(* end case *))
	  in
(* do we want to check for enums that have been removed? *)
            ATbl.app extend ext
	  end

  (* extend the command table by the definitions in ext *)
    fun extendCommands (base, ext) = let
          val findInBase = ATbl.find base
          val insertInBase = ATbl.insert base
	  fun mkProtoMap protos = let
		fun ins (proto as X.Proto{name, ...}, m) = AtomMap.insert(m, name, proto)
		in
		  List.foldl ins AtomMap.empty protos
		end
	  fun extend (cmd as X.Cmd{name, remove, protos}) = (
		case findInBase name
		 of SOME(X.Cmd{protos=protos', ...}) => let
(* FIXME: extend remove *)
		    (* we need to add any new prototypes into the existing list.  We also
		     * need to add mltype attributes, when they are provided by the extension,
		     * but not the base.
		     *)
		      val baseProtos = mkProtoMap protos'
		      val extProtos = mkProtoMap protos
		      fun merge (X.Proto baseProto, X.Proto extProto) = let
			    fun mergeOpts (NONE, SOME x) = SOME x
			      | mergeOpts (opt, _) = opt
			  (* extend the base return type with any new ML type info *)
			    val retTy = {
				    cty = #cty(#retTy baseProto),
				    mlty = mergeOpts (#mlty(#retTy baseProto), #mlty(#retTy extProto))
				  }
			  (* extend parameter types with any new ML type info *)
			    fun extParam (X.Param baseParam, X.Param extParam) = X.Param{
				    name = #name baseParam,
				    cty = #cty baseParam,
				    group = mergeOpts(#group baseParam, #group extParam),
				    mlty = mergeOpts(#mlty baseParam, #mlty extParam)
				  }
			    in
			      X.Proto{
				  name = #name baseProto,
				  retTy = retTy,
				  params = ListPair.mapEq extParam (#params baseProto, #params extProto)
				}
			    end
(* NOTE: we may want to add a consistency check for the overlapping definitions! *)
		    (* merge and sort the prototypes *)
		      val protos = sortItems (
			    AtomMap.listItemsi (
			      AtomMap.unionWith merge (baseProtos, extProtos)))
		      in
			insertInBase(name, X.Cmd{name=name, remove=remove, protos=protos})
		      end
		  | NONE => insertInBase(name, cmd)
		(* end case *))
	  in
            ATbl.app extend ext
	  end

 (* extend the enum-group table base by the constants in ext *)
    fun extendGroups (base, ext) = let
          val findInBase = ATbl.find base
          val insertInBase = ATbl.insert base
          fun extend (grp as X.EnumGrp{name=grpName, bitmask, consts}) = (
                case findInBase grpName
                 of SOME(X.EnumGrp{bitmask=b, consts=baseConsts, ...}) =>
                      if (bitmask <> b)
                        then conflict[
                            "enum group ", Atom.toString grpName, " bitmask conflict"
                          ]
                        else let
                        (* construct a set of the constants in base *)
                          val cSet = ASet.fromList baseConsts
                          fun match name = not(ASet.member(cSet, name))
                          in
                            case List.filter match consts
                             of [] => ()
                              | cs => insertInBase (grpName, X.EnumGrp{
                                    name = grpName,
                                    bitmask = bitmask,
                                    consts = baseConsts @ cs
                                  })
                            (* end case *)
                          end
                  | NONE => (* add the group to the base *)
                      insertInBase (grpName, grp)
                (* end case *))
          in
            ATbl.app extend ext
          end

  (* extend the list of features base by the list ext *)
    fun extendFeatures (base, ext) = let
          val findInBase = ATbl.find base
          val insertInBase = ATbl.insert base
	(* we extend the feature list by either extending a feature or by adding a new
	 * feature to the list.
	 *)
	  fun extend (f as X.Feature{name, version, types, enums, commands}) = (
		case findInBase name
		 of SOME(X.Feature{version=v', types=tys', enums=enums', commands=cmds', ...}) => let
		      fun extend' (base, ext) = let
			    val set = AtomSet.fromList base
			    in
			      List.filter (fn x => not(AtomSet.member(set, x))) ext
			    end
		      in
			if (version <> v')
			  then conflict["feature ", Atom.toString name, " has conflicting versions"]
			  else ();
			case (extend'(types, tys'), extend'(enums, enums'), extend'(commands, cmds'))
			 of ([], [], []) => () (* no change *)
			  | (newTys, newEnums, newCmds) => insertInBase (name, X.Feature{
				name = name, version = version,
				types = types @ newTys,
				enums = enums @ newEnums,
				commands = commands @ newCmds
			      })
			(* end case *)
		      end
		  | NONE => insertInBase (name, f)
		(* end case *))
	  in
	    ATbl.app extend ext
	  end

(*
          fun extend (cat as X.Category{name=catName, functs}) = (
                case findInBase catName
                 of SOME(X.Category{functs=baseFuns, ...}) => let
                    (* construct a map of the base functions *)
                      val baseMap = mkFunMap baseFuns
                      fun inBase (X.Fun{name, ...}) = AMap.inDomain(baseMap, name)
                    (* partition the extension functions into those that are possible updates
                     * of the functions in the base and those that are new.
                     *)
                      val (updateFns, newFns) = let
                            val (ufs, nfs) = List.partition inBase functs
                            val uMap = mkFunMap ufs
                            in
                              (uMap, nfs)
                            end
                    (* merge a base and extension version of an optional field *)
                      fun merge (name, fld, base, ext) = (case (base, ext)
                             of (NONE, _) => ext
                              | (SOME _, NONE) => base
                              | (SOME v1, SOME v2) =>
                                  if (v1 <> v2)
                                    then conflict[
                                        "function ", Atom.toString name, ": ",
                                        fld, " field has conflicting values"
                                      ]
                                    else ext
                            (* end case *))
                    (* check to see if a base function's definition is modified
                     * by the version in the extension.  We also make sure that alias
                     * functions are consistent with the canonical definition.
                     *)
                      fun update (baseFn as X.Fun{
                              name, alias=NONE, version, deprecated, retTy, params
                            }) = (case AMap.find (updateFns, name)
                             of NONE => baseFn
                              | SOME(X.Fun info) => let
                                  val version = merge (name, "version", version, #version info)
                                  val deprecated = merge (name, "deprecated", deprecated, #deprecated info)
                                  in
                                    X.Fun{
                                        name = name, version = version,
                                        alias = NONE,
                                        deprecated = deprecated,
                                        retTy = retTy,
                                        params = params
                                      }
                                  end
                          (* end case *))
                        | update (baseFn as X.Fun{
                              name, alias=SOME name', version, deprecated, retTy, params
                            }) = (case AMap.find (updateFns, name')
                             of NONE => baseFn
                              | SOME(X.Fun info) => let
                                (* want the alias to be consistent with the canonical version *)
                                  val version = merge (name, "version", version, #version info)
                                  val deprecated = merge (name, "deprecated", deprecated, #deprecated info)
                                  in
                                    X.Fun{
                                        name = name, version = version,
                                        alias = SOME name',
                                        deprecated = deprecated,
                                        retTy = retTy,
                                        params = params
                                      }
                                  end
                          (* end case *))
                      in
                        insertInBase (catName, X.Category{
                            name = catName,
                            functs = (List.map update baseFuns) @ newFns
                          })
                      end
                  | NONE => insertInBase (catName, cat)
                (* end case *))
          in
            ATbl.app extend ext
          end
*)

  (* extend the first database with any additional definitions, etc. that
   * are provided by the second database.
   *)
    fun extend (DB base, DB ext) = (
(* NOTE: maybe we should allow different spec files? *)
        (* check that the two databases are compatible *)
          if (#registry base <> #registry ext)
	  orelse (#api base <> #api ext)
	  orelse (#profile base <> #profile ext)
            then conflict["extend: database mismatch"]
            else ();
        (* extend the database tables *)
          extendTypes (#types base, #types ext);
          extendEnums (#enums base, #enums ext);
          extendCommands (#commands base, #commands ext);
	  extendGroups (#groups base, #groups ext);
	  extendFeatures (#features base, #features ext))

  end

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0