Added better malformed parsing messages
This commit is contained in:
parent
c2b9c1ff2a
commit
f7be39cb5f
|
@ -113,8 +113,29 @@ parseProblem level msg = do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
parseProblemAt pos level msg
|
parseProblemAt pos level msg
|
||||||
|
|
||||||
|
setCurrentContexts c = do
|
||||||
|
Ms.modify (\(list, _) -> (list, c))
|
||||||
|
|
||||||
|
getCurrentContexts = do
|
||||||
|
(_, context) <- Ms.get
|
||||||
|
return context
|
||||||
|
|
||||||
|
popContext = do
|
||||||
|
v <- getCurrentContexts
|
||||||
|
if not $ null v
|
||||||
|
then do
|
||||||
|
let (a:r) = v
|
||||||
|
setCurrentContexts r
|
||||||
|
return [a]
|
||||||
|
else
|
||||||
|
return []
|
||||||
|
|
||||||
|
pushContext c = do
|
||||||
|
v <- getCurrentContexts
|
||||||
|
setCurrentContexts (c:v)
|
||||||
|
|
||||||
parseProblemAt pos level msg = do
|
parseProblemAt pos level msg = do
|
||||||
Ms.modify ((ParseNote pos level msg):)
|
Ms.modify (\(list, current) -> ((ParseNote pos level msg):list, current))
|
||||||
|
|
||||||
-- Store non-parse problems inside
|
-- Store non-parse problems inside
|
||||||
addNoteFor id note = modifyMap $ Map.adjust (\(Metadata pos notes) -> Metadata pos (note:notes)) id
|
addNoteFor id note = modifyMap $ Map.adjust (\(Metadata pos notes) -> Metadata pos (note:notes)) id
|
||||||
|
@ -135,6 +156,11 @@ thenSkip main follow = do
|
||||||
optional follow
|
optional follow
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
unexpecting s p = try $ do
|
||||||
|
(try p >> unexpected s) <|> return ()
|
||||||
|
|
||||||
|
notFollowedBy2 = unexpecting "keyword/token"
|
||||||
|
|
||||||
disregard x = x >> return ()
|
disregard x = x >> return ()
|
||||||
|
|
||||||
reluctantlyTill p end = do
|
reluctantlyTill p end = do
|
||||||
|
@ -145,7 +171,7 @@ reluctantlyTill p end = do
|
||||||
<|> return []
|
<|> return []
|
||||||
|
|
||||||
reluctantlyTill1 p end = do
|
reluctantlyTill1 p end = do
|
||||||
notFollowedBy end
|
notFollowedBy2 end
|
||||||
x <- p
|
x <- p
|
||||||
more <- reluctantlyTill p end
|
more <- reluctantlyTill p end
|
||||||
return $ x:more
|
return $ x:more
|
||||||
|
@ -165,6 +191,16 @@ acceptButWarn parser level note = do
|
||||||
parseProblemAt pos level note
|
parseProblemAt pos level note
|
||||||
)
|
)
|
||||||
|
|
||||||
|
called s p = do
|
||||||
|
pos <- getPosition
|
||||||
|
pushContext (pos, s)
|
||||||
|
do
|
||||||
|
v <- p
|
||||||
|
popContext
|
||||||
|
return v
|
||||||
|
<|> do -- p failed without consuming input, abort context
|
||||||
|
popContext
|
||||||
|
fail $ ""
|
||||||
|
|
||||||
readConditionContents single = do
|
readConditionContents single = do
|
||||||
readCondContents `attempting` (lookAhead $ do
|
readCondContents `attempting` (lookAhead $ do
|
||||||
|
@ -205,7 +241,7 @@ readConditionContents single = do
|
||||||
return $ TC_Unary id typ s
|
return $ TC_Unary id typ s
|
||||||
|
|
||||||
readCondWord = do
|
readCondWord = do
|
||||||
notFollowedBy (try (spacing >> (string "]")))
|
notFollowedBy2 (try (spacing >> (string "]")))
|
||||||
x <- readNormalWord
|
x <- readNormalWord
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
if (endedWithBracket x)
|
if (endedWithBracket x)
|
||||||
|
@ -311,7 +347,7 @@ readArithmeticContents =
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
op <- choice (map (\x -> try $ do
|
op <- choice (map (\x -> try $ do
|
||||||
s <- string x
|
s <- string x
|
||||||
notFollowedBy $ oneOf "&|<>="
|
notFollowedBy2 $ oneOf "&|<>="
|
||||||
return s
|
return s
|
||||||
) op)
|
) op)
|
||||||
spacing
|
spacing
|
||||||
|
@ -394,7 +430,7 @@ readArithmeticContents =
|
||||||
where
|
where
|
||||||
readSignOp c = try $ do
|
readSignOp c = try $ do
|
||||||
char c
|
char c
|
||||||
notFollowedBy $ char c
|
notFollowedBy2 $ char c
|
||||||
spacing
|
spacing
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
@ -421,7 +457,7 @@ readArithmeticContents =
|
||||||
|
|
||||||
prop_readCondition = isOk readCondition "[ \\( a = b \\) -a \\( c = d \\) ]"
|
prop_readCondition = isOk readCondition "[ \\( a = b \\) -a \\( c = d \\) ]"
|
||||||
prop_readCondition2 = isOk readCondition "[[ (a = b) || (c = d) ]]"
|
prop_readCondition2 = isOk readCondition "[[ (a = b) || (c = d) ]]"
|
||||||
readCondition = do
|
readCondition = called "test expression" $ do
|
||||||
opos <- getPosition
|
opos <- getPosition
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
open <- (try $ string "[[") <|> (string "[")
|
open <- (try $ string "[[") <|> (string "[")
|
||||||
|
@ -481,7 +517,7 @@ readDollarBracedWord = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
list <- many readDollarBracedPart
|
list <- many readDollarBracedPart
|
||||||
return $ T_NormalWord id list
|
return $ T_NormalWord id list
|
||||||
|
|
||||||
readDollarBracedPart = readSingleQuoted <|> readDoubleQuoted <|> readExtglob <|> readDollar <|> readBackTicked <|> readDollarBracedLiteral
|
readDollarBracedPart = readSingleQuoted <|> readDoubleQuoted <|> readExtglob <|> readDollar <|> readBackTicked <|> readDollarBracedLiteral
|
||||||
|
|
||||||
readDollarBracedLiteral = do
|
readDollarBracedLiteral = do
|
||||||
|
@ -491,7 +527,7 @@ readDollarBracedLiteral = do
|
||||||
|
|
||||||
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
||||||
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
||||||
readProcSub = do
|
readProcSub = called "process substitution" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
dir <- try $ do
|
dir <- try $ do
|
||||||
x <- oneOf "<>"
|
x <- oneOf "<>"
|
||||||
|
@ -505,12 +541,12 @@ readProcSub = do
|
||||||
|
|
||||||
prop_readSingleQuoted = isOk readSingleQuoted "'foo bar'"
|
prop_readSingleQuoted = isOk readSingleQuoted "'foo bar'"
|
||||||
prop_readSingleQuoted2 = isWarning readSingleQuoted "'foo bar\\'"
|
prop_readSingleQuoted2 = isWarning readSingleQuoted "'foo bar\\'"
|
||||||
readSingleQuoted = do
|
readSingleQuoted = called "single quoted string" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
singleQuote
|
singleQuote
|
||||||
s <- readSingleQuotedPart `reluctantlyTill` singleQuote
|
s <- readSingleQuotedPart `reluctantlyTill` singleQuote
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
singleQuote <?> "End single quoted string"
|
singleQuote <?> "end of single quoted string"
|
||||||
|
|
||||||
let string = concat s
|
let string = concat s
|
||||||
return (T_SingleQuoted id string) `attempting` do
|
return (T_SingleQuoted id string) `attempting` do
|
||||||
|
@ -528,22 +564,22 @@ readSingleQuotedPart =
|
||||||
<|> anyChar `reluctantlyTill1` (singleQuote <|> backslash)
|
<|> anyChar `reluctantlyTill1` (singleQuote <|> backslash)
|
||||||
|
|
||||||
prop_readBackTicked = isWarning readBackTicked "`ls *.mp3`"
|
prop_readBackTicked = isWarning readBackTicked "`ls *.mp3`"
|
||||||
readBackTicked = do
|
readBackTicked = called "backtick expansion" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
parseNote WarningC "Use $(..) instead of deprecated `..` backtick expansion."
|
parseNote WarningC "Use $(..) instead of deprecated `..` backtick expansion."
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
char '`'
|
char '`'
|
||||||
f <- readGenericLiteral (char '`')
|
f <- readGenericLiteral (char '`')
|
||||||
char '`' `attempting` (eof >> parseProblemAt pos ErrorC "Can't find terminating backtick for this one.")
|
char '`'
|
||||||
return $ T_Backticked id f
|
return $ T_Backticked id f
|
||||||
|
|
||||||
|
|
||||||
prop_readDoubleQuoted = isOk readDoubleQuoted "\"Hello $FOO\""
|
prop_readDoubleQuoted = isOk readDoubleQuoted "\"Hello $FOO\""
|
||||||
readDoubleQuoted = do
|
readDoubleQuoted = called "double quoted string" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
doubleQuote
|
doubleQuote
|
||||||
x <- many doubleQuotedPart
|
x <- many doubleQuotedPart
|
||||||
doubleQuote <?> "End double quoted"
|
doubleQuote <?> "end of double quoted string"
|
||||||
return $ T_DoubleQuoted id x
|
return $ T_DoubleQuoted id x
|
||||||
|
|
||||||
doubleQuotedPart = readDoubleLiteral <|> readDollar <|> readBackTicked
|
doubleQuotedPart = readDoubleLiteral <|> readDollar <|> readBackTicked
|
||||||
|
@ -584,7 +620,7 @@ readGlob = readExtglob <|> readSimple <|> readClass <|> readGlobbyLiteral
|
||||||
s <- many1 (letter <|> digit <|> oneOf "^-_:")
|
s <- many1 (letter <|> digit <|> oneOf "^-_:")
|
||||||
char ']'
|
char ']'
|
||||||
return $ T_Glob id $ "[" ++ s ++ "]"
|
return $ T_Glob id $ "[" ++ s ++ "]"
|
||||||
|
|
||||||
readGlobbyLiteral = do
|
readGlobbyLiteral = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
c <- extglobStart <|> char '['
|
c <- extglobStart <|> char '['
|
||||||
|
@ -593,7 +629,7 @@ readGlob = readExtglob <|> readSimple <|> readClass <|> readGlobbyLiteral
|
||||||
readNormalLiteralPart = do
|
readNormalLiteralPart = do
|
||||||
readNormalEscaped <|> (anyChar `reluctantlyTill1` (quotable <|> extglobStart <|> char '['))
|
readNormalEscaped <|> (anyChar `reluctantlyTill1` (quotable <|> extglobStart <|> char '['))
|
||||||
|
|
||||||
readNormalEscaped = do
|
readNormalEscaped = called "escaped char" $ do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
backslash
|
backslash
|
||||||
do
|
do
|
||||||
|
@ -601,7 +637,7 @@ readNormalEscaped = do
|
||||||
return $ if next == '\n' then "" else [next]
|
return $ if next == '\n' then "" else [next]
|
||||||
<|>
|
<|>
|
||||||
do
|
do
|
||||||
next <- anyChar <?> "No character after \\"
|
next <- anyChar
|
||||||
parseNoteAt pos WarningC $ "Did you mean \"$(printf \"\\" ++ [next] ++ "\")\"? The shell just ignores the \\ here."
|
parseNoteAt pos WarningC $ "Did you mean \"$(printf \"\\" ++ [next] ++ "\")\"? The shell just ignores the \\ here."
|
||||||
return [next]
|
return [next]
|
||||||
|
|
||||||
|
@ -610,7 +646,7 @@ prop_readExtglob1 = isOk readExtglob "!(*.mp3)"
|
||||||
prop_readExtglob2 = isOk readExtglob "!(*.mp3|*.wmv)"
|
prop_readExtglob2 = isOk readExtglob "!(*.mp3|*.wmv)"
|
||||||
prop_readExtglob4 = isOk readExtglob "+(foo \\) bar)"
|
prop_readExtglob4 = isOk readExtglob "+(foo \\) bar)"
|
||||||
prop_readExtglob5 = isOk readExtglob "+(!(foo *(bar)))"
|
prop_readExtglob5 = isOk readExtglob "+(!(foo *(bar)))"
|
||||||
readExtglob = do
|
readExtglob = called "extglob" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
c <- try $ do
|
c <- try $ do
|
||||||
f <- extglobStart
|
f <- extglobStart
|
||||||
|
@ -680,33 +716,23 @@ readBraced = try $ do
|
||||||
readDollar = readDollarArithmetic <|> readDollarBraced <|> readDollarExpansion <|> readDollarVariable <|> readDollarSingleQuote <|> readDollarLonely
|
readDollar = readDollarArithmetic <|> readDollarBraced <|> readDollarExpansion <|> readDollarVariable <|> readDollarSingleQuote <|> readDollarLonely
|
||||||
|
|
||||||
prop_readDollarSingleQuote = isOk readDollarSingleQuote "$'foo\\\'lol'"
|
prop_readDollarSingleQuote = isOk readDollarSingleQuote "$'foo\\\'lol'"
|
||||||
readDollarSingleQuote = do
|
readDollarSingleQuote = called "$'..' expression" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try $ string "$'"
|
try $ string "$'"
|
||||||
str <- readGenericLiteral (char '\'')
|
str <- readGenericLiteral (char '\'')
|
||||||
char '\''
|
char '\''
|
||||||
return $ T_Literal id str
|
return $ T_Literal id str
|
||||||
|
|
||||||
readParenLiteralHack = do
|
|
||||||
strs <- (readParenHack <|> (anyChar >>= \x -> return [x])) `reluctantlyTill1` (string "))")
|
|
||||||
return $ concat strs
|
|
||||||
|
|
||||||
readParenHack = do
|
|
||||||
char '('
|
|
||||||
x <- (readParenHack <|> (anyChar >>= (\x -> return [x]))) `reluctantlyTill` (oneOf ")")
|
|
||||||
char ')'
|
|
||||||
return $ "(" ++ (concat x) ++ ")"
|
|
||||||
|
|
||||||
prop_readDollarArithmetic = isOk readDollarArithmetic "$(( 3 * 4 +5))"
|
prop_readDollarArithmetic = isOk readDollarArithmetic "$(( 3 * 4 +5))"
|
||||||
prop_readDollarArithmetic2 = isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
|
prop_readDollarArithmetic2 = isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
|
||||||
readDollarArithmetic = do
|
readDollarArithmetic = called "$((..)) expression" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "$((")
|
try (string "$((")
|
||||||
c <- readArithmeticContents
|
c <- readArithmeticContents
|
||||||
string "))"
|
string "))"
|
||||||
return (T_DollarArithmetic id c)
|
return (T_DollarArithmetic id c)
|
||||||
|
|
||||||
readArithmeticExpression = do
|
readArithmeticExpression = called "((..)) command" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "((")
|
try (string "((")
|
||||||
c <- readArithmeticContents
|
c <- readArithmeticContents
|
||||||
|
@ -717,19 +743,19 @@ prop_readDollarBraced1 = isOk readDollarBraced "${foo//bar/baz}"
|
||||||
prop_readDollarBraced2 = isOk readDollarBraced "${foo/'{cow}'}"
|
prop_readDollarBraced2 = isOk readDollarBraced "${foo/'{cow}'}"
|
||||||
prop_readDollarBraced3 = isOk readDollarBraced "${foo%%$(echo cow})}"
|
prop_readDollarBraced3 = isOk readDollarBraced "${foo%%$(echo cow})}"
|
||||||
prop_readDollarBraced4 = isOk readDollarBraced "${foo#\\}}"
|
prop_readDollarBraced4 = isOk readDollarBraced "${foo#\\}}"
|
||||||
readDollarBraced = do
|
readDollarBraced = called "parameter expansion" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "${")
|
try (string "${")
|
||||||
word <- readDollarBracedWord
|
word <- readDollarBracedWord
|
||||||
char '}' <?> "matching }"
|
char '}'
|
||||||
return $ T_DollarBraced id word
|
return $ T_DollarBraced id word
|
||||||
|
|
||||||
prop_readDollarExpansion = isOk readDollarExpansion "$(echo foo; ls\n)"
|
prop_readDollarExpansion = isOk readDollarExpansion "$(echo foo; ls\n)"
|
||||||
readDollarExpansion = do
|
readDollarExpansion = called "command expansion" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "$(")
|
try (string "$(")
|
||||||
cmds <- readCompoundList
|
cmds <- readCompoundList
|
||||||
char ')'
|
char ')' <?> "end of $(..) expression"
|
||||||
return $ (T_DollarExpansion id cmds)
|
return $ (T_DollarExpansion id cmds)
|
||||||
|
|
||||||
prop_readDollarVariable = isOk readDollarVariable "$@"
|
prop_readDollarVariable = isOk readDollarVariable "$@"
|
||||||
|
@ -773,7 +799,7 @@ readDollarLonely = do
|
||||||
|
|
||||||
prop_readHereDoc = isOk readHereDoc "<< foo\nlol\ncow\nfoo"
|
prop_readHereDoc = isOk readHereDoc "<< foo\nlol\ncow\nfoo"
|
||||||
prop_readHereDoc2 = isWarning readHereDoc "<<- EOF\n cow\n EOF"
|
prop_readHereDoc2 = isWarning readHereDoc "<<- EOF\n cow\n EOF"
|
||||||
readHereDoc = do
|
readHereDoc = called "here document" $ do
|
||||||
let stripLiteral (T_Literal _ x) = x
|
let stripLiteral (T_Literal _ x) = x
|
||||||
stripLiteral (T_SingleQuoted _ x) = x
|
stripLiteral (T_SingleQuoted _ x) = x
|
||||||
fid <- getNextId
|
fid <- getNextId
|
||||||
|
@ -819,7 +845,7 @@ readFilename = readNormalWord
|
||||||
readIoFileOp = choice [g_LESSAND, g_GREATAND, g_DGREAT, g_LESSGREAT, g_CLOBBER, redirToken '<' T_Less, redirToken '>' T_Greater ]
|
readIoFileOp = choice [g_LESSAND, g_GREATAND, g_DGREAT, g_LESSGREAT, g_CLOBBER, redirToken '<' T_Less, redirToken '>' T_Greater ]
|
||||||
|
|
||||||
prop_readIoFile = isOk readIoFile ">> \"$(date +%YYmmDD)\""
|
prop_readIoFile = isOk readIoFile ">> \"$(date +%YYmmDD)\""
|
||||||
readIoFile = do
|
readIoFile = called "redirection" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
op <- readIoFileOp
|
op <- readIoFileOp
|
||||||
spacing
|
spacing
|
||||||
|
@ -847,7 +873,7 @@ readIoRedirect = choice [ readIoNumberRedirect, readHereString, readHereDoc, rea
|
||||||
readRedirectList = many1 readIoRedirect
|
readRedirectList = many1 readIoRedirect
|
||||||
|
|
||||||
prop_readHereString = isOk readHereString "<<< \"Hello $world\""
|
prop_readHereString = isOk readHereString "<<< \"Hello $world\""
|
||||||
readHereString = do
|
readHereString = called "here string" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try $ string "<<<"
|
try $ string "<<<"
|
||||||
spacing
|
spacing
|
||||||
|
@ -861,7 +887,7 @@ readLineBreak = optional readNewlineList
|
||||||
prop_roflol = isWarning readScript "a &; b"
|
prop_roflol = isWarning readScript "a &; b"
|
||||||
prop_roflol2 = isOk readScript "a & b"
|
prop_roflol2 = isOk readScript "a & b"
|
||||||
readSeparatorOp = do
|
readSeparatorOp = do
|
||||||
notFollowedBy (g_AND_IF <|> g_DSEMI)
|
notFollowedBy2 (g_AND_IF <|> g_DSEMI)
|
||||||
f <- (try $ do
|
f <- (try $ do
|
||||||
char '&'
|
char '&'
|
||||||
spacing
|
spacing
|
||||||
|
@ -890,7 +916,7 @@ makeSimpleCommand id1 id2 tokens =
|
||||||
in T_Redirecting id1 redirections $ T_SimpleCommand id2 assignment rest2
|
in T_Redirecting id1 redirections $ T_SimpleCommand id2 assignment rest2
|
||||||
|
|
||||||
prop_readSimpleCommand = isOk readSimpleCommand "echo test > file"
|
prop_readSimpleCommand = isOk readSimpleCommand "echo test > file"
|
||||||
readSimpleCommand = do
|
readSimpleCommand = called "simple command" $ do
|
||||||
id1 <- getNextId
|
id1 <- getNextId
|
||||||
id2 <- getNextId
|
id2 <- getNextId
|
||||||
prefix <- option [] readCmdPrefix
|
prefix <- option [] readCmdPrefix
|
||||||
|
@ -904,7 +930,7 @@ readSimpleCommand = do
|
||||||
|
|
||||||
prop_readPipeline = isOk readPipeline "! cat /etc/issue | grep -i ubuntu"
|
prop_readPipeline = isOk readPipeline "! cat /etc/issue | grep -i ubuntu"
|
||||||
readPipeline = do
|
readPipeline = do
|
||||||
notFollowedBy $ try readKeyword
|
unexpecting "keyword/token" readKeyword
|
||||||
do
|
do
|
||||||
(T_Bang id) <- g_Bang `thenSkip` spacing
|
(T_Bang id) <- g_Bang `thenSkip` spacing
|
||||||
pipe <- readPipeSequence
|
pipe <- readPipeSequence
|
||||||
|
@ -947,7 +973,7 @@ readPipeSequence = do
|
||||||
return $ T_Pipeline id list
|
return $ T_Pipeline id list
|
||||||
|
|
||||||
readPipe = do
|
readPipe = do
|
||||||
notFollowedBy g_OR_IF
|
notFollowedBy2 g_OR_IF
|
||||||
char '|' `thenSkip` spacing
|
char '|' `thenSkip` spacing
|
||||||
|
|
||||||
readCommand = (readCompoundCommand <|> readSimpleCommand)
|
readCommand = (readCompoundCommand <|> readSimpleCommand)
|
||||||
|
@ -965,7 +991,7 @@ readCmdWord = do
|
||||||
prop_readIfClause = isOk readIfClause "if false; then foo; elif true; then stuff; more stuff; else cows; fi"
|
prop_readIfClause = isOk readIfClause "if false; then foo; elif true; then stuff; more stuff; else cows; fi"
|
||||||
prop_readIfClause2 = isWarning readIfClause "if false; then; echo oo; fi"
|
prop_readIfClause2 = isWarning readIfClause "if false; then; echo oo; fi"
|
||||||
prop_readIfClause3 = isWarning readIfClause "if false; then true; else; echo lol; fi"
|
prop_readIfClause3 = isWarning readIfClause "if false; then true; else; echo lol; fi"
|
||||||
readIfClause = do
|
readIfClause = called "if expression" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
(condition, action) <- readIfPart
|
(condition, action) <- readIfPart
|
||||||
|
@ -988,14 +1014,15 @@ readIfPart = do
|
||||||
try . lookAhead $ g_Fi
|
try . lookAhead $ g_Fi
|
||||||
parseProblemAt pos ErrorC "Did you forget the 'then' for this 'if'?")
|
parseProblemAt pos ErrorC "Did you forget the 'then' for this 'if'?")
|
||||||
|
|
||||||
g_Then `orFail` parseProblem ErrorC "Expected 'then'."
|
called "then clause" $ do
|
||||||
|
g_Then `orFail` parseProblem ErrorC "Expected 'then'."
|
||||||
|
|
||||||
acceptButWarn g_Semi ErrorC "No semicolons directly after 'then'."
|
acceptButWarn g_Semi ErrorC "No semicolons directly after 'then'."
|
||||||
allspacing
|
allspacing
|
||||||
action <- readTerm
|
action <- readTerm
|
||||||
return (condition, action)
|
return (condition, action)
|
||||||
|
|
||||||
readElifPart = do
|
readElifPart = called "elif clause" $ do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
g_Elif
|
g_Elif
|
||||||
allspacing
|
allspacing
|
||||||
|
@ -1006,14 +1033,14 @@ readElifPart = do
|
||||||
action <- readTerm
|
action <- readTerm
|
||||||
return (condition, action)
|
return (condition, action)
|
||||||
|
|
||||||
readElsePart = do
|
readElsePart = called "else clause" $ do
|
||||||
g_Else
|
g_Else
|
||||||
acceptButWarn g_Semi ErrorC "No semicolons directly after 'else'."
|
acceptButWarn g_Semi ErrorC "No semicolons directly after 'else'."
|
||||||
allspacing
|
allspacing
|
||||||
readTerm
|
readTerm
|
||||||
|
|
||||||
prop_readSubshell = isOk readSubshell "( cd /foo; tar cf stuff.tar * )"
|
prop_readSubshell = isOk readSubshell "( cd /foo; tar cf stuff.tar * )"
|
||||||
readSubshell = do
|
readSubshell = called "explicit subshell" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
char '('
|
char '('
|
||||||
allspacing
|
allspacing
|
||||||
|
@ -1023,7 +1050,7 @@ readSubshell = do
|
||||||
return $ T_Subshell id list
|
return $ T_Subshell id list
|
||||||
|
|
||||||
prop_readBraceGroup = isOk readBraceGroup "{ a; b | c | d; e; }"
|
prop_readBraceGroup = isOk readBraceGroup "{ a; b | c | d; e; }"
|
||||||
readBraceGroup = do
|
readBraceGroup = called "brace group" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
char '{'
|
char '{'
|
||||||
allspacing
|
allspacing
|
||||||
|
@ -1033,7 +1060,7 @@ readBraceGroup = do
|
||||||
return $ T_BraceGroup id list
|
return $ T_BraceGroup id list
|
||||||
|
|
||||||
prop_readWhileClause = isOk readWhileClause "while [[ -e foo ]]; do sleep 1; done"
|
prop_readWhileClause = isOk readWhileClause "while [[ -e foo ]]; do sleep 1; done"
|
||||||
readWhileClause = do
|
readWhileClause = called "while loop" $ do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
(T_While id) <- g_While
|
(T_While id) <- g_While
|
||||||
condition <- readTerm
|
condition <- readTerm
|
||||||
|
@ -1041,7 +1068,7 @@ readWhileClause = do
|
||||||
return $ T_WhileExpression id condition statements
|
return $ T_WhileExpression id condition statements
|
||||||
|
|
||||||
prop_readUntilClause = isOk readUntilClause "until kill -0 $PID; do sleep 1; done"
|
prop_readUntilClause = isOk readUntilClause "until kill -0 $PID; do sleep 1; done"
|
||||||
readUntilClause = do
|
readUntilClause = called "until loop" $ do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
(T_Until id) <- g_Until
|
(T_Until id) <- g_Until
|
||||||
condition <- readTerm
|
condition <- readTerm
|
||||||
|
@ -1068,7 +1095,7 @@ readDoGroup loopPos = do
|
||||||
|
|
||||||
prop_readForClause = isOk readForClause "for f in *; do rm \"$f\"; done"
|
prop_readForClause = isOk readForClause "for f in *; do rm \"$f\"; done"
|
||||||
prop_readForClause3 = isOk readForClause "for f; do foo; done"
|
prop_readForClause3 = isOk readForClause "for f; do foo; done"
|
||||||
readForClause = do
|
readForClause = called "for loop" $ do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
(T_For id) <- g_For
|
(T_For id) <- g_For
|
||||||
spacing
|
spacing
|
||||||
|
@ -1094,7 +1121,7 @@ readInClause = do
|
||||||
return things
|
return things
|
||||||
|
|
||||||
prop_readCaseClause = isOk readCaseClause "case foo in a ) lol; cow;; b|d) fooo; esac"
|
prop_readCaseClause = isOk readCaseClause "case foo in a ) lol; cow;; b|d) fooo; esac"
|
||||||
readCaseClause = do
|
readCaseClause = called "case expression" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
g_Case
|
g_Case
|
||||||
word <- readNormalWord
|
word <- readNormalWord
|
||||||
|
@ -1107,8 +1134,8 @@ readCaseClause = do
|
||||||
|
|
||||||
readCaseList = many readCaseItem
|
readCaseList = many readCaseItem
|
||||||
|
|
||||||
readCaseItem = do
|
readCaseItem = called "case item" $ do
|
||||||
notFollowedBy g_Esac
|
notFollowedBy2 g_Esac
|
||||||
optional g_Lparen
|
optional g_Lparen
|
||||||
spacing
|
spacing
|
||||||
pattern <- readPattern
|
pattern <- readPattern
|
||||||
|
@ -1124,7 +1151,7 @@ prop_readFunctionDefinition1 = isOk readFunctionDefinition "foo (){ command fo
|
||||||
prop_readFunctionDefinition2 = isWarning readFunctionDefinition "function foo() { command foo --lol \"$@\"; }"
|
prop_readFunctionDefinition2 = isWarning readFunctionDefinition "function foo() { command foo --lol \"$@\"; }"
|
||||||
prop_readFunctionDefinition3 = isWarning readFunctionDefinition "function foo { lol; }"
|
prop_readFunctionDefinition3 = isWarning readFunctionDefinition "function foo { lol; }"
|
||||||
prop_readFunctionDefinition4 = isWarning readFunctionDefinition "foo(a, b) { true; }"
|
prop_readFunctionDefinition4 = isWarning readFunctionDefinition "foo(a, b) { true; }"
|
||||||
readFunctionDefinition = do
|
readFunctionDefinition = called "function" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
name <- try readFunctionSignature
|
name <- try readFunctionSignature
|
||||||
allspacing
|
allspacing
|
||||||
|
@ -1155,7 +1182,7 @@ readFunctionSignature = do
|
||||||
optional spacing
|
optional spacing
|
||||||
readParens
|
readParens
|
||||||
return name
|
return name
|
||||||
|
|
||||||
readParens = do
|
readParens = do
|
||||||
g_Lparen
|
g_Lparen
|
||||||
optional spacing
|
optional spacing
|
||||||
|
@ -1203,7 +1230,7 @@ readAssignmentWord = try $ do
|
||||||
when (space == "" && space2 /= "") $ parseNoteAt pos StyleC "Use var='' if you intended to assign the empty string."
|
when (space == "" && space2 /= "") $ parseNoteAt pos StyleC "Use var='' if you intended to assign the empty string."
|
||||||
return $ T_Assignment id variable value
|
return $ T_Assignment id variable value
|
||||||
|
|
||||||
readArray = do
|
readArray = called "array assignment" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
char '('
|
char '('
|
||||||
allspacing
|
allspacing
|
||||||
|
@ -1221,7 +1248,7 @@ tryToken s t = try $ do
|
||||||
redirToken c t = try $ do
|
redirToken c t = try $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
char c
|
char c
|
||||||
notFollowedBy $ char '('
|
notFollowedBy2 $ char '('
|
||||||
return $ t id
|
return $ t id
|
||||||
|
|
||||||
tryWordToken s t = tryParseWordToken (string s) t `thenSkip` spacing
|
tryWordToken s t = tryParseWordToken (string s) t `thenSkip` spacing
|
||||||
|
@ -1269,7 +1296,7 @@ g_Bang = tryToken "!" T_Bang
|
||||||
commonCommands = [ "admin", "alias", "ar", "asa", "at", "awk", "basename", "batch", "bc", "bg", "break", "c99", "cal", "cat", "cd", "cflow", "chgrp", "chmod", "chown", "cksum", "cmp", "colon", "comm", "command", "compress", "continue", "cp", "crontab", "csplit", "ctags", "cut", "cxref", "date", "dd", "delta", "df", "diff", "dirname", "dot", "du", "echo", "ed", "env", "eval", "ex", "exec", "exit", "expand", "export", "expr", "fc", "fg", "file", "find", "fold", "fort77", "fuser", "gencat", "get", "getconf", "getopts", "grep", "hash", "head", "iconv", "ipcrm", "ipcs", "jobs", "join", "kill", "lex", "link", "ln", "locale", "localedef", "logger", "logname", "lp", "ls", "m4", "mailx", "make", "man", "mesg", "mkdir", "mkfifo", "more", "mv", "newgrp", "nice", "nl", "nm", "nohup", "od", "paste", "patch", "pathchk", "pax", "pr", "printf", "prs", "ps", "pwd", "qalter", "qdel", "qhold", "qmove", "qmsg", "qrerun", "qrls", "qselect", "qsig", "qstat", "qsub", "read", "readonly", "renice", "return", "rm", "rmdel", "rmdir", "sact", "sccs", "sed", "set", "sh", "shift", "sleep", "sort", "split", "strings", "strip", "stty", "tabs", "tail", "talk", "tee", "test", "time", "times", "touch", "tput", "tr", "trap", "tsort", "tty", "type", "ulimit", "umask", "unalias", "uname", "uncompress", "unexpand", "unget", "uniq", "unlink", "unset", "uucp", "uudecode", "uuencode", "uustat", "uux", "val", "vi", "wait", "wc", "what", "who", "write", "xargs", "yacc", "zcat" ]
|
commonCommands = [ "admin", "alias", "ar", "asa", "at", "awk", "basename", "batch", "bc", "bg", "break", "c99", "cal", "cat", "cd", "cflow", "chgrp", "chmod", "chown", "cksum", "cmp", "colon", "comm", "command", "compress", "continue", "cp", "crontab", "csplit", "ctags", "cut", "cxref", "date", "dd", "delta", "df", "diff", "dirname", "dot", "du", "echo", "ed", "env", "eval", "ex", "exec", "exit", "expand", "export", "expr", "fc", "fg", "file", "find", "fold", "fort77", "fuser", "gencat", "get", "getconf", "getopts", "grep", "hash", "head", "iconv", "ipcrm", "ipcs", "jobs", "join", "kill", "lex", "link", "ln", "locale", "localedef", "logger", "logname", "lp", "ls", "m4", "mailx", "make", "man", "mesg", "mkdir", "mkfifo", "more", "mv", "newgrp", "nice", "nl", "nm", "nohup", "od", "paste", "patch", "pathchk", "pax", "pr", "printf", "prs", "ps", "pwd", "qalter", "qdel", "qhold", "qmove", "qmsg", "qrerun", "qrls", "qselect", "qsig", "qstat", "qsub", "read", "readonly", "renice", "return", "rm", "rmdel", "rmdir", "sact", "sccs", "sed", "set", "sh", "shift", "sleep", "sort", "split", "strings", "strip", "stty", "tabs", "tail", "talk", "tee", "test", "time", "times", "touch", "tput", "tr", "trap", "tsort", "tty", "type", "ulimit", "umask", "unalias", "uname", "uncompress", "unexpand", "unget", "uniq", "unlink", "unset", "uucp", "uudecode", "uuencode", "uustat", "uux", "val", "vi", "wait", "wc", "what", "who", "write", "xargs", "yacc", "zcat" ]
|
||||||
|
|
||||||
g_Semi = do
|
g_Semi = do
|
||||||
notFollowedBy g_DSEMI
|
notFollowedBy2 g_DSEMI
|
||||||
tryToken ";" T_Semi
|
tryToken ";" T_Semi
|
||||||
|
|
||||||
keywordSeparator = eof <|> disregard whitespace <|> (disregard $ oneOf ";()[")
|
keywordSeparator = eof <|> disregard whitespace <|> (disregard $ oneOf ";()[")
|
||||||
|
@ -1295,18 +1322,15 @@ readScript = do
|
||||||
return $ T_Script id $ [T_EOF id];
|
return $ T_Script id $ [T_EOF id];
|
||||||
}
|
}
|
||||||
|
|
||||||
rp p filename contents = Ms.runState (runParserT p initialState filename contents) []
|
rp p filename contents = Ms.runState (runParserT p initialState filename contents) ([], [])
|
||||||
|
|
||||||
isWarning :: (ParsecT String (Id, Map.Map Id Metadata, [ParseNote]) (Ms.State [ParseNote]) t) -> String -> Bool
|
|
||||||
isWarning p s = (fst cs) && (not . null . snd $ cs) where cs = checkString p s
|
isWarning p s = (fst cs) && (not . null . snd $ cs) where cs = checkString p s
|
||||||
|
|
||||||
isOk :: (ParsecT String (Id, Map.Map Id Metadata, [ParseNote]) (Ms.State [ParseNote]) t) -> String -> Bool
|
|
||||||
isOk p s = (fst cs) && (null . snd $ cs) where cs = checkString p s
|
isOk p s = (fst cs) && (null . snd $ cs) where cs = checkString p s
|
||||||
|
|
||||||
checkString parser string =
|
checkString parser string =
|
||||||
case rp (parser >> eof >> getState) "-" string of
|
case rp (parser >> eof >> getState) "-" string of
|
||||||
(Right (tree, map, notes), problems) -> (True, (notesFromMap map) ++ notes ++ problems)
|
(Right (tree, map, notes), (problems, _)) -> (True, (notesFromMap map) ++ notes ++ problems)
|
||||||
(Left _, n) -> (False, n)
|
(Left _, (n, _)) -> (False, n)
|
||||||
|
|
||||||
parseWithNotes parser = do
|
parseWithNotes parser = do
|
||||||
item <- parser
|
item <- parser
|
||||||
|
@ -1330,21 +1354,25 @@ makeErrorFor parsecError =
|
||||||
|
|
||||||
getStringFromParsec errors =
|
getStringFromParsec errors =
|
||||||
case map snd $ sortWith fst $ map f errors of
|
case map snd $ sortWith fst $ map f errors of
|
||||||
(s:_) -> s
|
r -> (intercalate " " $ take 1 $ nub r) ++ " Fix any mentioned problems and try again."
|
||||||
_ -> "Unknown error"
|
|
||||||
where f err =
|
where f err =
|
||||||
case err of
|
case err of
|
||||||
UnExpect s -> (1, unexpected s)
|
UnExpect s -> (1, unexpected s)
|
||||||
SysUnExpect s -> (2, unexpected s)
|
SysUnExpect s -> (2, unexpected s)
|
||||||
Expect s -> (3, "Expected " ++ s ++ "")
|
Expect s -> (3, "Expected " ++ s ++ ".")
|
||||||
Message s -> (4, "Message: " ++ s)
|
Message s -> (4, s ++ ".")
|
||||||
wut "" = "eof"
|
wut "" = "eof"
|
||||||
wut x = x
|
wut x = x
|
||||||
unexpected s = "Aborting due to unexpected " ++ (wut s) ++ ". Fix any mentioned problems and try again."
|
unexpected s = "Unexpected " ++ (wut s) ++ "."
|
||||||
|
|
||||||
parseShell filename contents = do
|
parseShell filename contents = do
|
||||||
case rp (parseWithNotes readScript) filename contents of
|
case rp (parseWithNotes readScript) filename contents of
|
||||||
(Right (script, map, notes), parsenotes) -> ParseResult (Just (script, map)) (nub $ sortNotes $ notes ++ parsenotes)
|
(Right (script, map, notes), (parsenotes, _)) -> ParseResult (Just (script, map)) (nub $ sortNotes $ notes ++ parsenotes)
|
||||||
(Left err, p) -> ParseResult Nothing (nub $ sortNotes $ p ++ ([makeErrorFor err]))
|
(Left err, (p, context)) -> ParseResult Nothing (nub $ sortNotes $ p ++ (notesForContext context) ++ ([makeErrorFor err]))
|
||||||
|
|
||||||
|
where
|
||||||
|
notesForContext r = map contextNote $ take 1 r
|
||||||
|
contextNote (pos, str) = ParseNote pos ErrorC $ "This " ++ str ++" is malformed."
|
||||||
|
|
||||||
lt x = trace (show x) x
|
lt x = trace (show x) x
|
||||||
|
ltt t x = trace (show t) x
|
||||||
|
|
Loading…
Reference in New Issue