packages feed

live-sequencer-0.0.6.1: src/TermParser.hs

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