{-# LANGUAGE CPP, RecursiveDo #-} module Text.Earley.Mixfix where #if !MIN_VERSION_base(4,8,0) import Control.Applicative #endif import Data.Either import Data.Foldable(asum, foldrM) import Text.Earley data Associativity = LeftAssoc | NonAssoc | RightAssoc deriving (Eq, Show) -- | An identifier with identifier parts ('Just's), and holes ('Nothing's) -- representing the positions of its arguments. -- -- Example (commonly written "if_then_else_"): -- @['Just' "if", Nothing, 'Just' "then", Nothing, 'Just' "else", Nothing] :: 'Holey' 'String'@ type Holey a = [Maybe a] -- | Create a grammar for parsing mixfix expressions. mixfixExpression :: [[(Holey (Prod r e t ident), Associativity)]] -- ^ A table of holey identifier parsers, with associativity information. -- The identifiers should be in groups of precedence levels listed from -- binding the least to the most tightly. -- -- The associativity is taken into account when an identifier starts or -- ends with a hole, or both. Internal holes (e.g. after "if" in -- "if_then_else_") start from the beginning of the table. -> Prod r e t expr -- ^ An atom, i.e. what is parsed at the lowest level. This will -- commonly be a (non-mixfix) identifier or a parenthesised expression. -> (Holey ident -> [expr] -> expr) -- ^ How to combine the successful application of a holey identifier to its -- arguments into an expression. -> Grammar r e (Prod r e t expr) mixfixExpression table atom app = mdo expr <- foldrM ($) atom $ map (level expr) table return expr where app' xs = app (either (const Nothing) Just <$> xs) $ lefts xs level expr idents next = mdo same <- rule $ asum $ next : map (mixfixIdent same) idents return same where cons p q = (:) <$> p <*> q mixfixIdent same (ps, a) = app' <$> go ps where go ps' = case ps' of [] -> pure [] [Just p] -> pure . Right <$> p Nothing:rest -> cons (Left <$> if a == RightAssoc then next else same) $ go rest [Just p, Nothing] -> cons (Right <$> p) $ pure . Left <$> if a == LeftAssoc then next else same Just p:Nothing:rest -> cons (Right <$> p) $ cons (Left <$> expr) $ go rest Just p:rest@(Just _:_) -> cons (Right <$> p) $ go rest