/- Copyright (c) 2017 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Luke Nelson, Jared Roesch, Leonardo de Moura, Sebastian Ullrich -/ prelude import Init.Control.EState import Init.Control.Reader import Init.Data.String.Basic import Init.Data.ByteArray import Init.System.IOError import Init.System.FilePath /-- Like https://hackage.haskell.org/package/ghc-Prim-0.5.2.0/docs/GHC-Prim.html#t:RealWorld. Makes sure we never reorder `IO` operations. TODO: mark opaque -/ def IO.RealWorld : Type := Unit /- TODO(Leo): mark it as an opaque definition. Reason: prevent functions defined in other modules from accessing `IO.RealWorld`. We don't want action such as ``` def getWorld : IO (IO.RealWorld) := get ``` -/ def EIO (ε : Type) : Type → Type := EStateM ε IO.RealWorld @[inline] def EIO.adaptExcept {α ε ε'} (f : ε → ε') (x : EIO ε α) : EIO ε' α := EStateM.adaptExcept f x @[inline] def EIO.catchExceptions {α ε} (x : EIO ε α) (h : ε → EIO Empty α) : EIO Empty α := fun s => match x s with | EStateM.Result.ok a s => EStateM.Result.ok a s | EStateM.Result.error ex s => h ex s instance (ε : Type) : Monad (EIO ε) := inferInstanceAs (Monad (EStateM ε IO.RealWorld)) instance (ε : Type) : MonadExcept ε (EIO ε) := inferInstanceAs (MonadExcept ε (EStateM ε IO.RealWorld)) instance (α ε : Type) : HasOrelse (EIO ε α) := ⟨MonadExcept.orelse⟩ instance {ε : Type} {α : Type} [Inhabited ε] : Inhabited (EIO ε α) := inferInstanceAs (Inhabited (EStateM ε IO.RealWorld α)) abbrev IO : Type → Type := EIO IO.Error section /- After we inline `EState.run'`, the closed term `((), ())` is generated, where the second `()` represents the "initial world". We don't want to cache this closed term. So, we disable the "extract closed terms" optimization. -/ set_option compiler.extract_closed false @[inline] unsafe def unsafeIO {α : Type} (fn : IO α) : Except IO.Error α := match fn.run () with | EStateM.Result.ok a _ => Except.ok a | EStateM.Result.error e _ => Except.error e end @[extern "lean_io_timeit"] constant timeit {α : Type} (msg : @& String) (fn : IO α) : IO α := arbitrary _ @[extern "lean_io_allocprof"] constant allocprof {α : Type} (msg : @& String) (fn : IO α) : IO α := arbitrary _ /- Programs can execute IO actions during initialization that occurs before the `main` function is executed. The attribute `[init ]` specifies which IO action is executed to set the value of an opaque constant. The action `initializing` returns `true` iff it is invoked during initialization. -/ @[extern "lean_io_initializing"] constant IO.initializing : IO Bool := arbitrary _ class MonadIO (m : Type → Type) extends HasMonadLiftT IO m, MonadExcept IO.Error m instance : MonadIO IO := {} /- Omitted instances of MonadIO: OptionT, ExceptT and EStateT. The possibility for errors introduces the risk that `withStdout` will not restore the previous handle when an error is returned in the topmost monad. -/ instance ReaderT.monadIO {ρ} (m : Type → Type) [Monad m] [MonadIO m] : MonadIO (ReaderT ρ m) := {} instance StateT.monadIO {σ} (m : Type → Type) [Monad m] [MonadIO m] : MonadIO (StateT σ m) := {} namespace IO def ofExcept {ε α : Type} [HasToString ε] (e : Except ε α) : IO α := match e with | Except.ok a => pure a | Except.error e => throw (IO.userError (toString e)) def lazyPure {α : Type} (fn : Unit → α) : IO α := pure (fn ()) inductive FS.Mode | read | write | readWrite | append constant FS.Handle : Type := Unit namespace Prim open FS @[extern "lean_get_stdin"] constant stdin : IO FS.Handle := arbitrary _ @[extern "lean_get_stdout"] constant stdout : IO FS.Handle := arbitrary _ @[extern "lean_get_stderr"] constant stderr : IO FS.Handle := arbitrary _ /-- Run action with `stdin` closed and `stdout+stderr` captured into a `String`. -/ @[extern "lean_with_isolated_streams"] constant withIsolatedStreams {α : Type} : IO α → IO (String × Except IO.Error α) := arbitrary _ @[specialize] partial def iterate {α β : Type} : α → (α → IO (Sum α β)) → IO β | a, f => do v ← f a; match v with | Sum.inl a => iterate a f | Sum.inr b => pure b -- @[export lean_fopen_flags] def fopenFlags (m : FS.Mode) (b : Bool) : String := let mode := match m with | FS.Mode.read => "r" | FS.Mode.write => "w" | FS.Mode.readWrite => "r+" | FS.Mode.append => "a" ; let bin := if b then "b" else "t"; mode ++ bin @[extern "lean_io_prim_handle_mk"] constant Handle.mk (s : @& String) (mode : @& String) : IO Handle := arbitrary _ @[extern "lean_io_prim_handle_is_eof"] constant Handle.isEof (h : @& Handle) : IO Bool := arbitrary _ @[extern "lean_io_prim_handle_flush"] constant Handle.flush (h : @& Handle) : IO Unit := arbitrary _ -- TODO: replace `String` with byte buffer @[extern "lean_io_prim_handle_read"] constant Handle.read (h : @& Handle) (bytes : USize) : IO ByteArray := arbitrary _ @[extern "lean_io_prim_handle_write"] constant Handle.write (h : @& Handle) (buffer : @& ByteArray) : IO Unit := arbitrary _ @[extern "lean_io_prim_handle_get_line"] constant Handle.getLine (h : @& Handle) : IO String := arbitrary _ @[extern "lean_io_prim_handle_put_str"] constant Handle.putStr (h : @& Handle) (s : @& String) : IO Unit := arbitrary _ @[extern "lean_io_getenv"] constant getEnv (var : @& String) : IO (Option String) := arbitrary _ @[extern "lean_io_realpath"] constant realPath (fname : String) : IO String := arbitrary _ @[extern "lean_io_is_dir"] constant isDir (fname : @& String) : IO Bool := arbitrary _ @[extern "lean_io_file_exists"] constant fileExists (fname : @& String) : IO Bool := arbitrary _ @[extern "lean_io_app_dir"] constant appPath : IO String := arbitrary _ @[extern "lean_io_current_dir"] constant currentDir : IO String := arbitrary _ @[inline] def liftIO {m : Type → Type} {α : Type} [MonadIO m] (x : IO α) : m α := monadLift x end Prim namespace FS variables {m : Type → Type} [Monad m] [MonadIO m] def Handle.mk (s : String) (Mode : Mode) (bin : Bool := false) : m Handle := Prim.liftIO (Prim.Handle.mk s (Prim.fopenFlags Mode bin)) @[inline] def withFile {α} (fn : String) (mode : Mode) (f : Handle → m α) : m α := Handle.mk fn mode >>= f /-- returns whether the end of the file has been reached while reading a file. `h.isEof` returns true /after/ the first attempt at reading past the end of `h`. Once `h.isEof` is true, the reading `h` raises `IO.Error.eof`. -/ def Handle.isEof : Handle → m Bool := Prim.liftIO ∘ Prim.Handle.isEof def Handle.flush : Handle → m Unit := Prim.liftIO ∘ Prim.Handle.flush def Handle.read (h : Handle) (bytes : Nat) : m ByteArray := Prim.liftIO (Prim.Handle.read h (USize.ofNat bytes)) def Handle.write (h : Handle) (s : ByteArray) : m Unit := Prim.liftIO (Prim.Handle.write h s) def Handle.getLine : Handle → m String := Prim.liftIO ∘ Prim.Handle.getLine def Handle.putStr (h : Handle) (s : String) : m Unit := Prim.liftIO $ Prim.Handle.putStr h s def Handle.putStrLn (h : Handle) (s : String) : m Unit := h.putStr s *> h.putStr "\n" -- TODO: support for binary files partial def Handle.readToEndAux (h : Handle) : String → m String | s => do line ← h.getLine; if line.length == 0 then pure s else Handle.readToEndAux (s ++ line) -- TODO: support for binary files def Handle.readToEnd (h : Handle) : m String := Handle.readToEndAux h "" -- TODO: support for binary files def readFile (fname : String) : m String := do h ← Handle.mk fname Mode.read false; h.readToEnd partial def linesAux (h : Handle) : Array String → m (Array String) | lines => do line ← h.getLine; if line.length == 0 then pure lines else if line.back == '\n' then let line := line.dropRight 1; let line := if System.Platform.isWindows && line.back == '\x0d' then line.dropRight 1 else line; linesAux $ lines.push line else pure $ lines.push line def lines (fname : String) : m (Array String) := do h ← Handle.mk fname Mode.read false; linesAux h #[] end FS section variables {m : Type → Type} [Monad m] [MonadIO m] def stdin : m FS.Handle := Prim.liftIO Prim.stdin def stdout : m FS.Handle := Prim.liftIO Prim.stdout def stderr : m FS.Handle := Prim.liftIO Prim.stderr def print {α} [HasToString α] (s : α) : m Unit := do out ← stdout; out.putStr $ toString s def println {α} [HasToString α] (s : α) : m Unit := print s *> print "\n" def eprint {α} [HasToString α] (s : α) : m Unit := do out ← stderr; out.putStr $ toString s def eprintln {α} [HasToString α] (s : α) : m Unit := eprint s *> eprint "\n" def getEnv : String → m (Option String) := Prim.liftIO ∘ Prim.getEnv def realPath : String → m String := Prim.liftIO ∘ Prim.realPath def isDir : String → m Bool := Prim.liftIO ∘ Prim.isDir def fileExists : String → m Bool := Prim.liftIO ∘ Prim.fileExists def appPath : m String := Prim.liftIO Prim.appPath def appDir : m String := do p ← appPath; realPath (System.FilePath.dirName p) def currentDir : m String := Prim.liftIO Prim.currentDir end /- namespace Proc def child : Type := MonadIOProcess.child ioCore def child.stdin : child → Handle := MonadIOProcess.stdin def child.stdout : child → Handle := MonadIOProcess.stdout def child.stderr : child → Handle := MonadIOProcess.stderr def spawn (p : IO.process.spawnArgs) : IO child := MonadIOProcess.spawn ioCore p def wait (c : child) : IO Nat := MonadIOProcess.wait c end Proc -/ structure AccessRight := (read write execution : Bool := false) def AccessRight.flags (acc : AccessRight) : UInt32 := let r : UInt32 := if acc.read then 0x4 else 0; let w : UInt32 := if acc.write then 0x2 else 0; let x : UInt32 := if acc.execution then 0x1 else 0; r.lor $ w.lor x structure FileRight := (user group other : AccessRight := { }) def FileRight.flags (acc : FileRight) : UInt32 := let u : UInt32 := acc.user.flags.shiftLeft 6; let g : UInt32 := acc.group.flags.shiftLeft 3; let o : UInt32 := acc.other.flags; u.lor $ g.lor o @[extern "lean_chmod"] constant Prim.setAccessRights (filename : @& String) (mode : UInt32) : IO Unit := arbitrary _ def setAccessRights (filename : String) (mode : FileRight) : IO Unit := Prim.setAccessRights filename mode.flags /- References -/ constant RefPointed (α : Type) : PointedType := arbitrary _ def Ref (α : Type) : Type := (RefPointed α).type instance (α : Type) : Inhabited (Ref α) := ⟨(RefPointed α).val⟩ namespace Prim @[extern "lean_io_mk_ref"] constant mkRef {α : Type} (a : α) : IO (Ref α) := arbitrary _ @[extern "lean_io_ref_get"] constant Ref.get {α : Type} (r : @& Ref α) : IO α := arbitrary _ @[extern "lean_io_ref_set"] constant Ref.set {α : Type} (r : @& Ref α) (a : α) : IO Unit := arbitrary _ @[extern "lean_io_ref_swap"] constant Ref.swap {α : Type} (r : @& Ref α) (a : α) : IO α := arbitrary _ @[extern "lean_io_ref_reset"] constant Ref.reset {α : Type} (r : @& Ref α) : IO Unit := arbitrary _ @[extern "lean_io_ref_ptr_eq"] constant Ref.ptrEq {α : Type} (r1 r2 : @& Ref α) : IO Bool := arbitrary _ end Prim section variables {m : Type → Type} [Monad m] [MonadIO m] @[inline] def mkRef {α : Type} (a : α) : m (Ref α) := Prim.liftIO (Prim.mkRef a) @[inline] def Ref.get {α : Type} (r : Ref α) : m α := Prim.liftIO (Prim.Ref.get r) @[inline] def Ref.set {α : Type} (r : Ref α) (a : α) : m Unit := Prim.liftIO (Prim.Ref.set r a) @[inline] def Ref.swap {α : Type} (r : Ref α) (a : α) : m α := Prim.liftIO (Prim.Ref.swap r a) @[inline] def Ref.reset {α : Type} (r : Ref α) : m Unit := Prim.liftIO (Prim.Ref.reset r) @[inline] def Ref.ptrEq {α : Type} (r1 r2 : Ref α) : m Bool := Prim.liftIO (Prim.Ref.ptrEq r1 r2) @[inline] def Ref.modify {α : Type} (r : Ref α) (f : α → α) : m Unit := do v ← r.get; r.reset; r.set (f v) end end IO universe u namespace Lean /-- Typeclass used for presenting the output of an `#eval` command. -/ class HasEval (α : Type u) := -- We default `hideUnit` to `true`, but set it to `false` in the direct call from `#eval` -- so that `()` output is hidden in chained instances such as for some `m Unit`. (eval : α → forall (hideUnit : optParam Bool true), IO Unit) instance HasRepr.hasEval {α : Type u} [HasRepr α] : HasEval α := ⟨fun a _ => IO.println (repr a)⟩ instance Unit.hasEval : HasEval Unit := ⟨fun u hideUnit => if hideUnit then pure () else IO.println (repr u)⟩ instance IO.HasEval {α : Type} [HasEval α] : HasEval (IO α) := ⟨fun x _ => do a ← x; HasEval.eval a⟩ end Lean