packages feed

g2-0.2.0.0: src/G2/QuasiQuotes/Parser.hs

module G2.QuasiQuotes.Parser
  ( QuotedExtract (..)
  , extractQuotedData
  , transformQuoted
  , quotedEx2Hsk
  , getConcVars
  ) where


import Data.Array
import Data.Char
import Data.List
import Text.Regex
import Text.Regex.Base

trim :: String -> String
trim = dropWhileEnd isSpace . dropWhile isSpace

--------------------
-- Regular expressions

-- RS stands for Regex String
idRs :: String
idRs = "[A-Za-z][0-9A-Za-z_]*"

whiteSpaceRs :: String
whiteSpaceRs = "[ \t\r\n\v\f]"

paddedIdRs :: String
paddedIdRs = whiteSpaceRs ++ "*" ++ idRs ++ whiteSpaceRs ++ "*"

bar :: String
bar = "|"

symbVarRegex :: Regex
symbVarRegex = mkRegex $ "[?][(]" ++ paddedIdRs ++ "::[^!?|]*"

lamDividerRegex :: Regex
lamDividerRegex = mkRegex $ "->" ++ whiteSpaceRs ++ "*" ++ "[?]"

dividerRegex :: Regex
dividerRegex = mkRegex $ "[" ++ bar ++ "]"

-- Output from matchAlltext
matchAllText2 :: RegexLike regex source => regex -> source -> [source]
matchAllText2 regex = map (fst . snd) . concatMap assocs . matchAllText regex

idPairFrom2Colon :: String -> (String, String)
idPairFrom2Colon twoColonSepd =
  case splitRegex (mkRegex "::") twoColonSepd of
    (s1 : s2 : []) ->
      case (splitRegex (mkRegex "[(]") s1, reverse $ trim s2) of
        (_ : varName : [], ')' : revVarType)
          -> (trim varName, "(" ++ reverse revVarType ++ ")")
        _ -> error $ "idPairFromColon: incorrect decomp: " ++ show (s1, s2)
    _ -> error $ "idPairFrom2Colon: incorrect string " ++ show twoColonSepd

  {-
  case matchAllText2 (mkRegex idRs) twoColonSepd of
    (s1 : s2 : []) -> (trim s1, trim s2)
    _ -> error $ "idPairFrom2Colon: incorrect string " ++ show twoColonSepd
  -}

-- These three functions need to be fed processed strings
-- getConcVars :: String -> [(String, String)]
-- getConcVars chewed = map idPairFrom2Colon $ matchAllText2 concVarRegex chewed

getConcVars :: String -> [(String, String)]
getConcVars = getConcVars' 0 ""

getConcVars' :: Int -> String -> String -> [(String, String)]
getConcVars' n pr ('(':xs)
    | n == 0 = getConcVars' (n + 1) pr xs
    | otherwise = getConcVars' (n + 1) ('(':pr) xs
getConcVars' n pr (')':xs)
    | n == 1
    , [v, t] <- splitRegex (mkRegex "::") (reverse pr) = (filter (not . isSpace) v, "(" ++ t ++ ")"):getConcVars' (n - 1) "" xs
    | otherwise = getConcVars' (n - 1) (')':pr) xs
getConcVars' n pr (x:xs)
  | n > 0 = getConcVars' n (x:pr) xs
  | otherwise = getConcVars' n (x:pr) xs
getConcVars' _ pr []
    | [v, t] <- splitRegex (mkRegex "::") (reverse pr) = [(filter (not . isSpace) v, "(" ++ t ++ ")")]
    | otherwise = []

getSymbVars :: String -> [(String, String)]
getSymbVars chewed = map idPairFrom2Colon $ matchAllText2 symbVarRegex chewed

-----------------------
-- Parsing extracted stuff

data QuotedExtract = QuotedExtract
  { concVars :: [(String, String)] -- (varName, VarType)
  , symbVars :: [(String, String)] -- (varName, varType)
  , bodyExpr :: String
  } deriving (Show, Eq)

-- Extract the conc var-ty pairs, symb var-ty pairs, and fun body
extractQuotedData :: String -> QuotedExtract
extractQuotedData raw
  | (h:raw') <- dropWhile isSpace raw
  , h == '\\' = 
  -- First split based on the divider bar "|"
  -- into variable declarations and body
  case splitRegex dividerRegex raw' of
    (hd : tl) ->
      -- Further partiton with "-> ?" into concrete and symbolics
      -- Remember that splitting will consume the initial ?
      case splitRegex lamDividerRegex hd of
        (concs : symbs) ->
          QuotedExtract
            { concVars = getConcVars concs
            , symbVars = getSymbVars $ '?' : concat symbs
            , bodyExpr = trim $ intercalate bar tl
            }
        _ -> error $ "extractQuotedData: bad text " ++ show raw
    _ -> error $ "extractQuotedData: bad text " ++ show raw
  | otherwise = error $ "extractQuotedData: bad text " ++ show raw

quotedEx2Hsk :: QuotedExtract -> String
quotedEx2Hsk quoted =
  let (concVs, concTs) = unzip $ concVars quoted
      (symbVs, symbTs) = unzip $ symbVars quoted
      bodyStr = bodyExpr quoted
      typeStr = intercalate " -> " (concTs ++ symbTs ++ ["Bool"])
      concParamStr = intercalate " " concVs
      symbParamStr = intercalate " " symbVs
  in
    "(\\ " ++ concParamStr ++ " -> " 
      ++ "\\ " ++ symbParamStr ++ " -> "
        ++ bodyStr ++ ") :: " ++ typeStr

-------------------
-- The one-pass function
transformQuoted :: String -> String
transformQuoted = quotedEx2Hsk . extractQuotedData