Advise-me-0.1: src/Recognize/SubExpr/SEParser.hs
-----------------------------------------------------------------------------
-- Copyright 2019, Advise-Me project team. This file is distributed under
-- the terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Recognize.SubExpr.SEParser
( -- * SubExpression Parser
SEParser, get, put, gets, modify, seParse
-- * User state
, SEState(..), getVarKey, addMatching
-- * Input type
, InputType(..), determineInputType, conformsTo, resetAfter, resetSEState
) where
import Control.Applicative
import Control.Monad
import Control.Monad.State
import qualified Data.Map as M
import Data.Maybe
import qualified Data.Set as S
import Domain.Math.Expr
import Domain.Math.Data.Relation
import Recognize.Data.Math
import qualified Recognize.Expr.Functions as F
import Recognize.SubExpr.Functions
import Recognize.Parsing.Parser
import Control.Monad.Identity
-- | Describes some math type
data InputType = Expr | Definition | Equation | LinearWithType RelationType | Linear deriving (Eq,Show)
-- | Given an expression, check whether it matches the specified input type.
conformsTo :: Expr -> InputType -> Bool
conformsTo e Expr = isNothing $ getEqE e
conformsTo e Definition = isJust $ do
(x :==: _) <- getEqE e
guard $ isFunctionCall x || F.isVar x
conformsTo e Equation = isJust $ do
rel <- getRelationE e
let x = leftHandSide rel
let y = rightHandSide rel
guard (not (F.hasVar x || F.hasVar y) && not (isFunctionCall x))
conformsTo e Linear = isJust $ do
rel <- getRelationE e
let x = leftHandSide rel
let y = rightHandSide rel
guard (not (F.isVar x) && (F.hasVar x || F.hasVar y))
--LinearWithType is a linear expression with the given relationType.
conformsTo e (LinearWithType t) = (conformsTo e Linear) && (isJust $ do
rel <- getRelationE e
let sym = relationType rel
guard (sym == t))
-- | Determine the input type of the given expression
determineInputType :: Expr -> InputType
determineInputType e
| e `conformsTo` Definition = Definition
| e `conformsTo` Equation = Equation
| e `conformsTo` Linear = Linear
| e `conformsTo` (LinearWithType EqualTo) = Linear
| e `conformsTo` (LinearWithType LessThan) = Linear
| otherwise = Expr
-- | The user state of the subexpression recognizer
--
-- It carries parameters for the recognizer, mapping of vars to expressions and other information
data SEState = SEState
{ optGrow :: Bool -- Let the mother expression grow
, growF :: Expr -> Expr -- Grow function
, optIterate :: Bool -- Try more than one iteration
, optTraverse :: Bool -- Allow traversal of mother expression
, optSimplify :: Bool -- ^ Allow simplification of mother expression
, optSkipOnce :: Bool -- Allow expressions to be skipOnced
, chainedEquations :: Bool -- Does the solution contain chained equations? Then maybe we want to parse differently.
, precision :: Int -- Number of decimals that are kept in simplification
, matchings :: S.Set Expr
, usedVariables :: M.Map String Expr -- Variables that are known to be used
, inputType :: Maybe [InputType] -- ^ Type of expressions that may be parsed
, matchPredicate :: Expr -> Bool
}
-- | Default user state
emptyState :: SEState
emptyState = SEState
{ optGrow = False
, growF = id
, optIterate = True
, optTraverse = True
, optSimplify = True
, optSkipOnce = False
, chainedEquations = False
, precision = 2
, matchings = S.empty
, usedVariables = M.empty
, inputType = Nothing
, matchPredicate = const True
}
type SEParser = ParserT SEState Math Identity
seParse :: SEParser a -> [Math] -> Maybe a
seParse p ss =
case runIdentity (runParserT p emptyState ss) of
[] -> Nothing
(a, _, _):_ -> Just a
getVarKey :: Expr -> SEParser String
getVarKey (Sym s [Var x]) = guard (isVarSymbol s) >> return x
getVarKey _ = empty
addMatching :: Expr -> SEParser ()
addMatching e =
modify $ \st -> st { matchings = S.insert e (matchings st) }
-- | Reset the user state to `dSEState` after executing the parser
resetAfter :: SEParser a -> SEParser a
resetAfter sp = do
a <- sp
put emptyState
return a
-- | Reset the user state to `dSEState`. Returns the user state before resetting.
resetSEState :: SEParser SEState
resetSEState = do
us <- get
put emptyState
return us