%{ /* * $Id$ */ /* * Harbour Project source code: * Compiler YACC rules and actions * * Copyright 1999 Antonio Linares * 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 of the License, or * (at your option) any later version, with one exception: * * The exception is that if you link the Harbour Runtime Library (HRL) * and/or the Harbour Virtual Machine (HVM) 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 HRL * and/or HVM code into it. * * 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 program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit * their web site at http://www.gnu.org/). * */ /* TODO list * 1) jumps longer then 2^15 bytes * 2) macro support * 3) correct support for undeclared variables (all variables are assumed * memvars now - we should add runtime recognition of variable type) * - work in progress (rglab@imid.med.pl) * 4) in some cases incorrect warnings are generated when -w option is used * 5) in some cases GPF occurs when -w option is used, example: * LOCAL nDouble := NumAnd32(RandGetLong() / 65536, 32767) / 997 * 6) support for expr:expr (QSELF():cVar) syntax. * see tests/broken/clasname.prg * 7) Change the pcode generated by ::cVar from Self:cVar to QSELF():cVar * 8) Support this syntax: nPtr := @Hello() * 9) Support for syntax: FOR [ array | object | alias_expr | other ] := value */ /* Compile using: bison -d -v harbour.y */ #include #include #include #include #include /* required for allocating and freeing memory */ #include #include #include "extend.h" #include "pcode.h" /* pcode values */ #include "compiler.h" #include "hberrors.h" #include "hbpp.h" #include "hbver.h" #define debug_msg( x, z ) extern FILE *yyin; /* currently yacc parsed file */ extern int iLine; /* currently parsed file line number */ /* Following two lines added for preprocessor */ extern BOOL _bPPO; /* flag indicating, is ppo output needed */ extern FILE *yyppo; /* output .ppo file */ extern char *yytext; /* have accees to original string form of numbers */ typedef struct /* #include support */ { FILE * handle; /* handle of the opened file */ void * pBuffer; /* buffer used by yacc */ char * szFileName; /* name of the file */ void * pPrev; /* pointer to the previous opened file */ void * pNext; /* pointer to the next opened file */ int iLine; /* currently processed line number */ } _FILE, * PFILE; /* structure to hold an opened PRG or CH */ typedef struct { PFILE pLast; /* pointer to the last opened file */ int iFiles; /* number of files currently opened */ } FILES; /* structure to control several opened PRGs and CHs */ int Include( char * szFileName, PATHNAMES * pSearchPath ); /* end #include support */ /* pcode chunks bytes size */ #define PCODE_CHUNK 100 typedef struct __ELSEIF { ULONG ulOffset; struct __ELSEIF * pNext; } _ELSEIF, * PELSEIF; /* support structure for else if pcode fixups */ typedef struct _LOOPEXIT { ULONG ulOffset; int iLine; USHORT wSeqCounter; struct _LOOPEXIT * pLoopList; struct _LOOPEXIT * pExitList; struct _LOOPEXIT * pNext; } LOOPEXIT, * PTR_LOOPEXIT; /* support structure for EXIT and LOOP statements */ static void LoopStart( void ); static void LoopEnd( void ); static void LoopLoop( void ); static void LoopExit( void ); static void LoopHere( void ); typedef struct __EXTERN { char * szName; struct __EXTERN * pNext; } _EXTERN, * PEXTERN; /* support structure for extern symbols */ /* as they have to be placed on the symbol table later than the first public symbol */ /* Support for aliased expressions */ typedef struct _ALIASID { char type; union { int iAlias; char * szAlias; } alias; struct _ALIASID * pPrev; } ALIASID, *ALIASID_PTR; #define ALIAS_NUMBER 1 #define ALIAS_NAME 2 #define ALIAS_EVAL 3 static ULONG PackDateTime( void ); void AliasAddInt( int ); void AliasAddExp( void ); void AliasAddStr( char * ); void AliasPush( void ); void AliasPop( void ); void AliasSwap( void ); void AliasAdd( ALIASID_PTR ); void AliasRemove( void ); /* Support for parenthesized expressions */ typedef struct _EXPLIST { BYTE * prevPCode; /* pcode buffer used at the start of expression */ ULONG prevSize; ULONG prevPos; BYTE * exprPCode; /* pcode buffer for current expression */ ULONG exprSize; struct _EXPLIST *pPrev; /* previous expression in the list */ struct _EXPLIST *pNext; /* next expression in the list */ } EXPLIST, *EXPLIST_PTR; void ExpListPush( void ); /* pushes the new expression on the stack */ void ExpListPop( int ); /* pops previous N expressions */ #ifdef __cplusplus typedef struct yy_buffer_state *YY_BUFFER_STATE; YY_BUFFER_STATE yy_create_buffer( FILE *, int ); /* yacc functions to manage multiple files */ void yy_switch_to_buffer( YY_BUFFER_STATE ); /* yacc functions to manage multiple files */ void yy_delete_buffer( YY_BUFFER_STATE ); /* yacc functions to manage multiple files */ #else void * yy_create_buffer( FILE *, int ); /* yacc functions to manage multiple files */ void yy_switch_to_buffer( void * ); /* yacc functions to manage multiple files */ void yy_delete_buffer( void * ); /* yacc functions to manage multiple files */ #endif /* lex & yacc related prototypes */ void yyerror( char * ); /* parsing error management function */ int yylex( void ); /* main lex token function, called by yyparse() */ int yyparse( void ); /* main yacc parsing function */ #ifdef __cplusplus extern "C" int yywrap( void ); #else int yywrap( void ); /* manages the EOF of current processed file */ #endif /* Following line added for preprocessor */ void Hbpp_init ( void ); /* production related functions */ static void ArrayAt( void ); static void ArrayPut( void ); void ArrayDim( int iDimensions ); /* instructs the virtual machine to build an array with wDimensions */ PFUNCTION AddFunCall( char * szFuntionName ); void AddExtern( char * szExternName ); /* defines a new extern name */ void AddSearchPath( char *, PATHNAMES * * ); /* add pathname to a search list */ void AddVar( char * szVarName ); /* add a new param, local, static variable to a function definition or a public or private */ void SetVarMacro( void ); /* Set the stack for the creation of a MACRO variable */ void FunMacroAssign( void ); /* stores the last interpreted macro to be reused if inline assignment */ PCOMSYMBOL AddSymbol( char *, USHORT * ); void CheckDuplVars( PVAR pVars, char * szVarName, int iVarScope ); /*checks for duplicate variables definitions */ void Dec( void ); /* generates the pcode to decrement the latest value on the virtual machine stack */ void Do( BYTE bParams ); /* generates the pcode to execute a Clipper function discarding its result */ void Duplicate( void ); /* duplicates the virtual machine latest stack latest value and places it on the stack */ void DupPCode( ULONG ulStart ); /* duplicates the current generated pcode from an offset */ void FieldPCode( BYTE , char * ); /* generates the pcode for database field */ void FixElseIfs( void * pIfElseIfs ); /* implements the ElseIfs pcode fixups */ void FixReturns( void ); /* fixes all last defined function returns jumps offsets */ void Function( BYTE bParams ); /* generates the pcode to execute a Clipper function pushing its result */ PFUNCTION FunctionNew( char *, char ); /* creates and initialises the _FUNC structure */ void FunDef( char * szFunName, HB_SYMBOLSCOPE cScope, int iType ); /* starts a new Clipper language function definition */ void GenArray( int iElements ); /* instructs the virtual machine to build an array and load elemnst from the stack */ void GenBreak( void ); /* generate code for BREAK statement */ void * GenElseIf( void * pFirstElseIf, ULONG ulOffset ); /* generates a support structure for elseifs pcode fixups */ void GenExterns( void ); /* generates the symbols for the EXTERN names */ void GenIfInline( void ); /* generates pcodes for IIF( expr1, expr2, expr3 ) */ int GetFieldVarPos( char *, PFUNCTION ); /* return if passed name is a field variable */ int GetMemvarPos( char *, PFUNCTION ); /* return if passed name is a memvar variable */ USHORT GetVarPos( PVAR pVars, char * szVarName ); /* returns the order + 1 of a variable if defined or zero */ int GetLocalVarPos( char * szVarName ); /* returns the order + 1 of a local variable */ void Inc( void ); /* generates the pcode to increment the latest value on the virtual machine stack */ ULONG Jump( LONG lOffset ); /* generates the pcode to jump to a specific offset */ ULONG JumpFalse( LONG lOffset ); /* generates the pcode to jump if false */ void JumpHere( ULONG ulOffset ); /* returns the pcode pos where to set a jump offset */ void JumpThere( ULONG ulFrom, ULONG ulTo ); /* sets a jump offset */ ULONG JumpTrue( LONG lOffset ); /* generates the pcode to jump if true */ void Line( void ); /* generates the pcode with the currently compiled source code line */ void LineDebug( void ); /* generates the pcode with the currently compiled source code line */ void LineBody( void ); /* generates the pcode with the currently compiled source code line */ void VariablePCode( BYTE , char * ); /* generates the pcode for undeclared variable */ void MemvarPCode( BYTE , char * ); /* generates the pcode for memvar variable */ void Message( char * szMsgName ); /* sends a message to an object */ void MessageFix( char * szMsgName ); /* fix a generated message to an object */ void MessageDupl( char * szMsgName ); /* fix a one generated message to an object and duplicate */ void PopId( char * szVarName ); /* generates the pcode to pop a value from the virtual machine stack onto a variable */ void PushDouble( double fNumber, BYTE bDec ); /* Pushes a number on the virtual machine stack */ void PushFunCall( char * ); /* generates the pcode to push function's call */ void PushId( char * szVarName ); /* generates the pcode to push a variable value to the virtual machine stack */ void PushIdByRef( char * szVarName ); /* generates the pcode to push a variable by reference to the virtual machine stack */ void PushInteger( int iNumber ); /* Pushes a integer number on the virtual machine stack */ void PushLogical( int iTrueFalse ); /* pushes a logical value on the virtual machine stack */ void PushLong( long lNumber ); /* Pushes a long number on the virtual machine stack */ void PushNil( void ); /* Pushes nil on the virtual machine stack */ void PushString( char * szText ); /* Pushes a string on the virtual machine stack */ void PushSymbol( char * szSymbolName, int iIsFunction ); /* Pushes a symbol on to the Virtual machine stack */ char * SetData( char * szMsg ); /* generates an underscore-symbol name for a data assignment */ static void GenPlusPCode( BYTE ); /* Generates opcode for plus/minus operands */ static void GenNumPCode( BYTE ); /* Generates opcode for numerical operands */ static void GenRelPCode( BYTE ); /* Generates opcode for relational operands */ static void GenNotPCode( void ); /* Generates HB_P_NOT opcode */ static void GenNegatePCode( void ); /* Generates HB_P_NEGATE opcode */ static void GenPopPCode( void ); /* Generates HB_P_POP opcode */ static void RemoveExtraPush( void ); /* Removes redundant push opcode */ static void SetLastPushPos( void ); /* Resets postion of last possible redundant push opcode */ static void GenPCode1( BYTE ); /* generates 1 byte of pcode */ static void GenPCode3( BYTE, BYTE, BYTE ); /* generates 3 bytes of pcode */ static void GenPCodeN( BYTE * pBuffer, ULONG ulSize ); /* copy bytes to a pcode buffer */ ULONG SequenceBegin( void ); ULONG SequenceEnd( void ); void SequenceFinish( ULONG, int ); /* Managing value type */ extern void ValTypePush( char cType ); /* Pushes the type of expression (used with -w3 option */ extern void ValTypePop( int ); extern void ValTypePlus( void ); extern void ValTypeRelational( void ); extern void ValTypeCheck( char, int, int ); extern void ValTypeCheck2( char, int, int ); extern char ValTypeGet( void ); extern void ValTypePut( char ); extern void ValTypeAssign( char * ); extern void ValTypeReset( void ); /* support for FIELD declaration */ void FieldsSetAlias( char *, int ); int FieldsCount( void ); /* Codeblocks */ void CodeBlockStart( void ); /* starts a codeblock creation */ void CodeBlockEnd( void ); /* end of codeblock creation */ /* Static variables */ void StaticDefStart( void ); void StaticDefEnd( int ); void StaticAssign( void ); /* checks if static variable is initialized with function call */ /* output related functions */ extern void GenCCode( PHB_FNAME ); /* generates the C language output */ extern void GenJava( PHB_FNAME ); /* generates the Java language output */ extern void GenPascal( PHB_FNAME ); /* generates the Pascal language output */ extern void GenRC( PHB_FNAME ); /* generates the RC language output */ extern void GenPortObj( PHB_FNAME ); /* generates the portable objects */ #ifdef HARBOUR_OBJ_GENERATION extern void GenObj32( PHB_FNAME ); /* generates OBJ 32 bits */ #endif /* argument checking */ void CheckArgs( char *, int ); static BOOL SwitchCmp( char * szString, char * szSwitch ); void PrintUsage( char * ); void PrintCredits( void ); #ifdef HARBOUR_YYDEBUG #define YYDEBUG 1 /* Parser debug information support */ #endif typedef enum { LANG_C, /* C language (by default) */ LANG_OBJ32, /* DOS/Windows 32 bits */ LANG_JAVA, /* Java */ LANG_PASCAL, /* Pascal */ LANG_RESOURCES, /* Resources */ LANG_PORT_OBJ /* Portable objects */ } LANGUAGES; /* supported Harbour output languages */ int iVarScope = VS_LOCAL; /* holds the scope for next variables to be defined */ /* different values for iVarScope */ /* Table with parse errors */ char * _szCErrors[] = { "Statement not allowed outside of procedure or function", "Redefinition of procedure or function: \'%s\'", "Duplicate variable declaration: \'%s\'", "%s declaration follows executable statement", "Outer codeblock variable is out of reach: \'%s\'", "Invalid numeric format '.'", "Unterminated string: \'%s\'", "Redefinition of predefined function %s: \'%s\'", "Illegal initializer: \'%s\'", "ENDIF does not match IF", "ENDDO does not match WHILE", "ENDCASE does not match DO CASE", "NEXT does not match FOR", "ELSE does not match IF", "ELSEIF does not match IF", "Syntax error: \'%s\'", "Unclosed control structures", "%s statement with no loop in sight", "Syntax error: \'%s\' in: \'%s\'", "Incomplete statement: %s", "Incorrect number of arguments: %s %s", "Invalid lvalue", "Invalid use of \'@\' (pass by reference): \'%s\'", "Formal parameters already declared", "Invalid %s from within of SEQUENCE code", "Unterminated array index", "Memory allocation error", "Memory reallocation error", "Freeing a NULL memory pointer", "Parser says: \"%s\"", "Jump offset too long", "Can't create output file: \'%s\'", "Can't create preprocessed output file: \'%s\'", "Bad command line option: \'%s\'", "Bad command line parameter: \'%s\'", "Invalid filename: \'%s\'", "Mayhem in CASE handler", "Invalid alias expression: \'%s'" }; /* Table with parse warnings */ /* NOTE: The first character stores the warning's level that triggers this * warning. The warning's level is set by -w command line option. */ char * _szCWarnings[] = { "1Ambiguous reference: \'%s\'", "1Ambiguous reference, assuming memvar: \'%s\'", "2Variable: \'%s\' declared but not used in function: \'%s\'", "2CodeBlock Parameter: \'%s\' declared but not used in function: \'%s\'", "1RETURN statement with no return value in function", "1Procedure returns value", "1Function \'%s\' does not end with RETURN statement", "3Incompatible type in assignment to: \'%s\' expected: \'%s\'", "3Incompatible operand type: \'%s\' expected: \'Logical\'", "3Incompatible operand type: \'%s\' expected: \'Numeric\'", "3Incompatible operand types: \'%s\' and: \'%s\'", "3Suspicious type in assignment to: \'%s\' expected: \'%s\'", "3Suspicious operand type: \'UnKnown\' expected: \'%s\'", "3Suspicious operand type: \'UnKnown\' expected: \'Logical\'", "3Suspicious operand type: \'UnKnown\' expected: \'Numeric\'" }; /* Table with reserved functions names * NOTE: THIS TABLE MUST BE SORTED ALPHABETICALLY */ static const char * _szReservedFun[] = { "AADD" , "ABS" , "ASC" , "AT" , "BOF" , "BREAK" , "CDOW" , "CHR" , "CMONTH" , "COL" , "CTOD" , "DATE" , "DAY" , "DELETED" , "DEVPOS" , "DOW" , "DTOC" , "DTOS" , "EMPTY" , "EOF" , "EXP" , "FCOUNT" , "FIELDNAME" , "FLOCK" , "FOUND" , "INKEY" , "INT" , "LASTREC" , "LEFT" , "LEN" , "LOCK" , "LOG" , "LOWER" , "LTRIM" , "MAX" , "MIN" , "MONTH" , "PCOL" , "PCOUNT" , "PROW" , "QSELF" , "RECCOUNT" , "RECNO" , "REPLICATE" , "RLOCK" , "ROUND" , "ROW" , "RTRIM" , "SECONDS" , "SELECT" , "SETPOS" , "SETPOSBS" , "SPACE" , "SQRT" , "STR" , "SUBSTR" , "TIME" , "TRANSFORM" , "TRIM" , "TYPE" , "UPPER" , "VAL" , "WORD" , "YEAR" }; #define RESERVED_FUNCTIONS sizeof( _szReservedFun ) / sizeof( char * ) static char * reserved_name( char * ); #define RESERVED_FUNC(szName) reserved_name( (szName) ) FILES files; FUNCTIONS functions, funcalls; PFUNCTION _pInitFunc; SYMBOLS symbols; /* /ES command line setting types */ #define HB_EXITLEVEL_DEFAULT 0 #define HB_EXITLEVEL_SETEXIT 1 #define HB_EXITLEVEL_DELTARGET 2 int iFunctions = 0; BOOL _bStartProc = TRUE; /* holds if we need to create the starting procedure */ BOOL _bLineNumbers = TRUE; /* holds if we need pcodes with line numbers */ BOOL _bCredits = FALSE; /* print credits */ BOOL _bLogo = TRUE; /* print logo */ BOOL _bQuiet = FALSE; /* quiet mode */ BOOL _bSyntaxCheckOnly = FALSE; /* syntax check only */ int _iLanguage = LANG_C; /* default Harbour generated output language */ BOOL _bRestrictSymbolLength = FALSE; /* generate 10 chars max symbols length */ BOOL _bShortCuts = TRUE; /* .and. & .or. expressions shortcuts */ int _iWarnings = 0; /* enable parse warnings */ BOOL _bAnyWarning = FALSE; /* holds if there was any warning during the compilation process */ BOOL _bAutoMemvarAssume = FALSE; /* holds if undeclared variables are automatically assumed MEMVAR (-a)*/ BOOL _bForceMemvars = FALSE; /* holds if memvars are assumed when accesing undeclared variable (-v)*/ BOOL _bDebugInfo = FALSE; /* holds if generate debugger required info */ char _szPrefix[ 20 ] = { '\0' }; /* holds the prefix added to the generated symbol init function name (in C output currently) */ BOOL _bGenCVerbose = TRUE; /* C code generation should be verbose (use comments) or not */ int _iExitLevel = HB_EXITLEVEL_DEFAULT; /* holds if there was any warning during the compilation process */ USHORT _wSeqCounter = 0; USHORT _wForCounter = 0; USHORT _wIfCounter = 0; USHORT _wWhileCounter = 0; USHORT _wCaseCounter = 0; ULONG _ulMessageFix = 0; /* Position of the message which needs to be changed */ ULONG _ulBookMark = 0; int _iStatics = 0; /* number of defined statics variables on the PRG */ PEXTERN pExterns = NULL; PTR_LOOPEXIT pLoops = NULL; PATHNAMES *_pIncludePath = NULL; PHB_FNAME _pFileName = NULL; PHB_FNAME _pOutPath = NULL; ALIASID_PTR pAliasId = NULL; ULONG _ulLastLinePos = 0; /* position of last opcode with line number */ BOOL _bDontGenLineNum = FALSE; /* suppress line number generation */ EXPLIST_PTR _pExpList = NULL; /* stack used for parenthesized expressions */ char _cVarType = ' '; /* current declared variable type */ #define LOOKUP 0 extern int _iState; /* current parser state (defined in harbour.l */ %} %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 number returned by lex */ long lNumber; /* to hold a long number returned by lex */ struct { double dNumber; /* to hold a double number returned by lex */ /* NOTE: Intentionally using "unsigned char" instead of "BYTE" */ unsigned char bDec; /* to hold the number of decimal points in the value */ } dNum; void * pVoid; /* to hold any memory structure we may need */ }; %token FUNCTION PROCEDURE IDENTIFIER RETURN NIL NUM_DOUBLE INASSIGN NUM_INTEGER NUM_LONG %token LOCAL STATIC IIF IF ELSE ELSEIF END ENDIF LITERAL TRUEVALUE FALSEVALUE %token EXTERN INIT EXIT AND OR NOT PUBLIC EQ NE1 NE2 %token INC DEC ALIASOP DOCASE CASE OTHERWISE ENDCASE ENDDO MEMVAR %token WHILE EXIT LOOP END FOR NEXT TO STEP LE GE FIELD IN PARAMETERS %token PLUSEQ MINUSEQ MULTEQ DIVEQ POWER EXPEQ MODEQ EXITLOOP %token PRIVATE BEGINSEQ BREAK RECOVER USING DO WITH SELF LINE %token AS_NUMERIC AS_CHARACTER AS_LOGICAL AS_DATE AS_ARRAY AS_BLOCK AS_OBJECT DECLARE_FUN %token DOT MACROALIAS MACROOP /*the lowest precedence*/ /*postincrement and postdecrement*/ %left POST /*assigment - from right to left*/ %right INASSIGN %left PLUSEQ MINUSEQ %left MULTEQ DIVEQ MODEQ %left EXPEQ /*logical operators*/ %left OR %left AND %left NOT /*relational operators*/ %left '=' '<' '>' EQ NE1 NE2 LE GE '$' /*mathematical operators*/ %left '+' '-' %left '*' '/' '%' %left POWER %left UNARY /*preincrement and predecrement*/ %left PRE /*special operators*/ %left ALIASOP MACROOP '@' ')' %right '\n' ';' ',' /*the highest precedence*/ %type IDENTIFIER LITERAL FunStart MethStart IdSend ObjectData AliasVar %type NUM_DOUBLE %type ArgList ElemList PareExpList ExpList FunCall FunScope IncDec %type Params ParamList Logical ArrExpList %type NUM_INTEGER BlockExpList Argument IfBegin VarList MethParams ObjFunCall, ExtVarList %type MethCall BlockList FieldList DoArgList VarAt %type NUM_LONG WhileBegin BlockBegin %type IfElseIf Cases %% Main : { Line(); } Source { FixReturns(); /* fix all previous function returns offsets */ if( ! _bQuiet ) { if( ! _bStartProc ) --iFunctions; printf( "\rLines %i, Functions/Procedures %i\n", iLine, iFunctions ); } } Source : Crlf | VarDefs | FieldsDef | MemvarDef | Function | Statement | Line | Source Crlf | Source Function | Source { LineBody(); } Statement | Source VarDefs | Source FieldsDef | Source MemvarDef | Source Line ; Line : LINE NUM_INTEGER LITERAL Crlf | LINE NUM_INTEGER LITERAL '@' LITERAL Crlf /* XBase++ style */ ; Function : FunScope FUNCTION IDENTIFIER { _cVarType = ' '; FunDef( $3, ( HB_SYMBOLSCOPE ) $1, 0 ); } Params Crlf {} | FunScope PROCEDURE IDENTIFIER { _cVarType = ' '; FunDef( $3, ( HB_SYMBOLSCOPE ) $1, FUN_PROCEDURE ); } Params Crlf {} | FunScope DECLARE_FUN IDENTIFIER Params AsType Crlf { AddSymbol( $3, NULL ); } ; FunScope : { $$ = FS_PUBLIC; } | STATIC { $$ = FS_STATIC; } | INIT { $$ = FS_INIT; } | EXIT { $$ = FS_EXIT; } ; Params : { $$ = 0; } | '(' ')' { $$ = 0; } | '(' { iVarScope = VS_PARAMETER; } ParamList ')' { $$ = $3; } ; AsType : /* unknown */ { _cVarType = ' '; } | AS_NUMERIC { _cVarType = 'N'; } | AS_CHARACTER { _cVarType = 'C'; } | AS_DATE { _cVarType = 'D'; } | AS_LOGICAL { _cVarType = 'L'; } | AS_ARRAY { _cVarType = 'A'; } | AS_BLOCK { _cVarType = 'B'; } | AS_OBJECT { _cVarType = 'O'; } ; ParamList : IDENTIFIER AsType { AddVar( $1 ); $$ = 1; } | ParamList ',' IDENTIFIER AsType { AddVar( $3 ); $$++; } ; Statement : ExecFlow Crlf {} | FunCall Crlf { Do( $1 ); } | AliasFunc Crlf {} | IfInline Crlf { GenPopPCode(); } | ObjectMethod Crlf { GenPopPCode(); } | VarUnary Crlf { GenPopPCode(); } | VarExpr Crlf { RemoveExtraPush(); } | { PushSymbol( yy_strdup( "__MVPUT" ), 1); PushNil(); } VarMacro MacroAssign Expression { Do( 2 ); } Crlf | IDENTIFIER '=' Expression { PopId( $1 ); } Crlf | AliasVar '=' { $$=( void * )pAliasId; pAliasId = NULL; } Expression { pAliasId=(ALIASID_PTR) $3; PopId( $1 ); AliasRemove(); } Crlf | AliasFunc '=' Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } Crlf | VarAt '=' Expression { ArrayPut(); GenPopPCode(); } Crlf | ArrayAt '=' Expression { GenPopPCode();GenPopPCode(); GenPopPCode(); } Crlf | FunCallArray '=' Expression { ArrayPut(); GenPopPCode(); } Crlf | ObjectData '=' { MessageFix( SetData( $1 ) ); } Expression { Function( 1 ); GenPopPCode(); } Crlf | ObjectData ArrayIndex '=' Expression { ArrayPut(); GenPopPCode(); } Crlf | ObjectMethod ArrayIndex '=' Expression { ArrayPut(); GenPopPCode(); } Crlf | BREAK { GenBreak(); } Crlf { Do( 0 ); } | BREAK { GenBreak(); } Expression Crlf { Do( 1 ); } | RETURN { if( _wSeqCounter ) { GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "RETURN", NULL ); } GenPCode1( HB_P_ENDPROC ); if( (functions.pLast->bFlags & FUN_PROCEDURE) == 0 ) { /* return from a function without a return value */ GenWarning( _szCWarnings, 'W', WARN_NO_RETURN_VALUE, NULL, NULL ); } functions.pLast->bFlags |= FUN_WITH_RETURN; } Crlf | RETURN Expression { if( _wSeqCounter ) { GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "RETURN", NULL ); } GenPCode1( HB_P_RETVALUE ); GenPCode1( HB_P_ENDPROC ); ValTypePop( 1 ); /* TODO: check if return value agree with declared value */ if( functions.pLast->bFlags & FUN_PROCEDURE ) { /* procedure returns a value */ GenWarning( _szCWarnings, 'W', WARN_PROC_RETURN_VALUE, NULL, NULL ); } functions.pLast->bFlags |= FUN_WITH_RETURN; } Crlf | PUBLIC { iVarScope = VS_PUBLIC; } ExtVarList Crlf | PRIVATE { iVarScope = VS_PRIVATE; } ExtVarList Crlf | EXITLOOP Crlf { LoopExit(); } | LOOP Crlf { LoopLoop(); } | DoProc Crlf | EXTERN ExtList Crlf ; LineStat : Crlf { $$ = 0; } | Statement { $$ = 1; } ; Statements : LineStat { $$ = $1; } | Statements { Line(); } LineStat { $$ += $3; } ; ExtList : IDENTIFIER { AddExtern( $1 ); } | ExtList ',' IDENTIFIER { AddExtern( $3 ); } ; FunCall : FunStart ')' { $$=0; CheckArgs( $1, $$ ); } | FunStart ArgList ')' { $$=$2; CheckArgs( $1, $$ ); } ; FunStart : IDENTIFIER '(' { StaticAssign(); PushFunCall( $1 ); $$ = $1; } ; MethCall : MethStart ')' { $$ = 0; } | MethStart ArgList ')' { $$ = $2; } ; MethStart : IDENTIFIER '(' { StaticAssign(); Message( $1 ); $$ = $1; } ; ArgList : ',' { PushNil(); PushNil(); $$ = 2; } | Argument { $$ = 1; } | ArgList ',' { PushNil(); $$++; } | ArgList ',' Argument { $$++; } | ',' { PushNil(); } Argument { $$ = 2; } ; Argument : Expression {} | '@' IDENTIFIER { PushIdByRef( $2 ); } | '@' IDENTIFIER '(' ')' { PushSymbol( $2, 1 ); GenPCode1( HB_P_FUNCPTR ); } ; MethParams : /* empty */ { $$ = 0; } | ArgList { $$ = $1; } ; ObjectData : IdSend IDENTIFIER { $$ = $2; _ulMessageFix = functions.pLast->lPCodePos; Message( $2 ); Function( 0 ); } | VarAtSend IDENTIFIER { $$ = $2; _ulMessageFix = functions.pLast->lPCodePos; Message( $2 ); Function( 0 ); } | ObjFunCall IDENTIFIER { $$ = $2; _ulMessageFix = functions.pLast->lPCodePos; Message( $2 ); Function( 0 ); } | ObjFunArray ':' IDENTIFIER { $$ = $3; _ulMessageFix = functions.pLast->lPCodePos; Message( $3 ); Function( 0 ); } | ObjectMethod ':' IDENTIFIER { $$ = $3; _ulMessageFix = functions.pLast->lPCodePos; Message( $3 ); Function( 0 ); } | ObjectData ':' IDENTIFIER { $$ = $3; _ulMessageFix = functions.pLast->lPCodePos; Message( $3 ); Function( 0 ); } | ObjectArr IDENTIFIER { $$ = $2; _ulMessageFix = functions.pLast->lPCodePos; Message( $2 ); Function( 0 ); } ; ObjectMethod : IdSend IDENTIFIER { Message( $2 ); } '(' MethParams ')' { Function( $5 ); } | VarAtSend MethCall { Function( $2 ); } | ObjFunCall MethCall { Function( $2 ); } | ObjFunArray ':' MethCall { Function( $3 ); } | ObjectData ':' MethCall { Function( $3 ); } | ObjectArr MethCall { Function( $2 ); } | ObjectMethod ':' MethCall { Function( $3 ); } ; VarAtSend : VarAt ':' { ArrayAt(); } | ArrayAt ':' { ArrayAt(); } ; ObjectArr : ObjectData ArrayIndex ':' { ArrayAt(); } ; IdSend : IDENTIFIER ':' { PushId( $1 ); $$ = $1; } ; ObjFunCall : FunCall ':' { Function( $1 ); $$ = $1; } ; FunCallArray : FunCall { Function( $1 ); } ArrayIndex ; ObjFunArray : FunCallArray ':' { ArrayAt(); } ; NumExpression : NUM_DOUBLE { PushDouble( $1.dNumber,$1.bDec ); } | NUM_INTEGER { PushInteger( $1 ); ValTypePush( 'N' ); } | NUM_LONG { PushLong( $1 ); } ; ConExpression : NIL { PushNil(); } | LITERAL { PushString( $1 ); } | CodeBlock {} | Logical { PushLogical( $1 ); } | SELF { GenPCode1( HB_P_PUSHSELF ); ValTypePush( 'O' ); } ; DynExpression : Variable | VarUnary | Operators {} | FunCall { Function( $1 ); } | IfInline {} | Array {} | ObjectMethod {} | Macro {} | AliasVar { PushId( $1 ); AliasRemove(); } | AliasFunc {} ; /* NOTE: We have to distinguish IDENTIFIER here because it is repeated * in DoExpression (a part of DO WITH .. statement) * where it generates different action. */ SimpleExpression : IDENTIFIER { PushId( $1 ); } | NumExpression | ConExpression | DynExpression ; Expression : SimpleExpression {} | PareExpList {} ; EmptyExpression: /* nothing => nil */ | Expression ; IfInline : IIF PareExpList3 { GenIfInline(); } | IF PareExpList3 { GenIfInline(); } ; AliasVar : NUM_INTEGER ALIASOP { AliasAddInt( $1 ); } IDENTIFIER { $$ = $4; } | IDENTIFIER ALIASOP { AliasAddStr( $1 ); } IDENTIFIER { $$ = $4; } | PareExpList ALIASOP { AliasAddExp(); } IDENTIFIER { $$ = $4; } ; /* NOTE: In the case: * alias->( Expression ) * alias always selects a workarea even if it is MEMVAR or M */ AliasFunc : NUM_INTEGER ALIASOP { AliasPush(); PushInteger( $1 ); AliasPop(); } PareExpList { AliasSwap(); } | IDENTIFIER ALIASOP { AliasPush(); PushSymbol( $1, 0 ); AliasPop(); } PareExpList { AliasSwap(); } | PareExpList ALIASOP { AliasPush(); AliasSwap(); } PareExpList { AliasSwap(); } ; VarUnary : IDENTIFIER IncDec %prec POST { PushId( $1 ); Duplicate(); $2 ? Inc(): Dec(); PopId( $1 ); } | IncDec IDENTIFIER %prec PRE { PushId( $2 ); $1 ? Inc(): Dec(); Duplicate(); PopId( $2 ); } | VarAt IncDec %prec POST { DupPCode( $1 ); ArrayAt(); $2 ? Inc(): Dec(); ArrayPut(); $2 ? Dec(): Inc(); } | IncDec VarAt %prec PRE { DupPCode( $2 ); ArrayAt(); $1 ? Inc(): Dec(); ArrayPut(); } | ArrayAt IncDec %prec POST { ArrayAt(); } | IncDec ArrayAt %prec PRE { ArrayAt(); $1 ? Inc(): Dec(); } | FunCallArray IncDec %prec POST { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $2 ? Inc(): Dec(); ArrayPut(); $2 ? Dec(): Inc(); } | IncDec FunCallArray %prec PRE { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $1 ? Inc(): Dec(); ArrayPut(); } | ObjectData IncDec %prec POST { MessageDupl( SetData( $1 ) ); Function( 0 ); $2 ? Inc(): Dec(); Function( 1 ); $2 ? Dec(): Inc(); } | IncDec ObjectData %prec PRE { MessageDupl( SetData( $2 ) ); Function( 0 ); $1 ? Inc(): Dec(); Function( 1 ); } | ObjectData ArrayIndex IncDec %prec POST { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $3 ? Inc(): Dec(); ArrayPut(); $3 ? Dec(): Inc(); } | IncDec ObjectData ArrayIndex %prec PRE { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $1 ? Inc(): Dec(); ArrayPut(); } | ObjectMethod ArrayIndex IncDec %prec POST { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $3 ? Inc(): Dec(); ArrayPut(); $3 ? Dec(): Inc(); } | IncDec ObjectMethod ArrayIndex %prec PRE { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); $1 ? Inc(): Dec(); ArrayPut(); } | AliasVar IncDec %prec POST { PushId( $1 ); Duplicate(); $2 ? Inc(): Dec(); PopId( $1 ); AliasRemove(); } | IncDec AliasVar %prec PRE { PushId( $2 ); $1 ? Inc(): Dec(); Duplicate(); PopId( $2 ); AliasRemove(); } ; IncDec : INC { $$ = 1; } | DEC { $$ = 0; } ; Variable : VarAt { ArrayAt(); } | ArrayAt { ArrayAt(); } | FunCallArray { ArrayAt(); } | ObjectData {} | ObjectData ArrayIndex { ArrayAt(); } | ObjectMethod ArrayIndex { ArrayAt(); } ; VarAt : IDENTIFIER { $$ = functions.pLast->lPCodePos; PushId( $1 ); } ArrayIndex { $$ =$2; } ; ArrayAt : Array ArrayIndex ; ArrayIndex : '[' IndexList ']' | ArrayIndex { ArrayAt(); } '[' IndexList ']' ; IndexList : Expression | IndexList { ArrayAt(); } ',' Expression ; VarAssign : IDENTIFIER INASSIGN Expression { PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | VarAt INASSIGN Expression { ArrayPut(); } | ArrayAt INASSIGN { GenPopPCode();GenPopPCode(); } Expression | FunCallArray INASSIGN Expression { ArrayPut(); } | ObjectData INASSIGN { MessageFix ( SetData( $1 ) ); } Expression { Function( 1 ); } | ObjectData ArrayIndex INASSIGN Expression { ArrayPut(); } | ObjectMethod ArrayIndex INASSIGN Expression { ArrayPut(); } | AliasVar INASSIGN { $$=( void * ) pAliasId; pAliasId = NULL; } Expression { pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasFunc INASSIGN Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } ; VarOpEq : IDENTIFIER PLUSEQ { PushId( $1 ); } Expression { GenPlusPCode( HB_P_PLUS ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | IDENTIFIER MINUSEQ { PushId( $1 ); } Expression { GenPlusPCode( HB_P_MINUS ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | IDENTIFIER MULTEQ { PushId( $1 ); } Expression { GenNumPCode( HB_P_MULT ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | IDENTIFIER DIVEQ { PushId( $1 ); } Expression { GenNumPCode( HB_P_DIVIDE ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | IDENTIFIER EXPEQ { PushId( $1 ); } Expression { GenNumPCode( HB_P_POWER ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | IDENTIFIER MODEQ { PushId( $1 ); } Expression { GenNumPCode( HB_P_MODULUS ); PopId( $1 ); SetLastPushPos(); PushId( $1 ); } | VarAt PLUSEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenPlusPCode( HB_P_PLUS ); ArrayPut(); } | VarAt MINUSEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenPlusPCode( HB_P_MINUS ); ArrayPut(); } | VarAt MULTEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenNumPCode( HB_P_MULT ); ArrayPut(); } | VarAt DIVEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenNumPCode( HB_P_DIVIDE ); ArrayPut(); } | VarAt EXPEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenNumPCode( HB_P_POWER ); ArrayPut(); } | VarAt MODEQ { DupPCode( $1 ); ArrayAt(); } Expression { GenNumPCode( HB_P_MODULUS ); ArrayPut(); } | ArrayAt PLUSEQ { ArrayAt(); } Expression { GenPlusPCode( HB_P_PLUS ); } | ArrayAt MINUSEQ { ArrayAt(); } Expression { GenPlusPCode( HB_P_MINUS ); } | ArrayAt MULTEQ { ArrayAt(); } Expression { GenNumPCode( HB_P_MULT ); } | ArrayAt DIVEQ { ArrayAt(); } Expression { GenNumPCode( HB_P_DIVIDE ); } | ArrayAt EXPEQ { ArrayAt(); } Expression { GenNumPCode( HB_P_POWER ); } | ArrayAt MODEQ { ArrayAt(); } Expression { GenNumPCode( HB_P_MODULUS ); } | FunCallArray PLUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_PLUS ); ArrayPut(); } | FunCallArray MINUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_MINUS ); ArrayPut(); } | FunCallArray MULTEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MULT ); ArrayPut(); } | FunCallArray DIVEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_DIVIDE ); ArrayPut(); } | FunCallArray EXPEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_POWER ); ArrayPut(); } | FunCallArray MODEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MODULUS ); ArrayPut(); } | ObjectData PLUSEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenPlusPCode( HB_P_PLUS ); Function( 1 ); } | ObjectData MINUSEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenPlusPCode( HB_P_MINUS ); Function( 1 ); } | ObjectData MULTEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenNumPCode( HB_P_MULT ); Function( 1 ); } | ObjectData DIVEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenNumPCode( HB_P_DIVIDE ); Function( 1 ); } | ObjectData EXPEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenNumPCode( HB_P_POWER ); Function( 1 ); } | ObjectData MODEQ { MessageDupl( SetData( $1 ) ); Function( 0 ); } Expression { GenNumPCode( HB_P_MODULUS ); Function( 1 ); } | ObjectData ArrayIndex PLUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_PLUS ); ArrayPut(); } | ObjectData ArrayIndex MINUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_MINUS ); ArrayPut(); } | ObjectData ArrayIndex MULTEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MULT ); ArrayPut(); } | ObjectData ArrayIndex DIVEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_DIVIDE ); ArrayPut(); } | ObjectData ArrayIndex EXPEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_POWER ); ArrayPut(); } | ObjectData ArrayIndex MODEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MODULUS ); ArrayPut(); } | ObjectMethod ArrayIndex PLUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_PLUS ); ArrayPut(); } | ObjectMethod ArrayIndex MINUSEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenPlusPCode( HB_P_MINUS ); ArrayPut(); } | ObjectMethod ArrayIndex MULTEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MULT ); ArrayPut(); } | ObjectMethod ArrayIndex DIVEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_DIVIDE ); ArrayPut(); } | ObjectMethod ArrayIndex EXPEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_POWER ); ArrayPut(); } | ObjectMethod ArrayIndex MODEQ { GenPCode1( HB_P_DUPLTWO ); ArrayAt(); } Expression { GenNumPCode( HB_P_MODULUS ); ArrayPut(); } | AliasVar PLUSEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenPlusPCode( HB_P_PLUS ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasVar MINUSEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenPlusPCode( HB_P_MINUS ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasVar MULTEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenNumPCode( HB_P_MULT ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasVar DIVEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenNumPCode( HB_P_DIVIDE ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasVar EXPEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenNumPCode( HB_P_POWER ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasVar MODEQ { PushId( $1 ); $$=(void*)pAliasId; pAliasId = NULL; } Expression { GenNumPCode( HB_P_MODULUS ); pAliasId=(ALIASID_PTR) $3; PopId( $1 ); SetLastPushPos(); PushId( $1 ); AliasRemove(); } | AliasFunc PLUSEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } | AliasFunc MINUSEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } | AliasFunc MULTEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } | AliasFunc DIVEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } | AliasFunc EXPEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } | AliasFunc MODEQ Expression { --iLine; GenError( _szCErrors, 'E', ERR_INVALID_LVALUE, NULL, NULL ); } ; VarExpr : VarAssign | VarOpEq ; Operators : Expression '=' Expression { GenRelPCode( HB_P_EQUAL ); } /* compare */ | Expression '+' Expression { GenPlusPCode( HB_P_PLUS ); } | Expression '-' Expression { GenPlusPCode( HB_P_MINUS ); } | Expression '*' Expression { GenNumPCode( HB_P_MULT ); } | Expression '/' Expression { GenNumPCode( HB_P_DIVIDE ); } | Expression '<' Expression { GenRelPCode( HB_P_LESS ); } | Expression '>' Expression { GenRelPCode( HB_P_GREATER ); } | Expression '$' Expression { GenRelPCode( HB_P_INSTRING ); } | Expression '%' Expression { GenNumPCode( HB_P_MODULUS ); } | Expression LE Expression { GenRelPCode( HB_P_LESSEQUAL ); } | Expression GE Expression { GenRelPCode( HB_P_GREATEREQUAL ); } | Expression AND { if( _bShortCuts ){ Duplicate(); $$ = JumpFalse( 0 ); } } Expression { GenPCode1( HB_P_AND ); if( _bShortCuts ) JumpHere( $3 ); } | Expression OR { if( _bShortCuts ){ Duplicate(); $$ = JumpTrue( 0 ); } } Expression { GenPCode1( HB_P_OR ); if( _bShortCuts ) JumpHere( $3 ); } | Expression EQ Expression { GenRelPCode( HB_P_EXACTLYEQUAL ); } | Expression NE1 Expression { GenRelPCode( HB_P_NOTEQUAL ); } | Expression NE2 Expression { GenRelPCode( HB_P_NOTEQUAL ); } | Expression POWER Expression { GenNumPCode( HB_P_POWER ); } | NOT Expression { GenNotPCode( ); } | '-' Expression %prec UNARY { GenNegatePCode( ); } | '+' Expression %prec UNARY | VarExpr { } ; Logical : TRUEVALUE { $$ = 1; } | FALSEVALUE { $$ = 0; } ; Array : '{' ElemList '}' { GenArray( $2 ); } ; ElemList : /*empty array*/ { $$ = 0; } | Expression { $$ = 1; } | ElemList ',' { if( $$ == 0 ) { PushNil(); PushNil(); $$ = 2; } else { PushNil(); $$++; } } | ElemList ',' Expression { if( $$ == 0 ) { PushNil(); $$ = 2; } else $$++; } ; CodeBlock : BlockBegin '|' BlockExpList '}' { CodeBlockEnd(); } | BlockBegin { $$=iVarScope; iVarScope=VS_LOCAL; } BlockList '|' BlockExpList '}' { CodeBlockEnd(); iVarScope=$2; } ; BlockBegin : '{' '|' { CodeBlockStart(); } ; BlockExpList : Expression { $$ = 1; } | ',' { SetLastPushPos(); PushNil(); RemoveExtraPush(); PushNil(); $$ = 2; } | BlockExpList ',' { RemoveExtraPush(); PushNil(); $$++; } | BlockExpList ',' { RemoveExtraPush(); } Expression { $$++; } ; BlockList : IDENTIFIER AsType { AddVar( $1 ); $$ = 1; } | BlockList ',' IDENTIFIER AsType { AddVar( $3 ); $$++; } ; /* There is a conflict between the use of IF( Expr1, Expr2, Expr3 ) * and parenthesized expression ( Expr1, Expr2, Expr3 ) * To solve this conflict we have to split the definitions into more * atomic ones. * Also the generation of pcodes have to be delayed and moved to the * end of whole parenthesized expression. */ PareExpList1: ExpList1 ')' { ExpListPop( 1 ); } ; PareExpList2: ExpList2 ')' { ExpListPop( 2 ); } ; PareExpList3: ExpList3 ')' { /* this needs the special handling if used in inline IF */ } ; PareExpListN: ExpList ')' { ExpListPop( $1 ); } ; PareExpList : PareExpList1 { } | PareExpList2 { } | PareExpList3 { ExpListPop( 3 ); } | PareExpListN { } ; ExpList1 : '(' { ExpListPush(); } EmptyExpression ; ExpList2 : ExpList1 ',' { ExpListPush(); } EmptyExpression ; ExpList3 : ExpList2 ',' { ExpListPush(); } EmptyExpression ; ExpList : ExpList3 { ExpListPush(); } ',' EmptyExpression { $$ = 4; } | ExpList { ExpListPush(); } ',' EmptyExpression { $$++; } ; VarDefs : LOCAL { iVarScope = VS_LOCAL; Line(); } VarList Crlf { _cVarType = ' '; } | STATIC { StaticDefStart() } VarList Crlf { StaticDefEnd( $3 ); } | PARAMETERS { if( functions.pLast->bFlags & FUN_USES_LOCAL_PARAMS ) GenError( _szCErrors, 'E', ERR_PARAMETERS_NOT_ALLOWED, NULL, NULL ); else functions.pLast->wParamNum=0; iVarScope = ( VS_PRIVATE | VS_PARAMETER ); } MemvarList Crlf ; ExtVarList : VarDef { $$ = 1; } | ExtVarList ',' VarDef { $$++; } | SetVarMacro { Do( 1 ); } | ExtVarList ',' SetVarMacro { Do( 1 ); } | SetVarMacro MacroAssign { FunMacroAssign(); } Expression { Do( 2 ); } | ExtVarList ',' SetVarMacro MacroAssign { FunMacroAssign(); } Expression { Do( 2 ); } ; SetVarMacro : { SetVarMacro(); } VarMacro ; VarMacro : MACROOP IDENTIFIER { PushId( $2 ); } | MACROALIAS IDENTIFIER { PushId( $2 ); } | Macro | VarMacro DOT IDENTIFIER { PushString( $3 ); GenPlusPCode( HB_P_PLUS ); } | VarMacro NUM_DOUBLE { PushString( yy_strdup( yytext + 1 ) ); GenPlusPCode( HB_P_PLUS ); } ; MacroAssign : INASSIGN | '=' ; Macro : MACROOP Variable | MACROOP '(' Expression ')' | MACROALIAS Variable | MACROALIAS '(' Expression ')' ; VarList : VarDef { $$ = 1; } | VarList ',' VarDef { $$++; } ; VarDef : IDENTIFIER AsType { AddVar( $1 ); } | IDENTIFIER AsType INASSIGN { AddVar( $1 ); } Expression { PopId( $1 ); } | IDENTIFIER ArrExpList ']' { _cVarType = 'A'; AddVar( $1 ); ArrayDim( $2 ); PopId( $1 ); } | IDENTIFIER ArrExpList ']' AS_ARRAY { _cVarType = 'A'; AddVar( $1 ); ArrayDim( $2 ); PopId( $1 ); } ; ArrExpList : '[' Expression { $$ = 1; } | ArrExpList ',' Expression { $$++; } | ArrExpList ']' '[' Expression { $$++; } ; FieldsDef : FIELD { iVarScope = VS_FIELD; } FieldList Crlf ; FieldList : IDENTIFIER AsType { $$=FieldsCount(); AddVar( $1 ); } | FieldList ',' IDENTIFIER AsType { AddVar( $3 ); } | FieldList IN IDENTIFIER { FieldsSetAlias( $3, $1 ); } ; MemvarDef : MEMVAR { iVarScope = VS_MEMVAR; } MemvarList Crlf ; MemvarList : IDENTIFIER { AddVar( $1 ); } | MemvarList ',' IDENTIFIER { AddVar( $3 ); } ; ExecFlow : IfEndif | DoCase | DoWhile | ForNext | BeginSeq ; IfEndif : IfBegin EndIf { JumpHere( $1 ); } | IfBegin IfElse EndIf { JumpHere( $1 ); } | IfBegin IfElseIf EndIf { JumpHere( $1 ); FixElseIfs( $2 ); } | IfBegin IfElseIf IfElse EndIf { JumpHere( $1 ); FixElseIfs( $2 ); } ; EmptyStats : /* empty */ | Statements ; IfBegin : IF SimpleExpression { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = Jump( 0 ); JumpHere( $5 ); } | IF PareExpList1 { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = Jump( 0 ); JumpHere( $5 ); } | IF PareExpList2 { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = Jump( 0 ); JumpHere( $5 ); } | IF PareExpListN { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = Jump( 0 ); JumpHere( $5 ); } ; IfElse : ELSE Crlf { Line(); } EmptyStats ; IfElseIf : ELSEIF Expression Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = GenElseIf( 0, Jump( 0 ) ); JumpHere( $4 ); } | IfElseIf ELSEIF Expression Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = GenElseIf( $1, Jump( 0 ) ); JumpHere( $5 ); } ; EndIf : ENDIF { --_wIfCounter; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } | END { --_wIfCounter; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; DoCase : DoCaseBegin Cases EndCase { FixElseIfs( $2 ); } | DoCaseBegin Otherwise EndCase | DoCaseBegin EndCase | DoCaseBegin Cases Otherwise EndCase { FixElseIfs( $2 ); } ; EndCase : ENDCASE { --_wCaseCounter; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } | END { --_wCaseCounter; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; DoCaseStart : DOCASE { ++_wCaseCounter; } Crlf { Line(); } ; DoCaseBegin : DoCaseStart { } | DoCaseStart Statements { if( $2 > 0 ) { --iLine; GenError( _szCErrors, 'E', ERR_MAYHEM_IN_CASE, NULL, NULL ); } } ; Cases : CASE Expression Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = GenElseIf( 0, Jump( 0 ) ); JumpHere( $4 ); Line(); } | Cases CASE Expression Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { $$ = GenElseIf( $1, Jump( 0 ) ); JumpHere( $5 ); Line(); } ; Otherwise : OTHERWISE Crlf { Line(); } EmptyStats ; /* DoWhile : WhileBegin Expression Crlf { $$ = JumpFalse( 0 ); Line(); } { Jump( $1 - functions.pLast->lPCodePos ); } EndWhile { JumpHere( $4 ); --_wWhileCounter; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } | WhileBegin Expression Crlf { $$ = JumpFalse( 0 ); Line(); } Statements { LoopHere(); Jump( $1 - functions.pLast->lPCodePos ); } EndWhile { JumpHere( $4 ); --_wWhileCounter; LoopEnd(); functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; */ DoWhile : WhileBegin Expression Crlf { $$ = JumpFalse( 0 ); Line(); } EmptyStats { LoopHere(); Jump( $1 - functions.pLast->lPCodePos ); } EndWhile { JumpHere( $4 ); --_wWhileCounter; LoopEnd(); functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; WhileBegin : WHILE { $$ = functions.pLast->lPCodePos; ++_wWhileCounter; LoopStart(); } ; EndWhile : END | ENDDO ; ForNext : FOR IDENTIFIER ForAssign Expression { PopId( $2 ); $$ = functions.pLast->lPCodePos; ++_wForCounter; LoopStart(); } TO Expression { PushId( $2 ); } StepExpr Crlf { if( $9 ) GenPCode1( HB_P_FORTEST ); else GenPCode1( HB_P_LESS ); ValTypePop( 1 ); /* TODO: check for proper type of loop variable */ $$ = JumpTrue( 0 ); Line(); } ForStatements { LoopHere(); PushId( $2 ); if( $9 ) GenPCode1( HB_P_PLUS ); else Inc(); PopId( $2 ); Jump( $5 - functions.pLast->lPCodePos ); JumpHere( $11 ); LoopEnd(); if( $9 ) GenPopPCode(); functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; ForAssign : '=' | INASSIGN ; StepExpr : /* default step expression */ { $$ =0; } | STEP Expression { $$ =1; } ; ForStatements : EmptyStats NEXT { --_wForCounter; } | EmptyStats NEXT IDENTIFIER { --_wForCounter; } ; BeginSeq : BEGINSEQ { ++_wSeqCounter; $$ = SequenceBegin(); } Crlf { Line(); } EmptyStats { /* Set jump address for HB_P_SEQBEGIN opcode - this address * will be used in BREAK code if there is no RECOVER clause */ JumpHere( $2 ); $$ = SequenceEnd(); Line(); } RecoverSeq { /* Replace END address with RECOVER address in * HB_P_SEQBEGIN opcode if there is RECOVER clause */ if( $7 ) JumpThere( $2, $7-( _bLineNumbers ? 3 : 0 ) ); } END { /* Fix END address * There is no line number after HB_P_SEQEND in case no * RECOVER clause is used */ JumpThere( $6, functions.pLast->lPCodePos-((_bLineNumbers && !$7)?3:0) ); if( $7 ) /* only if there is RECOVER clause */ LineDebug(); else --_wSeqCounter; /* RECOVER is also considered as end of sequence */ SequenceFinish( $2, $5 ); functions.pLast->bFlags &= ~ FUN_WITH_RETURN; } ; RecoverSeq : /* no recover */ { $$ = 0; } | RecoverEmpty Crlf { $$ = $1; Line(); } EmptyStats | RecoverUsing Crlf { $$ = $1; Line(); } EmptyStats ; RecoverEmpty : RECOVER { $$ = functions.pLast->lPCodePos; --_wSeqCounter; GenPCode1( HB_P_SEQRECOVER ); GenPopPCode(); } ; RecoverUsing : RECOVER USING IDENTIFIER { $$ = functions.pLast->lPCodePos; --_wSeqCounter; GenPCode1( HB_P_SEQRECOVER ); PopId( $3 ); } ; /* NOTE: In Clipper all variables used in DO .. WITH are passed by reference * however if they are part of an expression then they are passed by value * for example: * DO .. WITH ++variable * will pass the value of variable not a reference */ DoProc : DO IDENTIFIER { PushSymbol( $2, 1 ); PushNil(); Do( 0 ); } | DO IDENTIFIER { PushSymbol( $2, 1 ); PushNil(); } WITH DoArgList { Do( $5 ); } | WHILE { PushSymbol( yy_strdup("WHILE"), 1 ); PushNil(); } WITH DoArgList { Do( $4 ); } ; DoArgList : ',' { PushNil(); PushNil(); $$ = 2; } | DoExpression { $$ = 1; } | DoArgList ',' { PushNil(); $$++; } | DoArgList ',' DoExpression { $$++; } | ',' { PushNil(); } DoExpression { $$ = 2; } ; DoExpression: IDENTIFIER { PushIdByRef( $1 ); } | NumExpression { } | ConExpression { } | DynExpression { } | PareExpList { } ; Crlf : '\n' { ++iLine; } | ';' { _bDontGenLineNum = TRUE; } ; %% void yyerror( char * s ) { GenError( _szCErrors, 'E', ERR_YACC, s, NULL ); } void * GenElseIf( void * pFirst, ULONG ulOffset ) { PELSEIF pElseIf = ( PELSEIF ) hb_xgrab( sizeof( _ELSEIF ) ), pLast; pElseIf->ulOffset = ulOffset; pElseIf->pNext = 0; if( ! pFirst ) pFirst = pElseIf; else { pLast = ( PELSEIF ) pFirst; while( pLast->pNext ) pLast = pLast->pNext; pLast->pNext = pElseIf; } return pFirst; } void GenError( char* _szErrors[], char cPrefix, int iError, char * szError1, char * szError2 ) { if( files.pLast != NULL && files.pLast->szFileName != NULL ) printf( "\r%s(%i) ", files.pLast->szFileName, iLine ); printf( "Error %c%04i ", cPrefix, iError ); printf( _szErrors[ iError - 1 ], szError1, szError2 ); printf( "\n" ); exit( EXIT_FAILURE ); } void GenWarning( char* _szWarnings[], char cPrefix, int iWarning, char * szWarning1, char * szWarning2) { if( _iWarnings ) { char *szText = _szWarnings[ iWarning - 1 ]; if( (szText[ 0 ] - '0') <= _iWarnings ) { printf( "\r%s(%i) ", files.pLast->szFileName, iLine ); printf( "Warning %c%04i ", cPrefix, iWarning ); printf( szText + 1, szWarning1, szWarning2 ); printf( "\n" ); _bAnyWarning = TRUE; /* report warnings at exit */ } } } void EXTERNAL_LINKAGE close_on_exit( void ) { PFILE pFile = files.pLast; while( pFile ) { /* printf( "\nClosing file: %s\n", pFile->szFileName ); */ fclose( pFile->handle ); pFile = ( PFILE ) pFile->pPrev; } } int harbour_main( int argc, char * argv[] ) { int iStatus = 0; int iArg; BOOL bSkipGen; /* Check for the nologo switch /q0 before everything else. */ for( iArg = 1; iArg < argc; iArg++ ) { if( SwitchCmp( argv[ iArg ], "Q0" ) ) _bLogo = FALSE; else if( SwitchCmp( argv[ iArg ], "CREDITS" ) || SwitchCmp( argv[ iArg ], "CREDIT" ) || SwitchCmp( argv[ iArg ], "CREDI" ) || SwitchCmp( argv[ iArg ], "CRED" ) ) _bCredits = TRUE; } if( _bLogo ) { printf( "Harbour Compiler, Build %i%s (%04d.%02d.%02d)\n", hb_build, hb_revision, hb_year, hb_month, hb_day ); printf( "Copyright 1999, http://www.harbour-project.org\n" ); } if( _bCredits ) { PrintCredits(); return iStatus; } if( argc > 1 ) { char szFileName[ _POSIX_PATH_MAX ]; /* filename to parse */ char szPpoName[ _POSIX_PATH_MAX ]; _pFileName = NULL; _pOutPath = NULL; Hbpp_init(); /* Initialization of preprocessor arrays */ /* Command line options */ for( iArg = 1; iArg < argc; iArg++ ) { if( IS_OPT_SEP( argv[ iArg ][ 0 ] ) ) { switch( argv[ iArg ][ 1 ] ) { case '1': if( argv[ iArg ][ 2 ] == '0' ) _bRestrictSymbolLength = TRUE; break; case 'a': case 'A': /* variables declared by PRIVATE and PUBLIC statement are * automatically assumed as MEMVAR */ _bAutoMemvarAssume = TRUE; break; case 'b': case 'B': _bDebugInfo = TRUE; _bLineNumbers = TRUE; break; case 'd': case 'D': /* defines a Lex #define from the command line */ { unsigned int i = 0; char * szDefText = yy_strdup( argv[ iArg ] + 2 ); while( i < strlen( szDefText ) && szDefText[ i ] != '=' ) i++; if( szDefText[ i ] != '=' ) AddDefine( szDefText, 0 ); else { szDefText[ i ] = '\0'; AddDefine( szDefText, szDefText + i + 1 ); } free( szDefText ); } break; case 'e': case 'E': if( argv[ iArg ][ 2 ] == 's' || argv[ iArg ][ 2 ] == 'S' ) { switch( argv[ iArg ][ 3 ] ) { case '\0': case '0': _iExitLevel = HB_EXITLEVEL_DEFAULT; break; case '1': _iExitLevel = HB_EXITLEVEL_SETEXIT; break; case '2': _iExitLevel = HB_EXITLEVEL_DELTARGET; break; default: GenError( _szCErrors, 'E', ERR_BADOPTION, argv[ iArg ], NULL ); } } else GenError( _szCErrors, 'E', ERR_BADOPTION, argv[ iArg ], NULL ); break; case 'g': case 'G': switch( argv[ iArg ][ 2 ] ) { case 'c': case 'C': _iLanguage = LANG_C; switch( argv[ iArg ][ 3 ] ) { case '\0': case '1': _bGenCVerbose = TRUE; break; case '0': _bGenCVerbose = FALSE; break; default: GenError( _szCErrors, 'E', ERR_BADOPTION, argv[ iArg ], NULL ); } break; case 'f': case 'F': _iLanguage = LANG_OBJ32; break; case 'j': case 'J': _iLanguage = LANG_JAVA; break; case 'p': case 'P': _iLanguage = LANG_PASCAL; break; case 'r': case 'R': _iLanguage = LANG_RESOURCES; break; case 'h': case 'H': _iLanguage = LANG_PORT_OBJ; break; default: printf( "\nUnsupported output language option\n" ); exit( EXIT_FAILURE ); } break; case 'i': case 'I': { char * pPath; char * pDelim; char * szInclude; pPath = szInclude = yy_strdup( argv[ iArg ] + 2 ); while( ( pDelim = strchr( pPath, OS_PATH_LIST_SEPARATOR ) ) != NULL ) { * pDelim = '\0'; AddSearchPath( pPath, &_pIncludePath ); pPath = pDelim + 1; } AddSearchPath( pPath, &_pIncludePath ); } break; case 'l': case 'L': _bLineNumbers = FALSE; break; case 'm': case 'M': /* TODO: Implement this switch */ printf( "Not yet supported command line option: %s\n", argv[ iArg ] ); break; case 'n': case 'N': _bStartProc = FALSE; break; case 'o': case 'O': _pOutPath = hb_fsFNameSplit( argv[ iArg ] + 2 ); break; /* Added for preprocessor needs */ case 'p': case 'P': _bPPO = TRUE; break; case 'q': case 'Q': _bQuiet = TRUE; break; case 'r': case 'R': /* TODO: Implement this switch */ printf( "Not yet supported command line option: %s\n", argv[ iArg ] ); break; case 's': case 'S': _bSyntaxCheckOnly = TRUE; break; case 't': case 'T': /* TODO: Implement this switch */ printf( "Not yet supported command line option: %s\n", argv[ iArg ] ); break; case 'u': case 'U': /* TODO: Implement this switch */ printf( "Not yet supported command line option: %s\n", argv[ iArg ] ); break; case 'v': case 'V': /* All undeclared variables are assumed MEMVAR variables */ _bForceMemvars = TRUE; break; case 'w': case 'W': _iWarnings = 1; if( argv[ iArg ][ 2 ] ) { /*there is -w<0,1,2,3> probably */ _iWarnings = argv[ iArg ][ 2 ] - '0'; if( _iWarnings < 0 || _iWarnings > 3 ) GenError( _szCErrors, 'E', ERR_BADOPTION, argv[ iArg ], NULL ); } break; case 'x': case 'X': { if( strlen( argv[ iArg ] + 2 ) == 0 ) sprintf( _szPrefix, "%08lX_", PackDateTime() ); else { strncpy( _szPrefix, argv[ iArg ] + 2, 16 ); _szPrefix[ 16 ] = '\0'; strcat( _szPrefix, "_" ); } } break; #ifdef YYDEBUG case 'y': case 'Y': yydebug = TRUE; break; #endif case 'z': case 'Z': _bShortCuts = FALSE; break; default: GenError( _szCErrors, 'E', ERR_BADOPTION, argv[ iArg ], NULL ); break; } } else if( argv[ iArg ][ 0 ] == '@' ) /* TODO: Implement this switch */ printf( "Not yet supported command line option: %s\n", argv[ iArg ] ); else { if( _pFileName ) GenError( _szCErrors, 'E', ERR_BADPARAM, argv[ iArg ], NULL ); else { _pFileName = hb_fsFNameSplit( argv[ iArg ] ); if( ! _pFileName->szName ) GenError( _szCErrors, 'E', ERR_BADFILENAME, argv[ iArg ], NULL ); } } } if( _pFileName ) { if( !_pFileName->szExtension ) _pFileName->szExtension = ".prg"; hb_fsFNameMerge( szFileName, _pFileName ); if( _bPPO ) { _pFileName->szExtension = ".ppo"; hb_fsFNameMerge( szPpoName, _pFileName ); yyppo = fopen( szPpoName, "w" ); if( ! yyppo ) { GenError( _szCErrors, 'E', ERR_CREATE_PPO, szPpoName, NULL ); return iStatus; } } } else { PrintUsage( argv[ 0 ] ); return iStatus; } files.iFiles = 0; /* initialize support variables */ files.pLast = NULL; functions.iCount = 0; functions.pFirst = NULL; functions.pLast = NULL; funcalls.iCount = 0; funcalls.pFirst = NULL; funcalls.pLast = NULL; symbols.iCount = 0; symbols.pFirst = NULL; symbols.pLast = NULL; _pInitFunc = NULL; _bAnyWarning = FALSE; atexit( close_on_exit ); if( Include( szFileName, NULL ) ) { char * szInclude = getenv( "INCLUDE" ); if( szInclude ) { char * pPath; char * pDelim; pPath = szInclude = yy_strdup( szInclude ); while( ( pDelim = strchr( pPath, OS_PATH_LIST_SEPARATOR ) ) != NULL ) { *pDelim = '\0'; AddSearchPath( pPath, &_pIncludePath ); pPath = pDelim + 1; } AddSearchPath( pPath, &_pIncludePath ); } /* Generate the starting procedure frame */ if( _bStartProc ) FunDef( yy_strupr( yy_strdup( _pFileName->szName ) ), FS_PUBLIC, FUN_PROCEDURE ); else /* Don't pass the name of module if the code for starting procedure * will be not generated. The name cannot be placed as first symbol * because this symbol can be used as function call or memvar's name. */ FunDef( yy_strupr( yy_strdup( "" ) ), FS_PUBLIC, FUN_PROCEDURE ); yyparse(); GenExterns(); /* generates EXTERN symbols names */ fclose( yyin ); files.pLast = NULL; bSkipGen = FALSE; if( _bAnyWarning ) { if( _iExitLevel == HB_EXITLEVEL_SETEXIT ) iStatus = 1; if( _iExitLevel == HB_EXITLEVEL_DELTARGET ) { iStatus = 1; bSkipGen = TRUE; printf( "\nNo code generated\n" ); } } if( ! _bSyntaxCheckOnly && ! bSkipGen ) { if( _pInitFunc ) { PCOMSYMBOL pSym; /* Fix the number of static variables */ _pInitFunc->pCode[ 3 ] = HB_LOBYTE( _iStatics ); _pInitFunc->pCode[ 4 ] = HB_HIBYTE( _iStatics ); _pInitFunc->iStaticsBase = _iStatics; pSym = AddSymbol( _pInitFunc->szName, NULL ); pSym->cScope |= _pInitFunc->cScope; functions.pLast->pNext = _pInitFunc; ++functions.iCount; } _pFileName->szPath = NULL; _pFileName->szExtension = NULL; /* we create a the output file */ if( _pOutPath ) { if( _pOutPath->szPath ) _pFileName->szPath = _pOutPath->szPath; if( _pOutPath->szName ) { _pFileName->szName = _pOutPath->szName; if( _pOutPath->szExtension ) _pFileName->szExtension = _pOutPath->szExtension; } } switch( _iLanguage ) { case LANG_C: GenCCode( _pFileName ); break; case LANG_OBJ32: #ifdef HARBOUR_OBJ_GENERATION GenObj32( _pFileName ); #endif break; case LANG_JAVA: GenJava( _pFileName ); break; case LANG_PASCAL: GenPascal( _pFileName ); break; case LANG_RESOURCES: GenRC( _pFileName ); break; case LANG_PORT_OBJ: GenPortObj( _pFileName ); break; } } if( _bPPO ) fclose( yyppo ); } else { printf( "Cannot open input file: %s\n", szFileName ); iStatus = 1; } hb_xfree( ( void * ) _pFileName ); if( _pOutPath ) hb_xfree( _pOutPath ); } else PrintUsage( argv[ 0 ] ); return iStatus; } /* * Prints available options */ void PrintUsage( char * szSelf ) { printf( "\nSyntax: %s [options]" "\n" "\nOptions: /a automatic memvar declaration" "\n /b debug info" "\n /d[=] #define " "\n /es[] set exit severity" "\n /g output type generated is (see below)" "\n /gc[] output type: C source (.c) (default)" "\n : 0=without comments, 1=normal (default)" #ifdef HARBOUR_OBJ_GENERATION "\n /gf output type: Windows/DOS OBJ32 (.obj)" #endif "\n /gh output type: Harbour Portable Object (.hrb)" "\n /gj output type: Java source (.java)" "\n /gp output type: Pascal source (.pas)" "\n /gr output type: Windows resource (.rc)" "\n /i add #include file search path" "\n /l suppress line number information" /* TODO: "\n /m compile module only" */ "\n /n no implicit starting procedure" "\n /o object file drive and/or path" "\n /p generate pre-processed output (.ppo) file" "\n /q quiet" "\n /q0 quiet and don't display program header" /* TODO: "\n /r[] request linker to search (or none)" */ "\n /s syntax check only" /* TODO: "\n /t path for temp file creation" */ /* TODO: "\n /u[] use command def set in (or none)" */ "\n /v variables are assumed M->" "\n /w[] set warning level number (0..3, default 1)" "\n /x[] set symbol init function name prefix (for .c only)" #ifdef YYDEBUG "\n /y trace lex & yacc activity" #endif "\n /z suppress shortcutting (.and. & .or.)" "\n /10 restrict symbol length to 10 characters" /* TODO: "\n @ compile list of modules in " */ "\n" , szSelf ); } /* * Prints credits */ void PrintCredits( void ) { printf( "\nCredits: The Harbour Team at www.harbour-project.com" ); } /* * Function that adds specified path to the list of pathnames to search */ void AddSearchPath( char * szPath, PATHNAMES * * pSearchList ) { PATHNAMES * pPath = *pSearchList; if( pPath ) { while( pPath->pNext ) pPath = pPath->pNext; pPath->pNext = ( PATHNAMES * ) hb_xgrab( sizeof( PATHNAMES ) ); pPath = pPath->pNext; } else { *pSearchList = pPath = ( PATHNAMES * ) hb_xgrab( sizeof( PATHNAMES ) ); } pPath->pNext = NULL; pPath->szPath = szPath; } /* * This function adds the name of called function into the list * as they have to be placed on the symbol table later than the first * public symbol */ PFUNCTION AddFunCall( char * szFunctionName ) { PFUNCTION pFunc = FunctionNew( szFunctionName, 0 ); if( ! funcalls.iCount ) { funcalls.pFirst = pFunc; funcalls.pLast = pFunc; } else { ( ( PFUNCTION ) funcalls.pLast )->pNext = pFunc; funcalls.pLast = pFunc; } funcalls.iCount++; return pFunc; } /* * This function adds the name of external symbol into the list of externals * as they have to be placed on the symbol table later than the first * public symbol */ void AddExtern( char * szExternName ) /* defines a new extern name */ { PEXTERN pExtern = ( PEXTERN ) hb_xgrab( sizeof( _EXTERN ) ), pLast; pExtern->szName = szExternName; pExtern->pNext = NULL; if( pExterns == NULL ) pExterns = pExtern; else { pLast = pExterns; while( pLast->pNext ) pLast = pLast->pNext; pLast->pNext = pExtern; } } void SetVarMacro( void ) { if( iVarScope == VS_PRIVATE ) PushSymbol( yy_strdup( "__MVPRIVATE" ), 1); else PushSymbol( yy_strdup( "__MVPUBLIC" ), 1); PushNil(); /* Mark Position of the begining ogf the interpreted macro pcode. */ _ulBookMark = functions.pLast->lPCodePos ; } void FunMacroAssign( void ) { ULONG _ulBufferSize, i = 0; BYTE * _pCodeBuffer; /* We'll need the interpreted value of the macro (recently pushed) again for use by __MVPUT() */ _ulBufferSize = functions.pLast->lPCodePos - _ulBookMark ; _pCodeBuffer = ( BYTE * ) hb_xgrab( _ulBufferSize + 1); while( i < _ulBufferSize ) { _pCodeBuffer[ i ] = functions.pLast->pCode[ _ulBookMark + i ]; i++; } Do( 1 ); if( functions.pLast->lPCodeSize - functions.pLast->lPCodePos <= _ulBufferSize ) functions.pLast->pCode = ( BYTE * ) hb_xrealloc( functions.pLast->pCode, functions.pLast->lPCodeSize += ( _ulBufferSize + PCODE_CHUNK ) ); PushSymbol( yy_strdup( "__MVPUT" ), 1); PushNil(); i = 0; while( i < _ulBufferSize ) { functions.pLast->pCode[ functions.pLast->lPCodePos++ ] = _pCodeBuffer[ i ]; i++ ; } hb_xfree( ( void * ) _pCodeBuffer ); } void AddVar( char * szVarName ) { PVAR pVar, pLastVar; PFUNCTION pFunc = functions.pLast; if( ! _bStartProc && functions.iCount <= 1 && iVarScope == VS_LOCAL ) { /* Variable declaration is outside of function/procedure body. In this case only STATIC and PARAMETERS variables are allowed. */ --iLine; GenError( _szCErrors, 'E', ERR_OUTSIDE, NULL, NULL ); } /* check if we are declaring local/static variable after some * executable statements * Note: FIELD and MEMVAR are executable statements */ if( ( functions.pLast->bFlags & FUN_STATEMENTS ) && !( iVarScope == VS_FIELD || ( iVarScope & VS_MEMVAR ) ) ) { --iLine; GenError( _szCErrors, 'E', ERR_FOLLOWS_EXEC, ( iVarScope == VS_LOCAL ? "LOCAL" : "STATIC" ), NULL ); } /* When static variable is added then functions.pLast points to function * that will initialise variables. The function where variable is being * defined is stored in pOwner member. */ if( iVarScope == VS_STATIC ) { pFunc = pFunc->pOwner; /* Check if an illegal action was invoked during a static variable * value initialization */ if( _pInitFunc->bFlags & FUN_ILLEGAL_INIT ) GenError( _szCErrors, 'E', ERR_ILLEGAL_INIT, szVarName, pFunc->szName ); } /* Check if a declaration of duplicated variable name is requested */ if( pFunc->szName ) { /* variable defined in a function/procedure */ CheckDuplVars( pFunc->pFields, szVarName, iVarScope ); CheckDuplVars( pFunc->pStatics, szVarName, iVarScope ); if( !( iVarScope == VS_PRIVATE || iVarScope == VS_PUBLIC ) ) CheckDuplVars( pFunc->pMemvars, szVarName, iVarScope ); } else /* variable defined in a codeblock */ iVarScope = VS_PARAMETER; CheckDuplVars( pFunc->pLocals, szVarName, iVarScope ); pVar = ( PVAR ) hb_xgrab( sizeof( VAR ) ); pVar->szName = szVarName; pVar->szAlias = NULL; pVar->cType = _cVarType; pVar->iUsed = 0; pVar->pNext = NULL; if( iVarScope & VS_MEMVAR ) { PCOMSYMBOL pSym; USHORT wPos; if( _bAutoMemvarAssume || iVarScope == VS_MEMVAR ) { /** add this variable to the list of MEMVAR variables */ if( ! pFunc->pMemvars ) pFunc->pMemvars = pVar; else { pLastVar = pFunc->pMemvars; while( pLastVar->pNext ) pLastVar = pLastVar->pNext; pLastVar->pNext = pVar; } } switch( iVarScope ) { case VS_MEMVAR: /* variable declared in MEMVAR statement */ break; case ( VS_PARAMETER | VS_PRIVATE ): { BOOL bNewParameter = FALSE; if( ++functions.pLast->wParamNum > functions.pLast->wParamCount ) { functions.pLast->wParamCount = functions.pLast->wParamNum; bNewParameter = TRUE; } pSym = GetSymbol( szVarName, &wPos ); /* check if symbol exists already */ if( ! pSym ) pSym = AddSymbol( yy_strdup( szVarName ), &wPos ); pSym->cScope |= VS_MEMVAR; GenPCode3( HB_P_PARAMETER, HB_LOBYTE( wPos ), HB_HIBYTE( wPos ) ); GenPCode1( HB_LOBYTE( functions.pLast->wParamNum ) ); /* Add this variable to the local variables list - this will * allow to use the correct positions for real local variables. * The name of variable have to be hidden because we should * not find this name on the local variables list. * We have to use the new structure because it is used in * memvars list already. */ if( bNewParameter ) { pVar = ( PVAR ) hb_xgrab( sizeof( VAR ) ); pVar->szName = yy_strdup( szVarName ); pVar->szAlias = NULL; pVar->cType = _cVarType; pVar->iUsed = 0; pVar->pNext = NULL; pVar->szName[ 0 ] ='!'; if( ! pFunc->pLocals ) pFunc->pLocals = pVar; else { pLastVar = pFunc->pLocals; while( pLastVar->pNext ) pLastVar = pLastVar->pNext; pLastVar->pNext = pVar; } } } break; case VS_PRIVATE: { PushSymbol( yy_strdup( "__MVPRIVATE" ), 1); PushNil(); PushSymbol( yy_strdup( szVarName ), 0 ); Do( 1 ); pSym = GetSymbol( szVarName, NULL ); pSym->cScope |= VS_MEMVAR; } break; case VS_PUBLIC: { PushSymbol( yy_strdup( "__MVPUBLIC" ), 1); PushNil(); PushSymbol( yy_strdup( szVarName ), 0 ); Do( 1 ); pSym = GetSymbol( szVarName, NULL ); pSym->cScope |= VS_MEMVAR; } break; } } else { switch( iVarScope ) { case VS_LOCAL: case VS_PARAMETER: { USHORT wLocal = 1; if( ! pFunc->pLocals ) pFunc->pLocals = pVar; else { pLastVar = pFunc->pLocals; while( pLastVar->pNext ) { pLastVar = pLastVar->pNext; wLocal++; } pLastVar->pNext = pVar; } if( iVarScope == VS_PARAMETER ) { ++functions.pLast->wParamCount; functions.pLast->bFlags |= FUN_USES_LOCAL_PARAMS; } if( _bDebugInfo ) { GenPCode3( HB_P_LOCALNAME, HB_LOBYTE( wLocal ), HB_HIBYTE( wLocal ) ); GenPCodeN( ( BYTE * )szVarName, strlen( szVarName ) ); GenPCode1( 0 ); } } break; case VS_STATIC: if( ! pFunc->pStatics ) pFunc->pStatics = pVar; else { pLastVar = pFunc->pStatics; while( pLastVar->pNext ) pLastVar = pLastVar->pNext; pLastVar->pNext = pVar; } break; case VS_FIELD: if( ! pFunc->pFields ) pFunc->pFields = pVar; else { pLastVar = pFunc->pFields; while( pLastVar->pNext ) pLastVar = pLastVar->pNext; pLastVar->pNext = pVar; } break; } } } PCOMSYMBOL AddSymbol( char * szSymbolName, USHORT * pwPos ) { PCOMSYMBOL pSym = ( PCOMSYMBOL ) hb_xgrab( sizeof( COMSYMBOL ) ); pSym->szName = szSymbolName; pSym->cScope = 0; pSym->cType = _cVarType; pSym->pNext = NULL; if( ! symbols.iCount ) { symbols.pFirst = pSym; symbols.pLast = pSym; } else { ( ( PCOMSYMBOL ) symbols.pLast )->pNext = pSym; symbols.pLast = pSym; } symbols.iCount++; if( pwPos ) *pwPos = symbols.iCount; /*if( _cVarType != ' ') printf("\nDeclared %s as type %c at symbol %i\n", szSymbolName, _cVarType, symbols.iCount );*/ return pSym; } /* Adds new alias to the alias stack */ void AliasAdd( ALIASID_PTR pAlias ) { pAlias->pPrev = pAliasId; pAliasId = pAlias; } /* Restores previously selected alias */ void AliasRemove( void ) { ALIASID_PTR pAlias = pAliasId; pAliasId = pAliasId->pPrev; hb_xfree( pAlias ); } /* Adds an integer workarea number into alias stack */ void AliasAddInt( int iWorkarea ) { ALIASID_PTR pAlias = ( ALIASID_PTR ) hb_xgrab( sizeof( ALIASID ) ); pAlias->type = ALIAS_NUMBER; pAlias->alias.iAlias = iWorkarea; AliasAdd( pAlias ); } /* Adds an expression into alias stack */ void AliasAddExp( void ) { ALIASID_PTR pAlias = ( ALIASID_PTR ) hb_xgrab( sizeof( ALIASID ) ); pAlias->type = ALIAS_EVAL; AliasAdd( pAlias ); } /* Adds an alias name into alias stack */ void AliasAddStr( char * szAlias ) { ALIASID_PTR pAlias = ( ALIASID_PTR ) hb_xgrab( sizeof( ALIASID ) ); pAlias->type = ALIAS_NAME; pAlias->alias.szAlias = szAlias; AliasAdd( pAlias ); } /* Generates pcodes to store the current workarea number */ void AliasPush( void ) { GenPCode1( HB_P_PUSHALIAS ); } /* Generates pcodes to select the workarea number using current value * from the eval stack */ void AliasPop( void ) { GenPCode1( HB_P_POPALIAS ); } /* Generates pcodes to swap two last items from the eval stack. * Last item (after swaping) is next popped as current workarea */ void AliasSwap( void ) { GenPCode1( HB_P_SWAPALIAS ); } /* Generates pcodes to access an array element */ static void ArrayAt( void ) { GenPCode1( HB_P_ARRAYAT ); ValTypeCheck( 'N', WARN_NUMERIC_TYPE, WARN_NUMERIC_SUSPECT ); ValTypePop( 1 ); } /* Generates pcodes to assign a value to an array element */ static void ArrayPut( void ) { GenPCode1( HB_P_ARRAYPUT ); /* Pop a value and a last array index * - rest of array indexes were popped in ArrayAt() */ ValTypePop( 2 ); } int Include( char * szFileName, PATHNAMES * pSearch ) { PFILE pFile; yyin = fopen( szFileName, "r" ); if( ! yyin ) { if( pSearch ) { PHB_FNAME pFileName = hb_fsFNameSplit( szFileName ); char szFName[ _POSIX_PATH_MAX ]; /* filename to parse */ pFileName->szName = szFileName; pFileName->szExtension = NULL; while( pSearch && !yyin ) { pFileName->szPath = pSearch->szPath; hb_fsFNameMerge( szFName, pFileName ); yyin = fopen( szFName, "r" ); if( ! yyin ) { pSearch = pSearch->pNext; if( ! pSearch ) return 0; } } hb_xfree( ( void * ) pFileName ); } else return 0; } if( ! _bQuiet ) printf( "\nCompiling \'%s\'\n", szFileName ); pFile = ( PFILE ) hb_xgrab( sizeof( _FILE ) ); pFile->handle = yyin; pFile->szFileName = szFileName; pFile->pPrev = NULL; if( ! files.iFiles ) files.pLast = pFile; else { files.pLast->iLine = iLine; iLine = 1; pFile->pPrev = files.pLast; files.pLast = pFile; } #ifdef __cplusplus yy_switch_to_buffer( ( YY_BUFFER_STATE ) ( pFile->pBuffer = yy_create_buffer( yyin, 8192 * 2 ) ) ); #else yy_switch_to_buffer( pFile->pBuffer = yy_create_buffer( yyin, 8192 * 2 ) ); #endif files.iFiles++; return 1; } int yywrap( void ) /* handles the EOF of the currently processed file */ { void * pLast; if( files.iFiles == 1 ) return 1; /* we have reached the main EOF */ else { pLast = files.pLast; fclose( files.pLast->handle ); files.pLast = ( PFILE ) ( ( PFILE ) files.pLast )->pPrev; iLine = files.pLast->iLine; if( ! _bQuiet ) printf( "\nCompiling %s\n", files.pLast->szFileName ); #ifdef __cplusplus yy_delete_buffer( ( YY_BUFFER_STATE ) ( ( PFILE ) pLast )->pBuffer ); #else yy_delete_buffer( ( ( PFILE ) pLast )->pBuffer ); #endif free( pLast ); files.iFiles--; yyin = files.pLast->handle; #ifdef __cplusplus yy_switch_to_buffer( ( YY_BUFFER_STATE ) files.pLast->pBuffer ); #else yy_switch_to_buffer( files.pLast->pBuffer ); #endif return 0; /* we close the currently include file and continue */ } } void Duplicate( void ) { GenPCode1( HB_P_DUPLICATE ); ValTypePush( ValTypeGet() ); } void DupPCode( ULONG ulStart ) /* duplicates the current generated pcode from an offset */ { ULONG w, wEnd = functions.pLast->lPCodePos - ulStart; for( w = 0; w < wEnd; w++ ) GenPCode1( functions.pLast->pCode[ ulStart + w ] ); } /* * Starts a new expression in the parenthesized epressions list */ void ExpListPush( void ) { EXPLIST_PTR pExp = ( EXPLIST_PTR ) hb_xgrab( sizeof( EXPLIST ) ); pExp->pNext = pExp->pPrev = NULL; /* Store the previous state on the stack */ if( _pExpList ) { _pExpList->pNext = pExp; pExp->pPrev = _pExpList; /* save currently used pcode buffer */ _pExpList->exprSize = functions.pLast->lPCodePos; _pExpList->exprPCode = functions.pLast->pCode; } _pExpList = pExp; /* store current pcode buffer */ pExp->prevPCode = functions.pLast->pCode; pExp->prevSize = functions.pLast->lPCodeSize; pExp->prevPos = functions.pLast->lPCodePos; /* and create the new one */ functions.pLast->pCode = ( BYTE * ) hb_xgrab( PCODE_CHUNK ); functions.pLast->lPCodeSize = PCODE_CHUNK; functions.pLast->lPCodePos = 0; pExp->exprPCode = functions.pLast->pCode; } /* * Pops specified number of expressions from the stack */ void ExpListPop( int iExpCount ) { EXPLIST_PTR pExp, pDel; /* save currently used pcode buffer */ _pExpList->exprSize = functions.pLast->lPCodePos; _pExpList->exprPCode = functions.pLast->pCode; /* find the first expression in the list */ while( --iExpCount ) _pExpList = _pExpList->pPrev; /* return to the original pcode buffer */ functions.pLast->pCode = _pExpList->prevPCode; functions.pLast->lPCodeSize = _pExpList->prevSize; functions.pLast->lPCodePos = _pExpList->prevPos; pExp = _pExpList; if( _pExpList->pPrev ) { _pExpList = _pExpList->pPrev; _pExpList->pNext = NULL; } else _pExpList = NULL; while( pExp ) { if( pExp->exprSize ) { GenPCodeN( pExp->exprPCode, pExp->exprSize ); if( pExp->pNext ) GenPopPCode(); } else { /* exprN, , exprN1 * in this context empty expression is not allowed * * NOTE: * We don't have to generate this error - it is safe to continue * pcode generation - in this case an empty expression will not * generate any opcode */ GenError( _szCErrors, 'E', ERR_SYNTAX, ")", NULL ); } hb_xfree( pExp->exprPCode ); pDel = pExp; pExp = pExp->pNext; hb_xfree( pDel ); } } /* * Function generates passed pcode for passed database field */ void FieldPCode( BYTE bPCode, char * szVarName ) { USHORT wVar; PCOMSYMBOL pVar; pVar = GetSymbol( szVarName, &wVar ); if( ! pVar ) pVar = AddSymbol( szVarName, &wVar ); pVar->cScope |= VS_MEMVAR; GenPCode3( bPCode, HB_LOBYTE( wVar ), HB_HIBYTE( wVar ) ); } /* * This function creates and initialises the _FUNC structure */ PFUNCTION FunctionNew( char * szName, HB_SYMBOLSCOPE cScope ) { PFUNCTION pFunc; pFunc = ( PFUNCTION ) hb_xgrab( sizeof( _FUNC ) ); pFunc->szName = szName; pFunc->cScope = cScope; pFunc->pLocals = NULL; pFunc->pStatics = NULL; pFunc->pFields = NULL; pFunc->pMemvars = NULL; pFunc->pCode = NULL; pFunc->lPCodeSize = 0; pFunc->lPCodePos = 0; pFunc->lLastPushPos = -1; pFunc->pNext = NULL; pFunc->wParamCount = 0; pFunc->wParamNum = 0; pFunc->iStaticsBase = _iStatics; pFunc->pOwner = NULL; pFunc->bFlags = 0; return pFunc; } /* * Stores a Clipper defined function/procedure * szFunName - name of a function * cScope - scope of a function * iType - FUN_PROCEDURE if a procedure or 0 */ void FunDef( char * szFunName, HB_SYMBOLSCOPE cScope, int iType ) { PCOMSYMBOL pSym; PFUNCTION pFunc; char * szFunction; FixReturns(); /* fix all previous function returns offsets */ pFunc = GetFunction( szFunName ); if( pFunc ) { /* The name of a function/procedure is already defined */ if( ( pFunc != functions.pFirst ) || _bStartProc ) /* it is not a starting procedure that was automatically created */ GenError( _szCErrors, 'E', ERR_FUNC_DUPL, szFunName, NULL ); } szFunction = RESERVED_FUNC( szFunName ); if( szFunction && !( functions.iCount==0 && !_bStartProc ) ) { /* We are ignoring it when it is the name of PRG file and we are * not creating implicit starting procedure */ GenError( _szCErrors, 'E', ERR_FUNC_RESERVED, szFunction, szFunName ); } iFunctions++; pSym = GetSymbol( szFunName, NULL ); if( ! pSym ) /* there is not a symbol on the symbol table for this function name */ pSym = AddSymbol( szFunName, NULL ); if( cScope != FS_PUBLIC ) /* pSym->cScope = FS_PUBLIC; */ /* else */ pSym->cScope |= cScope; /* we may have a non public function and a object message */ pFunc = FunctionNew( szFunName, cScope ); pFunc->bFlags |= iType; if( functions.iCount == 0 ) { functions.pFirst = pFunc; functions.pLast = pFunc; } else { functions.pLast->pNext = pFunc; functions.pLast = pFunc; } functions.iCount++; _ulLastLinePos = 0; /* optimization of line numbers opcode generation */ GenPCode3( HB_P_FRAME, 0, 0 ); /* frame for locals and parameters */ GenPCode3( HB_P_SFRAME, 0, 0 ); /* frame for statics variables */ if( _bDebugInfo ) { GenPCode1( HB_P_MODULENAME ); GenPCodeN( ( BYTE * )files.pLast->szFileName, strlen( files.pLast->szFileName ) ); GenPCode1( ':' ); GenPCodeN( ( BYTE * )szFunName, strlen( szFunName ) ); GenPCode1( 0 ); } } PFUNCTION KillFunction( PFUNCTION pFunc ) { PFUNCTION pNext = pFunc->pNext; PVAR pVar; while( pFunc->pLocals ) { pVar = pFunc->pLocals; pFunc->pLocals = pVar->pNext; hb_xfree( ( void * ) pVar->szName ); hb_xfree( ( void * ) pVar ); } while( pFunc->pStatics ) { pVar = pFunc->pStatics; pFunc->pStatics = pVar->pNext; hb_xfree( ( void * ) pVar->szName ); hb_xfree( ( void * ) pVar ); } while( pFunc->pFields ) { pVar = pFunc->pFields; pFunc->pFields = pVar->pNext; hb_xfree( ( void * ) pVar->szName ); if( pVar->szAlias ) { hb_xfree( ( void * ) pVar->szAlias ); } hb_xfree( ( void * ) pVar ); } while( pFunc->pMemvars ) { pVar = pFunc->pMemvars; pFunc->pMemvars = pVar->pNext; hb_xfree( ( void * ) pVar->szName ); if( pVar->szAlias ) { hb_xfree( ( void * ) pVar->szAlias ); } hb_xfree( ( void * ) pVar ); } hb_xfree( ( void * ) pFunc->pCode ); /* hb_xfree( ( void * ) pFunc->szName ); The name will be released in KillSymbol() */ hb_xfree( ( void * ) pFunc ); return pNext; } PCOMSYMBOL KillSymbol( PCOMSYMBOL pSym ) { PCOMSYMBOL pNext = pSym->pNext; hb_xfree( ( void * ) pSym->szName ); hb_xfree( ( void * ) pSym ); return pNext; } void GenBreak( void ) { PushSymbol( yy_strdup("BREAK"), 1 ); PushNil(); } void GenExterns( void ) /* generates the symbols for the EXTERN names */ { PEXTERN pDelete; if( _bDebugInfo ) AddExtern( yy_strdup( "__DBGENTRY" ) ); while( pExterns ) { if( GetSymbol( pExterns->szName, NULL ) ) { if( ! GetFuncall( pExterns->szName ) ) AddFunCall( pExterns->szName ); } else { AddSymbol( pExterns->szName, NULL ); AddFunCall( pExterns->szName ); } pDelete = pExterns; pExterns = pExterns->pNext; hb_xfree( ( void * ) pDelete ); } } /* This function generates pcodes for IIF( expr1, expr2, expr3 ) * or IF( expr1, expr2, expr3 ) * * NOTE: * 'IF' followed by parenthesized expression containing 3 expressions * is always interpreted as IF inlined - it is not possible to distinguish * it from IF( expr1, expr2, expr3 ); ENDIF syntax * (This behaviour is Clipper compatible) */ void GenIfInline( void ) { EXPLIST_PTR pExp, pDel; int iExpCount = 3; /* We are expecting 3 expressions here */ BOOL bGenPCode; /* save currently used pcode buffer */ _pExpList->exprSize = functions.pLast->lPCodePos; _pExpList->exprPCode = functions.pLast->pCode; /* find the first expression in the list */ while( --iExpCount ) _pExpList = _pExpList->pPrev; /* return to the original pcode buffer */ functions.pLast->pCode = _pExpList->prevPCode; functions.pLast->lPCodeSize = _pExpList->prevSize; functions.pLast->lPCodePos = _pExpList->prevPos; /* Update the pointer for nested or next expressions */ pExp = _pExpList; if( _pExpList->pPrev ) { _pExpList = _pExpList->pPrev; _pExpList->pNext = NULL; } else _pExpList = NULL; bGenPCode = TRUE; pDel = pExp; /* save it for later use */ /* pExp points now to pcode buffer for logical condition */ if( pExp->exprSize == 0 ) { /* The logical condition have to be specified. * If it is empty then generate the syntax error */ GenError( _szCErrors, 'E', ERR_SYNTAX, ",", NULL ); } else if( pExp->exprSize == 1 ) { /* one byte opcode for logical condition - check if it is TRUE or FALSE */ if( pExp->exprPCode[ 0 ] == HB_P_TRUE ) { /* move to the second expression */ pExp = pExp->pNext; if( pExp->exprSize ) GenPCodeN( pExp->exprPCode, pExp->exprSize ); else PushNil(); /* IIF have to return some value */ bGenPCode = FALSE; } else if( pExp->exprPCode[ 0 ] == HB_P_FALSE ) { /* move to the third expression */ pExp = pExp->pNext; pExp = pExp->pNext; if( pExp->exprSize ) GenPCodeN( pExp->exprPCode, pExp->exprSize ); else PushNil(); /* IIF have to return some value */ bGenPCode = FALSE; } } if( bGenPCode ) { /* generate pcodes for all expressions */ LONG lPosFalse, lPosEnd; GenPCodeN( pExp->exprPCode, pExp->exprSize ); lPosFalse = JumpFalse( 0 ); pExp = pExp->pNext; if( pExp->exprSize ) GenPCodeN( pExp->exprPCode, pExp->exprSize ); else PushNil(); /* IIF have to return some value */ lPosEnd = Jump( 0 ); JumpHere( lPosFalse ); pExp = pExp->pNext; if( pExp->exprSize ) GenPCodeN( pExp->exprPCode, pExp->exprSize ); else PushNil(); /* IIF have to return some value */ JumpHere( lPosEnd ); } while( pDel ) { pExp = pDel; pDel = pDel->pNext; hb_xfree( pExp->exprPCode ); hb_xfree( pExp ); } ValTypePop( 1 ); } PFUNCTION GetFuncall( char * szFunctionName ) /* returns a previously called defined function */ { PFUNCTION pFunc = funcalls.pFirst; while( pFunc ) { if( ! strcmp( pFunc->szName, szFunctionName ) ) return pFunc; else { if( pFunc->pNext ) pFunc = pFunc->pNext; else return NULL; } } return NULL; } PFUNCTION GetFunction( char * szFunctionName ) /* returns a previously defined function */ { PFUNCTION pFunc = functions.pFirst; while( pFunc ) { if( ! strcmp( pFunc->szName, szFunctionName ) ) return pFunc; else { if( pFunc->pNext ) pFunc = pFunc->pNext; else return NULL; } } return NULL; } PVAR GetVar( PVAR pVars, USHORT wOrder ) /* returns variable if defined or zero */ { USHORT w = 1; while( pVars->pNext && w++ < wOrder ) pVars = pVars->pNext; return pVars; } USHORT GetVarPos( PVAR pVars, char * szVarName ) /* returns the order + 1 of a variable if defined or zero */ { USHORT wVar = 1; while( pVars ) { if( pVars->szName && ! strcmp( pVars->szName, szVarName ) ) { /* TODO: This is not the best place to push the variable type * in some cases it will be called two times for the same variable */ ValTypePush( pVars->cType ); pVars->iUsed = 1; return wVar; } else { if( pVars->pNext ) { pVars = pVars->pNext; wVar++; } else return 0; } } return 0; } int GetLocalVarPos( char * szVarName ) /* returns the order + 1 of a variable if defined or zero */ { int iVar = 0; PFUNCTION pFunc = functions.pLast; if( pFunc->szName ) /* we are in a function/procedure -we don't need any tricks */ return GetVarPos( pFunc->pLocals, szVarName ); else { /* we are in a codeblock */ iVar = GetVarPos( pFunc->pLocals, szVarName ); if( iVar == 0 ) { /* this is not a current codeblock parameter * we have to check the list of nested codeblocks up to a function * where the codeblock is defined */ PFUNCTION pOutBlock = pFunc; /* the outermost codeblock */ pFunc = pFunc->pOwner; while( pFunc ) { iVar = GetVarPos( pFunc->pLocals, szVarName ); if( iVar ) { if( pFunc->pOwner ) { /* this variable is defined in a parent codeblock * It is not possible to access a parameter of a codeblock in which * the current codeblock is defined */ GenError( _szCErrors, 'E', ERR_OUTER_VAR, szVarName, NULL ); } else { /* We want to access a local variable defined in a function * that owns this codeblock. We cannot access this variable in * a normal way because at runtime the stack base will point * to local variables of EVAL function. * The codeblock cannot have static variables then we can * use this structure to store temporarily all referenced * local variables */ /* NOTE: The list of local variables defined in a function * and referenced in a codeblock will be stored in a outer * codeblock only. This makes sure that all variables will be * detached properly - the inner codeblock can be created * outside of a function where it was defined when the local * variables are not accessible. */ iVar = -GetVarPos( pOutBlock->pStatics, szVarName ); if( iVar == 0 ) { /* this variable was not referenced yet - add it to the list */ PVAR pVar; pVar = ( PVAR ) hb_xgrab( sizeof( VAR ) ); pVar->szName = szVarName; pVar->cType = ' '; pVar->iUsed = 0; pVar->pNext = NULL; /* Use negative order to signal that we are accessing a local * variable from a codeblock */ iVar = -1; /* first variable */ if( ! pOutBlock->pStatics ) pOutBlock->pStatics = pVar; else { PVAR pLastVar = pOutBlock->pStatics; --iVar; /* this will be at least second variable */ while( pLastVar->pNext ) { pLastVar = pLastVar->pNext; --iVar; } pLastVar->pNext = pVar; } } return iVar; } } pOutBlock = pFunc; pFunc = pFunc->pOwner; } } } return iVar; } /* * Gets position of passed static variables. * All static variables are hold in a single array at runtime then positions * are numbered for whole PRG module. */ int GetStaticVarPos( char * szVarName ) { int iPos; PFUNCTION pFunc = functions.pLast; /* First we have to check if this name belongs to a static variable * defined in current function */ if( pFunc->pOwner ) pFunc = pFunc->pOwner; /* we are in the static variable definition state */ iPos = GetVarPos( pFunc->pStatics, szVarName ); if( iPos ) return iPos + pFunc->iStaticsBase; /* Next we have to check the list of global static variables * Note: It is not possible to have global static variables when * implicit starting procedure is defined */ if( !_bStartProc ) { iPos = GetVarPos( functions.pFirst->pStatics, szVarName ); if( iPos ) return iPos; } return 0; } /* Checks if passed variable name is declared as FIELD * Returns 0 if not found in FIELD list or its position in this list if found */ int GetFieldVarPos( char * szVarName, PFUNCTION pFunc ) { int iVar; if( pFunc->szName ) /* we are in a function/procedure -we don't need any tricks */ iVar = GetVarPos( pFunc->pFields, szVarName ); else { /* we have to check the list of nested codeblock up to a function * where the codeblock is defined */ while( pFunc->pOwner ) pFunc = pFunc->pOwner; iVar = GetVarPos( pFunc->pFields, szVarName ); } return iVar; } /** Checks if passed variable name is declared as MEMVAR * Returns 0 if not found in MEMVAR list or its position in this list if found */ int GetMemvarPos( char * szVarName, PFUNCTION pFunc ) { int iVar; if( pFunc->szName ) /* we are in a function/procedure -we don't need any tricks */ iVar = GetVarPos( pFunc->pMemvars, szVarName ); else { /* we have to check the list of nested codeblock up to a function * where the codeblock is defined */ while( pFunc->pOwner ) pFunc = pFunc->pOwner; iVar = GetVarPos( pFunc->pMemvars, szVarName ); } return iVar; } USHORT FixSymbolPos( USHORT wCompilePos ) { return ( _bStartProc ? wCompilePos - 1 : wCompilePos - 2 ); } /* returns a symbol pointer from the symbol table * and sets its position in the symbol table */ PCOMSYMBOL GetSymbol( char * szSymbolName, USHORT * pwPos ) { PCOMSYMBOL pSym = symbols.pFirst; USHORT wCnt = 1; if( pwPos ) *pwPos = 0; while( pSym ) { if( ! strcmp( pSym->szName, szSymbolName ) ) { if( pwPos ) *pwPos = wCnt; return pSym; } else { if( pSym->pNext ) { pSym = pSym->pNext; ++wCnt; } else return NULL; } } return NULL; } PCOMSYMBOL GetSymbolOrd( USHORT wSymbol ) /* returns a symbol based on its index on the symbol table */ { PCOMSYMBOL pSym = symbols.pFirst; USHORT w = 1; while( w++ < wSymbol && pSym->pNext ) pSym = pSym->pNext; return pSym; } USHORT GetFunctionPos( char * szFunctionName ) /* return 0 if not found or order + 1 */ { PFUNCTION pFunc = functions.pFirst; USHORT wFunction = _bStartProc; while( pFunc ) { if( ! strcmp( pFunc->szName, szFunctionName ) && pFunc != functions.pFirst ) return wFunction; else { if( pFunc->pNext ) { pFunc = pFunc->pNext; wFunction++; } else return 0; } } return 0; } void Inc( void ) { GenPCode1( HB_P_INC ); ValTypeCheck( 'N', WARN_NUMERIC_TYPE, WARN_NUMERIC_SUSPECT ); } ULONG Jump( LONG lOffset ) { /* TODO: We need a longer offset (longer then two bytes) */ if( lOffset < ( LONG ) SHRT_MIN || lOffset > ( LONG ) SHRT_MAX ) GenError( _szCErrors, 'E', ERR_JUMP_TOO_LONG, NULL, NULL ); GenPCode3( HB_P_JUMP, HB_LOBYTE( lOffset ), HB_HIBYTE( lOffset ) ); return functions.pLast->lPCodePos - 2; } ULONG JumpFalse( LONG lOffset ) { /* TODO: We need a longer offset (longer then two bytes) */ if( lOffset < ( LONG ) SHRT_MIN || lOffset > ( LONG ) SHRT_MAX ) GenError( _szCErrors, 'E', ERR_JUMP_TOO_LONG, NULL, NULL ); GenPCode3( HB_P_JUMPFALSE, HB_LOBYTE( lOffset ), HB_HIBYTE( lOffset ) ); ValTypeCheck( 'L', WARN_LOGICAL_TYPE, WARN_LOGICAL_SUSPECT ); ValTypePop( 1 ); return functions.pLast->lPCodePos - 2; } void JumpThere( ULONG ulFrom, ULONG ulTo ) { BYTE * pCode = functions.pLast->pCode; LONG lOffset = ulTo - ulFrom + 1; /* TODO: We need a longer offset (longer then two bytes) */ if( lOffset < ( LONG ) SHRT_MIN || lOffset > ( LONG ) SHRT_MAX ) GenError( _szCErrors, 'E', ERR_JUMP_TOO_LONG, NULL, NULL ); pCode[ ( ULONG ) ulFrom ] = HB_LOBYTE( lOffset ); pCode[ ( ULONG ) ulFrom + 1 ] = HB_HIBYTE( lOffset ); } void JumpHere( ULONG ulOffset ) { JumpThere( ulOffset, functions.pLast->lPCodePos ); } ULONG JumpTrue( LONG lOffset ) { /* TODO: We need a longer offset (longer then two bytes) */ if( lOffset < ( LONG ) SHRT_MIN || lOffset > ( LONG ) SHRT_MAX ) GenError( _szCErrors, 'E', ERR_JUMP_TOO_LONG, NULL, NULL ); GenPCode3( HB_P_JUMPTRUE, HB_LOBYTE( lOffset ), HB_HIBYTE( lOffset ) ); ValTypeCheck( 'L', WARN_LOGICAL_TYPE, WARN_LOGICAL_SUSPECT ); ValTypePop( 1 ); return functions.pLast->lPCodePos - 2; } void Line( void ) /* generates the pcode with the currently compiled source code line */ { if( _bLineNumbers && ! _bDontGenLineNum ) { if( ( ( functions.pLast->lPCodePos - _ulLastLinePos ) > 3 ) || _bDebugInfo ) { _ulLastLinePos = functions.pLast->lPCodePos; GenPCode3( HB_P_LINE, HB_LOBYTE( iLine ), HB_HIBYTE( iLine ) ); } else { functions.pLast->pCode[ _ulLastLinePos +1 ] = HB_LOBYTE( iLine ); functions.pLast->pCode[ _ulLastLinePos +2 ] = HB_HIBYTE( iLine ); } } _bDontGenLineNum = FALSE; functions.pLast->bFlags &= ~ FUN_WITH_RETURN; /* clear RETURN flag */ functions.pLast->lLastPushPos = -1; } /* Generates the pcode with the currently compiled source code line * if debug code was requested only */ void LineDebug( void ) { if( _bDebugInfo ) Line(); else functions.pLast->bFlags &= ~ FUN_WITH_RETURN; /* clear RETURN flag */ } void LineBody( void ) /* generates the pcode with the currently compiled source code line */ { /* This line can be placed inside a procedure or function only */ /* except EXTERNAL */ if( _iState != EXTERN ) { if( ! _bStartProc && functions.iCount <= 1 ) { GenError( _szCErrors, 'E', ERR_OUTSIDE, NULL, NULL ); } } functions.pLast->bFlags |= FUN_STATEMENTS; Line(); } /** * Function generates passed pcode for passed variable name */ void VariablePCode( BYTE bPCode, char * szVarName ) { USHORT wVar; PCOMSYMBOL pSym; PFUNCTION pOwnerFunc = NULL; int iType = VS_LOCAL; /* not really */ /* Check if it is a FIELD declared in current function */ wVar = GetFieldVarPos( szVarName, functions.pLast ); if( wVar == 0 ) { /* Check if it is a MEMVAR declared in current function */ wVar = GetMemvarPos( szVarName, functions.pLast ); if( wVar ) iType = VS_MEMVAR; } else { iType = VS_FIELD; pOwnerFunc = functions.pLast; } /* if it is not declared in current function then check if it is * a symbol with file wide scope */ if( wVar == 0 && ! _bStartProc ) { wVar = GetFieldVarPos( szVarName, functions.pFirst ); if( wVar == 0 ) { wVar = GetMemvarPos( szVarName, functions.pFirst ); if( wVar ) iType = VS_MEMVAR; } else { iType = VS_FIELD; pOwnerFunc = functions.pFirst; } } if( wVar == 0 ) { /* This is undeclared variable */ /* * NOTE: * Clipper always assumes a memvar variable if undeclared variable * is popped (a value is asssigned to a variable). * */ #if defined( HARBOUR_STRICT_CLIPPER_COMPATIBILITY ) if( _bForceMemvars || bPCode == HB_P_POPVARIABLE ) #else if( _bForceMemvars ) #endif { /* -v switch was used -> assume it is a memvar variable */ iType = VS_MEMVAR; GenWarning( _szCWarnings, 'W', WARN_MEMVAR_ASSUMED, szVarName, NULL ); } else GenWarning( _szCWarnings, 'W', WARN_AMBIGUOUS_VAR, szVarName, NULL ); } if( iType == VS_FIELD ) { /* variable is declared using FIELD statement */ PVAR pField = GetVar( pOwnerFunc->pFields, wVar ); if( pField->szAlias ) { /* the alias was specified in FIELD declaration */ if( bPCode == HB_P_POPVARIABLE ) bPCode = HB_P_POPALIASEDFIELD; else if( bPCode == HB_P_PUSHVARIABLE ) bPCode = HB_P_PUSHALIASEDFIELD; else /* pushing fields by reference is not allowed */ GenError( _szCErrors, 'E', ERR_INVALID_REFER, szVarName, NULL ); /* * Push alias symbol before the field symbol */ PushSymbol( yy_strdup( pField->szAlias ), 0 ); } else { /* this is unaliased field */ if( bPCode == HB_P_POPVARIABLE ) bPCode = HB_P_POPFIELD; else if( bPCode == HB_P_PUSHVARIABLE ) bPCode = HB_P_PUSHFIELD; else if( bPCode == HB_P_PUSHMEMVARREF ) /* pushing fields by reference is not allowed */ GenError( _szCErrors, 'E', ERR_INVALID_REFER, szVarName, NULL ); } } else if( iType == VS_MEMVAR ) { /* variable is declared or assumed MEMVAR */ if( bPCode == HB_P_POPVARIABLE ) bPCode = HB_P_POPMEMVAR; else if( bPCode == HB_P_PUSHVARIABLE ) bPCode = HB_P_PUSHMEMVAR; } /* Check if this variable name is placed into the symbol table */ pSym = GetSymbol( szVarName, &wVar ); if( ! pSym ) pSym = AddSymbol( szVarName, &wVar ); pSym->cScope |= VS_MEMVAR; GenPCode3( bPCode, HB_LOBYTE( wVar ), HB_HIBYTE( wVar ) ); } /** * Function generates passed pcode for passed memvar name */ void MemvarPCode( BYTE bPCode, char * szVarName ) { USHORT wVar; PCOMSYMBOL pSym; /* Check if this variable name is placed into the symbol table */ pSym = GetSymbol( szVarName, &wVar ); if( ! pSym ) pSym = AddSymbol( szVarName, &wVar ); pSym->cScope |= VS_MEMVAR; GenPCode3( bPCode, HB_LOBYTE( wVar ), HB_HIBYTE( wVar ) ); } void Message( char * szMsgName ) /* sends a message to an object */ { USHORT wSym; PCOMSYMBOL pSym = GetSymbol( szMsgName, &wSym ); if( ! pSym ) /* the symbol was not found on the symbol table */ pSym = AddSymbol( szMsgName, &wSym ); pSym->cScope |= FS_MESSAGE; GenPCode3( HB_P_MESSAGE, HB_LOBYTE( wSym ), HB_HIBYTE( wSym ) ); ValTypePush( pSym->cType ); } void MessageDupl( char * szMsgName ) /* fix a generated message and duplicate to an object */ { USHORT wSetSym; PCOMSYMBOL pSym; BYTE bLoGetSym, bHiGetSym; /* get symbol */ PFUNCTION pFunc = functions.pLast; /* get the currently defined Clipper function */ pSym = GetSymbol( szMsgName, &wSetSym ); if( ! pSym ) /* the symbol was not found on the symbol table */ pSym = AddSymbol( szMsgName, &wSetSym ); pSym->cScope |= FS_MESSAGE; /* Get previously generated message */ bLoGetSym = pFunc->pCode[ _ulMessageFix + 1]; bHiGetSym = pFunc->pCode[ _ulMessageFix + 2]; pFunc->pCode[ _ulMessageFix + 1 ] = HB_LOBYTE( wSetSym ); pFunc->pCode[ _ulMessageFix + 2 ] = HB_HIBYTE( wSetSym ); pFunc->lPCodePos -= 3; /* Remove unnecessary function call */ Duplicate(); /* Duplicate object */ GenPCode3( HB_P_MESSAGE, bLoGetSym, bHiGetSym ); /* Generate new message */ } void MessageFix( char * szMsgName ) /* fix a generated message to an object */ { USHORT wSym; PCOMSYMBOL pSym; PFUNCTION pFunc = functions.pLast; /* get the currently defined Clipper function */ pSym = GetSymbol( szMsgName, &wSym ); if( ! pSym ) /* the symbol was not found on the symbol table */ pSym = AddSymbol( szMsgName, &wSym ); pSym->cScope |= FS_MESSAGE; pFunc->pCode[ _ulMessageFix + 1 ] = HB_LOBYTE( wSym ); pFunc->pCode[ _ulMessageFix + 2 ] = HB_HIBYTE( wSym ); pFunc->lPCodePos -= 3; /* Remove unnecessary function call */ } void PopId( char * szVarName ) /* generates the pcode to pop a value from the virtual machine stack onto a variable */ { int iVar; if( pAliasId == NULL ) { iVar = GetLocalVarPos( szVarName ); if( iVar ) GenPCode3( HB_P_POPLOCAL, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); else { iVar = GetStaticVarPos( szVarName ); if( iVar ) { GenPCode3( HB_P_POPSTATIC, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); functions.pLast->bFlags |= FUN_USES_STATICS; } else { VariablePCode( HB_P_POPVARIABLE, szVarName ); } } } else { if( pAliasId->type == ALIAS_NAME ) { if( pAliasId->alias.szAlias[ 0 ] == 'M' && pAliasId->alias.szAlias[ 1 ] == '\0' ) { /* M->variable */ MemvarPCode( HB_P_POPMEMVAR, szVarName ); } else { int iCmp = strncmp( pAliasId->alias.szAlias, "MEMVAR", 4 ); if( iCmp == 0 ) iCmp = strncmp( pAliasId->alias.szAlias, "MEMVAR", strlen( pAliasId->alias.szAlias ) ); if( iCmp == 0 ) { /* MEMVAR-> or MEMVA-> or MEMV-> */ MemvarPCode( HB_P_POPMEMVAR, szVarName ); } else { /* field variable */ iCmp = strncmp( pAliasId->alias.szAlias, "FIELD", 4 ); if( iCmp == 0 ) iCmp = strncmp( pAliasId->alias.szAlias, "FIELD", strlen( pAliasId->alias.szAlias ) ); if( iCmp == 0 ) { /* FIELD-> */ FieldPCode( HB_P_POPFIELD, szVarName ); } else { /* database alias */ PushSymbol( yy_strdup( pAliasId->alias.szAlias ), 0 ); FieldPCode( HB_P_POPALIASEDFIELD, szVarName ); } } } } else if( pAliasId->type == ALIAS_NUMBER ) { PushInteger( pAliasId->alias.iAlias ); FieldPCode( HB_P_POPALIASEDFIELD, szVarName ); } else /* Alias is already placed on stack */ FieldPCode( HB_P_POPALIASEDFIELD, szVarName ); } ValTypeAssign( szVarName ); } void PushId( char * szVarName ) /* generates the pcode to push a variable value to the virtual machine stack */ { int iVar; if( pAliasId == NULL ) { if( iVarScope == VS_STATIC && functions.pLast->szName ) { /* Reffering to any variable is not allowed during initialization * of static variable */ _pInitFunc->bFlags |= FUN_ILLEGAL_INIT; } iVar = GetLocalVarPos( szVarName ); if( iVar ) GenPCode3( HB_P_PUSHLOCAL, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); else { iVar = GetStaticVarPos( szVarName ); if( iVar ) { GenPCode3( HB_P_PUSHSTATIC, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); functions.pLast->bFlags |= FUN_USES_STATICS; } else { VariablePCode( HB_P_PUSHVARIABLE, szVarName ); } } } else { if( pAliasId->type == ALIAS_NAME ) { if( pAliasId->alias.szAlias[ 0 ] == 'M' && pAliasId->alias.szAlias[ 1 ] == '\0' ) { /* M->variable */ MemvarPCode( HB_P_PUSHMEMVAR, szVarName ); } else { int iCmp = strncmp( pAliasId->alias.szAlias, "MEMVAR", 4 ); if( iCmp == 0 ) iCmp = strncmp( pAliasId->alias.szAlias, "MEMVAR", strlen( pAliasId->alias.szAlias ) ); if( iCmp == 0 ) { /* MEMVAR-> or MEMVA-> or MEMV-> */ MemvarPCode( HB_P_PUSHMEMVAR, szVarName ); } else { /* field variable */ iCmp = strncmp( pAliasId->alias.szAlias, "FIELD", 4 ); if( iCmp == 0 ) iCmp = strncmp( pAliasId->alias.szAlias, "FIELD", strlen( pAliasId->alias.szAlias ) ); if( iCmp == 0 ) { /* FIELD-> */ FieldPCode( HB_P_PUSHFIELD, szVarName ); } else { /* database alias */ PushSymbol( yy_strdup( pAliasId->alias.szAlias ), 0 ); FieldPCode( HB_P_PUSHALIASEDFIELD, szVarName ); } } } } else if( pAliasId->type == ALIAS_NUMBER ) { PushInteger( pAliasId->alias.iAlias ); FieldPCode( HB_P_PUSHALIASEDFIELD, szVarName ); } else /* Alias is already placed on stack */ FieldPCode( HB_P_PUSHALIASEDFIELD, szVarName ); } } void PushIdByRef( char * szVarName ) /* generates the pcode to push a variable by reference to the virtual machine stack */ { USHORT iVar; if( iVarScope == VS_STATIC && functions.pLast->szName ) { /* Reffering to any variable is not allowed during initialization * of static variable */ _pInitFunc->bFlags |= FUN_ILLEGAL_INIT; } iVar = GetLocalVarPos( szVarName ); if( iVar ) GenPCode3( HB_P_PUSHLOCALREF, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); else { iVar = GetStaticVarPos( szVarName ); if( iVar ) { GenPCode3( HB_P_PUSHSTATICREF, HB_LOBYTE( iVar ), HB_HIBYTE( iVar ) ); functions.pLast->bFlags |= FUN_USES_STATICS; } else { /* if undeclared variable is passed by reference then a memvar * variable is assumed because fields cannot be passed by * a reference */ VariablePCode( HB_P_PUSHMEMVARREF, szVarName ); } } } void PushLogical( int iTrueFalse ) /* pushes a logical value on the virtual machine stack */ { if( iTrueFalse ) GenPCode1( HB_P_TRUE ); else GenPCode1( HB_P_FALSE ); ValTypePush( 'L' ); } void PushNil( void ) { GenPCode1( HB_P_PUSHNIL ); ValTypePush( ' ' ); /*TODO maybe 'U'*/ } /* generates the pcode to push a double number on the virtual machine stack */ void PushDouble( double dNumber, BYTE bDec ) { GenPCode1( HB_P_PUSHDOUBLE ); GenPCodeN( ( BYTE * ) &dNumber, sizeof( double ) ); GenPCode1( bDec ); ValTypePush( 'N' ); } void PushFunCall( char * szFunName ) { char * szFunction; szFunction = RESERVED_FUNC( szFunName ); if( szFunction ) { /* Abbreviated function name was used - change it for whole name */ PushSymbol( yy_strdup( szFunction ), 1 ); } else PushSymbol( szFunName, 1 ); GenPCode1( HB_P_PUSHNIL ); } /* generates the pcode to push a integer number on the virtual machine stack */ void PushInteger( int iNumber ) { if( iNumber ) GenPCode3( HB_P_PUSHINT, HB_LOBYTE( ( USHORT ) iNumber ), HB_HIBYTE( ( USHORT ) iNumber ) ); else GenPCode1( HB_P_ZERO ); } /* generates the pcode to push a long number on the virtual machine stack */ void PushLong( long lNumber ) { if( lNumber ) { GenPCode1( HB_P_PUSHLONG ); GenPCode1( ( ( char * ) &lNumber )[ 0 ] ); GenPCode1( ( ( char * ) &lNumber )[ 1 ] ); GenPCode1( ( ( char * ) &lNumber )[ 2 ] ); GenPCode1( ( ( char * ) &lNumber )[ 3 ] ); } else GenPCode1( HB_P_ZERO ); ValTypePush( 'N' ); } /* generates the pcode to push a string on the virtual machine stack */ void PushString( char * szText ) { int iStrLen = strlen( szText ); GenPCode3( HB_P_PUSHSTR, HB_LOBYTE( iStrLen ), HB_HIBYTE( iStrLen ) ); GenPCodeN( ( BYTE * ) szText, iStrLen ); ValTypePush( 'C' ); } /* generates the pcode to push a symbol on the virtual machine stack */ void PushSymbol( char * szSymbolName, int iIsFunction ) { USHORT wSym; PCOMSYMBOL pSym; if( iIsFunction ) { char * pName = RESERVED_FUNC( szSymbolName ); /* If it is reserved function name then we should truncate * the requested name. * We have to use passed szSymbolName so we can latter deallocate it * (pName points to static data) */ if( pName ) szSymbolName[ strlen( pName ) ] ='\0'; } pSym = GetSymbol( szSymbolName, &wSym ); if( ! pSym ) /* the symbol was not found on the symbol table */ { pSym = AddSymbol( szSymbolName, &wSym ); if( iIsFunction ) AddFunCall( szSymbolName ); } else { if( iIsFunction && ! GetFuncall( szSymbolName ) ) AddFunCall( szSymbolName ); } GenPCode3( HB_P_PUSHSYM, HB_LOBYTE( wSym ), HB_HIBYTE( wSym ) ); ValTypePush( iIsFunction ? pSym->cType : _cVarType ); } void CheckDuplVars( PVAR pVar, char * szVarName, int iVarScope ) { while( pVar ) { if( ! strcmp( pVar->szName, szVarName ) ) { if( ! ( iVarScope & VS_PARAMETER ) ) --iLine; GenError( _szCErrors, 'E', ERR_VAR_DUPL, szVarName, NULL ); } else pVar = pVar->pNext; } } void Dec( void ) { GenPCode1( HB_P_DEC ); ValTypeCheck( 'N', WARN_NUMERIC_TYPE, WARN_NUMERIC_SUSPECT ); } void ArrayDim( int iDimensions ) { GenPCode3( HB_P_ARRAYDIM, HB_LOBYTE( iDimensions ), HB_HIBYTE( iDimensions ) ); } void Do( BYTE bParams ) { GenPCode3( HB_P_DO, bParams, 0 ); ValTypePop( bParams + 1 ); /* pop all arguments and symbol type */ } void FixElseIfs( void * pFixElseIfs ) { PELSEIF pFix = ( PELSEIF ) pFixElseIfs; while( pFix ) { JumpHere( pFix->ulOffset ); pFix = pFix->pNext; } } void FixReturns( void ) /* fixes all last defined function returns jumps offsets */ { if( _iWarnings && functions.pLast ) { PVAR pVar; pVar = functions.pLast->pLocals; while( pVar ) { if( pVar->szName && functions.pLast->szName && functions.pLast->szName[0] && ! pVar->iUsed ) GenWarning( _szCWarnings, 'W', WARN_VAR_NOT_USED, pVar->szName, functions.pLast->szName ); pVar = pVar->pNext; } pVar = functions.pLast->pStatics; while( pVar ) { if( pVar->szName && functions.pLast->szName && functions.pLast->szName[0] && ! pVar->iUsed ) GenWarning( _szCWarnings, 'W', WARN_VAR_NOT_USED, pVar->szName, functions.pLast->szName ); pVar = pVar->pNext; } ValTypeReset( ); /* Check if the function returned some value */ if( (functions.pLast->bFlags & FUN_WITH_RETURN) == 0 && (functions.pLast->bFlags & FUN_PROCEDURE) == 0 ) GenWarning( _szCWarnings, 'W', WARN_FUN_WITH_NO_RETURN, functions.pLast->szName, NULL ); } /* TODO: check why it triggers this error in keywords.prg if( pLoops ) { PTR_LOOPEXIT pLoop = pLoops; char cLine[ 64 ]; while( pLoop->pNext ) pLoop = pLoop->pNext; itoa( pLoop->iLine, cLine, 10 ); GenError( _szCErrors, 'E', ERR_UNCLOSED_STRU, cLine, NULL ); } */ } void Function( BYTE bParams ) { GenPCode3( HB_P_FUNCTION, bParams, 0 ); ValTypePop( bParams + 1 ); /* pop all arguments and symbol type */ } void GenArray( int iElements ) { GenPCode3( HB_P_ARRAYGEN, HB_LOBYTE( iElements ), HB_HIBYTE( iElements ) ); ValTypePop( iElements ); /* pop all items of the array */ ValTypePush( 'A' ); } static void GenPlusPCode( BYTE opcode ) { GenPCode1( opcode ); ValTypePlus(); } static void GenNumPCode( BYTE opcode ) { GenPCode1( opcode ); ValTypeCheck2( 'N', WARN_NUMERIC_TYPE, WARN_NUMERIC_SUSPECT ); ValTypePop( 2 ); ValTypePush( 'N' ); } static void GenRelPCode( BYTE opcode ) { GenPCode1( opcode ); ValTypeRelational(); } static void GenNotPCode( void ) { GenPCode1( HB_P_NOT ); ValTypeCheck( 'L', WARN_LOGICAL_TYPE, WARN_LOGICAL_SUSPECT ); /* Leave the original type on the stack */ } static void GenNegatePCode( void ) { GenPCode1( HB_P_NEGATE ); ValTypeCheck( 'N', WARN_NUMERIC_TYPE, WARN_NUMERIC_SUSPECT ); /* Leave the original type on the stack */ } static void SetLastPushPos( void ) { functions.pLast->lLastPushPos = functions.pLast->lPCodePos; } static void RemoveExtraPush( void ) { if( functions.pLast->lLastPushPos > -1 && functions.pLast->lPCodePos > functions.pLast->lLastPushPos ) { functions.pLast->lPCodePos = functions.pLast->lLastPushPos; functions.pLast->lLastPushPos = -1; } else GenPCode1( HB_P_POP ); ValTypePop( 1 ); } static void GenPopPCode( void ) { GenPCode1( HB_P_POP ); ValTypePop( 1 ); } static void GenPCode1( BYTE byte ) { PFUNCTION pFunc = functions.pLast; /* get the currently defined Clipper function */ if( ! pFunc->pCode ) /* has been created the memory block to hold the pcode ? */ { pFunc->pCode = ( BYTE * ) hb_xgrab( PCODE_CHUNK ); pFunc->lPCodeSize = PCODE_CHUNK; pFunc->lPCodePos = 0; } else if( ( pFunc->lPCodeSize - pFunc->lPCodePos ) < 1 ) pFunc->pCode = ( BYTE * ) hb_xrealloc( pFunc->pCode, pFunc->lPCodeSize += PCODE_CHUNK ); pFunc->pCode[ pFunc->lPCodePos++ ] = byte; } static void GenPCode3( BYTE byte1, BYTE byte2, BYTE byte3 ) { PFUNCTION pFunc = functions.pLast; /* get the currently defined Clipper function */ if( ! pFunc->pCode ) /* has been created the memory block to hold the pcode ? */ { pFunc->pCode = ( BYTE * ) hb_xgrab( PCODE_CHUNK ); pFunc->lPCodeSize = PCODE_CHUNK; pFunc->lPCodePos = 0; } else if( ( pFunc->lPCodeSize - pFunc->lPCodePos ) < 3 ) pFunc->pCode = ( BYTE * ) hb_xrealloc( pFunc->pCode, pFunc->lPCodeSize += PCODE_CHUNK ); pFunc->pCode[ pFunc->lPCodePos++ ] = byte1; pFunc->pCode[ pFunc->lPCodePos++ ] = byte2; pFunc->pCode[ pFunc->lPCodePos++ ] = byte3; } static void GenPCodeN( BYTE * pBuffer, ULONG ulSize ) { PFUNCTION pFunc = functions.pLast; /* get the currently defined Clipper function */ if( ! pFunc->pCode ) /* has been created the memory block to hold the pcode ? */ { pFunc->lPCodeSize = ( ( ulSize / PCODE_CHUNK ) + 1 ) * PCODE_CHUNK; pFunc->pCode = ( BYTE * ) hb_xgrab( pFunc->lPCodeSize ); pFunc->lPCodePos = 0; } else if( pFunc->lPCodePos + ulSize > pFunc->lPCodeSize ) { /* not enough free space in pcode buffer - increase it */ pFunc->lPCodeSize += ( ( ( ulSize / PCODE_CHUNK ) + 1 ) * PCODE_CHUNK ); pFunc->pCode = ( BYTE * ) hb_xrealloc( pFunc->pCode, pFunc->lPCodeSize ); } memcpy( pFunc->pCode + pFunc->lPCodePos, pBuffer, ulSize ); pFunc->lPCodePos += ulSize; } char * SetData( char * szMsg ) /* generates an underscore-symbol name for a data assignment */ { char * szResult = ( char * ) hb_xgrab( strlen( szMsg ) + 2 ); strcpy( szResult, "_" ); strcat( szResult, szMsg ); return szResult; } /* Generate the opcode to open BEGIN/END sequence * This code is simmilar to JUMP opcode - the offset will be filled with * - either the address of HB_P_SEQEND opcode if there is no RECOVER clause * - or the address of RECOVER code */ ULONG SequenceBegin( void ) { GenPCode3( HB_P_SEQBEGIN, 0, 0 ); return functions.pLast->lPCodePos - 2; } /* Generate the opcode to close BEGIN/END sequence * This code is simmilar to JUMP opcode - the offset will be filled with * the address of first line after END SEQUENCE * This opcode will be executed if recover code was not requested (as the * last statement in code beetwen BEGIN ... RECOVER) or if BREAK was requested * and there was no matching RECOVER clause. */ ULONG SequenceEnd( void ) { GenPCode3( HB_P_SEQEND, 0, 0 ); return functions.pLast->lPCodePos - 2; } /* Remove unnecessary opcodes in case there were no executable statements * beetwen BEGIN and RECOVER sequence */ void SequenceFinish( ULONG ulStartPos, int bUsualStmts ) { if( ! _bDebugInfo ) /* only if no debugger info is required */ { if( ! bUsualStmts ) { functions.pLast->lPCodePos = ulStartPos - 1; /* remove also HB_P_SEQBEGIN */ _ulLastLinePos = ulStartPos - 4; } } } /* * Start a new fake-function that will hold pcodes for a codeblock */ void CodeBlockStart() { PFUNCTION pFunc = FunctionNew( NULL, FS_STATIC ); pFunc->pOwner = functions.pLast; pFunc->iStaticsBase = functions.pLast->iStaticsBase; functions.pLast = pFunc; LineDebug(); } void CodeBlockEnd() { PFUNCTION pCodeblock; /* pointer to the current codeblock */ PFUNCTION pFunc; /* pointer to a function that owns a codeblock */ USHORT wSize; USHORT wLocals = 0; /* number of referenced local variables */ USHORT wPos; PVAR pVar, pFree; pCodeblock = functions.pLast; /* return to pcode buffer of function/codeblock in which the current * codeblock was defined */ functions.pLast = pCodeblock->pOwner; /* find the function that owns the codeblock */ pFunc = pCodeblock->pOwner; while( pFunc->pOwner ) pFunc = pFunc->pOwner; pFunc->bFlags |= ( pCodeblock->bFlags & FUN_USES_STATICS ); /* generate a proper codeblock frame with a codeblock size and with * a number of expected parameters */ /*QUESTION: would be 64kB enough for a codeblock size? * we are assuming now a USHORT for a size of codeblock */ /* Count the number of referenced local variables */ pVar = pCodeblock->pStatics; while( pVar ) { pVar = pVar->pNext; ++wLocals; } /*NOTE: 8 = HB_P_PUSHBLOCK + USHORT( size ) + USHORT( wParams ) + USHORT( wLocals ) + _ENDBLOCK */ wSize = ( USHORT ) pCodeblock->lPCodePos + 8 + wLocals * 2; GenPCode3( HB_P_PUSHBLOCK, HB_LOBYTE( wSize ), HB_HIBYTE( wSize ) ); GenPCode1( HB_LOBYTE( pCodeblock->wParamCount ) ); GenPCode1( HB_HIBYTE( pCodeblock->wParamCount ) ); GenPCode1( HB_LOBYTE( wLocals ) ); GenPCode1( HB_HIBYTE( wLocals ) ); /* generate the table of referenced local variables */ pVar = pCodeblock->pStatics; while( wLocals-- ) { wPos = GetVarPos( pFunc->pLocals, pVar->szName ); GenPCode1( HB_LOBYTE( wPos ) ); GenPCode1( HB_HIBYTE( wPos ) ); pFree = pVar; hb_xfree( ( void * ) pFree->szName ); pVar = pVar->pNext; hb_xfree( ( void * ) pFree ); } GenPCodeN( pCodeblock->pCode, pCodeblock->lPCodePos ); GenPCode1( HB_P_ENDBLOCK ); /* finish the codeblock */ /* this fake-function is no longer needed */ hb_xfree( ( void * ) pCodeblock->pCode ); pVar = pCodeblock->pLocals; while( pVar ) { if( _iWarnings && pFunc->szName && pVar->szName && ! pVar->iUsed ) GenWarning( _szCWarnings, 'W', WARN_BLOCKVAR_NOT_USED, pVar->szName, pFunc->szName ); /* free used variables */ pFree = pVar; hb_xfree( ( void * ) pFree->szName ); pVar = pVar->pNext; hb_xfree( ( void * ) pFree ); } hb_xfree( ( void * ) pCodeblock ); ValTypePut( 'B' ); } /* Set the name of an alias for the list of previously declared FIELDs * * szAlias -> name of the alias * iField -> position of the first FIELD name to change */ void FieldsSetAlias( char * szAlias, int iField ) { PVAR pVar; pVar = functions.pLast->pFields; while( iField-- && pVar ) pVar = pVar->pNext; while( pVar ) { pVar->szAlias = szAlias; pVar = pVar->pNext; } } /* This functions counts the number of FIELD declaration in a function * We will required this information in FieldsSetAlias function */ int FieldsCount() { int iFields = 0; PVAR pVar = functions.pLast->pFields; while( pVar ) { ++iFields; pVar = pVar->pNext; } return iFields; } /* * Start of definition of static variable * We are using here the special function _pInitFunc which will store * pcode needed to initialize all static variables declared in PRG module. * pOwner member will point to a function where the static variable is * declared: * TODO: support for static variables in codeblock */ void StaticDefStart( void ) { iVarScope = VS_STATIC; Line(); functions.pLast->bFlags |= FUN_USES_STATICS; if( ! _pInitFunc ) { BYTE pBuffer[ 5 ]; _pInitFunc = FunctionNew( yy_strdup("(_INITSTATICS)"), FS_INIT ); _pInitFunc->pOwner = functions.pLast; _pInitFunc->bFlags = FUN_USES_STATICS | FUN_PROCEDURE; _pInitFunc->cScope = FS_INIT | FS_EXIT; functions.pLast = _pInitFunc; pBuffer[ 0 ] = HB_P_STATICS; pBuffer[ 1 ] = 0; pBuffer[ 2 ] = 0; pBuffer[ 3 ] = 1; /* the number of static variables is unknown now */ pBuffer[ 4 ] = 0; GenPCodeN( pBuffer, 5 ); GenPCode3( HB_P_SFRAME, 0, 0 ); /* frame for statics variables */ } else { _pInitFunc->pOwner = functions.pLast; functions.pLast = _pInitFunc; } } /* * End of definition of static variable * Return to previously pcoded function. */ void StaticDefEnd( int iCount ) { functions.pLast = _pInitFunc->pOwner; _pInitFunc->pOwner = NULL; _iStatics += iCount; iVarScope = VS_LOCAL; } /* * This function checks if we are initializing a static variable. * It should be called only in case when the parser have recognized any * function or method invocation. */ void StaticAssign( void ) { if( iVarScope == VS_STATIC && functions.pLast->szName ) /* function call is allowed if it is inside a codeblock */ _pInitFunc->bFlags |= FUN_ILLEGAL_INIT; } /* * This function stores the position in pcode buffer where the FOR/WHILE * loop starts. It will be used to fix any LOOP/EXIT statements */ static void LoopStart( void ) { PTR_LOOPEXIT pLoop = ( PTR_LOOPEXIT ) hb_xgrab( sizeof( LOOPEXIT ) ); if( pLoops ) { PTR_LOOPEXIT pLast = pLoops; while( pLast->pNext ) pLast = pLast->pNext; pLast->pNext = pLoop; } else pLoops = pLoop; pLoop->pNext = NULL; pLoop->pExitList = NULL; pLoop->pLoopList = NULL; pLoop->ulOffset = functions.pLast->lPCodePos; /* store the start position */ pLoop->iLine = iLine; pLoop->wSeqCounter = _wSeqCounter; /* store current SEQUENCE counter */ } /* * Stores the position of LOOP statement to fix it later at the end of loop */ static void LoopLoop( void ) { PTR_LOOPEXIT pLast, pLoop; pLoop = ( PTR_LOOPEXIT ) hb_xgrab( sizeof( LOOPEXIT ) ); pLoop->pLoopList = NULL; pLoop->ulOffset = functions.pLast->lPCodePos; /* store the position to fix */ pLast = pLoops; while( pLast->pNext ) pLast = pLast->pNext; if( pLast->wSeqCounter != _wSeqCounter ) { /* Attempt to LOOP from BEGIN/END sequence * Current SEQUENCE counter is different then at the beginning of loop * Notice that LOOP is allowed in RECOVER code. */ GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "LOOP", NULL ); } while( pLast->pLoopList ) pLast = pLast->pLoopList; pLast->pLoopList = pLoop; Jump( 0 ); } /* * Stores the position of EXIT statement to fix it later at the end of loop */ static void LoopExit( void ) { PTR_LOOPEXIT pLast, pLoop; pLoop = ( PTR_LOOPEXIT ) hb_xgrab( sizeof( LOOPEXIT ) ); pLoop->pExitList = NULL; pLoop->ulOffset = functions.pLast->lPCodePos; /* store the position to fix */ pLast = pLoops; while( pLast->pNext ) pLast = pLast->pNext; if( pLast->wSeqCounter != _wSeqCounter ) { /* Attempt to LOOP from BEGIN/END sequence * Current SEQUENCE counter is different then at the beginning of loop * Notice that LOOP is allowed in RECOVER code. */ GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "EXIT", NULL ); } while( pLast->pExitList ) pLast = pLast->pExitList; pLast->pExitList = pLoop; Jump( 0 ); } /* * Fixes the LOOP statement */ static void LoopHere( void ) { PTR_LOOPEXIT pLoop = pLoops, pFree; while( pLoop->pNext ) pLoop = pLoop->pNext; pLoop = pLoop->pLoopList; while( pLoop ) { JumpHere( pLoop->ulOffset + 1 ); pFree = pLoop; pLoop = pLoop->pLoopList; hb_xfree( ( void * ) pFree ); } } /* * Fixes the EXIT statements and releases memory allocated for current loop */ static void LoopEnd( void ) { PTR_LOOPEXIT pExit, pLoop = pLoops, pLast = pLoops, pFree; while( pLoop->pNext ) { pLast = pLoop; pLoop = pLoop->pNext; } pExit = pLoop->pExitList; while( pExit ) { JumpHere( pExit->ulOffset + 1 ); pFree = pExit; pExit = pExit->pExitList; hb_xfree( ( void * ) pFree ); } pLast->pNext = NULL; if( pLoop == pLoops ) pLoops = NULL; hb_xfree( ( void * ) pLoop ); } #define IS_PATH_SEP( c ) ( strchr( OS_PATH_DELIMITER_LIST, ( c ) ) != NULL ) /* Split given filename into path, name and extension */ PHB_FNAME hb_fsFNameSplit( char * szFileName ) { PHB_FNAME pFileName = ( PHB_FNAME ) hb_xgrab( sizeof( HB_FNAME ) ); int iLen = strlen( szFileName ); int iSlashPos; int iDotPos; int iPos; pFileName->szPath = pFileName->szName = pFileName->szExtension = NULL; iSlashPos = iLen - 1; iPos = 0; while( iSlashPos >= 0 && !IS_PATH_SEP( szFileName[ iSlashPos ] ) ) --iSlashPos; if( iSlashPos == 0 ) { /* root path -> \filename */ pFileName->szBuffer[ 0 ] = OS_PATH_DELIMITER; pFileName->szBuffer[ 1 ] = '\0'; pFileName->szPath = pFileName->szBuffer; iPos = 2; /* first free position after the slash */ } else if( iSlashPos > 0 ) { /* If we are after a drive letter let's keep the following backslash */ if( IS_PATH_SEP( ':' ) && ( szFileName[ iSlashPos ] == ':' || szFileName[ iSlashPos - 1 ] == ':' ) ) { /* path with separator -> d:\path\filename or d:path\filename */ memcpy( pFileName->szBuffer, szFileName, iSlashPos + 1 ); pFileName->szBuffer[ iSlashPos + 1 ] = '\0'; iPos = iSlashPos + 2; /* first free position after the slash */ } else { /* path with separator -> path\filename */ memcpy( pFileName->szBuffer, szFileName, iSlashPos ); pFileName->szBuffer[ iSlashPos ] = '\0'; iPos = iSlashPos + 1; /* first free position after the slash */ } pFileName->szPath = pFileName->szBuffer; } iDotPos = iLen - 1; while( iDotPos > iSlashPos && szFileName[ iDotPos ] != '.' ) --iDotPos; if( ( iDotPos - iSlashPos ) > 1 ) { /* the dot was found * and there is at least one character between a slash and a dot */ if( iDotPos == iLen - 1 ) { /* the dot is the last character - use it as extension name */ pFileName->szExtension = pFileName->szBuffer + iPos; pFileName->szBuffer[ iPos++ ] = '.'; pFileName->szBuffer[ iPos++ ] = '\0'; } else { pFileName->szExtension = pFileName->szBuffer + iPos; /* copy rest of the string with terminating ZERO character */ memcpy( pFileName->szExtension, szFileName + iDotPos + 1, iLen - iDotPos ); iPos += iLen - iDotPos; } } else /* there is no dot in the filename or it is '.filename' */ iDotPos = iLen; if( ( iDotPos - iSlashPos - 1 ) > 0 ) { pFileName->szName = pFileName->szBuffer + iPos; memcpy( pFileName->szName, szFileName + iSlashPos + 1, iDotPos - iSlashPos - 1 ); pFileName->szName[ iDotPos - iSlashPos - 1 ] = '\0'; } /* DEBUG printf( "\nFilename: %s\n", szFileName ); printf( "\n szPath: %s\n", pFileName->szPath ); printf( "\n szName: %s\n", pFileName->szName ); printf( "\n szExt: %s\n", pFileName->szExtension ); */ return pFileName; } /* This function joins path, name and extension into a string with a filename */ char * hb_fsFNameMerge( char * szFileName, PHB_FNAME pFileName ) { if( pFileName->szPath && pFileName->szPath[ 0 ] ) { /* we have not empty path specified */ int iLen = strlen( pFileName->szPath ); strcpy( szFileName, pFileName->szPath ); /* if the path is a root directory then we don't need to add path separator */ if( !( IS_PATH_SEP( pFileName->szPath[ 0 ] ) && pFileName->szPath[ 0 ] == '\0' ) ) { /* add the path separator only in cases: * when a name doesn't start with it * when the path doesn't end with it */ if( !( IS_PATH_SEP( pFileName->szName[ 0 ] ) || IS_PATH_SEP( pFileName->szPath[ iLen-1 ] ) ) ) { szFileName[ iLen++ ] = OS_PATH_DELIMITER; szFileName[ iLen ] = '\0'; } } if( pFileName->szName ) strcpy( szFileName + iLen, pFileName->szName ); } else { if( pFileName->szName ) strcpy( szFileName, pFileName->szName ); } if( pFileName->szExtension ) { int iLen = strlen( szFileName ); if( !( pFileName->szExtension[ 0 ] == '.' || szFileName[ iLen - 1 ] == '.') ) { /* add extension separator only when extansion doesn't contain it */ szFileName[ iLen++ ] = '.'; szFileName[ iLen ] = '\0'; } strcpy( szFileName + iLen, pFileName->szExtension ); } /* DEBUG printf( "\nMERGE:\n" ); printf( "\n szPath: %s\n", pFileName->szPath ); printf( "\n szName: %s\n", pFileName->szName ); printf( "\n szExt: %s\n", pFileName->szExtension ); printf( "\nFilename result: %s\n", szFileName ); */ return szFileName; } void * hb_xgrab( ULONG ulSize ) /* allocates fixed memory, exits on failure */ { void * pMem = malloc( ulSize ); if( ! pMem ) GenError( _szCErrors, 'E', ERR_MEMALLOC, NULL, NULL ); return pMem; } void * hb_xrealloc( void * pMem, ULONG ulSize ) /* reallocates memory */ { void * pResult = realloc( pMem, ulSize ); if( ! pResult ) GenError( _szCErrors, 'E', ERR_MEMREALLOC, NULL, NULL ); return pResult; } void hb_xfree( void * pMem ) /* frees fixed memory */ { if( pMem ) free( pMem ); else GenError( _szCErrors, 'E', ERR_MEMFREE, NULL, NULL ); } char * yy_strupr( char * p ) { char * p1; for( p1 = p; * p1; p1++ ) * p1 = toupper( * p1 ); return p; } char * yy_strdup( char * p ) { char * pDup; int iLen; iLen = strlen( p ) + 1; pDup = ( char * ) hb_xgrab( iLen ); memcpy( pDup, p, iLen ); return pDup; } /* checks if passed string is a reserved function name */ static char * reserved_name( char * szName ) { USHORT wNum = 0; int iFound = 1; while( wNum < RESERVED_FUNCTIONS && iFound ) { /* Compare first 4 characters * If they are the same then compare the whole name * SECO() is not allowed because of Clipper function SECONDS() * however SECO32() is a valid name. */ iFound = strncmp( szName, _szReservedFun[ wNum ], 4 ); if( iFound == 0 ) iFound = strncmp( szName, _szReservedFun[ wNum ], strlen( szName ) ); ++wNum; } if( iFound ) return NULL; else return (char *) _szReservedFun[ wNum - 1 ]; } /* NOTE: iMinParam = -1, means no checking */ /* iMaxParam = -1, means no upper limit */ typedef struct { char * cFuncName; /* function name */ int iMinParam; /* min no of parms it needs */ int iMaxParam; /* max no of parms need */ } FUNCINFO, * PFUNCINFO; static FUNCINFO _StdFun[] = { { "AADD" , 2, 2 }, { "ABS" , 1, 1 }, { "ASC" , 1, 1 }, { "AT" , 2, 2 }, { "BOF" , 0, 0 }, { "BREAK" , 0, 1 }, { "CDOW" , 1, 1 }, { "CHR" , 1, 1 }, { "CMONTH" , 1, 1 }, { "COL" , 0, 0 }, { "CTOD" , 1, 1 }, { "DATE" , 0, 0 }, { "DAY" , 1, 1 }, { "DELETED" , 0, 0 }, { "DEVPOS" , 2, 2 }, { "DOW" , 1, 1 }, { "DTOC" , 1, 1 }, { "DTOS" , 1, 1 }, { "EMPTY" , 1, 1 }, { "EOF" , 0, 0 }, { "EVAL" , 1, -1 }, { "EXP" , 1, 1 }, { "FCOUNT" , 0, 0 }, { "FIELDNAME" , 1, 1 }, { "FILE" , 1, 1 }, { "FLOCK" , 0, 0 }, { "FOUND" , 0, 0 }, { "INKEY" , 0, 2 }, { "INT" , 1, 1 }, { "LASTREC" , 0, 0 }, { "LEFT" , 2, 2 }, { "LEN" , 1, 1 }, { "LOCK" , 0, 0 }, { "LOG" , 1, 1 }, { "LOWER" , 1, 1 }, { "LTRIM" , 1, 1 }, { "MAX" , 2, 2 }, { "MIN" , 2, 2 }, { "MONTH" , 1, 1 }, { "PCOL" , 0, 0 }, { "PCOUNT" , 0, 0 }, { "PROW" , 0, 0 }, { "RECCOUNT" , 0, 0 }, { "RECNO" , 0, 0 }, { "REPLICATE" , 2, 2 }, { "RLOCK" , 0, 0 }, { "ROUND" , 2, 2 }, { "ROW" , 0, 0 }, { "RTRIM" , 1, 2 }, /* Second parameter is a Harbour extension */ { "SECONDS" , 0, 0 }, { "SELECT" , 0, 1 }, { "SETPOS" , 2, 2 }, { "SETPOSBS" , 0, 0 }, { "SPACE" , 1, 1 }, { "SQRT" , 1, 1 }, { "STR" , 1, 3 }, { "SUBSTR" , 2, 3 }, { "TIME" , 0, 0 }, { "TRANSFORM" , 2, 2 }, { "TRIM" , 1, 2 }, /* Second parameter is a Harbour extension */ { "TYPE" , 1, 1 }, { "UPPER" , 1, 1 }, { "VAL" , 1, 1 }, { "VALTYPE" , 1, 1 }, { "WORD" , 1, 1 }, { "YEAR" , 1, 1 }, { 0 , 0, 0 } }; void CheckArgs( char * szFuncCall, int iArgs ) { FUNCINFO * f = _StdFun; int i = 0; int iPos = -1; int iCmp; while( f[ i ].cFuncName ) { iCmp = strncmp( szFuncCall, f[ i ].cFuncName, 4 ); if( iCmp == 0 ) iCmp = strncmp( szFuncCall, f[ i ].cFuncName, strlen( szFuncCall ) ); if( iCmp == 0 ) { iPos = i; break; } else ++i; } if( iPos >= 0 && ( f[ iPos ].iMinParam != -1 ) ) { if( iArgs < f[ iPos ].iMinParam || ( f[ iPos ].iMaxParam != -1 && iArgs > f[ iPos ].iMaxParam ) ) { char szMsg[ 40 ]; if( f[ iPos ].iMaxParam == -1 ) sprintf( szMsg, "\nPassed: %i, expected: at least %i", iArgs, f[ iPos ].iMinParam ); else if( f[ iPos ].iMinParam == f[ iPos ].iMaxParam ) sprintf( szMsg, "\nPassed: %i, expected: %i", iArgs, f[ iPos ].iMinParam ); else sprintf( szMsg, "\nPassed: %i, expected: %i - %i", iArgs, f[ iPos ].iMinParam, f[ iPos ].iMaxParam ); GenError( _szCErrors, 'E', ERR_CHECKING_ARGS, szFuncCall, szMsg ); /* Clipper way */ /* GenError( _szCErrors, 'E', ERR_CHECKING_ARGS, szFuncCall, NULL ); */ } } } /* NOTE: Making the date and time info to fit into 32 bits can only be done in a "lossy" way, in practice that means it's not possible to unpack the exact date/time info from the resulting ULONG. Since the year is only stored in 6 bits, 1980 will result in the same bit pattern as 2044. The purpose of this value is only used to *differenciate* between the dates ( the exact dates are not significant ), so this can be used here without problems. */ /* 76543210765432107654321076543210 |.......|.......|.......|....... |____| Year 6 bits |__| Month 4 bits |___| Day 5 bits |___| Hour 5 bits |____| Minute 6 bits |____| Second 6 bits */ static ULONG PackDateTime( void ) { BYTE szString[ 4 ]; BYTE nValue; time_t t; struct tm * oTime; time( &t ); oTime = localtime( &t ); nValue = ( BYTE ) ( ( ( oTime->tm_year + 1900 ) - 1980 ) & ( 2 ^ 6 ) ) ; /* 6 bits */ szString[ 0 ] = nValue << 2; nValue = ( BYTE ) ( oTime->tm_mon + 1 ); /* 4 bits */ szString[ 0 ] |= nValue >> 2; szString[ 1 ] = nValue << 6; nValue = ( BYTE ) ( oTime->tm_mday ); /* 5 bits */ szString[ 1 ] |= nValue << 1; nValue = ( BYTE ) oTime->tm_hour; /* 5 bits */ szString[ 1 ] = nValue >> 4; szString[ 2 ] = nValue << 4; nValue = ( BYTE ) oTime->tm_min; /* 6 bits */ szString[ 2 ] |= nValue >> 2; szString[ 3 ] = nValue << 6; nValue = ( BYTE ) oTime->tm_sec; /* 6 bits */ szString[ 3 ] |= nValue; return HB_MKLONG( szString[ 3 ], szString[ 2 ], szString[ 1 ], szString[ 0 ] ); } static BOOL SwitchCmp( char * szString, char * szSwitch ) { if( IS_OPT_SEP( *szString ) ) { szString++; if( strlen( szString ) == strlen( szSwitch ) ) { while( *szString != '\0' ) { if( toupper( *szString ) != *szSwitch ) return FALSE; szString++; szSwitch++; } return TRUE; } } return FALSE; }