diff --git a/harbour/ChangeLog b/harbour/ChangeLog index b78695eddd..fb9342211d 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,31 @@ +19990908-17:00 GMT+2 Ryszard Glab + + *source/compiler/harbour.y + * corrected handling of parenthesized expressions + * corrected generation of pcodes for declaration of dimensioned + array variables ( LOCAL var[ 2, 4 ] ) - there is no longer POP + opcode between array dimensions + + *source/vm/hvm.c + + added code that creates multi-dimensioned arrays + (arrays declared by LOCAL arr[ 3, 5, getDim() ] - it + supports PRIVATE and PUBLIC declaration too) + + *source/rtl/arrays.c + + added functions hb_arrayGetItemPointer() which returns a pointer + to an item occcupied by a specified array element + + *include/extend.h + + added declaration of hb_arrayGetItemPointer() function + + *tests/broken/parexpr.prg + ! moved to tests/working directory + + added some code to test IF and IIF expressions (it is really + related to parenthesized expressions) + + *tests/working/Makefile + + added parexpr.prg + 19990908-10:30 EDT David G. Holm * source/rtl/environ.c ! I restored the Windows #ifdef #include block from the 1.43 version, diff --git a/harbour/include/extend.h b/harbour/include/extend.h index e0c5b5ea45..0d95afbae0 100644 --- a/harbour/include/extend.h +++ b/harbour/include/extend.h @@ -288,6 +288,7 @@ extern BOOL hb_arrayLast( PHB_ITEM pArray, PHB_ITEM pResult ); extern BOOL hb_arrayRelease( PHB_ITEM pArray ); /* releases an array - don't call it - use ItemRelease() !!! */ extern BOOL hb_arraySet( PHB_ITEM pArray, ULONG ulIndex, PHB_ITEM pItem ); /* sets an array element */ extern BOOL hb_arrayGet( PHB_ITEM pArray, ULONG ulIndex, PHB_ITEM pItem ); /* retrieves an item */ +extern PHB_ITEM hb_arrayGetItemPointer( PHB_ITEM pArray, ULONG ulIndex ); /* returns pointer to specified element of the array */ extern ULONG hb_arrayCopyC( PHB_ITEM pArray, ULONG ulIndex, char * szBuffer, ULONG ulLen ); extern char * hb_arrayGetC( PHB_ITEM pArray, ULONG ulIndex ); /* retrieves the string contained on an array element */ extern char * hb_arrayGetCPtr( PHB_ITEM pArray, ULONG ulIndex ); /* retrieves the string pointer on an array element */ diff --git a/harbour/source/compiler/harbour.y b/harbour/source/compiler/harbour.y index 919d7646a4..e8e542241f 100644 --- a/harbour/source/compiler/harbour.y +++ b/harbour/source/compiler/harbour.y @@ -139,6 +139,22 @@ 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 */ + LONG prevSize; + LONG prevPos; + BYTE * exprPCode; /* pcode buffer for current expression */ + LONG 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 */ + /* lex & yacc related prototypes */ void yyerror( char * ); /* parsing error management function */ int yylex( void ); /* main lex token function, called by yyparse() */ @@ -192,6 +208,7 @@ void GenArray( WORD wElements ); /* instructs the virtual machine to build an ar void GenBreak( void ); /* generate code for BREAK statement */ void * GenElseIf( void * pFirstElseIf, WORD wOffset ); /* 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 ) */ PFUNCTION GetFuncall( char * szFunName ); /* locates a previously defined called function */ int GetFieldVarPos( char *, PFUNCTION * ); /* return if passed name is a field variable */ PVAR GetVar( PVAR pVars, WORD wOrder ); /* returns a variable if defined or zero */ @@ -477,6 +494,8 @@ ALIASID_PTR pAliasId = NULL; WORD _wLastLinePos = 0; /* position of last opcode with line number */ BOOL _bDontGenLineNum = FALSE; /* suppress line number generation */ +EXPLIST_PTR _pExpList = NULL; /* stack used for parenthesized expressions */ + PSTACK_VAL_TYPE pStackValType = NULL; /* compile time stack values linked list */ char cVarType = ' '; /* current declared variable type */ @@ -536,7 +555,7 @@ extern int _iState; /* current parser state (defined in harbour.l */ %type IDENTIFIER LITERAL FunStart MethStart IdSend ObjectData AliasVar %type DOUBLE %type ArgList ElemList PareExpList ExpList FunCall FunScope IncDec -%type Params ParamList Logical +%type Params ParamList Logical ArrExpList %type INTEGER BlockExpList Argument IfBegin VarId VarList MethParams ObjFunCall %type MethCall BlockList FieldList DoArgList VarAt %type INTLONG WhileBegin BlockBegin @@ -709,11 +728,11 @@ NumExpression : DOUBLE { PushDouble( $1.dNumber,$1.bDec ) ConExpression : NIL { PushNil(); } | LITERAL { PushString( $1 ); } | CodeBlock {} + | Logical { PushLogical( $1 ); } ; DynExpression : Variable | VarUnary - | Logical { PushLogical( $1 ); } | Operators {} | FunCall { Function( $1 ); } | IfInline {} @@ -722,61 +741,26 @@ DynExpression : Variable | Macro {} | AliasVar { PushId( $1 ); AliasRemove(); } | AliasFunc {} - | PareExpList {} | SELF { GenPCode1( HB_P_PUSHSELF ); } ; -Expression : NumExpression +SimpleExpression : NumExpression | ConExpression | DynExpression ; -IfInline : IIF '(' Expression ',' { $$ = JumpFalse( 0 ); } - IfInlExp ',' { $$ = Jump( 0 ); JumpHere( $5 ); } - IfInlExp ')' { JumpHere( $8 ); - if( _bWarnings ) - { - PSTACK_VAL_TYPE pFree; - - if( pStackValType ) - { - pFree = pStackValType; - debug_msg( "\n***---IIF()\n", NULL ); - - pStackValType = pStackValType->pPrev; - hb_xfree( ( void * )pFree ); - } - else - debug_msg( "\n***IIF() Compile time stack overflow\n", NULL ); - } - } - - | IF '(' Expression ',' { $$ = JumpFalse( 0 ); } - IfInlExp ',' { $$ = Jump( 0 ); JumpHere( $5 ); } - IfInlExp ')' { JumpHere( $8 ); - - if( _bWarnings ) - { - PSTACK_VAL_TYPE pFree; - - if( pStackValType ) - { - pFree = pStackValType; - debug_msg( "\n***---IIF()\n", NULL ); - - pStackValType = pStackValType->pPrev; - hb_xfree( ( void * )pFree ); - } - else - debug_msg( "\n***IIF() Compile time stack overflow\n", NULL ); - } - } +Expression : SimpleExpression {} + | PareExpList {} ; -IfInlExp : /* nothing => nil */ { PushNil(); } +EmptyExpression: /* nothing => nil */ | Expression ; +IfInline : IIF PareExpList3 { GenIfInline(); } + | IF PareExpList3 { GenIfInline(); } + ; + Macro : '&' Variable | '&' '(' Expression ')' ; @@ -983,11 +967,42 @@ BlockList : IDENTIFIER { cVarType = ' '; AddVar( $1 | BlockList ',' IDENTIFIER { AddVar( $3 ); $$++; } ; -PareExpList: '(' ExpList ')' { $$ = $2; } +/* 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 ); } ; -ExpList : Expression %prec POST { $$ = 1; } - | ExpList { GenPCode1( HB_P_POP ); } ',' Expression %prec POST { $$++; } +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 = ' '; } @@ -1019,8 +1034,12 @@ VarDef : IDENTIFIER { cVarType = ' '; AddV | IDENTIFIER AS_ARRAY INASSIGN Expression { cVarType = 'A'; AddVar( $1 ); PopId( $1 ); } | IDENTIFIER AS_BLOCK INASSIGN Expression { cVarType = 'B'; AddVar( $1 ); PopId( $1 ); } | IDENTIFIER AS_OBJECT INASSIGN Expression { cVarType = 'O'; AddVar( $1 ); PopId( $1 ); } - | IDENTIFIER '[' ExpList ']' { cVarType = ' '; AddVar( $1 ); DimArray( $3 ); PopId( $1 ); } - | IDENTIFIER '[' ExpList ']' AS_ARRAY { cVarType = 'A'; AddVar( $1 ); DimArray( $3 ); PopId( $1 ); } + | IDENTIFIER ArrExpList ']' { cVarType = ' '; AddVar( $1 ); DimArray( $2 ); PopId( $1 ); } + | IDENTIFIER ArrExpList ']' AS_ARRAY { cVarType = 'A'; AddVar( $1 ); DimArray( $2 ); PopId( $1 ); } + ; + +ArrExpList : '[' Expression { $$ = 1; } + | ArrExpList ',' Expression { $$++; } ; FieldsDef : FIELD { iVarScope =VS_FIELD; } FieldList Crlf @@ -1058,7 +1077,19 @@ IfEndif : IfBegin EndIf { JumpHere( $1 ); } | IfBegin IfElseIf IfElse EndIf { JumpHere( $1 ); FixElseIfs( $2 ); } ; -IfBegin : IF Expression { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } +IfBegin : IF SimpleExpression { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } + IfStats + { $$ = Jump( 0 ); JumpHere( $5 ); } + + | IF PareExpList1 { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } + IfStats + { $$ = Jump( 0 ); JumpHere( $5 ); } + + | IF PareExpList2 { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } + IfStats + { $$ = Jump( 0 ); JumpHere( $5 ); } + + | IF PareExpListN { ++_wIfCounter; } Crlf { $$ = JumpFalse( 0 ); Line(); } IfStats { $$ = Jump( 0 ); JumpHere( $5 ); } ; @@ -2199,6 +2230,97 @@ void DupPCode( WORD wStart ) /* duplicates the current generated pcode from an o GenPCode1( functions.pLast->pCode[ wStart + 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 = 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 ) + GenPCode1( HB_P_POP ); + } + 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 */ @@ -3329,6 +3451,134 @@ void GenExterns( void ) /* generates the symbols for the EXTERN names */ } } +/* 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 ); + } + + if( _bWarnings ) + { + PSTACK_VAL_TYPE pFree; + + if( pStackValType ) + { + pFree = pStackValType; + debug_msg( "\n***---IIF()\n", NULL ); + + pStackValType = pStackValType->pPrev; + hb_xfree( ( void * )pFree ); + } + else + debug_msg( "\n***IIF() Compile time stack overflow\n", NULL ); + } +} + + PFUNCTION GetFuncall( char * szFunctionName ) /* returns a previously called defined function */ { PFUNCTION pFunc = funcalls.pFirst; diff --git a/harbour/source/rtl/arrays.c b/harbour/source/rtl/arrays.c index 56b9993aca..e376ad79a5 100644 --- a/harbour/source/rtl/arrays.c +++ b/harbour/source/rtl/arrays.c @@ -262,6 +262,32 @@ char * hb_arrayGetDS( PHB_ITEM pArray, ULONG ulIndex, char * szDate ) return szDate; } +BOOL hb_arrayGetBool( PHB_ITEM pArray, ULONG ulIndex ) +{ + if( IS_ARRAY( pArray ) ) + { + if( ulIndex > 0 && ulIndex <= pArray->item.asArray.value->ulLen ) + return hb_itemGetL( pArray->item.asArray.value->pItems + ulIndex - 1 ); + } + + return FALSE; +} + +/* + * This function returns a pointer to an item occupied by the specified + * array element - it doesn't return an item's value + */ +PHB_ITEM hb_arrayGetItemPointer( PHB_ITEM pArray, ULONG ulIndex ) +{ + if( IS_ARRAY( pArray ) ) + { + if( ulIndex > 0 && ulIndex <= pArray->item.asArray.value->ulLen ) + return pArray->item.asArray.value->pItems + ( ulIndex - 1 ); + } + + return NULL; +} + BOOL hb_arrayGetL( PHB_ITEM pArray, ULONG ulIndex ) { if( IS_ARRAY( pArray ) && ulIndex > 0 && ulIndex <= pArray->item.asArray.value->ulLen ) diff --git a/harbour/source/vm/hvm.c b/harbour/source/vm/hvm.c index a39306ba29..a1629ac11c 100644 --- a/harbour/source/vm/hvm.c +++ b/harbour/source/vm/hvm.c @@ -68,6 +68,8 @@ static void hb_vmReleaseLocalSymbols( void ); /* releases the memory of the static void hb_vmDebuggerShowLine( WORD wLine ); /* makes the debugger shows a specific source code line */ static void hb_vmDebuggerEndProc( void ); /* notifies the debugger for an endproc */ +static void hb_vmArrayNew( HB_ITEM_PTR, WORD ); /* creates array */ + #ifdef HARBOUR_OBJ_GENERATION static void hb_vmProcessObjSymbols ( void ); /* process Harbour generated OBJ symbols */ @@ -896,24 +898,58 @@ void hb_vmDec( void ) } } +/* This function creates an array item using 'wDimension' as an index + * to retrieve the number of elements from the stack + */ +static void hb_vmArrayNew( HB_ITEM_PTR pArray, WORD wDimension ) +{ + ULONG ulElements; + HB_ITEM_PTR pDim = stack.pPos - wDimension; + + /* use the proper type of number of elements */ + switch( pDim->type & ~IT_BYREF ) + { + case IT_INTEGER: + ulElements = (ULONG) pDim->item.asInteger.value; + break; + + case IT_LONG: + ulElements = pDim->item.asLong.value; + break; + + case IT_DOUBLE: + ulElements = (ULONG) pDim->item.asDouble.value; + break; + + default: + /* NOTE: Clipper creates empty array if non-numeric value is + * specified as dimension and stops further processing. + * There is no runtime error generated. + */ + ulElements = 0; + break; + } + + /* create an array */ + hb_arrayNew( pArray, ulElements ); + + if( --wDimension ) + { + /* call self recursively to create next dimensions + */ + while( ulElements ) + hb_vmArrayNew( hb_arrayGetItemPointer( pArray, ulElements-- ), wDimension ); + } +} + void hb_vmDimArray( WORD wDimensions ) /* generates a wDimensions Array and initialize those dimensions from the stack values */ { HB_ITEM itArray; - WORD w; /* , wElements; */ itArray.type = IT_NIL; - hb_arrayNew( &itArray, ( stack.pPos - wDimensions )->item.asLong.value ); + hb_vmArrayNew( &itArray, wDimensions ); - if( wDimensions > 1 ) - hb_errInternal( 9999, "HVM.C hb_vmDimArray() does not supports multiple dimensions yet", NULL, NULL ); - -/* - for( w = 0; w < wElements; w++ ) - hb_itemCopy( itArray.item.asArray.value->pItems + w, - stack.pPos - wElements + w ); -*/ - - for( w = 0; w < wDimensions; w++ ) + while( wDimensions-- ) hb_stackPop(); hb_itemCopy( stack.pPos, &itArray ); diff --git a/harbour/tests/working/Makefile b/harbour/tests/working/Makefile index 7c0d11a4dc..1ac731da34 100644 --- a/harbour/tests/working/Makefile +++ b/harbour/tests/working/Makefile @@ -94,6 +94,7 @@ PRG_SOURCES=\ os.prg \ output.prg \ overload.prg \ + parexpr.prg \ passref.prg \ procline.prg \ procname.prg \ diff --git a/harbour/tests/working/parexpr.prg b/harbour/tests/working/parexpr.prg new file mode 100644 index 0000000000..7c96323965 --- /dev/null +++ b/harbour/tests/working/parexpr.prg @@ -0,0 +1,89 @@ +// +// $Id$ +// + +// The following code tests harbour's ability to cope with parenthesized +// expressions. + +// These tests were written by Dave Pearson and +// are placed into the public domain. + +Function Main() +Local x +Local y + + // Simple one to start with. + x := ( 1 ) + ? x + + // Now with a little more complex: + x := ( 1, 2 ) + ? x + + // And a little more, this is really the same as the previous one. + x := ( 1, 2, 3 ) + ? x + + // Expression within expression + x := ( ( 1, 2, 3 ) ) + ? x + + // And a little more: + x := ( ( 1, 2, 3 ), ( 1, 2, 3 ) ) + ? x + + // Some inline assignments + x := ( y := 10, y ) + ? x + + x := ( ( y := ( 1, 2, 3) ), y * ( 10, 20, 30 ) ) + ? x + + // Now mix with statements and functions + ? ( 1, 2, 3 ) + + If ( y := .t. ) + ? "Working" + Else + ? "Borken" + EndIf + + If ( x := 10, y := ( x == 10 ) ) + ? "Working" + Else + ? "Broken" + EndIf + + If ( Something( 1, 2, 3 ), .T. ) + ? "Working" + Else + ? "Broken" + EndIf + + ? + + // Now even some more testing of related code + // placed into public domain by Ryszard Glab + + ? IF( (.F.,0,.T.), ("some", "text", "IF Working"), ("some", "text", "Broken") ) + ? IF( (.T.,1,.F.), ("some", "text", "Broken"), ("some", "text", "IF Working") ) + + ? IF( (.T. .OR. .F.), IF( .T., "Working", "Broken" ), IF( .F., "Broken", "Working" ) ) + + /* The following code should generate syntax error if uncommented + * because IF token followed by any three expressions is interpreted + * as IIF inline + */ +// IF( .T., .F., .T. ) +// ? "Working" +// ELSE +// ? "Broken" +// ENDIF + +Return( NIL ) + +Static Function Something( x, y, z ) + + // This does something and it does it well/ + +Return( NIL )