## @file
# This file contained the miscellaneous functions for INF parser
#
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

'''
InfParserMisc
'''

##
# Import Modules
#
import re


from Library import DataType as DT


from Library.StringUtils import gMACRO_PATTERN
from Library.StringUtils import ReplaceMacro
from Object.Parser.InfMisc import ErrorInInf
from Logger.StringTable import ERR_MARCO_DEFINITION_MISS_ERROR

#
# Global variable
#

#
# Sections can exist in INF file
#
gINF_SECTION_DEF = {
       DT.TAB_UNKNOWN.upper()          : DT.MODEL_UNKNOWN,
       DT.TAB_HEADER.upper()           : DT.MODEL_META_DATA_FILE_HEADER,
       DT.TAB_INF_DEFINES.upper()      : DT.MODEL_META_DATA_DEFINE,
       DT.TAB_BUILD_OPTIONS.upper()    : DT.MODEL_META_DATA_BUILD_OPTION,
       DT.TAB_LIBRARY_CLASSES.upper()  : DT.MODEL_EFI_LIBRARY_CLASS,
       DT.TAB_PACKAGES.upper()         : DT.MODEL_META_DATA_PACKAGE,
       DT.TAB_INF_FIXED_PCD.upper()    : DT.MODEL_PCD_FIXED_AT_BUILD,
       DT.TAB_INF_PATCH_PCD.upper()    : DT.MODEL_PCD_PATCHABLE_IN_MODULE,
       DT.TAB_INF_FEATURE_PCD.upper()  : DT.MODEL_PCD_FEATURE_FLAG,
       DT.TAB_INF_PCD_EX.upper()       : DT.MODEL_PCD_DYNAMIC_EX,
       DT.TAB_INF_PCD.upper()          : DT.MODEL_PCD_DYNAMIC,
       DT.TAB_SOURCES.upper()          : DT.MODEL_EFI_SOURCE_FILE,
       DT.TAB_GUIDS.upper()            : DT.MODEL_EFI_GUID,
       DT.TAB_PROTOCOLS.upper()        : DT.MODEL_EFI_PROTOCOL,
       DT.TAB_PPIS.upper()             : DT.MODEL_EFI_PPI,
       DT.TAB_DEPEX.upper()            : DT.MODEL_EFI_DEPEX,
       DT.TAB_BINARIES.upper()         : DT.MODEL_EFI_BINARY_FILE,
       DT.TAB_USER_EXTENSIONS.upper()  : DT.MODEL_META_DATA_USER_EXTENSION
       #
       # EDK1 section
       # TAB_NMAKE.upper()            : MODEL_META_DATA_NMAKE
       #
       }

## InfExpandMacro
#
# Expand MACRO definition with MACROs defined in [Defines] section and specific section.
# The MACROs defined in specific section has high priority and will be expanded firstly.
#
# @param LineInfo      Contain information of FileName, LineContent, LineNo
# @param GlobalMacros  MACROs defined in INF [Defines] section
# @param SectionMacros MACROs defined in INF specific section
# @param Flag          If the flag set to True, need to skip macros in a quoted string
#
def InfExpandMacro(Content, LineInfo, GlobalMacros=None, SectionMacros=None, Flag=False):
    if GlobalMacros is None:
        GlobalMacros = {}
    if SectionMacros is None:
        SectionMacros = {}

    FileName = LineInfo[0]
    LineContent = LineInfo[1]
    LineNo = LineInfo[2]

    # Don't expand macros in comments
    if LineContent.strip().startswith("#"):
        return Content

    NewLineInfo = (FileName, LineNo, LineContent)

    #
    # First, replace MARCOs with value defined in specific section
    #
    Content = ReplaceMacro (Content,
                            SectionMacros,
                            False,
                            (LineContent, LineNo),
                            FileName,
                            Flag)
    #
    # Then replace MARCOs with value defined in [Defines] section
    #
    Content = ReplaceMacro (Content,
                            GlobalMacros,
                            False,
                            (LineContent, LineNo),
                            FileName,
                            Flag)

    MacroUsed = gMACRO_PATTERN.findall(Content)
    #
    # no macro found in String, stop replacing
    #
    if len(MacroUsed) == 0:
        return Content
    else:
        for Macro in MacroUsed:
            gQuotedMacro = re.compile(".*\".*\$\(%s\).*\".*"%(Macro))
            if not gQuotedMacro.match(Content):
                #
                # Still have MACROs can't be expanded.
                #
                ErrorInInf (ERR_MARCO_DEFINITION_MISS_ERROR,
                            LineInfo=NewLineInfo)

    return Content


## IsBinaryInf
#
# Judge whether the INF file is Binary INF or Common INF
#
# @param FileLineList     A list contain all INF file content.
#
def IsBinaryInf(FileLineList):
    if not FileLineList:
        return False

    ReIsSourcesSection = re.compile("^\s*\[Sources.*\]\s.*$", re.IGNORECASE)
    ReIsBinarySection = re.compile("^\s*\[Binaries.*\]\s.*$", re.IGNORECASE)
    BinarySectionFoundFlag = False

    for Line in FileLineList:
        if ReIsSourcesSection.match(Line):
            return False
        if ReIsBinarySection.match(Line):
            BinarySectionFoundFlag = True

    if BinarySectionFoundFlag:
        return True

    return False


## IsLibInstanceInfo
#
# Judge whether the string contain the information of ## @LIB_INSTANCES.
#
# @param  String
#
# @return Flag
#
def IsLibInstanceInfo(String):
    ReIsLibInstance = re.compile("^\s*##\s*@LIB_INSTANCES\s*$")
    if ReIsLibInstance.match(String):
        return True
    else:
        return False


## IsAsBuildOptionInfo
#
# Judge whether the string contain the information of ## @ASBUILD.
#
# @param  String
#
# @return Flag
#
def IsAsBuildOptionInfo(String):
    ReIsAsBuildInstance = re.compile("^\s*##\s*@AsBuilt\s*$")
    if ReIsAsBuildInstance.match(String):
        return True
    else:
        return False


class InfParserSectionRoot(object):
    def __init__(self):
        #
        # Macros defined in [Define] section are file scope global
        #
        self.FileLocalMacros = {}

        #
        # Current Section Header content.
        #
        self.SectionHeaderContent = []

        #
        # Last time Section Header content.
        #
        self.LastSectionHeaderContent = []

        self.FullPath = ''

        self.InfDefSection              = None
        self.InfBuildOptionSection      = None
        self.InfLibraryClassSection     = None
        self.InfPackageSection          = None
        self.InfPcdSection              = None
        self.InfSourcesSection          = None
        self.InfUserExtensionSection    = None
        self.InfProtocolSection         = None
        self.InfPpiSection              = None
        self.InfGuidSection             = None
        self.InfDepexSection            = None
        self.InfPeiDepexSection         = None
        self.InfDxeDepexSection         = None
        self.InfSmmDepexSection         = None
        self.InfBinariesSection         = None
        self.InfHeader                  = None
        self.InfSpecialCommentSection   = None
