module Language.Symantic.Grammar.Operators where
import Control.Applicative (Applicative(..))
import Control.Monad (void)
import Data.Foldable hiding (any)
import Prelude hiding (any)
import Language.Symantic.Grammar.Fixity
import Language.Symantic.Grammar.EBNF
import Language.Symantic.Grammar.Terminal
import Language.Symantic.Grammar.Regular
import Language.Symantic.Grammar.ContextFree
class
( Gram_Terminal g
, Gram_Rule g
, Gram_Alt g
, Gram_Try g
, Gram_App g
, Gram_AltApp g
, Gram_CF g
) => Gram_Op g where
operators
:: CF g a
-> CF g (Unifix, a -> a)
-> CF g (Infix , a -> a -> a)
-> CF g (Unifix, a -> a)
-> CF g (Either Error_Fixity a)
operators g prefixG infixG postfixG =
(evalOpTree <$>) <$> go g prefixG infixG postfixG
where
go
:: CF g a
-> CF g (Unifix, a -> a)
-> CF g (Infix , a -> a -> a)
-> CF g (Unifix, a -> a)
-> CF g (Either Error_Fixity (OpTree a))
go = rule4 "operators" $ \aG preG inG postG ->
(\pres a posts ->
let nod_a =
foldr insertUnifix
(foldl' (flip insertUnifix) (OpTree0 a) posts)
pres
in \case
Just (in_, b) -> insertInfix nod_a in_ b
Nothing -> Right nod_a)
<$> many (try preG)
<*> aG
<*> many (try postG)
<*> option Nothing (curry Just <$> try inG <*> go aG preG inG postG)
infixrG :: CF g a -> CF g (a -> a -> a) -> CF g a
infixrG = rule2 "infixr" $ \g opG ->
(\a -> \case
Just (op, b) -> a `op` b
Nothing -> a)
<$> g
<*> option Nothing (try $ curry Just <$> opG <*> infixrG g opG)
infixlG :: CF g a -> CF g (a -> a -> a) -> CF g a
infixlG = rule2 "infixl" $ \g opG ->
($ id) <$> go g opG
where
go :: CF g a -> CF g (a -> a -> a) -> CF g ((a -> a) -> a)
go g opG =
(\a -> \case
Just (op, kb) -> \k -> kb (k a `op`)
Nothing -> ($ a))
<$> g
<*> option Nothing (try $ curry Just <$> opG <*> go g opG)
deriving instance Gram_Op g => Gram_Op (CF g)
instance Gram_Op RuleEBNF
instance Gram_Op EBNF
data Error_Fixity
= Error_Fixity_Infix_not_combinable Infix Infix
deriving (Eq, Show)
data OpTree a
= OpTree0 a
| OpTree1 Unifix (a -> a) (OpTree a)
| OpTree2 Infix (a -> a -> a) (OpTree a) (OpTree a)
instance Show a => Show (OpTree a) where
showsPrec n (OpTree0 a) =
showParen (n > 10) $ showString "OpTree0 "
. showsPrec 11 a
showsPrec n (OpTree1 f _ a) =
showParen (n > 10) $ showString "OpTree1 "
. showsPrec 11 f
. showChar ' ' . showsPrec 11 a
showsPrec n (OpTree2 f _ a b) =
showParen (n > 10) $ showString "OpTree2 "
. showsPrec 11 f
. showChar ' ' . showsPrec 11 a
. showChar ' ' . showsPrec 11 b
insertUnifix :: (Unifix, a -> a) -> OpTree a -> OpTree a
insertUnifix a@(uni_a@(Prefix prece_a), op_a) nod_b =
case nod_b of
OpTree0{} -> OpTree1 uni_a op_a nod_b
OpTree1 Prefix{} _op_b _nod -> OpTree1 uni_a op_a nod_b
OpTree1 uni_b@(Postfix prece_b) op_b nod ->
case prece_b `compare` prece_a of
GT -> OpTree1 uni_a op_a nod_b
EQ -> OpTree1 uni_a op_a nod_b
LT -> OpTree1 uni_b op_b $ insertUnifix a nod
OpTree2 inf_b op_b l r ->
case infix_prece inf_b `compare` prece_a of
GT -> OpTree1 uni_a op_a nod_b
EQ -> OpTree1 uni_a op_a nod_b
LT -> OpTree2 inf_b op_b (insertUnifix a l) r
insertUnifix a@(uni_a@(Postfix prece_a), op_a) nod_b =
case nod_b of
OpTree0{} -> OpTree1 uni_a op_a nod_b
OpTree1 uni_b@(Prefix prece_b) op_b nod ->
case prece_b `compare` prece_a of
GT -> OpTree1 uni_a op_a nod_b
EQ -> OpTree1 uni_a op_a nod_b
LT -> OpTree1 uni_b op_b $ insertUnifix a nod
OpTree1 Postfix{} _op_b _nod -> OpTree1 uni_a op_a nod_b
OpTree2 inf_b op_b l r ->
case infix_prece inf_b `compare` prece_a of
GT -> OpTree1 uni_a op_a nod_b
EQ -> OpTree1 uni_a op_a nod_b
LT -> OpTree2 inf_b op_b l (insertUnifix a r)
insertInfix
:: OpTree a
-> (Infix, a -> a -> a)
-> Either Error_Fixity (OpTree a)
-> Either Error_Fixity (OpTree a)
insertInfix nod_a in_@(inf_a, op_a) e_nod_b = do
nod_b <- e_nod_b
case nod_b of
OpTree0{} -> Right $ OpTree2 inf_a op_a nod_a nod_b
OpTree1 uni_b op_b nod ->
case unifix_prece uni_b `compare` infix_prece inf_a of
EQ -> Right $ OpTree2 inf_a op_a nod_a nod_b
GT -> Right $ OpTree2 inf_a op_a nod_a nod_b
LT -> do
n <- insertInfix nod_a in_ (Right nod)
Right $ OpTree1 uni_b op_b n
OpTree2 inf_b op_b l r ->
case infix_prece inf_b `compare` infix_prece inf_a of
GT -> Right $ OpTree2 inf_a op_a nod_a nod_b
LT -> do
n <- insertInfix nod_a in_ (Right l)
Right $ OpTree2 inf_b op_b n r
EQ ->
let ass = \case
AssocL -> SideL
AssocR -> SideR
AssocB lr -> lr in
case (ass <$> infix_assoc inf_b, ass <$> infix_assoc inf_a) of
(Just SideL, Just SideL) -> do
n <- insertInfix nod_a in_ (Right l)
Right $ OpTree2 inf_b op_b n r
(Just SideR, Just SideR) ->
Right $ OpTree2 inf_a op_a nod_a nod_b
_ -> Left $ Error_Fixity_Infix_not_combinable inf_a inf_b
evalOpTree :: OpTree a -> a
evalOpTree (OpTree0 a) = a
evalOpTree (OpTree1 _uni op n) = op $ evalOpTree n
evalOpTree (OpTree2 _inf op l r) = evalOpTree l `op` evalOpTree r
gram_operators :: (Gram_Op g, Gram_RuleEBNF g) => [CF g ()]
gram_operators =
[ void $ operators (argEBNF "expr") (argEBNF "prefix") (argEBNF "infix") (argEBNF "postfix")
]