-- | The Language type that is the core of GroteTrap. module Language.GroteTrap.Language ( -- * Language Language(..), language, -- * Operators Operator(..), Fixity1(..), Fixity2(..), isUnary, isBinary, isNary, findOperator, -- * Functions Function(..), findFunction, function1, function2 ) where ------------------------------------ -- Language ------------------------------------ -- | Language connects the syntax of identifiers, numbers, operators and functions with their semantics. GroteTrap is able to derive a parser and evaluator from a Language, as well as convert between source text selections and tree selections. data Language a = Language { variable :: String -> a , number :: Int -> a , operators :: [Operator a] , functions :: [Function a] } -- | An empty language. Use this as the starting base of your languages, setting only those fields that are of importance. language :: Language a language = Language { variable = error "variables are not supported" , number = error "numbers are not supported" , operators = [] , functions = [] } ------------------------------------ -- Operators ------------------------------------ -- | Representation of an operator. data Operator a = -- | An operator expecting one operand. Unary { opSem1 :: a -> a , opFixity1 :: Fixity1 , opPrio :: Int , opToken :: String } | -- | An operator expecting two operands. Binary { opSem2 :: a -> a -> a , opFixity2 :: Fixity2 , opPrio :: Int , opToken :: String } | -- | An infix associative operator that chains together an arbitrary number of operands. Nary { opSemN :: [a] -> a , opSubranges :: Bool , opPrio :: Int , opToken :: String } -- | Fixity for unary operators. data Fixity1 = Prefix -- ^ The operator is written before its operand. | Postfix -- ^ The operator is written after its operand. deriving (Show, Enum, Eq) -- | Fixity for infix binary operators. data Fixity2 = InfixL -- ^ The operator associates to the left. | InfixR -- ^ The operator associates to the right. deriving (Show, Enum, Eq) isUnary, isBinary, isNary :: Operator a -> Bool isUnary (Unary _ _ _ _) = True isUnary _ = False isBinary (Binary _ _ _ _) = True isBinary _ = False isNary (Nary _ _ _ _) = True isNary _ = False -- | Yields the specified operator in a monad. Fails when there are no operators with the name, or where there are several operators with the name. findOperator :: Monad m => String -> [Operator a] -> m (Operator a) findOperator name os = case filter ((== name) . opToken) os of [] -> fail ("no operator " ++ name ++ " exists") [o] -> return o _ -> fail ("several operators " ++ name ++ " exist") ------------------------------------ -- Functions ------------------------------------ -- | Representation of a function. data Function a = Function { fnSem :: [a] -> a , fnName :: String , fnArity :: Int } -- | Lifts a unary function to a 'Function'. function1 :: (a -> a) -> String -> Function a function1 f s = Function (\[x] -> f x) s 1 -- | Lifts a binary function to a 'Function'. function2 :: (a -> a -> a) -> String -> Function a function2 f s = Function (\[x, y] -> f x y) s 2 -- | Yields the function with the specified name. If there are no functions with the name, or if there are several functions with the name, failure is returned. findFunction :: Monad m => String -> [Function a] -> m (Function a) findFunction name fs = case filter ((== name) . fnName) fs of [] -> fail ("no function named " ++ name) [f] -> return f _ -> fail ("duplicate function " ++ name) {- semFunction :: Function a -> [a] -> a semFunction fun args = if arity == length args then fnSem args else error $ concat ["function ", name, " expects ", show arity, " ", argtext, ", but got ", show $ length args] where arity = functionArity fun name = functionName fun argtext | arity == 1 = "argument" | otherwise = "arguments" -}