Added better malformed parsing messages

This commit is contained in:
Vidar Holen 2012-11-29 22:26:45 -08:00
parent c2b9c1ff2a
commit f7be39cb5f
1 changed files with 105 additions and 77 deletions

View File

@ -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