packages feed

ideas-math-1.1: src/Domain/RelationAlgebra/Parser.hs

-----------------------------------------------------------------------------
-- Copyright 2014, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
--  $Id: Parser.hs 6548 2014-05-16 10:34:18Z bastiaan $

module Domain.RelationAlgebra.Parser (parseRelAlg, ppRelAlg) where

import Domain.RelationAlgebra.Formula
import Ideas.Text.Parsing
import qualified Text.ParserCombinators.Parsec.Token as P

-----------------------------------------------------------
--- Parser

parseRelAlg  :: String -> Either String RelAlg
parseRelAlg = parseSimple relalg
 where
   relalg = buildExpressionParser table term

   term = foldl (flip ($)) <$> atom <*> many pUn

   pUn = choice
      [ Inv <$ reservedOp "~"
      , Not <$ reservedOp "-"
      ]

   atom = choice
      [ V     <$  P.reserved lexer "V"
      , empty <$  P.reserved lexer "E"
      , I     <$  P.reserved lexer "I"
      , Var   <$> P.identifier lexer
      , P.parens lexer relalg
      ]

   table =
      [ [ Infix ((:.:) <$ reservedOp ";") AssocRight -- or none-associative?
        , Infix ((:+:) <$ reservedOp "!") AssocRight -- or none-associative?
        ]
      , [ Infix ((:&&:) <$ reservedOp "/\\") AssocRight ]
      , [ Infix ((:||:) <$ reservedOp "\\/") AssocRight ]
      ]

-----------------------------------------------------------
--- Lexer

lexer :: P.TokenParser a
lexer = P.makeTokenParser $ emptyDef
   { reservedNames   = ["V", "E", "I"]
   , reservedOpNames = ["~", "-", ";", "!", "\\/", "/\\"]
   , identStart      = letter
   , identLetter     = letter
   , opStart         = fail ""
   , opLetter        = fail ""
   }

reservedOp :: String -> Parser ()
reservedOp = P.reservedOp lexer

-----------------------------------------------------------
--- Pretty-Printer

ppRelAlg :: RelAlg -> String
ppRelAlg = ppRelAlgPrio (0, "")

ppRelAlgPrio :: (Int, String) -> RelAlg -> String
ppRelAlgPrio = (\f n -> f n "") . flip (foldRelAlg alg)
 where
   alg = (var, binop 4 ";", binop 4 "!", binop 3 "/\\", binop 2 "\\/"
         , nott, inv, var "V", var "I"
         )
   binop prio op p q (n, parent) =
      parIf (n > prio || (prio==4 && n==4 && op/=parent)) (p (prio+1, op) . ((" "++op++" ")++) . q (prio, op))
   var       = const . (++)
   nott p _  = p (6, "") . ("-"++)
   inv  p _  = p (6, "") . ("~"++)
   parIf b f = if b then ("("++) . f . (")"++) else f