{-# LANGUAGE OverloadedStrings #-}
module Tokstyle.Cimple.Analysis.FuncScopes (analyse) where

import           Control.Monad               (foldM, when)
import qualified Control.Monad.State.Lazy    as State
import           Data.Text                   (Text)
import qualified Data.Text                   as Text
import           Tokstyle.Cimple.AST         (Node (..), Scope (..))
import           Tokstyle.Cimple.Diagnostics (warn)
import           Tokstyle.Cimple.Lexer       (Lexeme (..), lexemeLine,
                                              lexemeText)


analyse :: FilePath -> [Node (Lexeme Text)] -> [Text]
analyse file ast = reverse $ snd $ State.runState (foldM go [] ast) []
  where
    go decls (FunctionDecl declScope (FunctionPrototype _ name _)) =
        return $ (lexemeText name, (name, declScope)) : decls
    go decls (FunctionDefn defnScope (FunctionPrototype _ name _) _) =
        case lookup (lexemeText name) decls of
            Nothing -> return decls
            Just (decl, declScope) -> do
                when (declScope /= defnScope) $ warn file name $
                    warning decl declScope defnScope
                return decls
    go decls _ = return decls

    warning decl declScope defnScope =
        "function definition `" <> lexemeText decl
        <> "' does not agree with its declaration about scope: "
        <> "declaration on line " <> Text.pack (show (lexemeLine decl))
        <> " is " <> scopeKeyword declScope <> " but definition is "
        <> scopeKeyword defnScope

    scopeKeyword Global = "extern"
    scopeKeyword Static = "static"