packages feed

alpha-1.0.8: src/Syntax/Parse.hs

module Syntax.Parse (parseAlpha) where

import Syntax
import Text.ParserCombinators.Parsec hiding (spaces)
import My.Control.Monad
import Data.Maybe

parseAlpha file s = concat $ lazyMany axiom file s

-- Utilities
lazyMany p file contents = lm state0
  where Right state0 = parse getParserState file contents
        lm state = case parse p' "" "" of
          Left err -> error (show err)
          Right x -> x
          where p' = setParserState state >>
                     (try (oWhite >> eof >> return [])
                      <|> liftM2 (:) (p >>= \p -> optional (oneOf ",;") >> return p) (lm $< getParserState))
wrParen e@(_:_:_) = mkNode e
wrParen e = concat e
mkNode = return . Group . concat
free :: Parser a -> Parser a
free e = try $ between oWhite oWhite e

-- Productions
axiom = free boundExpr

atom = liftM return symbol <|> parGroup

wrap beg g end = between (string beg) (string end) g
parGroup = wrap "(" (inParen wrParen) ")"
        <|> wrap "[" (inParen mkNode) "]"
        <|> wrap "{" (inParen concat) "}"
inParen wr = wr $< oWhite >> looseExpr`endBy`oWhite

chain expr op e = (do o <- try op
                      e' <- expr
                      chain expr op (o e e'))
                  <|> return e

looseExpr = (<?>"loose expression") $ concat $< (boundExpr `sepBy1` free (oneOf ",;"))
boundExpr = (<?>"bound expression") $ wrParen $< (infExpr `sepBy1` free (char '_'))
infExpr = (<?>"infix expression") $ chain (return ()) op =<< tightExpr
  where opExpr = (<?>"operator expression") $ concat $< many1 atom
        op = (free (char '.') >> liftM (\o e _ -> [Group $ o++e]) opExpr)
             <|> (free (char ':') >> liftM2 (\o e' e _ -> [Group $ o++e++e']) opExpr (oWhite >> tightExpr))
tightExpr = (<?>"close expression") $ wrParen $< many1 atom

symbol =  Symbol $< ((string <?> "string") <|> (symbol <?> "symbol"))
  where string = do
          sep <- (char '\'' >> anyChar) <|> char '"'
          str <- many (quoted $ satisfy (/=sep))
          char sep
          return $ '\'':sep:str
        symbol = many1 $ quoted $ noneOf " \t\n()[]{},;.:_"
        quoted p = (char '\\' >> (esc $< anyChar)) <|> p
        esc c = fromMaybe c $ lookup c [('n','\n'),('t','\t')]

white = skipMany1 ((comment <|> spaces) <?> "whitespace")
  where spaces = skipMany1 $ space
        comment = (char '~' >> optional spaces >> void (many1 atom)) <?> "comment"
oWhite = optional $ white