The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
%{
/*******************************************************************************
*
* MODULE: pragma.y
*
********************************************************************************
*
* DESCRIPTION: Pragma parser
*
********************************************************************************
*
* Copyright (c) 2002-2015 Marcus Holland-Moritz. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the same terms as Perl itself.
*
*******************************************************************************/

/*===== GLOBAL INCLUDES ======================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*===== LOCAL INCLUDES =======================================================*/

#include "ctdebug.h"
#include "cterror.h"
#include "pragma.h"

#include "util/ccattr.h"
#include "util/memalloc.h"
#include "util/list.h"

#include "ucpp/cpp.h"


/*===== DEFINES ==============================================================*/

/* ADDITIONAL BISON CONFIGURATION */

#define YYPARSE_PARAM pState
#define YYLEX_PARAM   pState
#define YYERROR_VERBOSE

/*
 * Bison version >= 1.31 is needed for YYFPRINTF
 */
#if YYDEBUG && defined CTLIB_DEBUGGING
#define YYFPRINTF BisonDebugFunc
#endif

#define pragma_error( msg )     \
        CT_DEBUG( PRAGMA, ("pragma_error(): %s", msg) )

#define pragma_parse            CTlib_pragma_parse

/* MACROS */

#define PSTATE                  ((PragmaState *) pState)

#define VALID_PACK( value )     \
         (   (value) ==  0      \
          || (value) ==  1      \
          || (value) ==  2      \
          || (value) ==  4      \
          || (value) ==  8      \
         )


/*===== TYPEDEFS =============================================================*/

struct _pragmaState {

  CParseInfo  *pCPI;
  const char  *file;
  long int     line;
  const char  *code;

  struct {
    LinkedList stack;
    unsigned   current;
  }            pack;

};

typedef struct {
  unsigned size;
} PackElement;


/*===== EXTERNAL VARIABLES ===================================================*/

/*===== GLOBAL VARIABLES =====================================================*/

/*===== STATIC VARIABLES =====================================================*/

/* TOKEN MAPPING TABLE */

static const int tokentab[] = {
	0,		/* NONE, */		/* whitespace */
	0,		/* NEWLINE, */		/* newline */
	0,		/* COMMENT, */		/* comment */
	0,		/* NUMBER, */		/* number constant */
	0,		/* NAME, */		/* identifier */
	0,		/* BUNCH, */		/* non-C characters */
	0,		/* PRAGMA, */		/* a #pragma directive */
	0,		/* CONTEXT, */		/* new file or #line */
	0,		/* STRING, */		/* constant "xxx" */
	0,		/* CHAR, */		/* constant 'xxx' */
	'/',		/* SLASH, */		/*	/	*/
	0,		/* ASSLASH, */		/*	/=	*/
	'-',		/* MINUS, */		/*	-	*/
	0,		/* MMINUS, */		/*	--	*/
	0,		/* ASMINUS, */		/*	-=	*/
	0,		/* ARROW, */		/*	->	*/
	'+',		/* PLUS, */		/*	+	*/
	0,		/* PPLUS, */		/*	++	*/
	0,		/* ASPLUS, */		/*	+=	*/
	'<',		/* LT, */		/*	<	*/
	0,		/* LEQ, */		/*	<=	*/
	0,		/* LSH, */		/*	<<	*/
	0,		/* ASLSH, */		/*	<<=	*/
	'>',		/* GT, */		/*	>	*/
	0,		/* GEQ, */		/*	>=	*/
	0,		/* RSH, */		/*	>>	*/
	0,		/* ASRSH, */		/*	>>=	*/
	'=',		/* ASGN, */		/*	=	*/
	0,		/* SAME, */		/*	==	*/
#ifdef CAST_OP
	0,		/* CAST, */		/*	=>	*/
#endif
	'~',		/* NOT, */		/*	~	*/
	0,		/* NEQ, */		/*	!=	*/
	'&',		/* AND, */		/*	&	*/
	0,		/* LAND, */		/*	&&	*/
	0,		/* ASAND, */		/*	&=	*/
	'|',		/* OR, */		/*	|	*/
	0,		/* LOR, */		/*	||	*/
	0,		/* ASOR, */		/*	|=	*/
	'%',		/* PCT, */		/*	%	*/
	0,		/* ASPCT, */		/*	%=	*/
	'*',		/* STAR, */		/*	*	*/
	0,		/* ASSTAR, */		/*	*=	*/
	'^',		/* CIRC, */		/*	^	*/
	0,		/* ASCIRC, */		/*	^=	*/
	'!',		/* LNOT, */		/*	!	*/
	'{',		/* LBRA, */		/*	{	*/
	'}',		/* RBRA, */		/*	}	*/
	'[',		/* LBRK, */		/*	[	*/
	']',		/* RBRK, */		/*	]	*/
	'(',		/* LPAR, */		/*	(	*/
	')',		/* RPAR, */		/*	)	*/
	',',		/* COMMA, */		/*	,	*/
	'?',		/* QUEST, */		/*	?	*/
	';',		/* SEMIC, */		/*	;	*/
	':',		/* COLON, */		/*	:	*/
	'.',		/* DOT, */		/*	.	*/
	0,		/* MDOTS, */		/*	...	*/
	0,		/* SHARP, */		/*	#	*/
	0,		/* DSHARP, */		/*	##	*/

	0,		/* OPT_NONE, */		/* optional space to separate tokens in text output */

	0,		/* DIGRAPH_TOKENS, */		/* there begin digraph tokens */

	/* for DIG_*, do not change order, unless checking undig() in cpp.c */
	'[',		/* DIG_LBRK, */		/*	<:	*/
	']',		/* DIG_RBRK, */		/*	:>	*/
	'{',		/* DIG_LBRA, */		/*	<%	*/
	'}',		/* DIG_RBRA, */		/*	%>	*/
	0,		/* DIG_SHARP, */	/*	%:	*/
	0,		/* DIG_DSHARP, */	/*	%:%:	*/

	0,		/* DIGRAPH_TOKENS_END, */	/* digraph tokens end here */

	0,		/* LAST_MEANINGFUL_TOKEN, */	/* reserved words will go there */

	0,		/* MACROARG, */		/* special token for representing macro arguments */

	0,		/* UPLUS = CPPERR, */	/* unary + */
	0,		/* UMINUS */		/* unary - */
};

%}

/*===== YACC PARSER DEFINITION ================================================*/

%union {
  int ival;
}

%{

/*===== STATIC FUNCTION PROTOTYPES ===========================================*/

static        int          is_valid_pack_arg(PragmaState *pState, int arg);

static inline int          pragma_lex(YYSTYPE *plval, PragmaState *pState);

static        PackElement *packelem_new(unsigned size);
static        void         packelem_delete(PackElement *pPack);

%}

%token <ival> CONSTANT

%token PACK_TOK

%token PUSH_TOK POP_TOK

%pure_parser

%start pragma
%%

pragma
	: pragma_pack
	;

pragma_pack
	: PACK_TOK
	  { PSTATE->pack.current = 0; }
	| PACK_TOK '(' ')'
	  { PSTATE->pack.current = 0; }
	| PACK_TOK '(' pragma_pack_args ')'
	;

pragma_pack_args
	: CONSTANT
	  {
	    if (is_valid_pack_arg(PSTATE, $1))
	    {
	      PSTATE->pack.current = $1;
	    }
	  }
	| PUSH_TOK ',' CONSTANT
	  {
	    if (is_valid_pack_arg(PSTATE, $3))
	    {
	      LL_push(PSTATE->pack.stack, packelem_new(PSTATE->pack.current));
	      PSTATE->pack.current = $3;
	    }
	  }
	| POP_TOK
	  {
	    PackElement *pPack = LL_pop(PSTATE->pack.stack);

	    if (pPack)
	    {
	      PSTATE->pack.current = pPack->size;
	      packelem_delete(pPack);
	    }
	    else
	      PSTATE->pack.current = 0;
	  }
	;

%%

/*===== STATIC FUNCTIONS =====================================================*/

/*******************************************************************************
*
*   ROUTINE: is_valid_pack_arg
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jun 2007
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Pack element constructor.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static int is_valid_pack_arg(PragmaState *pState, int arg)
{
  if (VALID_PACK(arg))
    return 1;

  push_error(pState->pCPI, "%s, line %ld: invalid argument %d to #pragma pack",
             pState->file, pState->line, arg);

  return 0;
}

/*******************************************************************************
*
*   ROUTINE: packelem_new
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Pack element constructor.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static PackElement *packelem_new(unsigned size)
{
  PackElement *pPack;

  AllocF(PackElement *, pPack, sizeof(PackElement));

  pPack->size = size;

  return pPack;
}

/*******************************************************************************
*
*   ROUTINE: packelem_delete
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Pack element destructor.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void packelem_delete(PackElement *pPack)
{
  if (pPack)
    Free(pPack);
}

/*******************************************************************************
*
*   ROUTINE: pragma_lex
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Pragma lexer.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static inline int pragma_lex(YYSTYPE *plval, PragmaState *pState)
{
  int token, rval;

  CT_DEBUG( PRAGMA, ("pragma_lex()"));

  while ((token = (int) *pState->code++) != 0)
  {
    switch (token)
    {
      case NUMBER:
        {
          const char *num = pState->code;

          pState->code = strchr(num, PRAGMA_TOKEN_END) + 1;
          plval->ival = strtol(num, NULL, 0);

          CT_DEBUG(PRAGMA, ("pragma - constant: %d", plval->ival));

          return CONSTANT;
        }

      case NAME:
        {
          const char *tokstr = pState->code;
          int toklen, tokval;

#include "token/t_pragma.c"

        success:
          pState->code += toklen + 1;
          return tokval;

        unknown:
          break;
        }

      default:
        if ((rval = tokentab[token]) != 0)
          return rval;

        break;
    }
  }

  return 0;
}


/*===== FUNCTIONS ============================================================*/

/*******************************************************************************
*
*   ROUTINE: pragma_parser_parse
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jun 2007
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

int pragma_parser_parse(PragmaState *pPragma)
{
  return pragma_parse(pPragma);
}

/*******************************************************************************
*
*   ROUTINE: pragma_parser_new
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

PragmaState *pragma_parser_new(CParseInfo *pCPI)
{
  PragmaState *pState;

  CT_DEBUG(PRAGMA, ("pragma_parser_new"));

  AllocF(PragmaState *, pState, sizeof(PragmaState));

  pState->pCPI = pCPI;
  pState->file = 0;
  pState->line = 0;
  pState->code = 0;
  pState->pack.stack = LL_new();
  pState->pack.current = 0;

  return pState;
}

/*******************************************************************************
*
*   ROUTINE: pragma_parser_delete
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void pragma_parser_delete(PragmaState *pPragma)
{
  if (pPragma)
  {
    CT_DEBUG(PRAGMA, ("pragma_parser_delete"));
    LL_destroy(pPragma->pack.stack, (LLDestroyFunc) packelem_delete);
    Free(pPragma);
  }
}

/*******************************************************************************
*
*   ROUTINE: pragma_parser_set_context
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jun 2007
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void pragma_parser_set_context(PragmaState *pPragma, const char *file, long int line, const char *code)
{
  pPragma->file = file;
  pPragma->line = line;
  pPragma->code = code;
}

/*******************************************************************************
*
*   ROUTINE: pragma_parser_get_pack
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Jun 2007
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

unsigned pragma_parser_get_pack(PragmaState *pPragma)
{
  return pPragma->pack.current;
}