content: {-# LANGUAGE ScopedTypeVariables, DeriveFunctor #-}
  
  -- Most of this module follows the Haskell report, https://www.haskell.org/onlinereport/lexemes.html
  module Paren(Paren(..), parens, unparens) where
  
  import Data.Tuple.Extra
  import Lexer(Lexeme(..))
  
  -- | A list of items which are paranthesised.
  data Paren a
      = Item a -- Indiviaaul item
      | Paren a [Paren a] a -- parenthesise, open, inner, close
      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)
  
  parens :: [Lexeme] -> [Paren Lexeme]
  parens = parenOn lexeme [("(",")"),("[","]"),("{","}"),("`","`")]
  
  unparens :: [Paren a] -> [a]
  unparens = concatMap unparen
  
  unparen :: Paren a -> [a]
  unparen (Item x) = [x]
  unparen (Paren a b c) = [a] ++ unparens b ++ [c]
  