{-# LANGUAGE NoImplicitPrelude, TemplateHaskell #-}
-- | Language.Lua.Annotated.Syntax is kind of an AST, but not completely.
-- This module exist to correct that deficiency.
module Language.Lua.Annotated.Syntax.HighLevel where

import BasicPrelude
import Control.Lens
import Control.Lens.TH
import Data.Text (unpack)
import Language.Lua.Annotated.Syntax

data AStat a =
    AAssign a [(Var a, Exp a)]
  | AFunCall a (FunCall a)
  | ALabel a (Name a)
  | ABreak a
  | AGoto a (Name a)
  | ADo a (Block a)
  | AWhile a (Exp a) (Block a)
  | ARepeat a (Block a) (Exp a)
  | AIf a [(Exp a, Block a)] (Maybe (Block a))
  | AForRange a (Name a) (Exp a) (Exp a) (Maybe (Exp a)) (Block a)
  | AForIn a [(Name a, Exp a)] (Block a)
  | AFunAssign a (FunName a) (FunBody a)
  | ALocalFunAssign a (Name a) (FunBody a)
  | ALocalAssign a (Either [Name a] [(Name a, Exp a)])
  | AEmptyStat a

makePrisms ''AStat

abstract :: Show a => Iso' (Stat a) (AStat a)
abstract = iso abs con
  where
    abs (Assign a v e) | length v == length e = AAssign a $ zip v e
                       | otherwise            = error $ unpack (show a) ++ ": Number of variables and expressions must be the same"
    abs (FunCall a f) = AFunCall a f
    abs (Label a n) = ALabel a n
    abs (Break a) = ABreak a
    abs (Goto a n) = AGoto a n
    abs (Do a b) = ADo a b
    abs (While a e b) = AWhile a e b
    abs (Repeat a b e) = ARepeat a b e
    abs (If a b c) = AIf a b c
    abs (ForRange a n e f m b) = AForRange a n e f m b
    abs (ForIn a n e b) | length n == length e = AForIn a (zip n e) b
                        | otherwise            = error $ unpack (show a) ++ ": Number of variables and expressions must be the same"
    abs (FunAssign a n b) = AFunAssign a n b
    abs (LocalAssign a n (Just e)) | length n == length e = ALocalAssign a (Right $ zip n e)
                                   | otherwise            = error $ unpack (show a) ++ ": Number of variables and expressions must be the same"
    abs (LocalAssign a n Nothing) = ALocalAssign a $ Left n
    abs (EmptyStat a) = AEmptyStat a
    con (AAssign a z) = uncurry (Assign a) $ unzip z
    con (AFunCall a f) = FunCall a f
    con (ABreak a) = Break a
    con (AGoto a n) = Goto a n
    con (ADo a b) = Do a b
    con (AWhile a e b) = While a e b
    con (ARepeat a b e) = Repeat a b e
    con (AIf a b c) = If a b c
    con (AForRange a n e f m b) = ForRange a n e f m b
    con (AForIn a z b) = ForIn a n e b where (n,e) = unzip z
    con (AFunAssign a n b) = FunAssign a n b
    con (ALocalAssign a (Right z)) = LocalAssign a n $ Just e where (n,e) = unzip z
    con (ALocalAssign a (Left n)) = LocalAssign a n Nothing
    con (AEmptyStat a) = EmptyStat a
