src/Pure/ProofGeneral/pgip_types.ML
author wenzelm
Wed, 27 Aug 2008 12:00:28 +0200
changeset 28020 1ff5167592ba
parent 28017 4919bd124a58
child 28397 389c5e494605
permissions -rw-r--r--
get rid of tabs;
     1 (*  Title:      Pure/ProofGeneral/pgip_types.ML
     2     ID:         $Id$
     3     Author:     David Aspinall
     4 
     5 PGIP abstraction: types and conversions.
     6 *)
     7 
     8 (* TODO: PGML, proverflag *)
     9 signature PGIPTYPES =
    10 sig
    11     (* Object types: the types of values which can be manipulated externally.
    12        Ideally a list of other types would be configured as a parameter. *)
    13     datatype objtype = ObjFile | ObjTheory | ObjTheorem | ObjComment  
    14                      | ObjTerm | ObjType | ObjOther of string
    15   
    16     (* Names for values of objtypes (i.e. prover identifiers). Could be a parameter.
    17        Names for ObjFiles are URIs. *)
    18     type objname = string
    19 
    20     type idtable = { context: objname option,    (* container's objname *)
    21                      objtype: objtype, 
    22                      ids: objname list }
    23 
    24     (* Types and values (used for preferences and dialogs) *)
    25 
    26     datatype pgiptype = Pgipnull | Pgipbool | Pgipint of int option * int option | Pgipnat
    27                       | Pgipstring | Pgipconst of string | Pgipchoice of pgipdtype list
    28 
    29     and pgipdtype = Pgipdtype of string option * pgiptype  (* type with opt. description *)
    30 
    31     type pgipval   (* typed value *)
    32 
    33     (* URLs we can cope with *)
    34     type pgipurl
    35 
    36     (* Representation error in reading/writing PGIP *)
    37     exception PGIP of string
    38 
    39     (* Interface areas for message output *)
    40     datatype displayarea = Status | Message | Display | Tracing | Internal | Other of string
    41 
    42     (* Error levels *)
    43     datatype fatality = Info | Warning | Nonfatal | Fatal | Panic | Log | Debug
    44 
    45     (* File location *)
    46     type location = { descr: string option,
    47                       url: pgipurl option,
    48                       line: int option,
    49                       column: int option,
    50                       char: int option,
    51                       length: int option }
    52 
    53     (* Prover preference *)   
    54     type preference = { name: string,
    55                         descr: string option,
    56                         default: string option,
    57                         pgiptype: pgiptype }
    58 end
    59 
    60 signature PGIPTYPES_OPNS = 
    61 sig
    62     include PGIPTYPES
    63  
    64     (* Object types *)
    65     val name_of_objtype  : objtype -> string
    66     val attrs_of_objtype : objtype -> XML.attributes
    67     val objtype_of_attrs : XML.attributes -> objtype                    (* raises PGIP *)
    68     val idtable_to_xml   : idtable -> XML.tree
    69 
    70     (* Values as XML strings *)
    71     val read_pgipint       : (int option * int option) -> string -> int (* raises PGIP *)
    72     val read_pgipnat       : string -> int                              (* raises PGIP *)
    73     val read_pgipbool      : string -> bool                             (* raises PGIP *)
    74     val read_pgipstring    : string -> string                           (* raises PGIP *)
    75     val int_to_pgstring    : int -> string
    76     val bool_to_pgstring   : bool -> string
    77     val string_to_pgstring : string -> string
    78 
    79     (* PGIP datatypes *)
    80     val pgiptype_to_xml   : pgiptype -> XML.tree
    81     val read_pgval        : pgiptype -> string -> pgipval              (* raises PGIP *)
    82     val pgval_to_string   : pgipval -> string
    83 
    84     val attrs_of_displayarea : displayarea -> XML.attributes
    85     val attrs_of_fatality : fatality -> XML.attributes
    86     val attrs_of_location : location -> XML.attributes
    87     val location_of_attrs : XML.attributes -> location (* raises PGIP *)
    88 
    89     val haspref : preference -> XML.tree
    90 
    91     val pgipurl_of_url : Url.T -> pgipurl              (* raises PGIP *)
    92     val pgipurl_of_string : string -> pgipurl          (* raises PGIP *)
    93     val pgipurl_of_path : Path.T -> pgipurl
    94     val path_of_pgipurl : pgipurl -> Path.T
    95     val string_of_pgipurl : pgipurl -> string
    96     val attrs_of_pgipurl : pgipurl -> XML.attributes
    97     val pgipurl_of_attrs : XML.attributes -> pgipurl   (* raises PGIP *)
    98 
    99     (* XML utils, only for PGIP code *)
   100     val has_attr       : string -> XML.attributes -> bool
   101     val attr           : string -> string -> XML.attributes
   102     val opt_attr       : string -> string option -> XML.attributes
   103     val opt_attr_map   : ('a -> string) -> string -> 'a option -> XML.attributes
   104     val get_attr       : string -> XML.attributes -> string           (* raises PGIP *)
   105     val get_attr_opt   : string -> XML.attributes -> string option
   106     val get_attr_dflt  : string -> string -> XML.attributes -> string
   107 end
   108 
   109 structure PgipTypes : PGIPTYPES_OPNS =
   110 struct
   111 exception PGIP of string
   112 
   113 (** XML utils **)
   114 
   115 fun has_attr attr attrs = Properties.defined attrs attr
   116 
   117 fun get_attr_opt attr attrs = Properties.get attrs attr
   118 
   119 fun get_attr attr attrs =
   120     (case get_attr_opt attr attrs of
   121          SOME value => value
   122        | NONE => raise PGIP ("Missing attribute: " ^ quote attr))
   123 
   124 fun get_attr_dflt attr dflt attrs = the_default dflt (get_attr_opt attr attrs)
   125 
   126 fun attr x y = [(x,y)] : XML.attributes
   127 
   128 fun opt_attr_map f attr_name opt_val = 
   129     case opt_val of NONE => [] | SOME v => [(attr_name,f v)]
   130  (* or, if you've got function-itis: 
   131     the_default [] (Option.map (single o (pair attr_name) o f) opt_val)
   132   *)
   133 
   134 fun opt_attr attr_name = opt_attr_map I attr_name
   135 
   136 
   137 (** Objtypes **)
   138 
   139 datatype objtype = ObjFile | ObjTheory | ObjTheorem | ObjComment  
   140                  | ObjTerm | ObjType | ObjOther of string
   141 
   142 fun name_of_objtype obj = 
   143     case obj of 
   144         ObjFile    => "file"
   145       | ObjTheory  => "theory"
   146       | ObjTheorem => "theorem"
   147       | ObjComment => "comment"
   148       | ObjTerm    => "term"
   149       | ObjType    => "type"
   150       | ObjOther s => s
   151 
   152 val attrs_of_objtype = attr "objtype" o name_of_objtype
   153 
   154 fun objtype_of_attrs attrs = case get_attr "objtype" attrs of
   155        "file" => ObjFile
   156      | "theory" => ObjTheory
   157      | "theorem" => ObjTheorem
   158      | "comment" => ObjComment
   159      | "term" => ObjTerm
   160      | "type" => ObjType
   161      | s => ObjOther s    (* where s mem other_objtypes_parameter *)
   162 
   163 type objname = string
   164 type idtable = { context: objname option,    (* container's objname *)
   165                  objtype: objtype, 
   166                  ids: objname list }
   167 
   168 fun idtable_to_xml {context, objtype, ids} = 
   169     let 
   170         val objtype_attrs = attrs_of_objtype objtype
   171         val context_attrs = opt_attr "context" context
   172         val ids_content = map (fn x => XML.Elem("identifier",[],[XML.Text x])) ids
   173     in 
   174         XML.Elem ("idtable",
   175                   objtype_attrs @ context_attrs,
   176                   ids_content)
   177     end
   178 
   179 
   180 (** Types and values **)
   181 
   182 (* readers and writers for values represented in XML strings *)
   183 
   184 fun read_pgipbool s =
   185     (case s of 
   186          "false" => false 
   187        | "true" => true 
   188        | _ => raise PGIP ("Invalid boolean value: " ^ quote s))
   189 
   190 local
   191     fun int_in_range (NONE,NONE) (_: int) = true
   192       | int_in_range (SOME min,NONE) i = min<= i
   193       | int_in_range (NONE,SOME max) i = i<=max
   194       | int_in_range (SOME min,SOME max) i = min<= i andalso i<=max
   195 in
   196 fun read_pgipint range s =
   197     (case Int.fromString s of 
   198          SOME i => if int_in_range range i then i
   199                    else raise PGIP ("Out of range integer value: " ^ quote s)
   200        | NONE => raise PGIP ("Invalid integer value: " ^ quote s))
   201 end;
   202 
   203 fun read_pgipnat s =
   204     (case Int.fromString s of 
   205          SOME i => if i >= 0 then i
   206                    else raise PGIP ("Invalid natural number: " ^ quote s)
   207        | NONE => raise PGIP ("Invalid natural number: " ^ quote s))
   208 
   209 (* NB: we can maybe do without xml decode/encode here. *)
   210 fun read_pgipstring s =  (* non-empty strings, XML escapes decoded *)
   211     (case XML.parse_string s of
   212          SOME s => s
   213        | NONE => raise PGIP ("Expected a non-empty string: " ^ quote s))
   214     handle _ => raise PGIP ("Invalid XML string syntax: " ^ quote s)
   215 
   216 val int_to_pgstring = signed_string_of_int
   217 
   218 fun string_to_pgstring s = XML.text s
   219 
   220 fun bool_to_pgstring b = if b then "true" else "false"
   221 
   222 
   223 (* PGIP datatypes.
   224    
   225    This is a reflection of the type structure of PGIP configuration,
   226    which is meant for setting up user dialogs and preference settings.
   227    These are configured declaratively in XML, using a syntax for types
   228    and values which is like a (vastly cut down) form of XML Schema
   229    Datatypes.
   230 
   231    The prover needs to interpret the strings representing the typed
   232    values, at least for the types it expects from the dialogs it
   233    configures.  Here we go further and construct a type-safe
   234    encapsulation of these values, which would be useful for more
   235    advanced cases (e.g. generating and processing forms).  
   236 *)
   237 
   238 
   239 datatype pgiptype = 
   240          Pgipnull                            (* unit type: unique element is empty string *)
   241        | Pgipbool                            (* booleans: "true" or "false" *)
   242        | Pgipint of int option * int option  (* ranged integers, should be XSD canonical *)
   243        | Pgipnat                             (* naturals: non-negative integers (convenience) *)
   244        | Pgipstring                          (* non-empty strings *)
   245        | Pgipconst of string                 (* singleton type *)
   246        | Pgipchoice of pgipdtype list        (* union type *)
   247 
   248 (* Compared with the PGIP schema, we push descriptions of types inside choices. *)
   249 
   250 and pgipdtype = Pgipdtype of string option * pgiptype
   251 
   252 datatype pgipuval = Pgvalnull | Pgvalbool of bool | Pgvalint of int | Pgvalnat of int 
   253                   | Pgvalstring of string | Pgvalconst of string
   254 
   255 type pgipval = pgiptype * pgipuval      (* type-safe values *)
   256 
   257 fun pgipdtype_to_xml (desco,ty) = 
   258     let
   259         val desc_attr = opt_attr "descr" desco
   260 
   261         val elt = case ty of
   262                       Pgipnull => "pgipnull"
   263                     | Pgipbool => "pgipbool"
   264                     | Pgipint _ => "pgipint"
   265                     | Pgipnat => "pgipint"
   266                     | Pgipstring => "pgipstring"
   267                     | Pgipconst _ => "pgipconst"
   268                     | Pgipchoice _ => "pgipchoice"
   269 
   270         fun range_attr r v = attr r (int_to_pgstring v)
   271 
   272         val attrs = case ty of 
   273                         Pgipint (SOME min,SOME max) => (range_attr "min" min)@(range_attr "max" max)
   274                       | Pgipint (SOME min,NONE) => (range_attr "min" min)
   275                       | Pgipint (NONE,SOME max) => (range_attr "max" max)
   276                       | Pgipnat => (range_attr "min" 0)
   277                       | Pgipconst nm => attr "name" nm
   278                       | _ => []
   279 
   280         fun destpgipdtype (Pgipdtype x) = x
   281 
   282         val typargs = case ty of
   283                           Pgipchoice ds => map destpgipdtype ds
   284                         | _ => []
   285     in 
   286         XML.Elem (elt, (desc_attr @ attrs), (map pgipdtype_to_xml typargs))
   287     end
   288 
   289 val pgiptype_to_xml = pgipdtype_to_xml o pair NONE
   290 
   291 fun read_pguval Pgipnull s = 
   292     if s="" then Pgvalnull
   293     else raise PGIP ("Expecting empty string for null type, not: " ^ quote s)
   294   | read_pguval Pgipbool s = Pgvalbool (read_pgipbool s)
   295   | read_pguval (Pgipint range) s = Pgvalint (read_pgipint range s)
   296   | read_pguval Pgipnat s = Pgvalnat (read_pgipnat s)
   297   | read_pguval (Pgipconst c) s = 
   298     if c=s then Pgvalconst c 
   299     else raise PGIP ("Given string: "^quote s^" doesn't match expected string: "^quote c)
   300   | read_pguval Pgipstring s = 
   301     if s<>"" then Pgvalstring s
   302     else raise PGIP ("Expecting non-empty string, empty string illegal.")
   303   | read_pguval (Pgipchoice tydescs) s = 
   304     let 
   305         (* Union type: match first in list *)
   306         fun getty (Pgipdtype(_, ty)) = ty
   307         val uval = get_first 
   308                        (fn ty => SOME (read_pguval ty s) handle PGIP _ => NONE)
   309                        (map getty tydescs)
   310     in
   311         case uval of SOME pgv => pgv | NONE => raise PGIP ("Can't match string: "^quote s^
   312                                                            " against any allowed types.")
   313     end
   314 
   315 fun read_pgval ty s = (ty, read_pguval ty s)
   316             
   317 fun pgval_to_string (_, Pgvalnull) = ""
   318   | pgval_to_string (_, Pgvalbool b) = bool_to_pgstring b
   319   | pgval_to_string (_, Pgvalnat n) = int_to_pgstring n
   320   | pgval_to_string (_, Pgvalint i) = int_to_pgstring i
   321   | pgval_to_string (_, Pgvalconst c) = string_to_pgstring c
   322   | pgval_to_string (_, Pgvalstring s) = string_to_pgstring s
   323 
   324 
   325 type pgipurl = Path.T    (* URLs: only local files *)
   326 
   327 datatype displayarea = Status | Message | Display | Tracing | Internal | Other of string
   328 
   329 datatype fatality = Info | Warning | Nonfatal | Fatal | Panic | Log | Debug
   330 
   331 type location = { descr: string option,
   332                   url: pgipurl option,
   333                   line: int option,
   334                   column: int option,
   335                   char: int option,
   336                   length: int option }
   337 
   338 
   339 
   340 (** Url operations **)
   341 
   342 
   343 fun pgipurl_of_string url = (* only handle file:/// or file://localhost/ *)
   344         case Url.explode url of
   345             (Url.File path) => path
   346           | _ => raise PGIP ("Cannot access non-local URL " ^ quote url)
   347                        
   348 fun pgipurl_of_path p = p
   349 
   350 fun path_of_pgipurl p = p  (* potentially raises PGIP, but not with this implementation *)
   351 
   352 fun string_of_pgipurl p = Path.implode p
   353 
   354 fun attrval_of_pgipurl purl = "file:" ^ (XML.text (File.platform_path (File.full_path purl)))
   355 fun attrs_of_pgipurl purl = [("url", attrval_of_pgipurl purl)]
   356 
   357 val pgipurl_of_attrs = pgipurl_of_string o get_attr "url"
   358 
   359 fun pgipurl_of_url (Url.File p) = p
   360   | pgipurl_of_url url = 
   361     raise PGIP ("Cannot access non-local/non-file URL " ^ quote (Url.implode url))
   362 
   363 
   364 (** Messages and errors **)
   365 
   366 local
   367   fun string_of_area Status = "status"
   368     | string_of_area Message = "message"
   369     | string_of_area Display = "display"
   370     | string_of_area Tracing = "tracing"
   371     | string_of_area Internal = "internal"
   372     | string_of_area (Other s) = s
   373 
   374   fun string_of_fatality Info = "info"
   375     | string_of_fatality Warning = "warning"
   376     | string_of_fatality Nonfatal = "nonfatal"
   377     | string_of_fatality Fatal = "fatal"
   378     | string_of_fatality Panic = "panic"
   379     | string_of_fatality Log = "log"
   380     | string_of_fatality Debug = "debug"
   381 in
   382   fun attrs_of_displayarea area = [("area", string_of_area area)]
   383 
   384   fun attrs_of_fatality fatality = [("fatality", string_of_fatality fatality)]
   385 
   386   fun attrs_of_location ({ descr, url, line, column, char, length }:location) =
   387       let 
   388           val descr = opt_attr "location_descr" descr
   389           val url = opt_attr_map attrval_of_pgipurl "location_url" url
   390           val line = opt_attr_map int_to_pgstring "locationline" line
   391           val column = opt_attr_map int_to_pgstring "locationcolumn"  column
   392           val char = opt_attr_map int_to_pgstring "locationcharacter" char
   393           val length = opt_attr_map int_to_pgstring "locationlength" length
   394       in 
   395           descr @ url @ line @ column @ char @ length
   396       end
   397 
   398     fun pgipint_of_string err s = 
   399         case Int.fromString s of 
   400             SOME i => i
   401           | NONE => raise PGIP ("Type error in " ^ quote err ^ ": expected integer.")
   402 
   403   fun location_of_attrs attrs = 
   404       let
   405           val descr = get_attr_opt "location_descr" attrs
   406           val url = Option.map  pgipurl_of_string (get_attr_opt "location_url" attrs)
   407           val line = Option.map (pgipint_of_string "location element line attribute")
   408                                 (get_attr_opt "locationline" attrs)
   409           val column = Option.map (pgipint_of_string "location element column attribute")
   410                                   (get_attr_opt "locationcolumn" attrs)
   411           val char = Option.map (pgipint_of_string "location element char attribute")
   412                                 (get_attr_opt "locationcharacter" attrs)
   413           val length = Option.map (pgipint_of_string "location element length attribute")
   414                                   (get_attr_opt "locationlength" attrs)
   415       in 
   416           {descr=descr, url=url, line=line, column=column, char=char, length=length}
   417       end
   418 end
   419 
   420 (** Preferences **)
   421 
   422 type preference = { name: string,
   423                     descr: string option,
   424                     default: string option,
   425                     pgiptype: pgiptype }
   426 
   427 fun haspref ({ name, descr, default, pgiptype}:preference) = 
   428     XML.Elem ("haspref",
   429               attr "name" name @
   430               opt_attr "descr" descr @
   431               opt_attr "default" default,
   432               [pgiptype_to_xml pgiptype])
   433 
   434 end
   435