more Haskell operations;
authorwenzelm
Thu, 19 Aug 2021 14:23:47 +0200
changeset 74432 3f371ba2b4fc
parent 74431 b5eba4717648
child 74433 304f22435bc7
more Haskell operations;
src/Pure/System/options.ML
src/Tools/Haskell/Haskell.thy
src/Tools/Haskell/Test.thy
--- a/src/Pure/System/options.ML	Thu Aug 19 13:22:06 2021 +0200
+++ b/src/Pure/System/options.ML	Thu Aug 19 14:23:47 2021 +0200
@@ -26,7 +26,8 @@
   val put_string: string -> string -> T -> T
   val declare: {pos: Position.T, name: string, typ: string, value: string} -> T -> T
   val update: string -> string -> T -> T
-  val decode: XML.body -> T
+  val encode: T XML.Encode.T
+  val decode: T XML.Decode.T
   val default: unit -> T
   val default_typ: string -> string
   val default_bool: string -> bool
@@ -150,15 +151,24 @@
   in options' end;
 
 
-(* decode *)
+(* XML data *)
 
-fun decode_opt body =
-  let open XML.Decode
-  in list (pair properties (pair string (pair string string))) end body
-  |> map (fn (props, (name, (typ, value))) =>
-      {pos = Position.of_properties props, name = name, typ = typ, value = value});
+fun encode (Options tab) =
+  let
+    val opts =
+      (tab, []) |-> Symtab.fold (fn (name, {pos, typ, value}) =>
+        cons (Position.properties_of pos, (name, (typ, value))));
+    open XML.Encode;
+  in list (pair properties (pair string (pair string string))) opts end;
 
-fun decode body = fold declare (decode_opt body) empty;
+fun decode body =
+  let
+    open XML.Decode;
+    val decode_options =
+      list (pair properties (pair string (pair string string)))
+      #> map (fn (props, (name, (typ, value))) =>
+          {pos = Position.of_properties props, name = name, typ = typ, value = value});
+  in fold declare (decode_options body) empty end;
 
 
 
--- a/src/Tools/Haskell/Haskell.thy	Thu Aug 19 13:22:06 2021 +0200
+++ b/src/Tools/Haskell/Haskell.thy	Thu Aug 19 14:23:47 2021 +0200
@@ -2830,6 +2830,408 @@
       client socket
 \<close>
 
+generate_file "Isabelle/Time.hs" = \<open>
+{-  Title:      Isabelle/Time.hs
+    Author:     Makarius
+    LICENSE:    BSD 3-clause (Isabelle)
+
+Time based on milliseconds.
+
+See \<^file>\<open>~~/src/Pure/General/time.scala\<close>
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Isabelle.Time (
+  Time, seconds, minutes, ms, zero, is_zero, is_relevant,
+  get_seconds, get_minutes, get_ms, message
+)
+where
+
+import Text.Printf (printf)
+import Isabelle.Bytes (Bytes)
+import Isabelle.Library
+
+
+data Time = Time Int
+
+instance Eq Time where Time ms1 == Time ms2 = ms1 == ms2
+instance Ord Time where compare (Time ms1) (Time ms2) = compare ms1 ms2
+
+seconds :: Double -> Time
+seconds s = Time (round (s * 1000.0))
+
+minutes :: Double -> Time
+minutes m = Time (round (m * 60000.0))
+
+ms :: Int -> Time
+ms = Time
+
+zero :: Time
+zero = ms 0
+
+is_zero :: Time -> Bool
+is_zero (Time ms) = ms == 0
+
+is_relevant :: Time -> Bool
+is_relevant (Time ms) = ms >= 1
+
+get_seconds :: Time -> Double
+get_seconds (Time ms) = fromIntegral ms / 1000.0
+
+get_minutes :: Time -> Double
+get_minutes (Time ms) = fromIntegral ms / 60000.0
+
+get_ms :: Time -> Int
+get_ms (Time ms) = ms
+
+instance Show Time where
+  show t = printf "%.3f" (get_seconds t)
+
+message :: Time -> Bytes
+message t = make_bytes (show t) <> "s"
+\<close>
+
+generate_file "Isabelle/Timing.hs" = \<open>
+{-  Title:      Isabelle/Timing.hs
+    Author:     Makarius
+    LICENSE:    BSD 3-clause (Isabelle)
+
+Support for time measurement.
+
+See \<^file>\<open>~~/src/Pure/General/timing.ML\<close>
+and \<^file>\<open>~~/src/Pure/General/timing.scala\<close>
+-}
+
+module Isabelle.Timing (
+  Timing (..), zero, is_zero, is_relevant
+)
+where
+
+import qualified Isabelle.Time as Time
+import Isabelle.Time (Time)
+
+data Timing = Timing {elapsed :: Time, cpu :: Time, gc :: Time}
+  deriving (Show, Eq)
+
+zero :: Timing
+zero = Timing Time.zero Time.zero Time.zero
+
+is_zero :: Timing -> Bool
+is_zero t = Time.is_zero (elapsed t) && Time.is_zero (cpu t) && Time.is_zero (gc t)
+
+is_relevant :: Timing -> Bool
+is_relevant t = Time.is_relevant (elapsed t) || Time.is_relevant (cpu t) || Time.is_relevant (gc t)
+\<close>
+
+generate_file "Isabelle/Bash.hs" = \<open>
+{-  Title:      Isabelle/Bash.hs
+    Author:     Makarius
+    LICENSE:    BSD 3-clause (Isabelle)
+
+Support for GNU bash.
+
+See \<^file>\<open>$ISABELLE_HOME/src/Pure/System/bash.ML\<close>
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Isabelle.Bash (
+  string, strings,
+
+  Params,
+  get_script, get_input, get_cwd, get_putenv, get_redirect,
+  get_timeout, get_description,
+  script, input, cwd, putenv, redirect, timeout, description,
+)
+where
+
+import Text.Printf (printf)
+import qualified Isabelle.Symbol as Symbol
+import qualified Isabelle.Bytes as Bytes
+import Isabelle.Bytes (Bytes)
+import qualified Isabelle.Time as Time
+import Isabelle.Time (Time)
+import Isabelle.Library
+
+
+{- concrete syntax -}
+
+string :: Bytes -> Bytes
+string str =
+  if Bytes.null str then "\"\""
+  else str |> Bytes.unpack |> map trans |> Bytes.concat
+    where
+      trans b =
+        case Bytes.char b of
+          '\t' -> "$'\\t'"
+          '\n' -> "$'\\n'"
+          '\f' -> "$'\\f'"
+          '\r' -> "$'\\r'"
+          c ->
+            if Symbol.is_ascii_letter c || Symbol.is_ascii_digit c || c == '+' || c == ',' ||
+              c == '-' || c == '.' || c == '/' || c == ':' || c == '_'
+            then Bytes.singleton b
+            else if b < 32 || b >= 127 then make_bytes (printf "$'\\x%02x'" b :: String)
+            else "\\" <> Bytes.singleton b
+
+strings :: [Bytes] -> Bytes
+strings = space_implode " " . map string
+
+
+{- server parameters -}
+
+data Params = Params {
+    _script :: Bytes,
+    _input :: Bytes,
+    _cwd :: Maybe Bytes,
+    _putenv :: [(Bytes, Bytes)],
+    _redirect :: Bool,
+    _timeout :: Time,
+    _description :: Bytes}
+  deriving (Show, Eq)
+
+get_script :: Params -> Bytes
+get_script = _script
+
+get_input :: Params -> Bytes
+get_input = _input
+
+get_cwd :: Params -> Maybe Bytes
+get_cwd = _cwd
+
+get_putenv :: Params -> [(Bytes, Bytes)]
+get_putenv = _putenv
+
+get_redirect :: Params -> Bool
+get_redirect = _redirect
+
+get_timeout :: Params -> Time
+get_timeout = _timeout
+
+get_description :: Params -> Bytes
+get_description = _description
+
+script :: Bytes -> Params
+script script = Params script "" Nothing [] False Time.zero ""
+
+input :: Bytes -> Params -> Params
+input input params = params { _input = input }
+
+cwd :: Bytes -> Params -> Params
+cwd cwd params = params { _cwd = Just cwd }
+
+putenv :: [(Bytes, Bytes)] -> Params -> Params
+putenv putenv params = params { _putenv = putenv }
+
+redirect :: Params -> Params
+redirect params = params { _redirect = True }
+
+timeout :: Time -> Params -> Params
+timeout timeout params = params { _timeout = timeout }
+
+description :: Bytes -> Params -> Params
+description description params = params { _description = description }
+\<close>
+
+generate_file "Isabelle/Process_Result.hs" = \<open>
+{-  Title:      Isabelle/Process_Result.hs
+    Author:     Makarius
+    LICENSE:    BSD 3-clause (Isabelle)
+
+Result of system process.
+
+See \<^file>\<open>~~/src/Pure/System/process_result.ML\<close>
+and \<^file>\<open>~~/src/Pure/System/process_result.scala\<close>
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Isabelle.Process_Result (
+  interrupt_rc, timeout_rc,
+
+  T, make, rc, out_lines, err_lines, timing, timing_elapsed, out, err, ok, check
+)
+where
+
+import Isabelle.Time (Time)
+import qualified Isabelle.Timing as Timing
+import Isabelle.Timing (Timing)
+import Isabelle.Bytes (Bytes)
+import Isabelle.Library
+
+
+interrupt_rc :: Int
+interrupt_rc = 130
+
+timeout_rc :: Int
+timeout_rc = 142
+
+data T =
+  Process_Result {
+    _rc :: Int,
+    _out_lines :: [Bytes],
+    _err_lines :: [Bytes],
+    _timing :: Timing}
+  deriving (Show, Eq)
+
+make :: Int -> [Bytes] -> [Bytes] -> Timing -> T
+make = Process_Result
+
+rc :: T -> Int
+rc = _rc
+
+out_lines :: T -> [Bytes]
+out_lines = _out_lines
+
+err_lines :: T -> [Bytes]
+err_lines = _err_lines
+
+timing :: T -> Timing
+timing = _timing
+
+timing_elapsed :: T -> Time
+timing_elapsed = Timing.elapsed . timing
+
+out :: T -> Bytes
+out = trim_line . cat_lines . out_lines
+
+err :: T -> Bytes
+err = trim_line . cat_lines . err_lines
+
+ok :: T -> Bool
+ok result = rc result == 0
+
+check :: T -> T
+check result = if ok result then result else error (make_string $ err result)
+\<close>
+
+generate_file "Isabelle/Options.hs" = \<open>
+{-  Title:      Isabelle/Options.hs
+    Author:     Makarius
+    LICENSE:    BSD 3-clause (Isabelle)
+
+System options with external string representation.
+
+See \<^file>\<open>~~/src/Pure/System/options.ML\<close>
+and \<^file>\<open>~~/src/Pure/System/options.scala\<close>
+-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE InstanceSigs #-}
+
+module Isabelle.Options (
+  boolT, intT, realT, stringT, unknownT,
+
+  T, typ, bool, int, real, seconds, string,
+  decode
+)
+where
+
+import qualified Data.Map.Strict as Map
+import Data.Map.Strict (Map)
+import qualified Isabelle.Properties as Properties
+import Isabelle.Bytes (Bytes)
+import qualified Isabelle.Value as Value
+import qualified Isabelle.Time as Time
+import Isabelle.Time (Time)
+import Isabelle.Library
+import qualified Isabelle.XML.Decode as Decode
+import Isabelle.XML.Classes (Decode (..))
+
+
+{- representation -}
+
+boolT :: Bytes
+boolT = "bool"
+
+intT :: Bytes
+intT = "int"
+
+realT :: Bytes
+realT = "real"
+
+stringT :: Bytes
+stringT = "string"
+
+unknownT :: Bytes
+unknownT = "unknown"
+
+data Opt = Opt {
+  _pos :: Properties.T,
+  _name :: Bytes,
+  _typ :: Bytes,
+  _value :: Bytes }
+
+data T = Options (Map Bytes Opt)
+
+
+{- check -}
+
+check_name :: T -> Bytes -> Opt
+check_name (Options map) name =
+  case Map.lookup name map of
+    Just opt | _typ opt /= unknownT -> opt
+    _ -> error (make_string ("Unknown system option " <> quote name))
+
+check_type :: T -> Bytes -> Bytes -> Opt
+check_type options name typ =
+  let
+    opt = check_name options name
+    t = _typ opt
+  in
+    if t == typ then opt
+    else error (make_string ("Ill-typed system option " <> quote name <> " : " <> t <> " vs. " <> typ))
+
+
+{- get typ -}
+
+typ :: T -> Bytes -> Bytes
+typ options name = _typ (check_name options name)
+
+
+{- get value -}
+
+get :: Bytes -> (Bytes -> Maybe a) -> T -> Bytes -> a
+get typ parse options name =
+  let opt = check_type options name typ in
+    case parse (_value opt) of
+      Just x -> x
+      Nothing ->
+        error (make_string ("Malformed value for system option " <> quote name <>
+          " : " <> typ <> " =\n" <> quote (_value opt)))
+
+bool :: T -> Bytes -> Bool
+bool = get boolT Value.parse_bool
+
+int :: T -> Bytes -> Int
+int = get intT Value.parse_int
+
+real :: T -> Bytes -> Double
+real = get realT Value.parse_real
+
+seconds :: T -> Bytes -> Time
+seconds options = Time.seconds . real options
+
+string :: T -> Bytes -> Bytes
+string = get stringT Just
+
+
+{- decode -}
+
+instance Decode T where
+  decode :: Decode.T T
+  decode =
+    let
+      decode_entry :: Decode.T (Bytes, Opt)
+      decode_entry body =
+        let
+          (pos, (name, (typ, value))) =
+            Decode.pair Decode.properties (Decode.pair Decode.string (Decode.pair Decode.string Decode.string)) body
+        in (name, Opt { _pos = pos, _name = name, _typ = typ, _value = value })
+    in Options . Map.fromList . Decode.list decode_entry
+\<close>
+
 export_generated_files _
 
 end
--- a/src/Tools/Haskell/Test.thy	Thu Aug 19 13:22:06 2021 +0200
+++ b/src/Tools/Haskell/Test.thy	Thu Aug 19 14:23:47 2021 +0200
@@ -18,7 +18,7 @@
         GHC.new_project dir
           {name = "isabelle",
            depends =
-            ["array", "bytestring", "containers", "network", "split", "text", "threads", "uuid"],
+            ["array", "bytestring", "containers", "network", "split", "text", "time", "threads", "uuid"],
            modules = modules};
     in
       writeln (Generated_Files.execute dir \<open>Build\<close> "mv Isabelle src && isabelle ghc_stack build")