packages feed

elm-repl-0.4: src/Parse.hs

module Parse (rawInput) where

import qualified Data.Char as Char
import Data.Functor ((<$>))
import qualified Data.List as List
import Text.Parsec (Parsec, (<|>), anyChar, char, choice, eof, many, many1,
                    manyTill, parse, satisfy, space, spaces, string)

import qualified Input


type Parser = Parsec String ()


rawInput :: String -> Input.Input
rawInput input =
    case parse result "" input of
      Right action -> action
      Left errorMessage ->
          Input.Meta . Input.Help $ Just (show errorMessage)


result :: Parser Input.Input
result =
  do  spaces
      skip <|> cmd <|> term
  where
    skip = eof >> return Input.Skip
    cmd  = char ':' >> Input.Meta <$> command
    term = Input.Code . extractCode <$> many anyChar


command :: Parser Input.Command
command =
  do  flag <- many1 notSpace
      spaces
      case flag of
        "exit"  -> basicCommand Input.Exit
        "reset" -> basicCommand Input.Reset
        "help"  -> basicCommand (Input.Help Nothing)
        "flags" -> basicCommand (Input.InfoFlags Nothing) <|> flags
        _       -> return $ Input.Help (Just flag)
  where
    basicCommand cmd =
        eof >> return cmd


flags :: Parser Input.Command
flags =
  do  flag <- many1 notSpace
      case flag of
        "add"    -> srcDirFlag Input.AddFlag
        "remove" -> srcDirFlag Input.RemoveFlag
        "list"   -> return Input.ListFlags
        "clear"  -> return Input.ClearFlags
        _        -> return $ Input.InfoFlags . Just $ flag
  where
    srcDirFlag ctor =
      do  many1 space
          ctor <$> srcDir


notSpace :: Parser Char
notSpace =
    satisfy (not . Char.isSpace)


srcDir :: Parser String
srcDir =
  do  string "--src-dir="
      dir <- manyTill anyChar (choice [ space >> return (), eof ])
      return ("--src-dir=" ++ dir)


extractCode :: String -> (Maybe Input.DefName, String)
extractCode rawInput =
    (extractDefName rawInput, rawInput)


extractDefName :: String -> Maybe Input.DefName
extractDefName src
    | List.isPrefixOf "import " src =
        let getFirstCap tokens =
                case tokens of
                  token@(c:_) : rest ->
                      if Char.isUpper c then token else getFirstCap rest
                  _ -> src
        in
            Just $ Input.Import (getFirstCap (words src))

    | List.isPrefixOf "type alias " src =
        let name = takeWhile (/=' ') . drop 11 $ src
        in  Just $ Input.DataDef name

    | List.isPrefixOf "type " src =
        let name = takeWhile (/=' ') . drop 5 $ src
        in  Just $ Input.DataDef name

    | otherwise =
        case break (=='=') src of
          (_,"") -> Nothing

          (beforeEquals, _:c:_) ->
              if Char.isSymbol c || hasLet beforeEquals || hasBrace beforeEquals
                  then Nothing
                  else Just $ Input.VarDef (declName beforeEquals)

          _ -> error errorMessage

        where
          errorMessage =
              "Internal error in elm-repl function Parse.mkCode\n\
              \Please submit bug report to <https://github.com/elm-lang/elm-repl/issues>"

          declName pattern =
              case takeWhile Char.isSymbol $ dropWhile (not . Char.isSymbol) pattern of
                "" -> takeWhile (/=' ') pattern
                op -> op


hasLet :: String -> Bool
hasLet body =
    elem "let" $ map token (words body)
  where
    isVarChar c =
        Char.isAlpha c || Char.isDigit c || elem c "_'"

    token word =
        takeWhile isVarChar $ dropWhile (not . Char.isAlpha) word


hasBrace :: String -> Bool
hasBrace body =
    elem '{' body