packages feed

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

-----------------------------------------------------------------------------
-- Copyright 2009, 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, parseWith, pExpr
   , pEquations, pEquation, pOrList, pFractional
   ) where

import Prelude hiding ((^))
import Text.Parsing hiding (pParens)
import Control.Monad
import Data.List
import Data.Maybe
import Common.Transformation
import Domain.Math.Data.Equation
import Domain.Math.Expr.Data
import Domain.Math.Expr.Symbolic
import Domain.Math.Expr.Symbols
import Domain.Math.Data.OrList
import Test.QuickCheck (arbitrary)

import Text.OpenMath.Dictionary.Arith1
import Text.OpenMath.Dictionary.Logic1
import Text.OpenMath.Dictionary.Relation1
import Text.OpenMath.Dictionary.Calculus1
import Text.OpenMath.Dictionary.Fns1
import Text.OpenMath.Dictionary.Transc1

symbols :: [Symbol]
symbols = nubBy (\x y -> symbolName x == symbolName y) $ 
   concat dictionaries

dictionaries :: [[Symbol]]
dictionaries = 
   [ arith1List, logic1List, relation1List, calculus1List
   , fns1List, transc1List
   ]

dictionaryNames :: [String]
dictionaryNames = mapMaybe dictionary (concatMap (take 1) dictionaries)

scannerExpr :: Scanner
scannerExpr = defaultScanner 
   { keywords          = "sqrt" : map symbolName symbols ++ dictionaryNames
   , keywordOperators  = ["==" ]
   , specialCharacters = "+-*/^()[]{},."
   }

parseWith :: TokenParser a -> String -> Either SyntaxError a
parseWith p = f . parse p . scanWith scannerExpr
 where 
   f (e, []) = Right e
   f (_, xs) = Left $ ErrorMessage $ unlines $ map show xs

parseExpr :: String -> Either SyntaxError Expr
parseExpr = parseWith 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 -- flip ($) <$> expr6u <*> optional (flip (/) <$ pKey "/" <*> expr6u) id

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
      <|> (Var . fst) <$> pVarid
      <|> pParens pExpr

symb :: TokenParser ([Expr] -> Expr)
symb =  unqualifiedSymb
    <|> qualifiedSymb
    -- To fix: sqrt expects exactly one argument
    <|> (\xs -> function rootSymbol (xs ++ [2])) <$ pKey "sqrt" 

unqualifiedSymb :: TokenParser ([Expr] -> Expr)
unqualifiedSymb = pChoice (map (\s -> function s <$ pKey (symbolName s)) symbols)

qualifiedSymb :: TokenParser ([Expr] -> Expr)
qualifiedSymb = pChoice (map f dictionaries)
 where
   f xs = case map dictionary xs of
             Just d:_ -> pKey d <* pSpec '.' *> pChoice (map g xs)
             _        -> pFail
   g s  = function s <$ pKey (symbolName s)

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

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

pOrList :: TokenParser a -> TokenParser (OrList a)
pOrList p = (join . orList) <$> pSepList pTerm (pKey "or")
 where 
   pTerm =  return <$> p 
        <|> true   <$  pKey "true" 
        <|> false  <$  pKey "false"
   pSepList p q = (:) <$> p <*> pList (q *> p)

pParens :: TokenParser a -> TokenParser a
pParens p = pKey "(" *> p <* pKey ")"

-----------------------------------------------------------------------
-- 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