{-# LANGUAGE OverloadedStrings #-} -- | Handles parsing of both infix and RPN Predbox expressions. module Prednote.Expressions ( ExprDesc(..) , Error , Token , operand , opAnd , opOr , opNot , openParen , closeParen , parseExpression ) where import Data.Either (partitionEithers) import Data.Functor.Contravariant import qualified Data.Text as X import qualified Prednote.Expressions.Infix as I import qualified Prednote.Expressions.RPN as R import Prednote.Core (Pred) -- | A single type for both RPN tokens and infix tokens. newtype Token a = Token { unToken :: I.InfixToken a } instance Contravariant Token where contramap f = Token . contramap f . unToken type Error = X.Text -- | Creates Operands from Predbox. operand :: Pred a -> Token a operand p = Token (I.TokRPN (R.TokOperand p)) -- | The And operator opAnd :: Token a opAnd = Token (I.TokRPN (R.TokOperator R.OpAnd)) -- | The Or operator opOr :: Token a opOr = Token (I.TokRPN (R.TokOperator R.OpOr)) -- | The Not operator opNot :: Token a opNot = Token (I.TokRPN (R.TokOperator R.OpNot)) -- | Open parentheses openParen :: Token a openParen = Token (I.TokParen I.Open) -- | Close parentheses closeParen :: Token a closeParen = Token (I.TokParen I.Close) -- | Is this an infix or RPN expression? data ExprDesc = Infix | RPN deriving (Eq, Show) toksToRPN :: [Token a] -> Maybe [R.RPNToken a] toksToRPN toks = let toEither t = case unToken t of I.TokRPN tok -> Right tok _ -> Left () in case partitionEithers . map toEither $ toks of ([], xs) -> return xs _ -> Nothing -- | Parses expressions. Fails if the expression is nonsensical in -- some way (for example, unbalanced parentheses, parentheses in an -- RPN expression, or multiple stack values remaining.) Works by first -- changing infix expressions to RPN ones. parseExpression :: ExprDesc -> [Token a] -> Either Error (Pred a) parseExpression e toks = do rpnToks <- case e of Infix -> maybe (Left "unbalanced parentheses\n") Right . I.createRPN . map unToken $ toks RPN -> maybe (Left "parentheses in an RPN expression\n") Right $ toksToRPN toks R.parseRPN rpnToks