1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
------------------------------------------------------------------------------
--- The state of the REPL.
---
--- @author  Michael Hanus
--- @version October 2021
------------------------------------------------------------------------------

module REPL.State where

import Control.Monad      ( unless )
import Curry.Compiler.Distribution ( installDir )
import System.Environment ( getArgs )
import System.IO          ( Handle, hFlush, stdout )

import System.Directory   ( doesFileExist )
import System.FilePath    ( (</>), splitSearchPath )
import System.Process     ( getPID )

import REPL.Compiler

data ReplState = ReplState
  { compiler     :: CCDescription
  , usingOption  :: String     -- option for "using" text in banner
  , rcVars       :: [(String, String)] -- content of rc file
  , verbose      :: Int        -- verbosity level:
                               -- 0 = errors and warnings
                               -- 1 = show frontend compilation status
                               -- 2 = show also kics2c compilation status
                               -- 3 = show also ghc compilation status
                               -- 4 = show analysis information
  , libPaths     :: [String]   -- directories containg the standard libraries
  , importPaths  :: [String]   -- additional directories to search for imports
  , preludeName  :: String     -- the name of the standard prelude
  , currMod      :: String     -- name of current main module
  , addMods      :: [String]   -- names of additionally added modules
  , mainExpMod   :: String     -- name of module to store main expressions
  , prompt       :: String     -- repl prompt shown in front of user input
  , timeOut      :: Int        -- timeout (in seconds) for executing main goal
  , showTime     :: Bool       -- show execution time of main goal?
  , withEcho     :: Bool       -- echoing REPL commands?
  , withShow     :: Bool       -- use class `Show` to show results
  , showBindings :: Bool       -- show free variables in main goal in output?
  , safeExec     :: Bool       -- safe execution mode without I/O actions
  , parseOpts    :: String     -- additional options for the front end
  , rtsArgs      :: String     -- run-time arguments passed to main application
  , cmpOpts      :: [CCOptionImpl] -- current compiler-specific options
  , quit         :: Bool       -- terminate the REPL?
  , exitStatus   :: Int        -- exit status (set in case of REPL errors)
  , sourceguis   :: [(String,(String,Handle))] -- handles to SourceProgGUIs
  }

--- Initial state of REPL w.r.t. a compiler description
initReplState :: CCDescription -> IO ReplState
initReplState cd = do
  pid <- getPID
  let compilerid = filter isAlphaNum (ccName cd)
  mainmod <- getUnusedMod ("Main" ++ compilerid ++ show pid)
  return $ ReplState
    { compiler     = cd
    , usingOption  = ""
    , rcVars       = []
    , verbose      = 1
    , libPaths     = splitSearchPath (ccLibPath cd)
    , importPaths  = []
    , preludeName  = "Prelude"
    , currMod      = "Prelude"
    , addMods      = []
    , mainExpMod   = mainmod
    , prompt       = "%s> "
    , timeOut      = 0
    , showTime     = False
    , withEcho     = False
    , withShow     = False
    , showBindings = False
    , safeExec     = False
    , parseOpts    = ""
    , rtsArgs      = ""
    , cmpOpts      = map (\ (CCOption _ _ tags) -> head tags) (ccOpts cd)
    , quit         = False
    , exitStatus   = 0
    , sourceguis   = []
    }
 where
  getUnusedMod f = do
    ex <- doesFileExist (f ++ ".curry")
    if ex then getUnusedMod (f ++ "X")
          else return f

mainExpFile :: ReplState -> String
mainExpFile rst = mainExpMod rst ++ ".curry"

loadPaths :: ReplState -> [String]
loadPaths rst = "." : importPaths rst ++ libPaths rst

--- Show an info message for a given verbosity level
writeVerboseInfo :: ReplState -> Int -> String -> IO ()
writeVerboseInfo rst lvl msg = unless (verbose rst < lvl) $ do
  putStrLn $ (if lvl > 1 then "INFO  " else "") ++ msg
  hFlush stdout

------------------------------------------------------------------------------