packages feed

crux-0.8.0.0: src/Crux/UI/JS.hs

{-# Language MultiWayIf #-}
{-# Language OverloadedStrings #-}
-- | Utilites for generating JSON
module Crux.UI.JS where

import Data.Text(unpack, Text)
import qualified Data.Text as Text
import Numeric
import Data.List(intercalate)
import Data.Maybe(fromMaybe)
import System.Directory( canonicalizePath )

import What4.ProgramLoc

-- | 'jsLoc' takes a program location and renders it as a JavaScript string.
-- This returns @Nothing@ if it is unclear how to render the program location.
jsLoc :: ProgramLoc -> IO (Maybe JS)
jsLoc x =
  case plSourceLoc x of
    SourcePos fname l c -> parsePos fname l c
    -- Attempt to parse `OtherPos` in case it is in fact a code span:
    --
    --   * This case is necessary because of the particular shape of source
    --   spans that arise from `mir-json`
    --   (e.g., `test/symb_eval/num/checked_mul.rs:6:5: 6:12:`), which will
    --   always be represented as `OtherPos`.
    --   * While `crux` doesn't have the machinery to represent the entire
    --   source span in its UI framework, we can still achieve partial results
    --   by parsing the first location from the source span
    --   (e.g., `the test/symb_eval/num/checked_mul.rs:6:5:` bit). This does
    --   not show the entire span, but it is still better than nothing.
    OtherPos s
      | fname : line : col : _rest <- Text.split (==':') s
      , (l,[]):_ <- readDec (Text.unpack (Text.strip line))
      , (c,[]):_ <- readDec (Text.unpack (Text.strip col)) ->
        parsePos fname l c
    _ -> pure Nothing
    where
    parsePos :: Text -> Int -> Int -> IO (Maybe JS)
    parsePos f l c = do
      let fstr = unpack f
      fabsolute <-
        if null fstr
          then pure ""
          else canonicalizePath fstr
      pure $ Just $ jsObj
        [ "file" ~> jsStr fabsolute
        , "line" ~> jsStr (show l)
        , "col"  ~> jsStr (show c)
        ]

--------------------------------------------------------------------------------
newtype JS = JS { renderJS :: String }

jsList :: [JS] -> JS
jsList xs = JS $ "[" ++ intercalate "," [ x | JS x <- xs ] ++ "]"

infix 1 ~>

(~>) :: a -> b -> (a,b)
(~>) = (,)

jsObj :: [(String,JS)] -> JS
jsObj xs =
  JS $ "{" ++ intercalate "," [ show x ++ ": " ++ v | (x,JS v) <- xs ] ++ "}"

jsBool :: Bool -> JS
jsBool b = JS (if b then "true" else "false")

jsStr :: String -> JS
jsStr = JS . show

jsNull :: JS
jsNull = JS "null"

jsMaybe :: Maybe JS -> JS
jsMaybe = fromMaybe jsNull

jsNum :: Show a => a -> JS
jsNum = JS . show


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