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