{-# LANGUAGE ScopedTypeVariables, DeriveFunctor #-} -- Most of this module follows the Haskell report, https://www.haskell.org/onlinereport/lexemes.html module Paren(Paren(..), parenOn, unparen) where import Data.Tuple.Extra data Paren a = Item a | Paren a [Paren a] a deriving (Show,Eq,Functor) parenOn :: forall a b . Eq b => (a -> b) -> [(b, b)] -> [a] -> [Paren a] parenOn proj pairs = fst . go Nothing where -- invariant: if first argument is Nothing, second component of result will be Nothing go :: Maybe b -> [a] -> ([Paren a], Maybe (a, [a])) go (Just close) (x:xs) | close == proj x = ([], Just (x, xs)) go close (start:xs) | Just end <- lookup (proj start) pairs , (inner, res) <- go (Just end) xs = case res of Nothing -> (Item start : inner, Nothing) Just (end, xs) -> first (Paren start inner end :) $ go close xs go close (x:xs) = first (Item x :) $ go close xs go close [] = ([], Nothing) unparen :: [Paren a] -> [a] unparen = concatMap f where f (Item x) = [x] f (Paren a b c) = [a] ++ unparen b ++ [c]