Skip to content
This repository was archived by the owner on Apr 25, 2020. It is now read-only.

Commit 50dfffc

Browse files
Nicer parser for postfix matching.
1 parent 6e327ed commit 50dfffc

File tree

1 file changed

+34
-12
lines changed

1 file changed

+34
-12
lines changed

Language/Haskell/GhcMod/ImportedFrom.hs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import Control.Applicative
2121
import Control.Exception
2222
import Control.Monad
2323
import Control.Monad.Trans.Maybe
24-
import Data.Char (isAlpha)
24+
import Data.Char (isAlpha, isDigit)
2525
import Data.IORef
2626
import Data.List
2727
import Data.List.Split
@@ -44,6 +44,7 @@ import Language.Haskell.GhcMod.SrcUtils (listifySpans)
4444
import Outputable
4545
import System.Directory
4646
import System.FilePath
47+
import Text.ParserCombinators.ReadP ((+++))
4748

4849
import qualified Data.Map as M
4950
import qualified Data.Set as Set
@@ -207,7 +208,6 @@ toImportDecl dflags idecl = NiceImportDecl
207208
-- False
208209
-- >>> postfixMatch "bar" "bar"
209210
-- True
210-
211211
postfixMatch :: String -> QualifiedName -> Bool
212212
postfixMatch originalSymbol qName = endTerm `isSuffixOf` qName
213213
where endTerm = Safe.lastNote
@@ -439,22 +439,44 @@ refineRemoveHiding exports = map (\e -> e { qualifiedExports = f e }) exports
439439

440440
qualifyName :: [QualifiedName] -> String -> QualifiedName
441441
qualifyName qualifiedNames name
442-
-- = case filter (postfixMatch name) qualifiedNames of
443-
= case nub' (filter (name `f`) qualifiedNames) of
442+
= case nub' (filter (postfixMatch' name) qualifiedNames) of
444443
[match] -> match
445444
m -> fail $ "ImportedFrom: could not qualify "
446445
++ name ++ " from these exports: "
447446
++ show qualifiedNames ++ "\n matches: "
448447
++ show m
449448

450-
-- Time for some stringly typed rubbish. The previous test used
451-
-- postfixMatch but this failed on an import that had "hiding (lines, unlines)" since
452-
-- both lines and unlines matched. Prepending a dot doesn't work due to things like ".=" from
453-
-- Control.Lens. So we manually check that the suffix matches, that the next symbol is a dot,
454-
-- and then an alpha character, which hopefully is the end of a module name. Such a mess.
455-
where f n qn = if length qn - length n - 2 >= 0
456-
then n `isSuffixOf` qn && isAlpha (qn !! (length qn - length n - 2)) && (qn !! (length qn - length n - 1)) == '.'
457-
else throw $ GMEString $ "ImportedFrom internal error: trying to check if \"" ++ n ++ "\" is a match for \"" ++ qn ++ "\""
449+
postfixMatch' n qn
450+
| n == qn = True
451+
| otherwise = case runRP (f $ reverse n) (reverse qn) of
452+
Left _ -> False
453+
Right () -> True
454+
where
455+
f n = do
456+
_ <- RP.string n
457+
_ <- RP.char '.'
458+
_ <- RP.manyTill nameThenDot (nameThenEnd +++ nameThenEnd')
459+
return ()
460+
461+
-- Valid chars in a haskell module name:
462+
-- https://www.haskell.org/onlinereport/syntax-iso.html
463+
modChar c = isAlpha c || isDigit c || (c == '\'')
464+
465+
nameThenEnd = do
466+
RP.many1 $ RP.satisfy modChar
467+
RP.eof
468+
469+
nameThenEnd' = do
470+
RP.many1 $ RP.satisfy modChar
471+
RP.char ':'
472+
RP.manyTill RP.get RP.eof
473+
RP.eof
474+
475+
nameThenDot = do
476+
RP.many1 $ RP.satisfy modChar
477+
RP.char '.'
478+
return ()
479+
458480

459481
refineExportsIt :: MySymbol -> [ModuleExports] -> [ModuleExports]
460482
refineExportsIt mysymbol exports = map (\e -> e { qualifiedExports = f symbol e }) exports

0 commit comments

Comments
 (0)