Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add : to isOpSymbol (#326) #430

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion src/Language/Haskell/Exts/InternalLexer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,9 @@ isPragmaChar c = isAlphaNum c || c == '_'
-- Used in the lexing of type applications
-- Why is it like this? I don't know exactly but this is how it is in
-- GHC's parser.
-- Symbol `:` is added so that `@:` is lexed as `VarSym "@:"`. See Issue #326
Copy link
Author

@autotaker autotaker Mar 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GHC's counterpart notFollowedBySymbol does not contain :.

notFollowedBySymbol :: AlexAccPred ExtsBitmap
notFollowedBySymbol _ _ _ (AI _ buf)
  = nextCharIsNot buf (`elem` "!#$%&*+./<=>?@\\^|-~")

There are two rules that matches for @: in Lexer.x.

  -- See Note [Lexing type applications]
<0> {
    [^ $idchar \) ] ^
  "@"
    / { ifExtension TypeApplicationsBit `alexAndPred` notFollowedBySymbol }
    { token ITtypeApp }
}

and

<0> {
  @varsym                                          { varsym }
  ...
}

Then, alex chooses the rule that consumes the longest prefix. Thus it is recognized as an operator "@:".

When the input stream matches more than one rule, the rule which matches the longest prefix of the input stream wins. If there are still several rules which match an equal number of characters, then the rule which appears earliest in the file wins.

(Cited from 3.2. Syntax of Alex files)

This patch may be ad-hoc. Nonetheless it is correct because isOpSymbol is only used for TypeApplications.

isOpSymbol :: Char -> Bool
isOpSymbol c = c `elem` "!#$%&*+./<=>?@\\^|-~"
isOpSymbol c = c `elem` ":!#$%&*+./<=>?@\\^|-~"

-- | Checks whether the character would be legal in some position of a qvar.
-- Means that '..' and "AAA" will pass the test.
Expand Down
4 changes: 4 additions & 0 deletions tests/examples/T326.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{-# LANGUAGE TypeApplications #-}
module T326 where

(@:) a b = a
1 change: 1 addition & 0 deletions tests/examples/T326.hs.exactprinter.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Match
116 changes: 116 additions & 0 deletions tests/examples/T326.hs.parser.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
ParseOk
( Module
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 1 1 5 1
, srcInfoPoints =
[ SrcSpan "tests/examples/T326.hs" 1 1 1 1
, SrcSpan "tests/examples/T326.hs" 2 1 2 1
, SrcSpan "tests/examples/T326.hs" 2 1 2 1
, SrcSpan "tests/examples/T326.hs" 4 1 4 1
, SrcSpan "tests/examples/T326.hs" 5 1 5 1
, SrcSpan "tests/examples/T326.hs" 5 1 5 1
]
}
(Just
(ModuleHead
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 2 1 2 18
, srcInfoPoints =
[ SrcSpan "tests/examples/T326.hs" 2 1 2 7
, SrcSpan "tests/examples/T326.hs" 2 13 2 18
]
}
(ModuleName
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 2 8 2 12
, srcInfoPoints = []
}
"T326")
Nothing
Nothing))
[ LanguagePragma
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 1 1 1 34
, srcInfoPoints =
[ SrcSpan "tests/examples/T326.hs" 1 1 1 13
, SrcSpan "tests/examples/T326.hs" 1 31 1 34
]
}
[ Ident
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 1 14 1 30
, srcInfoPoints = []
}
"TypeApplications"
]
]
[]
[ FunBind
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 1 4 13
, srcInfoPoints = []
}
[ Match
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 1 4 13
, srcInfoPoints =
[ SrcSpan "tests/examples/T326.hs" 4 1 4 2
, SrcSpan "tests/examples/T326.hs" 4 2 4 4
, SrcSpan "tests/examples/T326.hs" 4 4 4 5
]
}
(Symbol
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 2 4 4
, srcInfoPoints = []
}
"@:")
[ PVar
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 6 4 7
, srcInfoPoints = []
}
(Ident
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 6 4 7
, srcInfoPoints = []
}
"a")
, PVar
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 8 4 9
, srcInfoPoints = []
}
(Ident
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 8 4 9
, srcInfoPoints = []
}
"b")
]
(UnGuardedRhs
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 10 4 13
, srcInfoPoints = [ SrcSpan "tests/examples/T326.hs" 4 10 4 11 ]
}
(Var
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 12 4 13
, srcInfoPoints = []
}
(UnQual
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 12 4 13
, srcInfoPoints = []
}
(Ident
SrcSpanInfo
{ srcInfoSpan = SrcSpan "tests/examples/T326.hs" 4 12 4 13
, srcInfoPoints = []
}
"a"))))
Nothing
]
]
, []
)
1 change: 1 addition & 0 deletions tests/examples/T326.hs.prettyparser.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Match
3 changes: 3 additions & 0 deletions tests/examples/T326.hs.prettyprinter.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{-# LANGUAGE TypeApplications #-}
module T326 where
(@:) a b = a