%pure-parser
%parse-param { HB_MACRO_PTR pMacro }
%lex-param   { HB_MACRO_PTR pMacro }
%name-prefix = "hb_macro"

%{
/*
 * $Id$
 */

/*
 * Harbour Project source code:
 * Macro compiler YACC rules and actions
 *
 * Copyright 1999 Antonio Linares <alinares@fivetech.com>
 * www - http://www.harbour-project.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

/* TODO list
 * 1) Change the pcode generated by ::cVar from Self:cVar to QSELF():cVar
 * 2) Support this syntax: nPtr := @Hello()
 */

/* this #define HAVE TO be placed before all #include directives
 */
#define  HB_MACRO_SUPPORT

#include "hbmacro.h"
#include "hbcomp.h"
#include "hbdate.h"
#include "hbpp.h"

/* Compile using: bison -d -p hb_comp macro.y */

/* to pacify some warnings in BCC */
#if defined( __BORLANDC__ ) && !defined( __STDC__ )
#  define __STDC__
#endif

#undef alloca
#define alloca  hb_xgrab
#undef malloc
#define malloc  hb_xgrab
#undef realloc
#define realloc hb_xrealloc
#undef free
#define free    hb_xfree

/* NOTE: these symbols are used internally in bison.simple
 */
#undef YYFREE
#define YYFREE hb_xfree
#undef YYMALLOC
#define YYMALLOC hb_xgrab

#define NO_YYERROR

/* yacc/lex related definitions
 */


/* Standard checking for valid expression creation
 */
#define HB_MACRO_CHECK( pExpr ) \
   if( ! ( HB_MACRO_DATA->status & HB_MACRO_CONT ) ) \
   { \
      YYABORT; \
   }

#define HB_MACRO_IFENABLED( pSet, pExpr, flag ) \
   if( HB_MACRO_DATA->supported & (flag) ) \
   { \
      pSet = (pExpr); \
   }\
   else \
   { \
      YYABORT; \
   }

#if defined( __BORLANDC__ ) || defined( __WATCOMC__ )
/* The if() inside this macro is always TRUE but it's used to hide BCC warning */
#define HB_MACRO_ABORT if( !( HB_MACRO_DATA->status & HB_MACRO_CONT ) ) { YYABORT; }
#else
#define HB_MACRO_ABORT { YYABORT; }
#endif

%}

%union                  /* special structure used by lex and yacc to share info */
{
   char *   string;     /* to hold a string returned by lex */
   int      iNumber;    /* to hold a temporary integer number */
   HB_LONG  lNumber;    /* to hold a temporary long number */
   void *   pVoid;      /* to hold any memory structure we may need */
   HB_EXPR_PTR asExpr;
   struct
   {
      char *   string;
      int      length;
   } valChar;
   struct
   {
      int      iNumber; /* to hold a number returned by lex */
   } valInteger;
   struct
   {
      HB_LONG  lNumber; /* to hold a long number returned by lex */
      UCHAR    bWidth;  /* to hold the width of the value */
   } valLong;
   struct
   {
      double   dNumber; /* to hold a double number returned by lex */
      UCHAR    bWidth;  /* to hold the width of the value */
      UCHAR    bDec;    /* to hold the number of decimal points in the value */
   } valDouble;
};

%{
/* This must be placed after the above union - the union is
 * typedef-ined to YYSTYPE
 */
extern int  yylex( YYSTYPE *, HB_MACRO_PTR );   /* main lex token function, called by yyparse() */
extern int  yyparse( HB_MACRO_PTR );            /* main yacc parsing function */
extern void yyerror( HB_MACRO_PTR, char * );    /* parsing error management function */

static void hb_macroIdentNew( HB_COMP_DECL, char * );

%}

%token IDENTIFIER NIL NUM_DOUBLE INASSIGN NUM_LONG NUM_DATE
%token IIF LITERAL TRUEVALUE FALSEVALUE
%token AND OR NOT EQ NE1 NE2 INC DEC ALIASOP HASHOP SELF
%token LE GE FIELD MACROVAR MACROTEXT
%token PLUSEQ MINUSEQ MULTEQ DIVEQ POWER EXPEQ MODEQ
%token EPSILON 

/*the lowest precedence*/
/*postincrement and postdecrement*/
%left   POST
/*assigment - from right to left*/
%right  INASSIGN
%right  PLUSEQ MINUSEQ
%right  MULTEQ DIVEQ MODEQ
%right  EXPEQ
/*logical operators*/
%right  OR
%right  AND
%right  NOT
/*relational operators*/
%right '=' EQ NE1 NE2
%right '<' '>' LE GE '$'
/*mathematical operators*/
%right  '+' '-'
%right  '*' '/' '%'
%right  POWER
%right  UNARY
/*preincrement and predecrement*/
%right  PRE
/*special operators*/
%right  ALIASOP '&' '@'
%right  ','
/*the highest precedence*/

%type <string>  IDENTIFIER MACROVAR MACROTEXT
%type <valChar>    LITERAL
%type <valDouble>  NUM_DOUBLE
%type <valLong>    NUM_LONG
%type <valLong>    NUM_DATE
%type <asExpr>  Argument ExtArgument RefArgument ArgList ElemList
%type <asExpr>  BlockExpList BlockVarList BlockVars
%type <asExpr>  NumValue NumAlias
%type <asExpr>  NilValue
%type <asExpr>  LiteralValue
%type <asExpr>  CodeBlock
%type <asExpr>  Logical
%type <asExpr>  SelfValue
%type <asExpr>  Array
%type <asExpr>  ArrayAt
%type <asExpr>  Hash HashList
%type <asExpr>  Variable VarAlias
%type <asExpr>  MacroVar MacroVarAlias
%type <asExpr>  MacroExpr MacroExprAlias
%type <asExpr>  AliasId AliasVar AliasExpr
%type <asExpr>  VariableAt
%type <asExpr>  FunCall FunRef
%type <asExpr>  ObjectData
%type <asExpr>  ObjectMethod
%type <asExpr>  IfInline
%type <asExpr>  ExpList PareExpList PareExpListAlias AsParamList RootParamList
%type <asExpr>  Expression ExtExpression SimpleExpression LeftExpression
%type <asExpr>  EmptyExpression
%type <asExpr>  ExprAssign ExprOperEq ExprPreOp ExprPostOp
%type <asExpr>  ExprMath ExprBool ExprRelation ExprUnary
%type <asExpr>  ExprPlusEq ExprMinusEq ExprMultEq ExprDivEq ExprModEq ExprExpEq
%type <asExpr>  ArrayIndex IndexList
%type <asExpr>  FieldAlias FieldVarAlias
%type <asExpr>  PostOp
%type <asExpr>  DateValue

%%

Main : Expression '\n'  {
                           HB_MACRO_DATA->exprType = hb_compExprType( $1 );
                           if( HB_MACRO_DATA->Flags &  HB_MACRO_GEN_PUSH )
                              hb_macroExprGenPush( $1, HB_COMP_PARAM );
                           else
                              hb_macroExprGenPop( $1, HB_COMP_PARAM );
                           hb_macroGenPCode1( HB_P_ENDPROC, HB_COMP_PARAM );
                        }

     | Expression       {
                           HB_MACRO_DATA->exprType = hb_compExprType( $1 );
                           if( HB_MACRO_DATA->Flags &  HB_MACRO_GEN_PUSH )
                              hb_macroExprGenPush( $1, HB_COMP_PARAM );
                           else
                              hb_macroExprGenPop( $1, HB_COMP_PARAM );
                           hb_macroGenPCode1( HB_P_ENDPROC, HB_COMP_PARAM );
                        }
     | AsParamList      {
                           HB_MACRO_DATA->exprType = hb_compExprType( $1 );
                           if( HB_MACRO_DATA->Flags &  HB_MACRO_GEN_PUSH )
                              hb_macroExprGenPush( $1, HB_COMP_PARAM );
                           else
                              hb_macroExprGenPop( $1, HB_COMP_PARAM );
                           hb_macroGenPCode1( HB_P_ENDPROC, HB_COMP_PARAM );
                        }
     | Expression error {
                  HB_TRACE(HB_TR_DEBUG, ("macro -> invalid expression: %s", HB_MACRO_DATA->string));
                  hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
                  HB_MACRO_ABORT;
               }
     | error   {
                  HB_TRACE(HB_TR_DEBUG, ("macro -> invalid syntax: %s", HB_MACRO_DATA->string));
                  hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
                  HB_MACRO_ABORT;
               }
     ;

/* Numeric values
 */
NumValue   : NUM_DOUBLE      { $$ = hb_compExprNewDouble( $1.dNumber, $1.bWidth, $1.bDec, HB_COMP_PARAM ); }
           | NUM_LONG        { $$ = hb_compExprNewLong( $1.lNumber, HB_COMP_PARAM ); }
           ;

DateValue  : NUM_DATE         { $$ = hb_compExprNewDate( $1.lNumber, HB_COMP_PARAM ); }
           ;
           
NumAlias   : NUM_LONG ALIASOP { $$ = hb_compExprNewLong( $1.lNumber, HB_COMP_PARAM ); }
           ;

/* NIL value
 */
NilValue   : NIL              { $$ = hb_compExprNewNil( HB_COMP_PARAM ); }
           ;

/* Literal string value
 */
LiteralValue : LITERAL        { $$ = hb_compExprNewString( $1.string, $1.length, FALSE, HB_COMP_PARAM ); }
             ;

/* Logical value
 */
Logical     : TRUEVALUE       { $$ = hb_compExprNewLogical( TRUE, HB_COMP_PARAM ); }
            | FALSEVALUE      { $$ = hb_compExprNewLogical( FALSE, HB_COMP_PARAM ); }
            ;

/* SELF value and expressions
 */
SelfValue   : SELF            { $$ = hb_compExprNewSelf( HB_COMP_PARAM ); }
            ;

/* Literal array
 */
Array       : '{' ElemList '}'      { $$ = hb_compExprNewArray( $2, HB_COMP_PARAM ); }
            ;

/* Literal array access
 */
ArrayAt     : Array ArrayIndex      { $$ = $2; }
            ;

/* Literal hash
 */
Hash        : '{' HASHOP '}'        { $$ = hb_compExprNewHash( NULL, HB_COMP_PARAM ); }
            | '{' HashList '}'      { $$ = hb_compExprNewHash( $2, HB_COMP_PARAM ); }
            ;

HashList    : Expression HASHOP EmptyExpression                { $$ = hb_compExprAddListExpr( hb_compExprNewList( $1, HB_COMP_PARAM ), $3 ); }
            | HashList ',' Expression HASHOP EmptyExpression   { $$ = hb_compExprAddListExpr( hb_compExprAddListExpr( $1, $3 ), $5 ); }
            ;


/* Variables
 */
Variable    : IDENTIFIER            { $$ = hb_compExprNewVar( $1, HB_COMP_PARAM ); }
            ;

VarAlias    : IDENTIFIER ALIASOP    { $$ = hb_compExprNewAlias( $1, HB_COMP_PARAM ); }
            ;

/* Macro variables - this can signal compilation errors
 */
MacroVar    : MACROVAR        {  $$ = hb_compExprNewMacro( NULL, '&', $1, HB_COMP_PARAM );
                                 HB_MACRO_CHECK( $$ );
                              }
            | MACROTEXT       {  BOOL fNewString;
                                 char * szVarName = hb_macroTextSymbol( $1, strlen( $1 ), &fNewString );
                                 if( szVarName )
                                 {
                                    if( fNewString )
                                       hb_macroIdentNew( HB_COMP_PARAM, szVarName );
                                    $$ = hb_compExprNewVar( szVarName, HB_COMP_PARAM );
                                    HB_MACRO_CHECK( $$ );
                                 }
                                 else
                                 {
                                    /* invalid variable name
                                     */
                                    HB_TRACE(HB_TR_DEBUG, ("macro -> invalid variable name: %s", $1));
                                    YYABORT;
                                 }
                              }
            ;

MacroVarAlias  : MacroVar ALIASOP   { hb_compExprMacroAsAlias( $1 ); }
               ;

/* Macro expressions
 */
MacroExpr  : '&' PareExpList        { $$ = hb_compExprNewMacro( $2, 0, NULL, HB_COMP_PARAM ); }
           ;

MacroExprAlias : MacroExpr ALIASOP
               ;

/* Aliased variables
 */
/* special case: _FIELD-> and FIELD-> can be nested
 */
FieldAlias  : FIELD ALIASOP               { $$ = hb_compExprNewAlias( "FIELD", HB_COMP_PARAM ); }
            | FIELD ALIASOP FieldAlias    { $$ = $3; }
            ;

/* ignore _FIELD-> or FIELD-> if a real alias is specified
 */
FieldVarAlias  : FieldAlias VarAlias            { $$ = $2; }
               | FieldAlias NumAlias            { $$ = $2; }
               | FieldAlias PareExpListAlias    { $$ = $2; }
               | FieldAlias MacroVarAlias       { $$ = $2; }
               | FieldAlias MacroExprAlias      { $$ = $2; }
               ;

AliasId     : IDENTIFIER      { $$ = hb_compExprNewVar( $1, HB_COMP_PARAM ); }
            | MacroVar
            ;

AliasVar   : NumAlias AliasId          { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | MacroVarAlias AliasId     { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | MacroExprAlias AliasId    { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | PareExpListAlias AliasId  { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | VarAlias AliasId          { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | FieldAlias AliasId        { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           | FieldVarAlias AliasId     { $$ = hb_compExprNewAliasVar( $1, $2, HB_COMP_PARAM ); }
           ;

/* Aliased expressions
 */
/* NOTE: In the case:
 * alias->( Expression )
 * alias always selects a workarea at runtime
 */
AliasExpr  : NumAlias PareExpList         { $$ = hb_compExprNewAliasExpr( $1, $2, HB_COMP_PARAM ); }
           | VarAlias PareExpList         { $$ = hb_compExprNewAliasExpr( $1, $2, HB_COMP_PARAM ); }
           | MacroVarAlias PareExpList    { $$ = hb_compExprNewAliasExpr( $1, $2, HB_COMP_PARAM ); }
           | MacroExprAlias PareExpList   { $$ = hb_compExprNewAliasExpr( $1, $2, HB_COMP_PARAM ); }
           | PareExpListAlias PareExpList { $$ = hb_compExprNewAliasExpr( $1, $2, HB_COMP_PARAM ); }
           ;

/* Array expressions access
 */
VariableAt  : NumValue      ArrayIndex    { $$ = $2; }
            | NilValue      ArrayIndex    { $$ = $2; }
            | DateValue     ArrayIndex    { $$ = $2; }
            | LiteralValue  ArrayIndex    { $$ = $2; }
            | CodeBlock     ArrayIndex    { $$ = $2; }
            | Logical       ArrayIndex    { $$ = $2; }
            | Hash          ArrayIndex    { $$ = $2; }
            | SelfValue     ArrayIndex    { $$ = $2; }
            | Variable      ArrayIndex    { $$ = $2; }
            | AliasVar      ArrayIndex    { $$ = $2; }
            | AliasExpr     ArrayIndex    { $$ = $2; }
            | MacroVar      ArrayIndex    { $$ = $2; }
            | MacroExpr     ArrayIndex    { $$ = $2; }
            | ObjectData    ArrayIndex    { $$ = $2; }
            | ObjectMethod  ArrayIndex    { $$ = $2; }
            | FunCall       ArrayIndex    { $$ = $2; }
            | IfInline      ArrayIndex    { $$ = $2; }
            | PareExpList   ArrayIndex    { $$ = $2; }
            ;

/* Function call
 */
FunCall     : IDENTIFIER '(' ArgList ')'  { $$ = hb_macroExprNewFunCall( hb_compExprNewFunName( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM );
                                            HB_MACRO_CHECK( $$ );
                                          }
            | MacroVar '(' ArgList ')'    { $$ = hb_macroExprNewFunCall( $1, $3, HB_COMP_PARAM );
                                            HB_MACRO_CHECK( $$ );
                                          }
            ;

FunRef      : '@' IDENTIFIER '(' ArgList ')' {  if( hb_compExprParamListLen( $4 ) != 0 )
                                                {
                                                   hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
                                                   YYABORT;
                                                }
                                                else
                                                   $$ = hb_compExprNewFunRef( $2, HB_COMP_PARAM );
                                             }
            ;

ArgList     : ExtArgument                 { $$ = hb_compExprNewArgList( $1, HB_COMP_PARAM ); }
            | ArgList ',' ExtArgument     { $$ = hb_compExprAddListExpr( $1, $3 ); }
            ;

Argument    : EmptyExpression
            | RefArgument
            ;

RefArgument : '@' IDENTIFIER              { $$ = hb_compExprNewVarRef( $2, HB_COMP_PARAM ); }
            | '@' MacroVar                { $$ = hb_compExprNewRef( $2, HB_COMP_PARAM ); }
            | '@' AliasVar                { $$ = hb_compExprNewRef( $2, HB_COMP_PARAM ); }
            | '@' ObjectData              { $$ = hb_compExprNewRef( $2, HB_COMP_PARAM ); }
            | '@' VariableAt              { $$ = $2; $$->value.asList.reference = TRUE; }
            ;

ExtArgument : EPSILON  { $$ = hb_compExprNewArgRef( HB_COMP_PARAM ); }
            | Argument
            ;

/* Object's instance variable
 */
ObjectData  : LeftExpression ':' IDENTIFIER  { $$ = hb_macroExprNewSend( $1, $3, NULL, HB_COMP_PARAM ); }
            | LeftExpression ':' MacroVar    { $$ = hb_macroExprNewSend( $1, NULL, $3, HB_COMP_PARAM ); }
            | LeftExpression ':' MacroExpr   { $$ = hb_macroExprNewSend( $1, NULL, $3, HB_COMP_PARAM ); }
            ;

/* Object's method
 */
ObjectMethod : ObjectData '(' ArgList ')'    { $$ = hb_compExprNewMethodCall( $1, $3 ); }
             ;

SimpleExpression :
              NumValue
            | NilValue
            | DateValue
            | LiteralValue
            | CodeBlock
            | Logical
            | SelfValue
            | Array
            | ArrayAt
            | Hash
            | AliasVar
            | AliasExpr
            | MacroVar
            | MacroExpr
            | Variable
            | VariableAt
            | FunCall
            | IfInline
            | ObjectData
            | ObjectMethod
            | ExprAssign
            | ExprOperEq            { HB_MACRO_IFENABLED( $$, $1, HB_SM_HARBOUR ); }
            | ExprPostOp            { HB_MACRO_IFENABLED( $$, $1, HB_SM_HARBOUR ); }
            | ExprPreOp             { HB_MACRO_IFENABLED( $$, $1, HB_SM_HARBOUR ); }
            | ExprUnary
            | ExprMath
            | ExprBool
            | ExprRelation
            | FunRef
            ;

Expression  : SimpleExpression      { $$ = $1; HB_MACRO_CHECK( $$ ); }
            | PareExpList           { $$ = $1; HB_MACRO_CHECK( $$ ); }
            ;

ExtExpression : EPSILON             { $$ = hb_compExprNewArgRef( HB_COMP_PARAM ); }
              | Expression
              ;

RootParamList : EmptyExpression ',' {
                                       if( !(HB_MACRO_DATA->Flags & HB_MACRO_GEN_LIST) )
                                       {
                                          HB_TRACE(HB_TR_DEBUG, ("macro -> invalid expression: %s", HB_MACRO_DATA->string));
                                          hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
                                          YYABORT;
                                       }
                                    }
                EmptyExpression     {
                                       HB_MACRO_DATA->uiListElements = 1;
                                       $$ = hb_compExprAddListExpr( ( HB_MACRO_DATA->Flags & HB_MACRO_GEN_PARE ) ? hb_compExprNewList( $1, HB_COMP_PARAM ) : hb_compExprNewArgList( $1, HB_COMP_PARAM ), $4 );
                                    }
              ;

AsParamList : RootParamList
            | AsParamList ',' EmptyExpression   { HB_MACRO_DATA->uiListElements++;
                                                  $$ = hb_compExprAddListExpr( $1, $3 ); }
            ;

EmptyExpression: /* nothing => nil */        { $$ = hb_compExprNewEmpty( HB_COMP_PARAM ); }
            | Expression
            ;

LeftExpression : NumValue
               | NilValue
               | DateValue
               | LiteralValue
               | CodeBlock
               | Logical
               | SelfValue
               | Array
               | ArrayAt
               | Hash
               | AliasVar
               | AliasExpr
               | MacroVar
               | MacroExpr
               | Variable
               | VariableAt
               | PareExpList
               | FunCall
               | IfInline
               | ObjectData
               | ObjectMethod
               ;

/* NOTE: PostOp can be used in one context only - it uses $0 rule
 *    (the rule that stands before PostOp)
 */
PostOp      : INC    { $$ = hb_compExprNewPostInc( $<asExpr>0, HB_COMP_PARAM ); }
            | DEC    { $$ = hb_compExprNewPostDec( $<asExpr>0, HB_COMP_PARAM ); }
            ;

/* NOTE: We cannot use 'Expression PostOp' because it caused
 * shift/reduce conflicts
 */
ExprPostOp  : LeftExpression PostOp %prec POST  { $$ = $2; }
            ;

ExprPreOp   : INC Expression  %prec PRE      { $$ = hb_compExprNewPreInc( $2, HB_COMP_PARAM ); }
            | DEC Expression  %prec PRE      { $$ = hb_compExprNewPreDec( $2, HB_COMP_PARAM ); }
            ;

ExprUnary   : NOT Expression                 { $$ = hb_compExprNewNot( $2, HB_COMP_PARAM ); }
            | '-' Expression  %prec UNARY    { $$ = hb_compExprNewNegate( $2, HB_COMP_PARAM ); }
            | '+' Expression  %prec UNARY    { $$ = $2; }
            ;

ExprAssign  : NumValue     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | NilValue     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | DateValue    INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | LiteralValue INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | CodeBlock    INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | Logical      INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | SelfValue    INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | Array        INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | ArrayAt      INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | Hash         INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | AliasVar     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | AliasExpr    INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | MacroVar     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | MacroExpr    INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | Variable     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | VariableAt   INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | PareExpList  INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | FunCall      INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | IfInline     INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            | ObjectData   INASSIGN Expression  { HB_MACRO_IFENABLED( $$, hb_compExprAssign( $1, $3, HB_COMP_PARAM ), HB_SM_HARBOUR ); }
            | ObjectMethod INASSIGN Expression  { $$ = hb_compExprAssign( $1, $3, HB_COMP_PARAM ); }
            ;

ExprPlusEq  : LeftExpression PLUSEQ  Expression { $$ = hb_compExprSetOperand( hb_compExprNewPlusEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprMinusEq : LeftExpression MINUSEQ Expression { $$ = hb_compExprSetOperand( hb_compExprNewMinusEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprMultEq  : LeftExpression MULTEQ  Expression { $$ = hb_compExprSetOperand( hb_compExprNewMultEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprDivEq   : LeftExpression DIVEQ   Expression { $$ = hb_compExprSetOperand( hb_compExprNewDivEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprModEq   : LeftExpression MODEQ   Expression { $$ = hb_compExprSetOperand( hb_compExprNewModEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprExpEq   : LeftExpression EXPEQ   Expression { $$ = hb_compExprSetOperand( hb_compExprNewExpEq( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprOperEq  : ExprPlusEq
            | ExprMinusEq
            | ExprMultEq
            | ExprDivEq
            | ExprModEq
            | ExprExpEq
            ;

ExprMath    : Expression '+' Expression     { $$ = hb_compExprSetOperand( hb_compExprNewPlus( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '-' Expression     { $$ = hb_compExprSetOperand( hb_compExprNewMinus( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '*' Expression     { $$ = hb_compExprSetOperand( hb_compExprNewMult( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '/' Expression     { $$ = hb_compExprSetOperand( hb_compExprNewDiv( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '%' Expression     { $$ = hb_compExprSetOperand( hb_compExprNewMod( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression POWER Expression   { $$ = hb_compExprSetOperand( hb_compExprNewPower( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprBool    : Expression AND Expression   { $$ = hb_compExprSetOperand( hb_compExprNewAnd( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression OR  Expression   { $$ = hb_compExprSetOperand( hb_compExprNewOr( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ExprRelation: Expression EQ  Expression   { $$ = hb_compExprSetOperand( hb_compExprNewEQ( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '<' Expression   { $$ = hb_compExprSetOperand( hb_compExprNewLT( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '>' Expression   { $$ = hb_compExprSetOperand( hb_compExprNewGT( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression LE  Expression   { $$ = hb_compExprSetOperand( hb_compExprNewLE( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression GE  Expression   { $$ = hb_compExprSetOperand( hb_compExprNewGE( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression NE1 Expression   { $$ = hb_compExprSetOperand( hb_compExprNewNE( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression NE2 Expression   { $$ = hb_compExprSetOperand( hb_compExprNewNE( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '$' Expression   { $$ = hb_compExprSetOperand( hb_compExprNewIN( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            | Expression '=' Expression   { $$ = hb_compExprSetOperand( hb_compExprNewEqual( $1, HB_COMP_PARAM ), $3, HB_COMP_PARAM ); }
            ;

ArrayIndex  : IndexList ']'
            ;

/* NOTE: $0 represents the expression before ArrayIndex
 *    Don't use ArrayIndex in other context than as an array index!
 */
IndexList   : '[' ExtExpression                 { $$ = hb_macroExprNewArrayAt( $<asExpr>0, $2, HB_COMP_PARAM ); }
            | IndexList ',' ExtExpression       { $$ = hb_macroExprNewArrayAt( $1, $3, HB_COMP_PARAM ); }
            | IndexList ']' '[' ExtExpression   { $$ = hb_macroExprNewArrayAt( $1, $4, HB_COMP_PARAM ); }
            ;

ElemList    : ExtArgument              { $$ = hb_compExprNewList( $1, HB_COMP_PARAM ); }
            | ElemList ',' ExtArgument { $$ = hb_compExprAddListExpr( $1, $3 ); }
            ;

CodeBlock   : '{' '|'
                  { $<asExpr>$ = hb_compExprNewCodeBlock( NULL, 0, 0, HB_COMP_PARAM ); }
               BlockVars '|' BlockExpList '}'
                  { $$ = $<asExpr>3; }
            ;

/* NOTE: This uses $-2 then don't use BlockExpList in other context
 */
BlockExpList: Expression                  { $$ = hb_compExprAddCodeblockExpr( $<asExpr>-2, $1 ); }
            | BlockExpList ',' Expression { $$ = hb_compExprAddCodeblockExpr( $<asExpr>-2, $3 ); }
            ;

/* NOTE: This uses $0 then don't use BlockVars and BlockVarList in other context
 */
BlockVars   : /* empty list */            { $$ = NULL; }
            | EPSILON                     { $$ = NULL; $<asExpr>0->value.asCodeblock.flags |= HB_BLOCK_VPARAMS; }
            | BlockVarList                { $$ = $1;   }
            | BlockVarList ',' EPSILON    { $$ = $1;   $<asExpr>0->value.asCodeblock.flags |= HB_BLOCK_VPARAMS; }
            ;

BlockVarList: IDENTIFIER                  { $$ = hb_compExprCBVarAdd( $<asExpr>0, $1, ' ', HB_COMP_PARAM ); }
            | BlockVarList ',' IDENTIFIER { $$ = hb_compExprCBVarAdd( $<asExpr>0, $3, ' ', HB_COMP_PARAM ); HB_MACRO_CHECK( $$ ); }
            ;

ExpList     : '(' EmptyExpression          { $$ = hb_compExprNewList( $2, HB_COMP_PARAM ); }
            | ExpList ',' EmptyExpression  { $$ = hb_compExprAddListExpr( $1, $3 ); }
            ;

PareExpList : ExpList ')'
            ;

PareExpListAlias : PareExpList ALIASOP
                 ;

/* Lexer should return IIF for "if" symbol */
IfInline    : IIF '(' Expression ',' Argument ',' Argument ')'
               { $$ = hb_compExprNewIIF( hb_compExprAddListExpr( hb_compExprAddListExpr( hb_compExprNewList( $3, HB_COMP_PARAM ), $5 ), $7 ) ); }
            ;

%%


/*
 ** ------------------------------------------------------------------------ **
 */

void yyerror( HB_MACRO_PTR pMacro, char * s )
{
   HB_SYMBOL_UNUSED( pMacro );
   HB_SYMBOL_UNUSED( s );
}

/* ************************************************************************* */

typedef struct HB_MEXPR_
{
   HB_EXPR Expression;
   struct HB_MEXPR_ *pPrev;
}
HB_MEXPR, * HB_MEXPR_PTR;

typedef struct HB_MIDENT_
{
   char * Identifier;
   struct HB_MIDENT_ *pPrev;
}
HB_MIDENT, * HB_MIDENT_PTR;

/* Allocates memory for Expression holder structure and stores it
 * on the linked list
*/
static HB_EXPR_PTR hb_macroExprAlloc( HB_COMP_DECL )
{
   HB_MEXPR_PTR pMExpr = ( HB_MEXPR_PTR ) hb_xgrab( sizeof( HB_MEXPR ) );
   pMExpr->pPrev = ( HB_MEXPR_PTR ) HB_MACRO_DATA->pExprLst;
   HB_MACRO_DATA->pExprLst = ( void * ) pMExpr;
   return &pMExpr->Expression;
}

static void hb_macroIdentNew( HB_COMP_DECL, char * szIdent )
{
   HB_MIDENT_PTR pMIdent = ( HB_MIDENT_PTR ) hb_xgrab( sizeof( HB_MIDENT ) );
   pMIdent->Identifier = szIdent;
   pMIdent->pPrev = ( HB_MIDENT_PTR ) HB_MACRO_DATA->pIdentLst;
   HB_MACRO_DATA->pIdentLst = ( void * ) pMIdent;
}

static HB_EXPR_PTR hb_macroExprNew( HB_COMP_DECL, HB_EXPRTYPE iType )
{
   HB_EXPR_PTR pExpr;

   HB_TRACE(HB_TR_DEBUG, ("hb_macroExprNew(%p,%i)", HB_COMP_PARAM, iType));

   pExpr = hb_macroExprAlloc( HB_COMP_PARAM );
   pExpr->ExprType = iType;
   pExpr->pNext    = NULL;
   pExpr->ValType  = HB_EV_UNKNOWN;
   pExpr->Counter  = 1;

   return pExpr;
}

/* Delete self - all components will be deleted somewhere else
 */
static void hb_macroExprClear( HB_COMP_DECL, HB_EXPR_PTR pExpr )
{
   HB_SYMBOL_UNUSED( HB_COMP_PARAM );
   if( --pExpr->Counter == 0 )
      pExpr->ExprType = HB_ET_NONE;
}

/* Delete all components and delete self
 */
static void hb_macroExprDelete( HB_COMP_DECL, HB_EXPR_PTR pExpr )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_macroExprDelete(%p,%p)", HB_COMP_PARAM, pExpr));
   if( pExpr && --pExpr->Counter == 0 )
   {
      HB_EXPR_USE( pExpr, HB_EA_DELETE );
      pExpr->ExprType = HB_ET_NONE;
   }
}

/* Delete all components and delete self
 */
static void hb_macroExprFree( HB_COMP_DECL, HB_EXPR_PTR pExpr )
{
   HB_TRACE(HB_TR_DEBUG, ("hb_macroExprFree()"));
   if( --pExpr->Counter == 0 )
   {
      HB_EXPR_USE( pExpr, HB_EA_DELETE );
      pExpr->ExprType = HB_ET_NONE;
   }
}

/* Deallocate all memory used by expression optimizer */
static void hb_macroLstFree( HB_MACRO_PTR pMacro )
{
   if( pMacro->pExprLst )
   {
      HB_MEXPR_PTR pMExpr = ( HB_MEXPR_PTR ) pMacro->pExprLst;
      do
      {
         hb_macroExprDelete( pMacro, &pMExpr->Expression );
         pMExpr = pMExpr->pPrev;
      }
      while( pMExpr );
      do
      {
         pMExpr = ( HB_MEXPR_PTR ) pMacro->pExprLst;
         pMacro->pExprLst = ( void * ) pMExpr->pPrev;
         hb_xfree( pMExpr );
      }
      while( pMacro->pExprLst );
   }

   while( pMacro->pIdentLst )
   {
      HB_MIDENT_PTR pMIdent = ( HB_MIDENT_PTR ) HB_MACRO_DATA->pIdentLst;
      HB_MACRO_DATA->pIdentLst = ( void * ) pMIdent->pPrev;
      hb_xfree( pMIdent->Identifier );
      hb_xfree( pMIdent );
   }
}

static HB_EXPR_PTR hb_macroErrorType( HB_COMP_DECL, HB_EXPR_PTR pExpr )
{
   hb_macroError( EG_ARG, HB_COMP_PARAM );
   return pExpr;
}

static HB_EXPR_PTR hb_macroErrorSyntax( HB_COMP_DECL, HB_EXPR_PTR pExpr )
{
   hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
   return pExpr;
}

static void hb_macroErrorDuplVar( HB_COMP_DECL, const char * szVarName )
{
   HB_SYMBOL_UNUSED( szVarName );
   hb_macroError( EG_SYNTAX, HB_COMP_PARAM );
}


static const HB_COMP_FUNCS s_macro_funcs =
{
   hb_macroExprNew,
   hb_macroExprClear,
   hb_macroExprFree,
   hb_macroExprDelete,

   hb_macroErrorType,
   hb_macroErrorSyntax,
   hb_macroErrorDuplVar,
};

int hb_macroYYParse( HB_MACRO_PTR pMacro )
{
   int iResult;

   pMacro->funcs = &s_macro_funcs;

   if( hb_macroLexNew( pMacro ) )
   {
      pMacro->status = HB_MACRO_CONT;
      pMacro->pExprLst = NULL;
      pMacro->pIdentLst = NULL;

      iResult = yyparse( pMacro );

      hb_macroLstFree( pMacro );
      hb_macroLexDelete( pMacro );
   }
   else
      iResult = HB_MACRO_FAILURE;

   return iResult;
}


#if defined( HB_MACRO_PPLEX )

/* it's an example of PP token translator which change tokens generated by
   PP into terminal symbols used by our grammar parser generated by Bison */
BOOL hb_macroLexNew( HB_MACRO_PTR pMacro )
{
   pMacro->pLex = ( void * ) hb_pp_lexNew( pMacro->string, pMacro->length );
   return pMacro->pLex != NULL;
}

void hb_macroLexDelete( HB_MACRO_PTR pMacro )
{
   if( pMacro->pLex )
   {
      hb_pp_free( ( PHB_PP_STATE ) pMacro->pLex );
      pMacro->pLex = NULL;
   }
}

int hb_macrolex( YYSTYPE *yylval_ptr, HB_MACRO_PTR pMacro )
{
   PHB_PP_TOKEN pToken = hb_pp_lexGet( ( PHB_PP_STATE ) pMacro->pLex );

   if( !pToken )
      return 0;

   switch( HB_PP_TOKEN_TYPE( pToken->type ) )
   {
      case HB_PP_TOKEN_KEYWORD:
         if( pToken->len >= 4 && pToken->len <= 6 && pToken->pNext &&
             HB_PP_TOKEN_TYPE( pToken->pNext->type ) == HB_PP_TOKEN_ALIAS &&
             ( hb_strnicmp( "_FIELD", pToken->value, pToken->len ) == 0 ||
               hb_strnicmp( "FIELD", pToken->value, pToken->len ) == 0 ) )
         {
            return FIELD;
         }
         else if( pToken->len == 3 && pToken->pNext &&
                  HB_PP_TOKEN_TYPE( pToken->pNext->type ) == HB_PP_TOKEN_LEFT_PB &&
                  hb_stricmp( "IIF", pToken->value ) == 0 )
         {
            return IIF;
         }
         else if( pToken->len == 2 && pToken->pNext &&
                  HB_PP_TOKEN_TYPE( pToken->pNext->type ) == HB_PP_TOKEN_LEFT_PB &&
                  hb_stricmp( "IF", pToken->value ) == 0 )
            return IIF;
         else if( pToken->len == 3 && hb_stricmp( "NIL", pToken->value ) == 0 )
            return NIL;

         hb_pp_tokenUpper( pToken );
         yylval_ptr->string = pToken->value;
         return IDENTIFIER;

      case HB_PP_TOKEN_MACROVAR:
         hb_pp_tokenUpper( pToken );
         yylval_ptr->string = pToken->value;
         return MACROVAR;

      case HB_PP_TOKEN_MACROTEXT:
         hb_pp_tokenUpper( pToken );
         yylval_ptr->string = pToken->value;
         return MACROTEXT;

      case HB_PP_TOKEN_NUMBER:
      {
         HB_LONG lNumber;
         double dNumber;
         int iDec, iWidth;

         if( hb_compStrToNum( pToken->value, pToken->len, &lNumber, &dNumber, &iDec, &iWidth ) )
         {
            yylval_ptr->valDouble.dNumber = dNumber;
            yylval_ptr->valDouble.bDec    = ( UCHAR ) iDec;
            yylval_ptr->valDouble.bWidth  = ( UCHAR ) iWidth;
            return NUM_DOUBLE;
         }
         else
         {
            yylval_ptr->valLong.lNumber = lNumber;
            yylval_ptr->valLong.bWidth  = ( UCHAR ) iWidth;
            return NUM_LONG;
         }
      }
      case HB_PP_TOKEN_DATE:
         if( pToken->len == 10 )
         {
            int year, month, day;
            hb_dateStrGet( pToken->value + 2, &year, &month, &day );
            yylval_ptr->valLong.lNumber = hb_dateEncode( year, month, day );
         }
         else
            yylval_ptr->valLong.lNumber = 0;
         return NUM_DATE;

      case HB_PP_TOKEN_STRING:
         yylval_ptr->valChar.string = pToken->value;
         yylval_ptr->valChar.length = pToken->len;
         return LITERAL;

      case HB_PP_TOKEN_LOGICAL:
         return pToken->value[ 1 ] == 'T' ? TRUEVALUE : FALSEVALUE;

      case HB_PP_TOKEN_HASH:
      case HB_PP_TOKEN_DIRECTIVE:
         return NE1;

      case HB_PP_TOKEN_NE:
         return NE2;

      case HB_PP_TOKEN_ASSIGN:
         return INASSIGN;

      case HB_PP_TOKEN_EQUAL:
         return EQ;

      case HB_PP_TOKEN_INC:
         return INC;

      case HB_PP_TOKEN_DEC:
         return DEC;

      case HB_PP_TOKEN_ALIAS:
         return ALIASOP;

      case HB_PP_TOKEN_LE:
         return LE;

      case HB_PP_TOKEN_GE:
         return GE;

      case HB_PP_TOKEN_PLUSEQ:
         return PLUSEQ;

      case HB_PP_TOKEN_MINUSEQ:
         return MINUSEQ;

      case HB_PP_TOKEN_MULTEQ:
         return MULTEQ;

      case HB_PP_TOKEN_DIVEQ:
         return DIVEQ;

      case HB_PP_TOKEN_MODEQ:
         return MODEQ;

      case HB_PP_TOKEN_EXPEQ:
         return EXPEQ;

      case HB_PP_TOKEN_POWER:
         return POWER;

      case HB_PP_TOKEN_AND:
         return AND;

      case HB_PP_TOKEN_OR:
         return OR;

      case HB_PP_TOKEN_NOT:
         return NOT;

      default:
         return pToken->value[ 0 ];
   }
}

#endif /* HB_MACRO_PPLEX */
