packages feed

debugger-hs-0.1.1.0: src/Debugger/Render.hs

module Debugger.Render
  ( renderToStdOut
  , renderIO
  , renderScript
  ) where

import Control.Monad.Reader
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Debugger.Statement


-- | Renders a GDB script and writes it to stdout.
renderToStdOut :: Script -> IO ()
renderToStdOut script =
  let txt = renderScript script
   in TIO.putStrLn txt

-- | Renders a GDB script and writes it to the given file path.
renderIO :: Script -> FilePath -> IO ()
renderIO script path =
  let txt = renderScript script
   in TIO.writeFile path txt

-- | Renders a GDB script
renderScript :: Script -> T.Text
renderScript script =
  interleaveNewlines $ map render script

render :: Statement -> T.Text
render stmt = runReader (go stmt) 0
  where
    go = \case
      Break loc ->
        pure $ "break " <> renderLoc loc
      Command bp stmts -> do
        block <- local (+2) $ traverse (indent <=< go) stmts
        -- need to indent the end explicitly, because lines will be joined
        -- together before returning and only start is indented
        end <- indent "end"
        pure $ interleaveNewlines
          [ "command " <> renderId bp
          , interleaveNewlines block
          , end
          ]
      Continue ->
        pure "continue"
      Next count ->
        pure $ "next" <> renderCount count
      Step count ->
        pure $ "step" <> renderCount count
      Run ->
        pure "run"
      Reset ->
        pure "monitor reset"
      Set var expr ->
        pure $ T.unwords ["set", var, "=", expr]
      Call expr ->
        pure $ T.unwords ["call", expr]
      Print val ->
        pure $ "print " <> "\"" <> val <> "\""
      Delete sel ->
        pure $ T.strip $ "delete " <> renderSelection sel
      Disable sel ->
        pure $ T.strip $ "disable " <> renderSelection sel
      Enable sel ->
        pure $ T.strip $ "enable " <> renderSelection sel
      Shell cmd ->
        pure $ "shell " <> cmd
      Source file ->
        pure $ "source " <> T.pack file
      Target target ->
        pure $ "target " <> renderTargetConfig target
      Info opts ->
        pure $ "info " <> renderInfoOpts opts

    indent txt = do
      spaces <- ask
      let indentation = T.replicate spaces " "
      pure $ indentation <> txt


renderId :: Id -> T.Text
renderId (Id txt) = txt

renderLoc :: Location -> T.Text
renderLoc = \case
  Function func -> func
  File path line -> T.pack path <> ":" <> T.pack (show line)

renderSelection :: Selection -> T.Text
renderSelection = \case
  Single bp -> renderId bp
  Many bps -> T.intercalate " " $ map renderId bps
  All -> ""

renderTargetConfig :: TargetConfig -> T.Text
renderTargetConfig = \case
  Remote port -> "remote tcp:localhost:" <> T.pack (show port)

renderInfoOpts :: InfoOptions -> T.Text
renderInfoOpts = \case
  Breakpoints -> "breakpoints"

renderCount :: Maybe Int -> T.Text
renderCount = \case
  Nothing -> ""
  Just x -> " " <> T.pack (show x)

interleaveNewlines :: [T.Text] -> T.Text
interleaveNewlines txts = T.intercalate "\n" txts