module Snail.Ast (
    -- * Constructors for AST
    Bracket (..),
    SnailAst (..),

    -- * Utilities for AST
    unwrap,
) where

import Data.Text (Text)
import Text.Megaparsec

-- | The bracket used to surround the s-expression
data Bracket
    = Round
    | Square
    | Curly
    deriving (Int -> Bracket -> ShowS
[Bracket] -> ShowS
Bracket -> String
(Int -> Bracket -> ShowS)
-> (Bracket -> String) -> ([Bracket] -> ShowS) -> Show Bracket
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Bracket -> ShowS
showsPrec :: Int -> Bracket -> ShowS
$cshow :: Bracket -> String
show :: Bracket -> String
$cshowList :: [Bracket] -> ShowS
showList :: [Bracket] -> ShowS
Show, Bracket -> Bracket -> Bool
(Bracket -> Bracket -> Bool)
-> (Bracket -> Bracket -> Bool) -> Eq Bracket
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Bracket -> Bracket -> Bool
== :: Bracket -> Bracket -> Bool
$c/= :: Bracket -> Bracket -> Bool
/= :: Bracket -> Bracket -> Bool
Eq)

{-
    A possibly empty tree of s-expressions

    Technically,
    @
    Token (SourcePos {..}, "hello")
    @

    isn't a valid s-expression. This is,

    @
    SExpression [Token (SourcePos {..}, "hello")]
    @

    and this is also valid,

    @
    SExpression []
    @

    The 'Data.Tree.Tree' type in containers is non-empty which isn't exactly what we are looking for
-}
data SnailAst
    = Lexeme (SourcePos, Text)
    | TextLiteral (SourcePos, Text)
    | SExpression (Maybe Char) Bracket [SnailAst]
    deriving (SnailAst -> SnailAst -> Bool
(SnailAst -> SnailAst -> Bool)
-> (SnailAst -> SnailAst -> Bool) -> Eq SnailAst
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnailAst -> SnailAst -> Bool
== :: SnailAst -> SnailAst -> Bool
$c/= :: SnailAst -> SnailAst -> Bool
/= :: SnailAst -> SnailAst -> Bool
Eq, Int -> SnailAst -> ShowS
[SnailAst] -> ShowS
SnailAst -> String
(Int -> SnailAst -> ShowS)
-> (SnailAst -> String) -> ([SnailAst] -> ShowS) -> Show SnailAst
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnailAst -> ShowS
showsPrec :: Int -> SnailAst -> ShowS
$cshow :: SnailAst -> String
show :: SnailAst -> String
$cshowList :: [SnailAst] -> ShowS
showList :: [SnailAst] -> ShowS
Show)

{- | Unwrap nested s-expressions, this is very useful when you are converting
'SnailAst' to your own AST.

You'll likely have a function `snailAstToMyAst :: SnailAst -> m MyAst` where
`m` is `ExceptT` or `MonadExcept`. Then, your final case statement should
unwrap nested expressions to their base with,

@
snailAstToMyAst . unwrap . SExpression initialChar bracket $ unwrap <$> exprs`
@

For example, `((read))` will become `SExpression _ _ [Lexeme (_, "read")]`
which your AST converter should handle.
-}
unwrap :: SnailAst -> SnailAst
unwrap :: SnailAst -> SnailAst
unwrap = \case
    SExpression Maybe Char
_ Bracket
_ [SnailAst
x] -> SnailAst
x
    SnailAst
x -> SnailAst
x