Fix parsing of here documents
This commit is contained in:
parent
51d8caf2c9
commit
2f0ae44de4
|
@ -166,7 +166,7 @@ analyze f g i =
|
||||||
dll l m v = do
|
dll l m v = do
|
||||||
x <- roundAll l
|
x <- roundAll l
|
||||||
y <- roundAll m
|
y <- roundAll m
|
||||||
return $ v x m
|
return $ v x y
|
||||||
d1 t v = do
|
d1 t v = do
|
||||||
x <- round t
|
x <- round t
|
||||||
return $ v x
|
return $ v x
|
||||||
|
|
|
@ -52,20 +52,24 @@ type SCParser m v = ParsecT String UserState (SCBase m) v
|
||||||
|
|
||||||
backslash :: Monad m => SCParser m Char
|
backslash :: Monad m => SCParser m Char
|
||||||
backslash = char '\\'
|
backslash = char '\\'
|
||||||
linefeed = optional carriageReturn >> char '\n'
|
linefeed :: Monad m => SCParser m Char
|
||||||
|
linefeed = do
|
||||||
|
optional carriageReturn
|
||||||
|
c <- char '\n'
|
||||||
|
readPendingHereDocs
|
||||||
|
return c
|
||||||
singleQuote = char '\'' <|> unicodeSingleQuote
|
singleQuote = char '\'' <|> unicodeSingleQuote
|
||||||
doubleQuote = char '"' <|> unicodeDoubleQuote
|
doubleQuote = char '"' <|> unicodeDoubleQuote
|
||||||
variableStart = upper <|> lower <|> oneOf "_"
|
variableStart = upper <|> lower <|> oneOf "_"
|
||||||
variableChars = upper <|> lower <|> digit <|> oneOf "_"
|
variableChars = upper <|> lower <|> digit <|> oneOf "_"
|
||||||
functionChars = variableChars <|> oneOf ":+-.?"
|
functionChars = variableChars <|> oneOf ":+-.?"
|
||||||
specialVariable = oneOf "@*#?-$!"
|
specialVariable = oneOf "@*#?-$!"
|
||||||
tokenDelimiter = oneOf "&|;<> \t\n\r" <|> almostSpace
|
|
||||||
quotableChars = "|&;<>()\\ '\t\n\r\xA0" ++ doubleQuotableChars
|
quotableChars = "|&;<>()\\ '\t\n\r\xA0" ++ doubleQuotableChars
|
||||||
quotable = almostSpace <|> unicodeDoubleQuote <|> oneOf quotableChars
|
quotable = almostSpace <|> unicodeDoubleQuote <|> oneOf quotableChars
|
||||||
bracedQuotable = oneOf "}\"$`'"
|
bracedQuotable = oneOf "}\"$`'"
|
||||||
doubleQuotableChars = "\"$`" ++ unicodeDoubleQuoteChars
|
doubleQuotableChars = "\"$`" ++ unicodeDoubleQuoteChars
|
||||||
doubleQuotable = unicodeDoubleQuote <|> oneOf doubleQuotableChars
|
doubleQuotable = unicodeDoubleQuote <|> oneOf doubleQuotableChars
|
||||||
whitespace = oneOf " \t\n" <|> carriageReturn <|> almostSpace
|
whitespace = oneOf " \t" <|> carriageReturn <|> almostSpace <|> linefeed
|
||||||
linewhitespace = oneOf " \t" <|> almostSpace
|
linewhitespace = oneOf " \t" <|> almostSpace
|
||||||
|
|
||||||
suspectCharAfterQuotes = variableChars <|> char '%'
|
suspectCharAfterQuotes = variableChars <|> char '%'
|
||||||
|
@ -138,15 +142,24 @@ data Context =
|
||||||
| ContextSource String
|
| ContextSource String
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
data HereDocContext =
|
||||||
|
HereDocPending Token -- on linefeed, read this T_HereDoc
|
||||||
|
| HereDocBoundary -- but don't consider heredocs before this
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
data UserState = UserState {
|
data UserState = UserState {
|
||||||
lastId :: Id,
|
lastId :: Id,
|
||||||
positionMap :: Map.Map Id SourcePos,
|
positionMap :: Map.Map Id SourcePos,
|
||||||
parseNotes :: [ParseNote]
|
parseNotes :: [ParseNote],
|
||||||
|
hereDocMap :: Map.Map Id [Token],
|
||||||
|
pendingHereDocs :: [HereDocContext]
|
||||||
}
|
}
|
||||||
initialUserState = UserState {
|
initialUserState = UserState {
|
||||||
lastId = Id $ -1,
|
lastId = Id $ -1,
|
||||||
positionMap = Map.empty,
|
positionMap = Map.empty,
|
||||||
parseNotes = []
|
parseNotes = [],
|
||||||
|
hereDocMap = Map.empty,
|
||||||
|
pendingHereDocs = []
|
||||||
}
|
}
|
||||||
|
|
||||||
codeForParseNote (ParseNote _ _ code _) = code
|
codeForParseNote (ParseNote _ _ code _) = code
|
||||||
|
@ -155,7 +168,6 @@ noteToParseNote map (Note id severity code message) =
|
||||||
where
|
where
|
||||||
pos = fromJust $ Map.lookup id map
|
pos = fromJust $ Map.lookup id map
|
||||||
|
|
||||||
|
|
||||||
getLastId = lastId <$> getState
|
getLastId = lastId <$> getState
|
||||||
|
|
||||||
getNextIdAt sourcepos = do
|
getNextIdAt sourcepos = do
|
||||||
|
@ -173,6 +185,58 @@ getNextId = do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
getNextIdAt pos
|
getNextIdAt pos
|
||||||
|
|
||||||
|
addToHereDocMap id list = do
|
||||||
|
state <- getState
|
||||||
|
let map = hereDocMap state
|
||||||
|
putState $ state {
|
||||||
|
hereDocMap = Map.insert id list map
|
||||||
|
}
|
||||||
|
|
||||||
|
withHereDocBoundary p = do
|
||||||
|
pushBoundary
|
||||||
|
do
|
||||||
|
v <- p
|
||||||
|
popBoundary
|
||||||
|
return v
|
||||||
|
<|> do
|
||||||
|
popBoundary
|
||||||
|
fail ""
|
||||||
|
where
|
||||||
|
pushBoundary = do
|
||||||
|
state <- getState
|
||||||
|
let docs = pendingHereDocs state
|
||||||
|
putState $ state {
|
||||||
|
pendingHereDocs = HereDocBoundary : docs
|
||||||
|
}
|
||||||
|
popBoundary = do
|
||||||
|
state <- getState
|
||||||
|
let docs = tail $ dropWhile (not . isHereDocBoundary) $
|
||||||
|
pendingHereDocs state
|
||||||
|
putState $ state {
|
||||||
|
pendingHereDocs = docs
|
||||||
|
}
|
||||||
|
|
||||||
|
addPendingHereDoc t = do
|
||||||
|
state <- getState
|
||||||
|
let docs = pendingHereDocs state
|
||||||
|
putState $ state {
|
||||||
|
pendingHereDocs = HereDocPending t : docs
|
||||||
|
}
|
||||||
|
|
||||||
|
popPendingHereDocs = do
|
||||||
|
state <- getState
|
||||||
|
let (pending, boundary) = break isHereDocBoundary $ pendingHereDocs state
|
||||||
|
putState $ state {
|
||||||
|
pendingHereDocs = boundary
|
||||||
|
}
|
||||||
|
return . map extract . reverse $ pendingHereDocs state
|
||||||
|
where
|
||||||
|
extract (HereDocPending t) = t
|
||||||
|
|
||||||
|
isHereDocBoundary x = case x of
|
||||||
|
HereDocBoundary -> True
|
||||||
|
otherwise -> False
|
||||||
|
|
||||||
getMap = positionMap <$> getState
|
getMap = positionMap <$> getState
|
||||||
getParseNotes = parseNotes <$> getState
|
getParseNotes = parseNotes <$> getState
|
||||||
|
|
||||||
|
@ -1384,14 +1448,17 @@ readDollarLonely = do
|
||||||
n <- lookAhead (anyChar <|> (eof >> return '_'))
|
n <- lookAhead (anyChar <|> (eof >> return '_'))
|
||||||
return $ T_Literal id "$"
|
return $ T_Literal id "$"
|
||||||
|
|
||||||
prop_readHereDoc = isOk readHereDoc "<< foo\nlol\ncow\nfoo"
|
prop_readHereDoc = isOk readScript "cat << foo\nlol\ncow\nfoo"
|
||||||
prop_readHereDoc2 = isWarning readHereDoc "<<- EOF\n cow\n EOF"
|
prop_readHereDoc2 = isWarning readScript "cat <<- EOF\n cow\n EOF"
|
||||||
prop_readHereDoc3 = isOk readHereDoc "<< foo\n$\"\nfoo"
|
prop_readHereDoc3 = isOk readScript "cat << foo\n$\"\nfoo"
|
||||||
prop_readHereDoc4 = isOk readHereDoc "<< foo\n`\nfoo"
|
prop_readHereDoc4 = isOk readScript "cat << foo\n`\nfoo"
|
||||||
prop_readHereDoc5 = isOk readHereDoc "<<- !foo\nbar\n!foo"
|
prop_readHereDoc5 = isOk readScript "cat <<- !foo\nbar\n!foo"
|
||||||
prop_readHereDoc6 = isOk readHereDoc "<< foo\\ bar\ncow\nfoo bar"
|
prop_readHereDoc6 = isOk readScript "cat << foo\\ bar\ncow\nfoo bar"
|
||||||
prop_readHereDoc7 = isOk readHereDoc "<< foo\n\\$(f ())\nfoo"
|
prop_readHereDoc7 = isOk readScript "cat << foo\n\\$(f ())\nfoo"
|
||||||
prop_readHereDoc8 = isOk readHereDoc "<<foo>>bar\netc\nfoo"
|
prop_readHereDoc8 = isOk readScript "cat <<foo>>bar\netc\nfoo"
|
||||||
|
prop_readHereDoc9 = isOk readScript "if true; then cat << foo; fi\nbar\nfoo\n"
|
||||||
|
prop_readHereDoc10= isOk readScript "if true; then cat << foo << bar; fi\nfoo\nbar\n"
|
||||||
|
prop_readHereDoc11= isOk readScript "cat << foo $(\nfoo\n)lol\nfoo\n"
|
||||||
readHereDoc = called "here document" $ do
|
readHereDoc = called "here document" $ do
|
||||||
fid <- getNextId
|
fid <- getNextId
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
|
@ -1408,24 +1475,11 @@ readHereDoc = called "here document" $ do
|
||||||
liftM (\ x -> (Quoted, stripLiteral x)) readDoubleQuotedLiteral
|
liftM (\ x -> (Quoted, stripLiteral x)) readDoubleQuotedLiteral
|
||||||
<|> liftM (\ x -> (Quoted, x)) readSingleQuotedLiteral
|
<|> liftM (\ x -> (Quoted, x)) readSingleQuotedLiteral
|
||||||
<|> (readToken >>= (\x -> return (Unquoted, x)))
|
<|> (readToken >>= (\x -> return (Unquoted, x)))
|
||||||
spacing
|
|
||||||
|
|
||||||
startPos <- getPosition
|
|
||||||
hereData <- anyChar `reluctantlyTill` do
|
|
||||||
linefeed
|
|
||||||
spacing
|
|
||||||
string endToken
|
|
||||||
disregard linefeed <|> eof
|
|
||||||
|
|
||||||
do
|
|
||||||
linefeed
|
|
||||||
spaces <- spacing
|
|
||||||
verifyHereDoc dashed quoted spaces hereData
|
|
||||||
string endToken
|
|
||||||
parsedData <- parseHereData quoted startPos hereData
|
|
||||||
return $ T_FdRedirect fid "" $ T_HereDoc hid dashed quoted endToken parsedData
|
|
||||||
`attempting` (eof >> debugHereDoc tokenPosition endToken hereData)
|
|
||||||
|
|
||||||
|
-- add empty tokens for now, read the rest in readPendingHereDocs
|
||||||
|
let doc = T_HereDoc hid dashed quoted endToken []
|
||||||
|
addPendingHereDoc doc
|
||||||
|
return $ T_FdRedirect fid "" doc
|
||||||
where
|
where
|
||||||
stripLiteral (T_Literal _ x) = x
|
stripLiteral (T_Literal _ x) = x
|
||||||
stripLiteral (T_SingleQuoted _ x) = x
|
stripLiteral (T_SingleQuoted _ x) = x
|
||||||
|
@ -1440,6 +1494,27 @@ readHereDoc = called "here document" $ do
|
||||||
c <- anyChar
|
c <- anyChar
|
||||||
return [c]
|
return [c]
|
||||||
|
|
||||||
|
|
||||||
|
readPendingHereDocs = do
|
||||||
|
docs <- popPendingHereDocs
|
||||||
|
mapM_ readDoc docs
|
||||||
|
where
|
||||||
|
readDoc (T_HereDoc id dashed quoted endToken _) = do
|
||||||
|
pos <- getPosition
|
||||||
|
hereData <- anyChar `reluctantlyTill` do
|
||||||
|
spacing
|
||||||
|
string endToken
|
||||||
|
disregard (char '\n') <|> eof
|
||||||
|
do
|
||||||
|
spaces <- spacing
|
||||||
|
verifyHereDoc dashed quoted spaces hereData
|
||||||
|
string endToken
|
||||||
|
parsedData <- parseHereData quoted pos hereData
|
||||||
|
list <- parseHereData quoted pos hereData
|
||||||
|
addToHereDocMap id list
|
||||||
|
|
||||||
|
`attempting` (eof >> debugHereDoc pos endToken hereData)
|
||||||
|
|
||||||
parseHereData Quoted startPos hereData = do
|
parseHereData Quoted startPos hereData = do
|
||||||
id <- getNextIdAt startPos
|
id <- getNextIdAt startPos
|
||||||
return [T_Literal id hereData]
|
return [T_Literal id hereData]
|
||||||
|
@ -1524,7 +1599,7 @@ readHereString = called "here string" $ do
|
||||||
word <- readNormalWord
|
word <- readNormalWord
|
||||||
return $ T_FdRedirect id "" $ T_HereString id2 word
|
return $ T_FdRedirect id "" $ T_HereString id2 word
|
||||||
|
|
||||||
readNewlineList = many1 ((newline <|> carriageReturn) `thenSkip` spacing)
|
readNewlineList = many1 ((linefeed <|> carriageReturn) `thenSkip` spacing)
|
||||||
readLineBreak = optional readNewlineList
|
readLineBreak = optional readNewlineList
|
||||||
|
|
||||||
prop_readSeparator1 = isWarning readScript "a &; b"
|
prop_readSeparator1 = isWarning readScript "a &; b"
|
||||||
|
@ -2475,9 +2550,8 @@ parsesCleanly parser string = runIdentity $ do
|
||||||
|
|
||||||
parseWithNotes parser = do
|
parseWithNotes parser = do
|
||||||
item <- parser
|
item <- parser
|
||||||
map <- getMap
|
state <- getState
|
||||||
parseNotes <- getParseNotes
|
return (item, state)
|
||||||
return (item, map, nub . sortNotes $ parseNotes)
|
|
||||||
|
|
||||||
compareNotes (ParseNote pos1 level1 _ s1) (ParseNote pos2 level2 _ s2) = compare (pos1, level1) (pos2, level2)
|
compareNotes (ParseNote pos1 level1 _ s1) (ParseNote pos2 level2 _ s2) = compare (pos1, level1) (pos2, level2)
|
||||||
sortNotes = sortBy compareNotes
|
sortNotes = sortBy compareNotes
|
||||||
|
@ -2517,11 +2591,12 @@ system = lift . lift . lift
|
||||||
parseShell sys name contents = do
|
parseShell sys name contents = do
|
||||||
(result, state) <- runParser sys (parseWithNotes readScript) name contents
|
(result, state) <- runParser sys (parseWithNotes readScript) name contents
|
||||||
case result of
|
case result of
|
||||||
Right (script, tokenMap, notes) ->
|
Right (script, userstate) ->
|
||||||
return ParseResult {
|
return ParseResult {
|
||||||
prComments = map toPositionedComment $ nub $ notes ++ parseProblems state,
|
prComments = map toPositionedComment $ nub $ parseNotes userstate ++ parseProblems state,
|
||||||
prTokenPositions = Map.map posToPos tokenMap,
|
prTokenPositions = Map.map posToPos (positionMap userstate),
|
||||||
prRoot = Just script
|
prRoot = Just $
|
||||||
|
reattachHereDocs script (hereDocMap userstate)
|
||||||
}
|
}
|
||||||
Left err ->
|
Left err ->
|
||||||
return ParseResult {
|
return ParseResult {
|
||||||
|
@ -2542,6 +2617,13 @@ parseShell sys name contents = do
|
||||||
second (ContextName pos str) = ParseNote pos InfoC 1009 $
|
second (ContextName pos str) = ParseNote pos InfoC 1009 $
|
||||||
"The mentioned parser error was in this " ++ str ++ "."
|
"The mentioned parser error was in this " ++ str ++ "."
|
||||||
|
|
||||||
|
reattachHereDocs root map =
|
||||||
|
doTransform f root
|
||||||
|
where
|
||||||
|
f t@(T_HereDoc id dash quote string []) = fromMaybe t $ do
|
||||||
|
list <- Map.lookup id map
|
||||||
|
return $ T_HereDoc id dash quote string list
|
||||||
|
f t = t
|
||||||
|
|
||||||
toPositionedComment :: ParseNote -> PositionedComment
|
toPositionedComment :: ParseNote -> PositionedComment
|
||||||
toPositionedComment (ParseNote pos severity code message) =
|
toPositionedComment (ParseNote pos severity code message) =
|
||||||
|
|
Loading…
Reference in New Issue