Fix issue 61: GF shell cannot parse a system command ending with a space

Trailing spaces caused the command line parse to be ambiguous, and
ambiguous parses were rejected by function readCommandLine, causing
the cryptic error message "command not parsed".
This commit is contained in:
hallgren
2013-11-11 15:13:24 +00:00
parent 827bd7aee9
commit d1314dcbcd

View File

@@ -1,63 +1,65 @@
module GF.Command.Parse(readCommandLine, pCommand) where module GF.Command.Parse(readCommandLine, pCommand) where
import PGF import PGF(pExpr,pIdent)
import GF.Command.Abstract import GF.Command.Abstract
import Data.Char import Data.Char(isDigit,isSpace)
import Control.Monad import Control.Monad(liftM2)
import qualified Text.ParserCombinators.ReadP as RP import Text.ParserCombinators.ReadP
readCommandLine :: String -> Maybe CommandLine readCommandLine :: String -> Maybe CommandLine
readCommandLine s = case [x | (x,cs) <- RP.readP_to_S pCommandLine s, all isSpace cs] of readCommandLine s =
[x] -> Just x case [x | (x,cs) <- readP_to_S pCommandLine s, all isSpace cs] of
_ -> Nothing [x] -> Just x
_ -> Nothing
pCommandLine = pCommandLine =
(RP.skipSpaces >> RP.char '-' >> RP.char '-' >> RP.skipMany (RP.satisfy (const True)) >> return []) -- comment (skipSpaces >> char '-' >> char '-' >> pTheRest >> return []) -- comment
RP.<++ <++
(RP.sepBy (RP.skipSpaces >> pPipe) (RP.skipSpaces >> RP.char ';')) (sepBy (skipSpaces >> pPipe) (skipSpaces >> char ';'))
pPipe = RP.sepBy1 (RP.skipSpaces >> pCommand) (RP.skipSpaces >> RP.char '|') pPipe = sepBy1 (skipSpaces >> pCommand) (skipSpaces >> char '|')
pCommand = (do pCommand = (do
cmd <- pIdent RP.<++ (RP.char '%' >> pIdent >>= return . ('%':)) cmd <- pIdent <++ (char '%' >> pIdent >>= return . ('%':))
RP.skipSpaces skipSpaces
opts <- RP.sepBy pOption RP.skipSpaces opts <- sepBy pOption skipSpaces
arg <- pArgument arg <- pArgument
return (Command cmd opts arg) return (Command cmd opts arg)
) )
RP.<++ (do <++ (do
RP.char '?' char '?'
c <- pSystemCommand skipSpaces
c <- pSystemCommand
return (Command "sp" [OFlag "command" (VStr c)] ANoArg) return (Command "sp" [OFlag "command" (VStr c)] ANoArg)
) )
pOption = do pOption = do
RP.char '-' char '-'
flg <- pIdent flg <- pIdent
RP.option (OOpt flg) (fmap (OFlag flg) (RP.char '=' >> pValue)) option (OOpt flg) (fmap (OFlag flg) (char '=' >> pValue))
pValue = do pValue = do
fmap VInt (RP.readS_to_P reads) fmap VInt (readS_to_P reads)
RP.<++ <++
fmap VStr (RP.readS_to_P reads) fmap VStr (readS_to_P reads)
RP.<++ <++
fmap VId pFilename fmap VId pFilename
pFilename = liftM2 (:) (RP.satisfy isFileFirst) (RP.munch (not . isSpace)) where pFilename = liftM2 (:) (satisfy isFileFirst) (munch (not . isSpace)) where
isFileFirst c = not (isSpace c) && not (isDigit c) isFileFirst c = not (isSpace c) && not (isDigit c)
pArgument = pArgument =
RP.option ANoArg option ANoArg
(fmap AExpr pExpr (fmap AExpr pExpr
RP.<++ <++
(RP.munch isSpace >> RP.char '%' >> fmap AMacro pIdent)) (skipSpaces >> char '%' >> fmap AMacro pIdent))
pSystemCommand = pSystemCommand =
RP.munch isSpace >> ( (char '"' >> (manyTill (pEsc <++ get) (char '"')))
(RP.char '"' >> (RP.manyTill (pEsc RP.<++ RP.get) (RP.char '"'))) <++
RP.<++ pTheRest
RP.many RP.get where
) pEsc = char '\\' >> get
where
pEsc = RP.char '\\' >> RP.get pTheRest = munch (const True)