Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 26 additions & 29 deletions Cabal/src/Distribution/Lex.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
-----------------------------------------------------------------------------

-- |
-- Module : Distribution.Lex
-- Copyright : Ben Gamari 2015-2019
Expand All @@ -12,39 +10,38 @@ module Distribution.Lex
( tokenizeQuotedWords
) where

import Distribution.Compat.DList
import Distribution.Compat.Prelude
import Prelude ()

-- | A simple parser supporting quoted strings.
--
-- Please be aware that this will only split strings when seeing whitespace
-- outside of quotation marks, i.e, @"foo\"bar baz\"qux quux"@ will be
-- converted to @["foobar bazqux", "quux"]@.
-- outside of quotation marks;
--
-- > foo"bar baz"qux quux
--
-- >>> tokenizeQuotedWords "foo\"bar baz\"qux quux"
-- ["foobar bazqux","quux"]
--
-- This behavior can be useful when parsing text like
-- @"ghc-options: -Wl,\"some option with spaces\""@, for instance.
-- This behavior can be useful when parsing text like;
--
-- > ghc-options: -Wl,"some option with spaces"
--
-- >>> tokenizeQuotedWords "ghc-options: -Wl,\"some option with spaces\""
-- ["ghc-options:","-Wl,some option with spaces"]
tokenizeQuotedWords :: String -> [String]
tokenizeQuotedWords = filter (not . null) . go False mempty
tokenizeQuotedWords xs = repack $ foldr alg (const ([] :| [])) xs False
where
go
:: Bool
-- \^ in quoted region
-> DList Char
-- \^ accumulator
-> String
-- \^ string to be parsed
-> [String]
-- \^ parse result
go _ accum []
| [] <- accum' = []
| otherwise = [accum']
where
accum' = runDList accum
go False accum (c : cs)
| isSpace c = runDList accum : go False mempty cs
| c == '"' = go True accum cs
go True accum (c : cs)
| c == '"' = go False accum cs
go quoted accum (c : cs) =
go quoted (accum <> singleton c) cs
repack :: NonEmpty String -> [String]
repack (zs :| acc) = if null zs then acc else zs : acc

alg
:: Char -- current character
-> (Bool -> NonEmpty String) -- continuation, depending on whether we are in a quoted region or not
-> Bool -- are we in a quoted region?
-> NonEmpty String
alg '"' rest mode = rest (not mode)
alg c rest False
| isSpace c = [] :| repack (rest False)
alg c rest mode = case rest mode of
w :| ws -> (c : w) :| ws
Loading