{-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS -fno-warn-name-shadowing #-} -- This module does a lot of it ----------------------------------------------------------------------------- -- -- GHC Interactive User Interface -- -- (c) The GHC Team 2005-2006 -- ----------------------------------------------------------------------------- module Clash.GHCi.UI ( interactiveUI, GhciSettings(..), defaultGhciSettings, ghciCommands, ghciWelcomeMsg, makeHDL ) where -- GHCi import qualified Clash.GHCi.UI.Monad as GhciMonad ( args, runStmt, runDecls' ) import Clash.GHCi.UI.Monad hiding ( args, runStmt ) import Clash.GHCi.UI.Info import Clash.GHCi.UI.Exception import GHC.Runtime.Debugger -- The GHC interface import GHC.Runtime.Interpreter import GHCi.RemoteTypes import GHCi.BreakArray( breakOn, breakOff ) import GHC.ByteCode.Types import GHC.Core.DataCon import GHC.Core.ConLike import GHC.Core.PatSyn import GHC.Driver.Errors import GHC.Driver.Errors.Types import GHC.Driver.Phases import GHC.Driver.Session as DynFlags import GHC.Driver.Ppr hiding (printForUser) import GHC.Utils.Error hiding (traceCmd) import GHC.Driver.Monad ( modifySession ) import GHC.Driver.Make ( newIfaceCache, ModIfaceCache(..) ) import GHC.Driver.Config.Parser (initParserOpts) import GHC.Driver.Config.Diagnostic import qualified GHC import GHC ( LoadHowMuch(..), Target(..), TargetId(..), Resume, SingleStep, Ghc, GetDocsFailure(..), pushLogHookM, getModuleGraph, handleSourceError, ms_mod ) import GHC.Driver.Main (hscParseModuleWithLocation, hscParseStmtWithLocation) import GHC.Hs.ImpExp import GHC.Hs import GHC.Driver.Env import GHC.Runtime.Context import GHC.Types.TyThing import GHC.Types.TyThing.Ppr import GHC.Core.TyCo.Ppr import GHC.Types.SafeHaskell ( getSafeMode ) import GHC.Types.SourceError ( SourceError ) import GHC.Types.Name import GHC.Types.Var ( varType ) import GHC.Iface.Syntax ( showToHeader ) import GHC.Builtin.Names import GHC.Builtin.Types( stringTyCon_RDR ) import GHC.Types.Name.Reader as RdrName ( getGRE_NameQualifier_maybes, getRdrName ) import GHC.Types.SrcLoc as SrcLoc import qualified GHC.Parser.Lexer as Lexer import GHC.Parser.Header ( toArgs ) import qualified GHC.Parser.Header as Header import GHC.Types.PkgQual import GHC.Unit import GHC.Unit.Finder as Finder import GHC.Unit.Module.Graph (filterToposortToModules) import GHC.Unit.Module.ModSummary import GHC.Data.StringBuffer import GHC.Utils.Outputable import GHC.Utils.Logger -- Other random utilities import GHC.Types.Basic hiding ( isTopLevel ) import GHC.Data.Graph.Directed import GHC.Utils.Encoding import GHC.Data.FastString import qualified GHC.Linker.Loader as Loader import GHC.Data.Maybe ( orElse, expectJust ) import GHC.Types.Name.Set import GHC.Utils.Panic hiding ( showException, try ) import GHC.Utils.Panic.Plain import GHC.Utils.Misc import qualified GHC.LanguageExtensions as LangExt import GHC.Data.Bag (unitBag) import qualified GHC.Data.Strict as Strict import GHC.Types.Error -- Haskell Libraries import System.Console.Haskeline as Haskeline import Control.Applicative hiding (empty) import Control.DeepSeq (deepseq) import Control.Monad as Monad import Control.Monad.Catch as MC import Control.Monad.IO.Class import Control.Monad.Trans.Class import Control.Monad.Trans.Except import Data.Array import qualified Data.ByteString.Char8 as BS import Data.Char import Data.Function import Data.IORef ( IORef, modifyIORef, newIORef, readIORef, writeIORef ) import Data.List ( elemIndices, find, intercalate, intersperse, minimumBy, isPrefixOf, isSuffixOf, nub, partition, sort, sortBy, (\\) ) import qualified Data.List.NonEmpty as NE import qualified Data.Set as S import Data.Maybe import qualified Data.Map as M import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as IntMap import Data.Time.LocalTime ( getZonedTime ) import Data.Time.Format ( formatTime, defaultTimeLocale ) import Data.Version ( showVersion ) import qualified Data.Semigroup as S import Prelude hiding ((<>)) import GHC.Utils.Exception as Exception hiding (catch, mask, handle) import Foreign hiding (void) import GHC.Stack hiding (SrcLoc(..)) import GHC.Unit.Env import GHC.Unit.Home.ModInfo import System.Directory import System.Environment import System.Exit ( exitWith, ExitCode(..) ) import System.FilePath import System.Info import System.IO import System.IO.Error import System.IO.Unsafe ( unsafePerformIO ) import System.Process import Text.Printf import Text.Read ( readMaybe ) import Text.Read.Lex (isSymbolChar) import Unsafe.Coerce #if !defined(mingw32_HOST_OS) import System.Posix hiding ( getEnv ) #else import qualified System.Win32 #endif import GHC.IO.Exception ( IOErrorType(InvalidArgument) ) import GHC.IO.Handle ( hFlushAll ) import GHC.TopHandler ( topHandler ) import Clash.GHCi.Leak import qualified GHC.Unit.Module.Graph as GHC -- clash additions import Clash.Backend (Backend(initBackend, hdlKind, primDirs)) import Clash.Backend.SystemVerilog (SystemVerilogState) import Clash.Backend.VHDL (VHDLState) import Clash.Backend.Verilog (VerilogState) import qualified Clash.Driver import Clash.Driver.Bool (fromGhcOverridingBool) import Clash.Driver.Types (ClashOpts(..), ClashEnv(..), ClashDesign(..)) import Clash.GHC.Evaluator import Clash.GHC.GenerateBindings import Clash.GHC.NetlistTypes import Clash.GHC.PartialEval import Clash.GHCi.Common import Clash.Util (clashLibVersion, reportTimeDiff) import Data.Proxy import qualified Data.Time.Clock as Clock import qualified Paths_clash_ghc ----------------------------------------------------------------------------- data GhciSettings = GhciSettings { availableCommands :: [Command], shortHelpText :: String, fullHelpText :: String, defPrompt :: PromptFunction, defPromptCont :: PromptFunction } defaultGhciSettings :: IORef ClashOpts -> GhciSettings defaultGhciSettings opts = GhciSettings { availableCommands = ghciCommands opts, shortHelpText = defShortHelpText, defPrompt = default_prompt, defPromptCont = default_prompt_cont, fullHelpText = defFullHelpText } ghciWelcomeMsg :: String ghciWelcomeMsg = "Clashi, version " ++ Data.Version.showVersion Paths_clash_ghc.version ++ " (using clash-lib, version " ++ Data.Version.showVersion clashLibVersion ++ "):\nhttps://clash-lang.org/ :? for help" ghciCommands :: IORef ClashOpts -> [Command] ghciCommands opts = map mkCmd [ -- Hugs users are accustomed to :e, so make sure it doesn't overlap ("?", keepGoing help, noCompletion), ("add", keepGoingPaths addModule, completeFilename), ("abandon", keepGoing abandonCmd, noCompletion), ("break", keepGoing breakCmd, completeBreakpoint), ("back", keepGoing backCmd, noCompletion), ("browse", keepGoing' (browseCmd False), completeModule), ("browse!", keepGoing' (browseCmd True), completeModule), ("cd", keepGoingMulti' changeDirectory, completeFilename), ("check", keepGoing' checkModule, completeHomeModule), ("continue", keepGoing continueCmd, noCompletion), ("cmd", keepGoing cmdCmd, completeExpression), ("def", keepGoing (defineMacro False), completeExpression), ("def!", keepGoing (defineMacro True), completeExpression), ("delete", keepGoing deleteCmd, noCompletion), ("disable", keepGoing disableCmd, noCompletion), ("doc", keepGoing' docCmd, completeIdentifier), ("edit", keepGoingMulti' editFile, completeFilename), ("enable", keepGoing enableCmd, noCompletion), ("force", keepGoing forceCmd, completeExpression), ("forward", keepGoing forwardCmd, noCompletion), ("help", keepGoingMulti help, noCompletion), ("history", keepGoingMulti historyCmd, noCompletion), ("info", keepGoingMulti' (info False), completeIdentifier), ("info!", keepGoingMulti' (info True), completeIdentifier), ("issafe", keepGoing' isSafeCmd, completeModule), ("ignore", keepGoing ignoreCmd, noCompletion), ("kind", keepGoingMulti' (kindOfType False), completeIdentifier), ("kind!", keepGoingMulti' (kindOfType True), completeIdentifier), ("load", keepGoingPaths loadModule_, completeHomeModuleOrFile), ("load!", keepGoingPaths loadModuleDefer, completeHomeModuleOrFile), ("list", keepGoing' listCmd, noCompletion), ("module", keepGoing moduleCmd, completeSetModule), ("main", keepGoing runMain, completeFilename), ("print", keepGoing printCmd, completeExpression), ("quit", quit, noCompletion), ("reload", keepGoingMulti' reloadModule, noCompletion), ("reload!", keepGoingMulti' reloadModuleDefer, noCompletion), ("run", keepGoing runRun, completeFilename), ("script", keepGoing' scriptCmd, completeFilename), ("set", keepGoingMulti setCmd, completeSetOptions), ("seti", keepGoingMulti setiCmd, completeSeti), ("show", keepGoingMulti' showCmd, completeShowOptions), ("showi", keepGoing showiCmd, completeShowiOptions), ("sprint", keepGoing sprintCmd, completeExpression), ("step", keepGoing stepCmd, completeIdentifier), ("steplocal", keepGoing stepLocalCmd, completeIdentifier), ("stepmodule",keepGoing stepModuleCmd, completeIdentifier), ("type", keepGoingMulti' typeOfExpr, completeExpression), ("trace", keepGoing traceCmd, completeExpression), ("unadd", keepGoingPaths unAddModule, completeFilename), ("undef", keepGoing undefineMacro, completeMacro), ("unset", keepGoing unsetOptions, completeSetOptions), ("where", keepGoing whereCmd, noCompletion), ("vhdl", keepGoingPaths (makeVHDL opts), completeHomeModuleOrFile), ("verilog", keepGoingPaths (makeVerilog opts), completeHomeModuleOrFile), ("systemverilog",keepGoingPaths (makeSystemVerilog opts), completeHomeModuleOrFile), ("instances", keepGoing' instancesCmd, completeExpression) ] ++ map mkCmdHidden [ -- hidden commands ("all-types", keepGoing' allTypesCmd), ("complete", keepGoing completeCmd), ("loc-at", keepGoing' locAtCmd), ("type-at", keepGoing' typeAtCmd), ("uses", keepGoing' usesCmd) ] where mkCmd (n,a,c) = Command { cmdName = n , cmdAction = a , cmdHidden = False , cmdCompletionFunc = c } mkCmdHidden (n,a) = Command { cmdName = n , cmdAction = a , cmdHidden = True , cmdCompletionFunc = noCompletion } -- We initialize readline (in the interactiveUI function) to use -- word_break_chars as the default set of completion word break characters. -- This can be overridden for a particular command (for example, filename -- expansion shouldn't consider '/' to be a word break) by setting the third -- entry in the Command tuple above. -- -- NOTE: in order for us to override the default correctly, any custom entry -- must be a SUBSET of word_break_chars. word_break_chars :: String word_break_chars = spaces ++ specials ++ symbols word_break_chars_pred :: Char -> Bool word_break_chars_pred '.' = False word_break_chars_pred c = c `elem` (spaces ++ specials) || isSymbolChar c symbols, specials, spaces :: String symbols = "!#$%&*+/<=>?@\\^|-~" specials = "(),;[]`{}" spaces = " \t\n" flagWordBreakChars :: String flagWordBreakChars = " \t\n" showSDocForUser' :: GHC.GhcMonad m => SDoc -> m String showSDocForUser' doc = do dflags <- getDynFlags unit_state <- hsc_units <$> GHC.getSession name_ppr_ctx <- GHC.getNamePprCtx pure $ showSDocForUser dflags unit_state name_ppr_ctx doc showSDocForUserQualify :: GHC.GhcMonad m => SDoc -> m String showSDocForUserQualify doc = do dflags <- getDynFlags unit_state <- hsc_units <$> GHC.getSession pure $ showSDocForUser dflags unit_state alwaysQualify doc keepGoing :: (String -> GHCi ()) -> (String -> InputT GHCi CmdExecOutcome) keepGoing a str = keepGoing' (lift . a) str keepGoingMulti :: (String -> GHCi ()) -> (String -> InputT GHCi CmdExecOutcome) keepGoingMulti a str = keepGoingMulti' (lift . a) str keepGoing' :: GhciMonad m => (a -> m ()) -> a -> m CmdExecOutcome keepGoing' a str = do in_multi <- inMultiMode if in_multi then liftIO $ hPutStrLn stderr "Command is not supported (yet) in multi-mode" else a str return CmdSuccess -- For commands which are actually support in multi-mode, initially just :reload keepGoingMulti' :: GhciMonad m => (String -> m ()) -> String -> m CmdExecOutcome keepGoingMulti' a str = a str >> return CmdSuccess inMultiMode :: GhciMonad m => m Bool inMultiMode = multiMode <$> getGHCiState keepGoingPaths :: ([FilePath] -> InputT GHCi ()) -> (String -> InputT GHCi CmdExecOutcome) keepGoingPaths a str = do case toArgsNoLoc str of Left err -> liftIO $ hPutStrLn stderr err >> return CmdSuccess Right args -> keepGoing' a args defShortHelpText :: String defShortHelpText = "use :? for help.\n" defFullHelpText :: String defFullHelpText = " Commands available from the prompt:\n" ++ "\n" ++ " evaluate/run \n" ++ " : repeat last command\n" ++ " :{\\n ..lines.. \\n:}\\n multiline command\n" ++ " :add [*] ... add module(s) to the current target set\n" ++ " :browse[!] [[*]] display the names defined by module \n" ++ " (!: more details; *: all top-level names)\n" ++ " :cd change directory to \n" ++ " :cmd run the commands returned by ::IO String\n" ++ " :complete [] list completions for partial input string\n" ++ " :def[!] define command : (later defined command has\n" ++ " precedence, :: is always a builtin command)\n" ++ " (!: redefine an existing command name)\n" ++ " :doc display docs for the given name (experimental)\n" ++ " :edit edit file\n" ++ " :edit edit last module\n" ++ " :help, :? display this list of commands\n" ++ " :info[!] [ ...] display information about the given names\n" ++ " (!: do not filter instances)\n" ++ " :instances display the class instances available for \n" ++ " :issafe [] display safe haskell information of module \n" ++ " :kind[!] show the kind of \n" ++ " (!: also print the normalised type)\n" ++ " :load[!] [*] ... load module(s) and their dependents\n" ++ " (!: defer type errors)\n" ++ " :main [ ...] run the main function with the given arguments\n" ++ " :module [+/-] [*] ... set the context for expression evaluation\n" ++ " :quit exit GHCi\n" ++ " :reload[!] reload the current module set\n" ++ " (!: defer type errors)\n" ++ " :run function [ ...] run the function with the given arguments\n" ++ " :script run the script \n" ++ " :type show the type of \n" ++ " :type +d show the type of , defaulting type variables\n" ++ " :unadd ... remove module(s) from the current target set\n" ++ " :undef undefine user-defined command :\n" ++ " :: run the builtin command\n" ++ " :! run the shell command \n" ++ " :vhdl synthesize currently loaded module to vhdl\n" ++ " :vhdl [] synthesize specified modules/files to vhdl\n" ++ " :verilog synthesize currently loaded module to verilog\n" ++ " :verilog [] synthesize specified modules/files to verilog\n" ++ " :systemverilog synthesize currently loaded module to systemverilog\n" ++ " :systemverilog [] synthesize specified modules/files to systemverilog\n" ++ "\n" ++ " -- Commands for debugging:\n" ++ "\n" ++ " :abandon at a breakpoint, abandon current computation\n" ++ " :back [] go back in the history N steps (after :trace)\n" ++ " :break [] [] set a breakpoint at the specified location\n" ++ " :break set a breakpoint on the specified function\n" ++ " :continue [] resume after a breakpoint [and set break ignore count]\n" ++ " :delete ... delete the specified breakpoints\n" ++ " :delete * delete all breakpoints\n" ++ " :disable ... disable the specified breakpoints\n" ++ " :disable * disable all breakpoints\n" ++ " :enable ... enable the specified breakpoints\n" ++ " :enable * enable all breakpoints\n" ++ " :force print , forcing unevaluated parts\n" ++ " :forward [] go forward in the history N step s(after :back)\n" ++ " :history [] after :trace, show the execution history\n" ++ " :ignore for break set break ignore \n" ++ " :list show the source code around current breakpoint\n" ++ " :list show the source code for \n" ++ " :list [] show the source code around line number \n" ++ " :print [ ...] show a value without forcing its computation\n" ++ " :sprint [ ...] simplified version of :print\n" ++ " :step single-step after stopping at a breakpoint\n"++ " :step single-step into \n"++ " :steplocal single-step within the current top-level binding\n"++ " :stepmodule single-step restricted to the current module\n"++ " :trace trace after stopping at a breakpoint\n"++ " :trace evaluate with tracing on (see :history)\n"++ "\n" ++ " -- Commands for changing settings:\n" ++ "\n" ++ " :set