packages feed

pisigma-0.1.0.2: src/PiSigma/Parser.hs

module PiSigma.Parser (module PiSigma.Parser, Parser) where

import Prelude hiding (pi)
import Data.Char
import Data.List

import Control.Monad
import Control.Applicative hiding ((<|>), many)

import Text.ParserCombinators.Parsec as P hiding (token, label)

import PiSigma.Syntax hiding (label)

type SParser = CharParser ()

-- * Comments and whitespace

blockComment :: SParser ()
blockComment = () <$ try (string "{-") <* inComment <* string "-}"

inComment :: SParser ()
inComment =
  () <$ many (choice [ () <$ noneOf "{-"
                     , try $ () <$ char '-' <* notFollowedBy (char '}')
                     , try $ () <$ char '{' <* notFollowedBy (char '-')
                     , blockComment ])

lineComment :: SParser ()
lineComment = () <$ try (string "--") <* many (noneOf "\n")

whiteSpace :: SParser ()
whiteSpace = () <$ spaces <* many ((lineComment <|> blockComment) <* spaces)
             <?> ""

-- * Identifiers

identChar :: SParser Char
identChar = alphaNum <|> oneOf "_'"

ident :: SParser String
ident = 
  (try $  do
            xs <- (:) <$> letter <*> many identChar <* whiteSpace
            guard (xs `notElem` keywords)
            return xs) <?> "identifier"

keywords :: [String]
keywords = [ "case", "of", "let", "in", "split", "Type", "with" ]

label :: SParser Name
label = char '\'' *> ident

-- * Tokens

token :: String -> SParser String
token xs = (try $ (string xs <* whiteSpace)) <?> show xs

-- * Brackets

parensed   = between (token "(") (token ")")
braced     = between (token "{") (token "}")
bracketed  = between (token "[") (token "]")

-- * Terms

sTerm5 :: SParser Term
sTerm5 =
       Type   <$  token "Type"
  <|>  (Enum  <$> braced (many ident) <?> "enumeration")
  <|>  Case   <$  token "case" <*> sTerm <* token "of"
              <*> braced (sBranch `sepBy` token "|")
  <|>  (Label <$> label <?> "label")
  <|>  (Box   <$> bracketed sTerm <?> "box")
  <|>  (Lift  <$  token "^" <*> sTerm5 <?> "'^'")
  <|>  Var    <$> ident
  <|>  parensed sTerm

sTerm4 :: SParser Term
sTerm4 =
       try (parensed (curry Pair <$> sTerm <* token "," <*> sTerm))
  <|>  sTerm5

sTerm3 :: SParser Term
sTerm3 =
       try (parensed (sigmas <$> many1 ident <* token ":" <*> sTerm) <* token "*")
          <*> sTerm3
  <|>  Force <$  token "!" <*> sTerm4
  <|>  foldl1 (:.) <$> many1 sTerm4

sTerm2 :: SParser Term
sTerm2 =
       foldl1 (-*-) <$> sTerm3 `sepBy1` token "*"

-- TODO: make more beautiful or renumber
sTerm1b :: SParser Term
sTerm1b =
       try (parensed (pis <$> many1 ident <* token ":" <*> sTerm) <* token "->")
          <*> sTerm
  <|>  sTerm2
       
sTerm1 :: SParser Term
sTerm1 =
       flip ($) <$> sTerm1b <*> option id (flip (->-) <$ token "->" <*> sTerm)

sTerm :: SParser Term
sTerm =
       lam   <$  token "\\" <*> many ident <* token "->" <*> sTerm
  <|>  split <$  token "split" <*> sTerm <* token "with"
             <*  token "(" <*> ident <* token "," <*> ident <* token ")"
             <*  token "->" <*> sTerm
  <|>  Let   <$  token "let" <*> sProg <* token "in" <*> sTerm
  <|>  sTerm1

sProg :: SParser Prog
sProg = concat <$> (sEntry `sepEndBy` token ";")

sEntry :: SParser [Entry]
sEntry = 
    do
      n <- ident
      b <- (True <$ token ":" <|> False <$ token "=")
      t <- sTerm
      if b
        then do
               d <- option Nothing (Just <$ token "=" <*> sTerm)
               case d of
                 Nothing -> return [Decl n t]
                 Just t' -> return [Decl n t, Defn n t']
        else return [Defn n t]

sBranch :: SParser (Label,Term)
sBranch = (,) <$> ident <* token "->" <*> sTerm

sPhrase :: SParser Phrase
sPhrase =
       (try $ Prog <$> sProg <* eof) <|> Term <$> sTerm <* eof

s2Terms :: SParser (Term, Term)
s2Terms = (,) <$> sTerm5 <*> sTerm5

parse p = P.parse (whiteSpace *> p <* eof)