packages feed

ideas-0.7: src/Domain/Math/Expr/Parser.hs

-----------------------------------------------------------------------------
-- Copyright 2010, 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)
--
-----------------------------------------------------------------------------
module Domain.Math.Expr.Parser 
   ( scannerExpr, parseExpr, parseExprWith, pExpr
   , pEquations, pEquation, pOrList, pFractional
   , pRelation, pLogic, pLogicRelation
   ) where

import Prelude hiding ((^))
import Text.Parsing
import Control.Monad.Error
import Common.Rewriting
import Common.Transformation
import qualified Domain.Logic.Formula as Logic
import Domain.Logic.Formula (Logic)
import Domain.Math.Data.Relation
import Domain.Math.Expr.Data
import Domain.Math.Expr.Symbols
import Domain.Math.Data.OrList
import Test.QuickCheck (arbitrary)

scannerExpr :: Scanner
scannerExpr = defaultScanner 
   { keywords             = ["sqrt", "root", "log", "and", "or", "true", "false", "D"]
   , keywordOperators     = ["==", "<=", ">=", "<", ">", "~=", "+", "-", "*", "^", "/"]
   , operatorCharacters   = "+-*/^.=<>~"
   , qualifiedIdentifiers = True
   }

parseExprWith :: TokenParser a -> String -> Either String a
parseExprWith = parseWithM scannerExpr

parseExpr :: String -> Either String Expr
parseExpr = parseExprWith pExpr

pExpr :: TokenParser Expr
pExpr = expr6

-- This expression could have a fraction at top-level: both the numerator
-- and denominator are atoms, optionally preceded by a (unary) minus
pFractional :: TokenParser Expr
pFractional = expr6u 

expr6, expr6u, expr7, expr8, term, atom :: TokenParser Expr
expr6  =  pChainl ((+) <$ pKey "+" <|> (-) <$ pKey "-") expr6u
expr6u =  optional (Negate <$ pKey "-") id <*> expr7
expr7  =  pChainl ((*) <$ pKey "*" <|> (/) <$ pKey "/") expr8
expr8  =  pChainr ((^) <$ pKey "^") term
term   =  symb <*> pList atom
      <|> atom
atom   =  fromInteger <$> pInteger
      <|> Number <$> pReal 
      <|> Var <$> pVarid
      <|> pParens pExpr

symb :: TokenParser ([Expr] -> Expr)
symb = qualifiedSymb
    -- To fix: sqrt expects exactly one argument
    <|> (\xs -> function rootSymbol (xs ++ [2])) <$ pKey "sqrt" 
    <|> function rootSymbol <$ pKey "root"
    <|> function logSymbol  <$ pKey "log"
    <|> makeDiff <$ pKey "D"
 where
   makeDiff [x,a] = unary diffSymbol (binary lambdaSymbol x a)
   makeDiff _     = symbol bottomSymbol

qualifiedSymb :: TokenParser ([Expr] -> Expr)
qualifiedSymb = f <$> (pQVarid <|> pQConid)
 where f (a, b) = function $ newSymbol (a, b)

pEquations :: TokenParser a -> TokenParser (Equations a)
pEquations = pLines True . pEquation

pEquation :: TokenParser a -> TokenParser (Equation a)
pEquation p = (:==:) <$> p <* pKey "==" <*> p

pRelation :: TokenParser a -> TokenParser (Relation a)
pRelation p = (\x f -> f x) <$> p <*> pRelationType <*> p

pRelationChain :: TokenParser a -> TokenParser [Relation a]
pRelationChain p = f <$> p <*> pList1 ((,) <$> pRelationType <*> p)
 where
   f _ [] = []
   f a ((op, b):xs) = op a b:f b xs

pRelationType :: TokenParser (a -> a -> Relation a)
pRelationType = pChoice (map make table)
 where 
   make (s, f) = f <$ pKey s
   table = 
      [ ("==", (.==.)), ("<=", (.<=.)), (">=", (.>=.))
      , ("<", (.<.)), (">", (.>.)), ("~=", (.~=.))
      ]
   
pOrList :: TokenParser a -> TokenParser (OrList a)
pOrList p = (join . orList) <$> pSepList pTerm (pKey "or")
 where 
   pTerm =  return <$> p 
        <|> true   <$  pKey "true" 
        <|> false  <$  pKey "false"

pLogic :: TokenParser a -> TokenParser (Logic a)
pLogic p = levelOr
 where 
   levelOr    =  pChainr ((Logic.:||:) <$ pKey "or")  levelAnd
   levelAnd   =  pChainr ((Logic.:&&:) <$ pKey "and") levelAtom
   levelAtom  =  Logic.Var <$> p
             <|> Logic.F   <$  pKey "false"
             <|> Logic.T   <$  pKey "true" 
             <|> pParens levelOr

pLogicRelation :: TokenParser a -> TokenParser (Logic (Relation a))
pLogicRelation p = (Logic.catLogic . fmap f) <$> pLogic (pRelationChain p)
 where
   f xs = if null xs then Logic.T else foldr1 (Logic.:&&:) (map Logic.Var xs)

-----------------------------------------------------------------------
-- Argument descriptor (for parameterized rules)

instance Argument Expr where
   makeArgDescr = exprArgDescr

exprArgDescr :: String -> ArgDescr Expr
exprArgDescr descr = ArgDescr descr Nothing (either (const Nothing) Just . parseExpr) show arbitrary