module TermParser where
import qualified Text.ParserCombinators.Parsec.Token as T
import qualified Text.ParserCombinators.Parsec.Language as L
import qualified Text.ParserCombinators.Parsec as Parsec
import Text.ParserCombinators.Parsec ( CharParser, Parser, (<|>) )
import Text.ParserCombinators.Parsec.Expr
( Assoc(AssocLeft, AssocRight, AssocNone) )
import Control.Monad ( liftM2 )
import Control.Functor.HT ( void )
lexer :: T.TokenParser st
lexer =
T.makeTokenParser $ L.emptyDef {
L.commentStart = "{-",
L.commentEnd = "-}",
L.commentLine = "--",
L.nestedComments = True,
L.identStart = identifierStart,
L.identLetter = identifierLetter,
L.opStart = operatorStart,
L.opLetter = operatorLetter,
L.caseSensitive = True,
L.reservedNames = [ "module", "where", "import", "qualified"
, "as", "data", "class", "instance", "case", "of"
, "infix", "infixl", "infixr" ],
L.reservedOpNames = [ "=", "::", "|" ]
}
{-
FIXME: This should be read from a file (Prelude.hs).
But then we need a parser that correctly handles fixity information on-the-fly.
A simplified solution could be:
Allow fixity definitions only between import and the first declaration.
With this restriction we could parse the preamble first
and then start with a fresh parser for the module body.
For now, we hard-code Prelude's fixities:
infixr 9 .
infixr 8 ^, ^^, **
infixl 7 *, /, `quot`, `rem`, `div`, `mod`
infixl 6 +, -
-- The (:) operator is built-in syntax, and cannot legally be given
-- a fixity declaration; but its fixity is given by:
-- infixr 5 :
infix 4 ==, /=, <, <=, >=, >
infixr 3 &&
infixr 2 ||
infixl 1 >>, >>=
infixr 1 =<<
infixr 0 $, $!, `seq`
-}
operators :: [[([Char], Assoc)]]
operators =
[ [ ( ".", AssocRight ), ( "!!", AssocLeft ) ]
, [ ( "^", AssocRight) ]
, [ ( "*", AssocLeft), ("/", AssocLeft), ("%", AssocLeft), ("+:+", AssocRight) ]
, [ ( "+", AssocLeft), ("-", AssocLeft), ("=:=", AssocRight) ]
, [ ( ":", AssocRight ), ( "++", AssocRight ) ]
, map ( \ s -> (s, AssocNone) ) [ "==", "/=", "<", "<=", ">=", ">" ]
, [ ( "&&", AssocRight ) ]
, [ ( "||", AssocRight ) ]
, [ ( "$", AssocRight ) ]
]
identifierStart, identifierLetter :: CharParser st Char
identifierStart = Parsec.letter <|> Parsec.char '_'
-- FIXME: check the distinction between '.' in qualified names, and as operator
identifierLetter =
Parsec.alphaNum <|> Parsec.char '_' <|> Parsec.char '.'
identifier :: Parser String
identifier =
liftM2 (:) identifierStart (Parsec.many identifierLetter)
operatorStart, operatorLetter :: CharParser st Char
operatorStart = Parsec.oneOf operatorSymbols
operatorLetter = Parsec.oneOf operatorSymbols
operatorSymbols :: [Char]
operatorSymbols = ":!#$%&*+./<=>?@\\^|-~"
symbol :: String -> Parser ()
symbol = void . T.symbol lexer