r'''cscm_mst3000.py

This file implements a CodeSonar compiler model for the fictional
mst3k compiler.

It should be placed in the $GTHOME\csurf\lib directory.

In order to use the compiler model, the following COMPILER_MODELS setting should be used:
    COMPILER_MODELS += mst3k -> pya_mst3000
    COMPILER_MODELS += mst3k.exe -> pya_mst3000
    
Note that all Python adapter compiler models should be identified as
pya_<model_name> in COMPILER_MODELS rules. When CodeSonar encounters the pya_ prefix, it will look for a file called csurf\lib\cscm_<model_name>.py
name() must return <model_name>'''

import cscm
import os
import re
import platform
import traceback
import logging

'''All compiler models should subclass cscm.CompilerModel.'''
class mst3000(cscm.CompilerModel):

    '''All compiler models need to implement name(). The returned name must be unique in the set of compiler model names.'''
    def name(self):
        return 'mst3000'
    
    '''All compiler models must implement verbose_name'''
    def verbose_name(self):
        return 'mst3000'   

    '''This is the equivalent of operator() in the C++ compiler models. 
       The first argument to __call__ is the list of arguments provided to the compiler.
       The second argument is the ParseConfiguration object.'''
    def __call__(self, *args, **kwargs):
    
        '''The list of options that will always be passed to the front end.'''
        fecmds_always = []
        '''The list of options that will be passed to the front end for all compilations in C++ mode.'''
        fecmds_always_cxx = []
        '''The list of options that will be passed to the front end for all compilations in C mode.'''
        fecmds_always_c = []

        native_flags = []
        native_cmd_line = []
        
        '''A list of flags that the compiler recognizes as enabling all warnings
          (IE, -Wall in gcc). If this list is empty, the compiler is assumed to
          enable all warnings by default. '''
        wall_enable_flags = ['-Wall']
        '''A list of all flags seen on the command line that disable reporting of warnings'''
        seen_wall_disable_flags = []
        '''A list of flags that the compiler recognizes as treating warnings as errors
           (IE, -Werror in gcc). '''
        werror_enable_flags = ['-Werror'] 
        '''A list of all flags seen on the command line that disable reporting of errors or
           downgrade errors to warnings.'''
        seen_werror_disable_flags = []
        
        arguments = args[0]
        config = args[1]
        
        '''Macros always predefined: ALPHA, BETA, GAMMA'''
        fecmds_always.append('-DALPHA')
        fecmds_always.append('-DBETA')
        fecmds_always.append('-DGAMMA')
        
        '''Additional macro predefined in C++ mode: BARATHRUM'''
        fecmds_always_cxx.append('-DBARATHRUM')
        
        '''Additional macro predefined in C mode: RUBRICK'''
        fecmds_always_c.append('-DRUBICK')
        
        '''Regex patterns for matching C/C++ source files'''
        dotc = '\\.(c|C)$'
        dotcxx = '\\.[cC]([pP][pP]|[xX][xX])$'
        
        source_files = []
        commands = []
        
        '''Reading an option from the environment.
           If DISABLE_MST3000 is set, the compiler will not function.'''
        disable_mst3000 = os.environ.get('DISABLE_MST3000')
        if disable_mst3000 == '1':
            return cscm.CompilerModelResult()
            
        '''Construct the path for a system include directory provided by the compiler'''
        sincdir = config.get_compiler_path_as_string()
        sincdir = os.path.join(sincdir, "../include")
        sincdir = os.path.normpath(sincdir)

        i = 0
        while i < len(arguments):
            arg = arguments[i]
            i += 1
            
            '''-D, -I, and -U flags behave the same as the front end flags of the same names.
                So we will just push them along.'''
            found = True
            for shortflag in 'DUI':
                shortflag = '-' + shortflag
                # Check for '-X foo'
                if arg == shortflag:
                    native_flags.append(arg + arguments[i])
                    if i < len(arguments):
                        commands.append(arg + arguments[i])
                        i += 1
                        break
                # Check for '-Xfoo'
                elif arg.startswith(shortflag):
                    native_flags.append(arg)
                    commands.append(arg)
                    break
            else:
                found = False
            if found:
                continue
            
            '''--ENABLE-RTTI is equivalent to the front-end flag --rtti'''    
            if arg == '--ENABLE-RTTI':
                # --rtti is only legal with c++ translation units
                fecmds_always_cxx.append('--rtti')
                native_flags.append(arg)
                continue
            
            '''-O causes a macro to be defined, so we translate it into a -D argument'''
            if arg == '-O':
                commands.append('-D__OPTIMIZE__')
                native_flags.append(arg)
                continue
                
            '''-Wno-error disables errors'''
            if arg == '-Wno-error':
                native_flags.append(arg)
                seen_werror_disable_flags.append(arg)
                continue
            
            '''-Wno-unused-parameter disables some warnings'''
            if arg == '-Wno-unused-parameter':
                native_flags.append(arg)
                seen_wall_disable_flags.append(arg)
                continue
            
            '''No effect on the compilation, ignore.'''    
            if arg == '-c' or arg == '-help':
                continue
            
            '''Read additional arguments out of a file and append them to the argument list for processing.'''
            if len(arg) > 1 and arg[0] == '@':
                options_from_file = cscm.read_response_file(arg[1:],False)
                arguments = arguments + options_from_file
                continue
            
            '''If an argument matches the pattern for C/C++ source files, add it to the list of source files.'''
            if re.search(dotc, arg):
                source_files.append((arg, cscm.csl_edgcp_c))
                continue
                
            '''If an argument matches the pattern for C/C++ source files, add it to the list of source files.'''
            if re.search(dotc, arg):
                source_files.append((arg, cscm.csl_edgcp_c))
                continue
                
            if re.search(dotcxx, arg):
                source_files.append((arg, cscm.csl_edgcp_cpp))
                continue
                
        '''Add platform-specific front-end argument.'''
        if platform.system() == 'Windows':
            commands.append('--cs_cygwin_path_translation')
            
        commands.append('--cs_define_edg_macros')
        
        bit_width = 32
        
        '''Obtain the basic front-end arguments for this source language, bit width and parse configuration.'''
        edg_fe_cmds_c = cscm.EDGFrontEnd.cs_and_fe_options(cscm.csl_edgcp_c, bit_width, config)
        edg_fe_cmds_cxx = cscm.EDGFrontEnd.cs_and_fe_options(cscm.csl_edgcp_cpp, bit_width, config)

        cso_native_cmd_line_file = cscm.CmdArgumentNativeCmdLineFile(native_flags, arguments, wall_enable_flags, seen_wall_disable_flags, werror_enable_flags, seen_werror_disable_flags)
        _vnative_cmd_line_file = []
        _vnative_cmd_line_file.append(cso_native_cmd_line_file)
        vnative_cmd_line_file = list(map(cscm.CmdArgument, _vnative_cmd_line_file))
        
        vfecmds_always = list(map(cscm.CmdArgument, map(cscm.CmdArgumentString, fecmds_always)))
        vfecmds_always_cxx = list(map(cscm.CmdArgument, map(cscm.CmdArgumentString, fecmds_always_cxx)))
        vfecmds_always_c = list(map(cscm.CmdArgument, map(cscm.CmdArgumentString, fecmds_always_c)))
        vcommands = list(map(cscm.CmdArgument, map(cscm.CmdArgumentString, commands)))
        
        fecs = []
        
        '''Loop through the list of source files and construct an appropriate FrontEndCommand for them.'''
        for sf in source_files:
            extras = vfecmds_always_cxx if sf[1] == cscm.csl_edgcp_cpp else vfecmds_always_c
            
            edg_fe_ffa = cscm.EDGFrontEnd.first_options(sf[0],'object_file.o')
            cao = list(edg_fe_ffa)

            edg_fe_commands = edg_fe_cmds_cxx if sf[1] == cscm.csl_edgcp_cpp else edg_fe_cmds_c
            
            '''Use cscm.concatenate_vector() to merge vectors of CmdArguments.'''
            cao += edg_fe_commands
            cao += vfecmds_always
            cao += extras
            cao += vcommands
            cao += vnative_cmd_line_file

            fec = cscm.FrontEndCommand( cao, sf[0], sf[1], cscm.FET_EDGCP)
            fecs.append(fec)
                    
        return cscm.CompilerModelResult(fecs)
        

cscm.register_compiler_model(mst3000())
