module Term where import qualified ModuleBase as Module import qualified TermParser as Parser import TermParser ( lexer, symbol, operators, operatorSymbols, operatorStart, operatorLetter ) import SourceText ( ModuleRange(ModuleRange), emptyRange, ParserRange, consRange, ranged ) import InOut ( Input, Output, input, output ) import qualified Text.ParserCombinators.Parsec.Token as T import qualified Text.ParserCombinators.Parsec.Expr as Expr import qualified Text.ParserCombinators.Parsec as Parsec import Text.ParserCombinators.Parsec.Expr ( Assoc ) import Text.ParserCombinators.Parsec ( Parser, getPosition, (<|>), (), ) import Text.PrettyPrint.HughesPJ ( Doc, (<+>), fsep, parens, render, text ) import qualified Control.Monad.Exception.Synchronous as ME import Control.Monad.Exception.Synchronous ( Exceptional(Exception) ) import Control.Monad ( liftM2, mzero ) import Control.Applicative ( (<$>) ) import Data.Char (isUpper, isLower) data Identifier range = Identifier { range :: range, name :: String } instance Functor Identifier where fmap f (Identifier rng idname) = Identifier (f rng) idname identifier :: Module.Name -> String -> Identifier ModuleRange identifier moduName = Identifier (ModuleRange moduName Module.noVersion emptyRange) isConstructor :: Identifier range -> Bool isConstructor i = case name i of c:_ -> c == '[' || c == ':' || isUpper c _ -> error "isConstructor: identifier must be non-empty" isVariable :: Identifier range -> Bool isVariable i = case name i of c:_ -> isLower c || elem c ('_':operatorSymbols) _ -> error "isVariable: identifier must be non-empty" parenOperator :: (ParserRange range) => Parser (Identifier range) parenOperator = T.parens lexer $ T.lexeme lexer $ fmap (uncurry Identifier) $ ranged $ liftM2 (:) operatorStart (Parsec.many operatorLetter) infixOperator :: (ParserRange range) => Parser (Identifier range) infixOperator = T.lexeme lexer $ fmap (uncurry Identifier) $ ranged $ Parsec.between (Parsec.char '`') (Parsec.char '`') Parser.identifier <|> liftM2 (:) operatorStart (Parsec.many operatorLetter) instance (ParserRange range) => Input (Identifier range) where input = T.lexeme lexer $ fmap (uncurry Identifier) $ ranged Parser.identifier instance Output (Identifier range) where output i = text $ name i instance Show (Identifier range) where show = render . output data Term range = Node (Identifier range) [ Term range ] | Number range Integer | StringLiteral range String instance Show (Term range) where show = render . output match :: Term range -> Term range -> Bool match (Node x xs) (Node y ys) = name x == name y && equatingList match xs ys match (Number _rngx x) (Number _rngy y) = x==y match (StringLiteral _rngx x) (StringLiteral _rngy y) = x==y match _ _ = False equatingList :: (a -> a -> Bool) -> [a] -> [a] -> Bool equatingList eq = let go (x:xs) (y:ys) = eq x y && go xs ys go [] [] = True go _ _ = False in go variable :: Module.Name -> String -> Term ModuleRange variable moduName n = Node (identifier moduName n) [] main :: Term ModuleRange main = variable Module.mainName "main" {- | simplifies case analysis -} viewNode :: Term range -> Maybe (String, [Term range]) viewNode (Node f xs) = Just (Term.name f, xs) viewNode _ = Nothing liftMonadFail :: (Monad m) => Exceptional String a -> m a liftMonadFail = ME.switch fail return {- | This function allows us to handle @((f) a) b@ equivalently to @f a b@ in any circumstance, i.e. parsing and rewriting. -} appendArguments :: Term range -> [Term range] -> Exceptional String (Term range) appendArguments g ys = case (g, ys) of (Node f xs, _) -> return $ Node f $ xs ++ ys (t, []) -> return t (t, _) -> Exception $ unwords [ "cannot apply ", show t, "to arguments like a function" ] {- | I would like to use 'T.stringLiteral' but this skips trailing spaces and we need the precise range of the literal. However this implementation is very simplistic, since T.stringChar is not exported. -} parseStringLiteral :: Parsec.Parser String parseStringLiteral = flip () "literal string" $ -- fmap catMaybes $ Parsec.between (Parsec.char '"') (Parsec.char '"' "end of string") (Parsec.many (Parsec.noneOf $ '"':"\n\r\\")) -- (Parsec.many (T.stringChar lexer)) parseAtom :: (ParserRange range) => Parser (Term range) parseAtom = (T.lexeme lexer $ fmap (uncurry Number) $ ranged (fmap read $ Parsec.many1 Parsec.digit)) <|> fmap (uncurry StringLiteral) (T.lexeme lexer (ranged parseStringLiteral)) -- <|> fmap (uncurry StringLiteral) (ranged (T.stringLiteral lexer)) <|> T.parens lexer input <|> bracketedList <|> fmap (flip Node []) input parse :: (ParserRange range) => Parser (Term range) parse = liftMonadFail =<< liftM2 appendArguments parseAtom (Parsec.many parseAtom) instance (ParserRange range) => Input (Term range) where input = Expr.buildExpressionParser table parse table :: (ParserRange range) => Expr.OperatorTable Char st (Term range) table = map ( map binary ) operators binary :: (ParserRange range) => (String, Assoc) -> Expr.Operator Char st (Term range) binary (s, assoc) = flip Expr.Infix assoc $ do rng <- Parsec.try $ T.lexeme lexer $ do (rng,_) <- ranged $ Parsec.string s Parsec.notFollowedBy operatorLetter ("end of " ++ show s) return rng return $ \ l r -> Node ( Identifier { name = s, range = rng } ) [ l, r ] bracketedList :: (ParserRange range) => Parser (Term range) bracketedList = do (r,_) <- ranged $ symbol "[" insideBracketedList r insideBracketedList :: (ParserRange range) => range -> Parser (Term range) insideBracketedList rng = do (r,_) <- ranged $ symbol "]" return $ Node ( Identifier { name = "[]", range = r } ) [] <|> do x <- input q <- getPosition xs <- do symbol "]" ; r <- getPosition return $ Node ( Identifier { name = "[]", range = consRange q r } ) [] <|> do symbol "," ; r <- getPosition insideBracketedList $ consRange q r return $ Node ( Identifier { name = ":", range = rng } ) [ x, xs ] instance Output (Term range) where output t = case t of Number _ n -> text $ show n StringLiteral _ s -> text $ show s Node f args -> output f <+> fsep ( map protected args ) protected :: Term range -> Doc protected t = case t of Node _f (_:_) -> parens $ output t _ -> output t termRange :: Term range -> range termRange (Node i _) = range i termRange (Number rng _) = rng termRange (StringLiteral rng _) = rng instance Functor Term where fmap f t = case t of Node i ts -> Node (f <$> i) (map (fmap f) ts) Number rng n -> Number (f rng) n StringLiteral rng str -> StringLiteral (f rng) str {- | compute the number of nodes in the same depth -} breadths :: Term range -> [ Int ] breadths t = 1 : case t of Node _f xs -> foldl addList [] $ map breadths xs _ -> [] addList :: [ Int ] -> [ Int ] -> [ Int ] addList (x:xs) (y:ys) = (x+y) : addList xs ys addList [] ys = ys addList xs [] = xs type Position = [ Int ] subterms :: Term range -> [ (Position, Term range) ] subterms t = ( [], t ) : case t of Node _f xs -> do (k, x) <- zip [ 0.. ] xs (p, s) <- subterms x return (k : p, s) _ -> [] peek :: Term range -> Position -> Maybe (Term range) peek t [] = return t peek (Node _f xs) (k : ks) = case drop k xs of x:_ -> peek x ks [] -> mzero peek _ _ = mzero poke :: Term range -> Position -> Term range -> Maybe (Term range) poke _t [] s = return s poke (Node f xs) (k : ks) s = case splitAt k xs of (pre, x : post) -> do y <- poke x ks s return $ Node f $ pre ++ y : post (_, []) -> error "Term.poke: index too large" poke _ (_:_) _ = error "Term.poke: cannot access a leaf with an index"