{-# OPTIONS_GHC -Wall #-}
module Transform.Declaration where

import Control.Applicative ((<$>))
import qualified SourceSyntax.Pattern as P
import SourceSyntax.Expression as E
import SourceSyntax.Declaration as D

import qualified Transform.Expression as Expr
import qualified Transform.Definition as Def


combineAnnotations :: [ParseDeclaration] -> Either String [Declaration]
combineAnnotations = go
    where
      msg x = "Syntax Error: The type annotation for '" ++ x ++
              "' must be directly above its definition."

      exprCombineAnnotations = Expr.crawlLet Def.combineAnnotations

      go decls =
          case decls of
            -- simple cases, pass them through with no changes
            [] -> return []

            Datatype name tvars ctors : rest ->
                (:) (Datatype name tvars ctors) <$> go rest

            TypeAlias name tvars alias : rest ->
                (:) (TypeAlias name tvars alias) <$> go rest

            Fixity assoc prec op : rest ->
                (:) (Fixity assoc prec op) <$> go rest

            -- combine definitions
            D.Definition def : defRest ->
                case def of
                  Def pat expr ->
                      do expr' <- exprCombineAnnotations expr
                         let def' = E.Definition pat expr' Nothing
                         (:) (D.Definition def') <$> go defRest

                  TypeAnnotation name tipe ->
                      case defRest of
                        D.Definition (Def pat@(P.Var name') expr) : rest | name == name' ->
                            do expr' <- exprCombineAnnotations expr
                               let def' = E.Definition pat expr' (Just tipe)
                               (:) (D.Definition def') <$> go rest

                        _ -> Left (msg name)

            -- combine ports
            Port port : portRest ->
                case port of
                  PPDef name _ -> Left (msg name)
                  PPAnnotation name tipe ->
                      case portRest of
                        Port (PPDef name' expr) : rest | name == name' ->
                            do expr' <- exprCombineAnnotations expr
                               (:) (Port (Out name expr' tipe)) <$> go rest

                        _ -> (:) (Port (In name tipe)) <$> go portRest