content: module Stmt where
  
  import Control.Applicative (Alternative (many), optional, (<|>))
  import Data.Maybe (fromMaybe)
  import Expr hiding (Class, initialiser)
  import Parser
  import Token qualified as T
  
  -- Statements!
  
  data Variable = Variable
    { ident :: T.Ident,
      value :: Maybe Expr
    }
    deriving (Show)
  
  variable :: Parser T.Tokens Variable
  variable = Variable <$> identifier' <*> optional (matches (T.MultiToken T.Equal) *> expression)
  
  data If = If
    { ifCondition :: Expr,
      thenBranch :: Stmt,
      elseBranch :: Maybe Stmt
    }
    deriving (Show)
  
  data While = While
    { whileCondition :: Expr,
      whileLoop :: Stmt
    }
    deriving (Show)
  
  data For = For
    { forInitialiser :: Maybe Stmt,
      forCondition :: Maybe Expr,
      forIncrement :: Maybe Expr,
      forLoop :: Stmt
    }
    deriving (Show)
  
  data Fun = Fun
    { name :: T.Ident,
      parameters :: [T.Ident],
      block :: Stmt
    }
    deriving (Show)
  
  data Class = Class
    { cls :: T.Ident,
      parent :: Maybe T.Ident,
      methods :: [Fun]
    }
    deriving (Show)
  
  data Stmt
    = ExprStmt Expr
    | PrintStmt Expr
    | BlockStmt Block
    | VariableStmt Variable
    | IfStmt If
    | WhileStmt While
    | ForStmt For
    | FunStmt Fun
    | ReturnStmt (Maybe Expr)
    | ClassStmt Class
    deriving (Show)
  
  type Block = [Stmt]
  
  -- Statement parsing!
  
  expr' :: Parser T.Tokens Stmt
  expr' = ExprStmt <$> expression <* matches (T.SingleToken T.Semicolon)
  
  print' :: Parser T.Tokens Stmt
  print' = matches (T.KeywordToken T.Print) *> (PrintStmt <$> expression) <* matches (T.SingleToken T.Semicolon)
  
  block' :: Parser T.Tokens Stmt
  block' = (matches (T.SingleToken T.LeftBrace) *> matches (T.SingleToken T.Newline)) *> (BlockStmt <$> many stmt) <* matches (T.SingleToken T.RightBrace)
  
  var :: Parser T.Tokens Stmt
  var = matches (T.KeywordToken T.Var) *> (VariableStmt <$> variable) <* matches (T.SingleToken T.Semicolon)
  
  if' :: Parser T.Tokens Stmt
  if' = IfStmt <$> (matches (T.KeywordToken T.If) *> (If <$> grouping <*> stmt <*> optional (matches (T.KeywordToken T.Else) *> stmt)))
  
  while :: Parser T.Tokens Stmt
  while = WhileStmt <$> (While <$> (matches (T.KeywordToken T.While) *> grouping) <*> stmt)
  
  parameters' :: Parser T.Tokens [T.Ident]
  parameters' = (:) <$> identifier' <*> many (matches (T.SingleToken T.Comma) *> identifier')
  
  fun' :: Parser T.Tokens Fun
  fun' =
    Fun
      <$> identifier'
      <*> (matches (T.SingleToken T.LeftParen) *> (fromMaybe [] <$> optional parameters') <* matches (T.SingleToken T.RightParen))
      <*> block'
  
  initialiser :: Parser T.Tokens (Maybe Stmt)
  initialiser = optional (expr' <|> var)
  
  for :: Parser T.Tokens Stmt
  for =
    ForStmt
      <$> ( For
              <$> (matches (T.KeywordToken T.For) *> matches (T.SingleToken T.LeftParen) *> initialiser)
              <*> (optional expression <* matches (T.SingleToken T.Semicolon))
              <*> (optional expression <* matches (T.SingleToken T.RightParen))
              <*> stmt
          )
  
  return' :: Parser T.Tokens Stmt
  return' = matches (T.KeywordToken T.Return) *> (ReturnStmt <$> optional expression) <* matches (T.SingleToken T.Semicolon)
  
  class' :: Parser T.Tokens Stmt
  class' =
    ClassStmt
      <$> ( Class
              <$> (matches (T.KeywordToken T.Class) *> identifier')
              <*> optional (matches (T.MultiToken T.Less) *> identifier')
              <*> ( matches (T.SingleToken T.LeftBrace)
                      *> many (matches (T.SingleToken T.Newline))
                      *> many methods
                      <* matches (T.SingleToken T.RightBrace)
                  )
          )
    where
      methods = fun' <* many (matches (T.SingleToken T.Newline))
  
  stmt :: Parser T.Tokens Stmt
  stmt =
    ( (FunStmt <$> (matches (T.KeywordToken T.Fun) *> fun'))
        <|> var
        <|> expr'
        <|> print'
        <|> block'
        <|> if'
        <|> while
        <|> for
        <|> return'
        <|> class'
    )
      <* many (matches (T.SingleToken T.Newline)) -- consume newlines!
  
  type Stmts = [Stmt]
  
  stmtise :: T.Tokens -> Maybe Stmts
  stmtise = consume stmt
  