/*****************************************************************************/
/*                                                                           */
/*                                declattr.c                                 */
/*                                                                           */
/*                           Declarator attributes                           */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 1998-2009, Ullrich von Bassewitz                                      */
/*                Roemerstrasse 52                                           */
/*                D-70794 Filderstadt                                        */
/* EMail:         uz@cc65.org                                                */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/



#include <string.h>

/* common */
#include "xmalloc.h"

/* cc65 */
#include "declare.h"
#include "declattr.h"
#include "error.h"
#include "scanner.h"
#include "symtab.h"
#include "typecmp.h"



/*****************************************************************************/
/*                                   Data                                    */
/*****************************************************************************/



/* Forwards for attribute handlers */
static void NoReturnAttr (Declarator* D);
static void UnusedAttr (Declarator* D);



/* Attribute table */
typedef struct AttrDesc AttrDesc;
struct AttrDesc {
    const char  Name[15];
    void        (*Handler) (Declarator*);
};
static const AttrDesc AttrTable [] = {
    { "__noreturn__",   NoReturnAttr    },
    { "__unused__",     UnusedAttr      },
    { "noreturn",       NoReturnAttr    },
    { "unused",         UnusedAttr      },
};



/*****************************************************************************/
/*                              Struct DeclAttr                              */
/*****************************************************************************/



static DeclAttr* NewDeclAttr (DeclAttrType AttrType)
/* Create a new DeclAttr struct and return it */
{
    /* Allocate memory */
    DeclAttr* A = xmalloc (sizeof (DeclAttr));

    /* Initialize the fields */
    A->AttrType = AttrType;

    /* Return the new struct */
    return A;
}



/*****************************************************************************/
/*                             Helper functions                              */
/*****************************************************************************/



static const AttrDesc* FindAttribute (const char* Attr)
/* Search the attribute and return the corresponding attribute descriptor.
** Return NULL if the attribute name is not known.
*/
{
    unsigned A;

    /* For now do a linear search */
    for (A = 0; A < sizeof (AttrTable) / sizeof (AttrTable[0]); ++A) {
        if (strcmp (Attr, AttrTable[A].Name) == 0) {
            /* Found */
            return AttrTable + A;
        }
    }

    /* Not found */
    return 0;
}



static void ErrorSkip (void)
{
    /* List of tokens to skip */
    static const token_t SkipList[] = { TOK_RPAREN, TOK_SEMI };

    /* Skip until closing brace or semicolon */
    SkipTokens (SkipList, sizeof (SkipList) / sizeof (SkipList[0]));

    /* If we have a closing brace, read it, otherwise bail out */
    if (CurTok.Tok == TOK_RPAREN) {
        /* Read the two closing braces */
        ConsumeRParen ();
        ConsumeRParen ();
    }
}



static void AddAttr (Declarator* D, DeclAttr* A)
/* Add an attribute to a declarator */
{
    /* Allocate the list if necessary, the add the attribute */
    if (D->Attributes == 0) {
        D->Attributes = NewCollection ();
    }
    CollAppend (D->Attributes, A);
}



/*****************************************************************************/
/*                          Attribute handling code                          */
/*****************************************************************************/



static void NoReturnAttr (Declarator* D)
/* Parse the "noreturn" attribute */
{
    /* Add the noreturn attribute */
    AddAttr (D, NewDeclAttr (atNoReturn));
}



static void UnusedAttr (Declarator* D)
/* Parse the "unused" attribute */
{
    /* Add the noreturn attribute */
    AddAttr (D, NewDeclAttr (atUnused));
}



void ParseAttribute (Declarator* D)
/* Parse an additional __attribute__ modifier */
{
    /* Do we have an attribute? */
    if (CurTok.Tok != TOK_ATTRIBUTE) {
        /* No attribute, bail out */
        return;
    }

    /* Skip the attribute token */
    NextToken ();

    /* Expect two(!) open braces */
    ConsumeLParen ();
    ConsumeLParen ();

    /* Read a list of attributes */
    while (1) {

        ident           AttrName;
        const AttrDesc* Attr = 0;

        /* Identifier follows */
        if (CurTok.Tok != TOK_IDENT) {

            /* No attribute name */
            Error ("Attribute name expected");

            /* Skip until end of attribute */
            ErrorSkip ();

            /* Bail out */
            return;
        }

        /* Map the attribute name to its id, then skip the identifier */
        strcpy (AttrName, CurTok.Ident);
        Attr = FindAttribute (AttrName);
        NextToken ();

        /* Did we find a valid attribute? */
        if (Attr) {

            /* Call the handler */
            Attr->Handler (D);

        } else {
            /* Attribute not known, maybe typo */
            Error ("Illegal attribute: '%s'", AttrName);

            /* Skip until end of attribute */
            ErrorSkip ();

            /* Bail out */
            return;
        }

        /* If a comma follows, there's a next attribute. Otherwise this is the
        ** end of the attribute list.
        */
        if (CurTok.Tok != TOK_COMMA) {
            break;
        }
        NextToken ();
    }

    /* The declaration is terminated with two closing braces */
    ConsumeRParen ();
    ConsumeRParen ();
}
