The Harbour implementation of codeblocks Author : Ryszard Glab <rglab@imid.med.pl> Compilation of a codeblock. During compile time the codeblock is stored in the following form: - the header - the stream of pcode bytes The header stores information about referenced local variables. +0: the pcode byte for _PUSHBLOCK +1: the number of bytes that defines a codeblock +3: number of codeblock parameters (declared between || in a codeblock) +5: number of used local variables declared in procedure/function where the codeblock is created +7: the list of procedure/function local variables positions on the eval stack of procedure/function. Every local variable used in a codeblock occupies 2 bytes in this list. When nested codeblocks are used then this list is created for the outermost codeblock only. +x: The stream of pcode bytes follows the header. +y: the pcode byte for _ENDBLOCK Creation of a codeblock. When HB_P_PUSHBLOCK opcode is executed then the HB_ITEM structure is created and placed on the eval stack. The type of item is IT_BLOCK. The value of this item is a pointer to HB_CODEBLOCK structure. Additionally this item stores the base of static variables defined for the current function/procedure - this is used during a codeblock evaluation when the evaluation is called from a code from other PRG module. Also the number of expected parameters is stored. The HB_CODEBLOCK structure stores a pointer to the pcodes stream that is executed during a codeblock evaluation. It stores also the pointer to a table with local variables references. Values of all local variables defined in a procedure and used in a codeblock are replaced with a reference to a value stored in a global memory variables pool. This allows the correct access to detached local variables in a codeblock returned from this function (either directly in RETURN statement or indirectly by assigning it to a static or memvar variable. This automatic and unconditional replace is required because there is no safe method to find if a codeblock will be accessed from an outside of a function where it is created. When nested codeblocks are used then only the outermost codeblock creates the table - all inner codeblock uses this table. The first element of this table contains a reference counter for this table. It allows to share the table between nested codeblock - the table is deleted if there is no more references to it. This is caused by the fact that a inner codeblock can be created during evaluation of outer codeblock when local variables don't exist like in this example: PROCEDUE Main() PRIVATE foo, bar Test() Eval( foo ) Eval( bar ) PROCEDURE Test() LOCAL a := "FOO", b := "BAR" foo := {|| a + ( bar := Eval( {|| b } ) ) } RETURN Evaluation of a codeblock. Parameters passed to a codeblock are placed on the eval stack before a codeblock evaluation. They are accessed just like usual function parameters. When a codeblock parameter is referenced then its position on the eval stack is used. When a procedure local variable is referenced then the index into the table of local variables positions (copied from the header) is used. The negative value is used as an index to distinguish it from the reference to a codeblock parameter. Incompatbility with the Clipper. 1) Detached locals passed by reference There is a little difference between the handling of variables passed by the reference in a codeblock. The following code explains it (thanks to David G. Holm) PROCEDURE Main() LOCAL nTest LOCAL bBlock1 := MakeBlock() LOCAL bBlock2 := {|| DoThing( @nTest ), QOut( "From Main: ", nTest ) } Eval( bBlock1 ) Eval( bBlock2 ) RETURN FUNCTION MakeBlock() LOCAL nTest RETURN {|| DoThing( @nTest ), QOut( "From MakeBlock: ", nTest ) } FUNCTION DoThing( n ) n := 42 RETURN NIL In Clipper it produces: From MakeBlock: NIL From Main: 42 In Harbour it produces (it is the correct output, IMHO) From MakeBlock: 42 From Main: 42 2) Scope of undeclared variables Consider the following code: PROCEDURE Main() LOCAL cb cb := Detach() ? Eval( cb, 10 ) RETURN FUNCTION Detach() LOCAL b := {| x | x + a } LOCAL a := 0 RETURN b In Clipper the 'a' variable in a codeblock has the *local* scope however in Harbour the 'a' variable has the *private* scope. As a result, in Clipper this code will print 10 and in Harbour it will raise 'argument error' in '+' operation. This will be true also when the 'a' variable will be declared as PRIVATE PROCEDURE Main() LOCAL cb PRIVATE a cb := Detach() ? Eval( cb, 10 ) RETURN The above code also prints 10 in Clipper (even if compiled with -a or -v switches) Source : https://github.com/harbour/core/blob/master/doc/codebloc.txt
Daily Archives: October 30, 2013
Command Line Issues
Harbour switch handling spec
============================
This spec goes for CLIPPERCMD, HARBOURCMD, Harbour
compiler and #pragma directives in the source code.
The command line always overrides the envvar.
Note that some switches are not accepted in envvar,
some others in #pragmas.
First the parser should start to step through
all the tokens in the string separated by
whitespace. (or just walk through all argv[])
1.) If the token begins with “-“, it
should be treated as a new style switch.
One or more switch characters can follow
this. The “-” sign inside the token
will turn off the switch.
If the switch has an argument all the following
characters are treated as part of the argument.
The “/” sign has no special meaning here.
Switch Resulting options
-wn ( W N )
-w-n ( !W N )
-wi/harbour/include/ ( W I=/harbour/include/ )
-wi/harbour/include/n ( W I=/harbour/include/n )
-wes0n ( W ES=0 N )
-wen ( W [invalid switch: e] N )
-wesn ( W ES=Default(0) N )
-wses ( W S ES=Default(0) )
-wess ( W ES=Default(0) S )
– ( [invalid switch] )
-w-n-p ( !W !N P )
-w-n-p- ( !W !N !P )
-w- -w -w- ( finally: !W )
2.) If the token begins with “/”, it
should be treated as a compatibility style switch.
The parser scans the token for the next “/” sign or EOS
and treats the resulting string as one switch.
This means that a switch with an argument containing
“/” sign has some limitations. This may be solved by
allowing the usage of quote characters. This is mostly
a problem on systems which use “/” as path separator.
The “-” sign has no special meaning here, it can’t be
used to disable a switch.
Switch Resulting options
/w/n ( W N )
/wo/n ( [invalid switch: wo] N )
/ihello/world/ ( I=hello [invalid switch: world] [invalid switch: /] )
/i”hello/world/”/w ( I=hello/world/ W )
/ihello\world\ ( I=hello\world\ )
3.) If the token begins with anything else it should
be skipped.
The Harbour switches are always case insensitive.
In the Harbour commandline the two style can be used together:
harbour -wnes2 /gc0/q0 -iC:\hello
Exceptions:
– Handling of the /CREDIT undocumented switch
on Harbour command line is unusual, check the current code
for this.
– The CLIPPER, HARBOUR and Harbour application
command line parsing is a different beast,
see cmdarg.c for a NOTE.
Just some examples for the various accepted forms:
//F20 == /F20 == F20 == F:20 == F20X
//TMPPATH:C:\hello
F20//TMPPATH:/temp///F:30000000 NOIDLE
F0NOIDLEX10
SQUAWKNOIDLE
“//” should always be used on the command line.
[ Copyright (c) 1999-2009 Viktor Szakats (vszakats.net/harbour)
Licensed under Creative Commons Attribution-ShareAlike 3.0:
http://creativecommons.org/licenses/by-sa/3.0/
See COPYING.txt. ]
Source : https://github.com/harbour/core/blob/master/doc/cmdline.txt
Class Template
Class Template /* NOTE: - Please use these template for your new files, replace parts between curly braces {} with the appropriate text. - You can find a history at the end of the file. */ FILE HEADER TEMPLATE ==================== /* * Harbour Project source code: * {one-liner description about the purpose of this source file} * * Copyright 2000 {list of individual authors and e-mail addresses} * www - http://harbour-project.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING.txt. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). * * As a special exception, the Harbour Project gives permission for * additional uses of the text contained in its release of Harbour. * * The exception is that, if you link the Harbour libraries with other * files to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the Harbour library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by the Harbour * Project under the name Harbour. If you copy code from other * Harbour Project or Free Software Foundation releases into a copy of * Harbour, as the General Public License permits, the exception does * not apply to the code that you add in this way. To avoid misleading * anyone as to the status of such modified files, you must delete * this exception notice from them. * * If you write modifications of your own for Harbour, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. * */ FILE HEADER TEMPLATE (OPTIONAL ADDITION FOR PARTIAL COPYRIGHTS) =============================================================== /* * The following parts are Copyright of the individual authors. * www - http://harbour-project.org * * Copyright 2000 {name} <{e-mail address}> * {function or subsystem name} * * See COPYING.txt for licensing terms. * */ CLASS HEADER TEMPLATE ======================== /* $CLASSDOC$ $NAME$ $CATEGORY$ $ONELINER$ $CONSTRUCTOR$ $ARGUMENTS$ $RETURNS$ $DESCRIPTION$ $DATALINK$ $DATANOLINK$ $METHODSLINK$ $METHODSNOLINK$ $EXAMPLES$ $STATUS$ $COMPLIANCE$ $PLATFORMS$ $FILES$ $SEEALSO$ $END$ */ /* $CLASSDOC$ $METHOD$ $CATEGORY$ $ONELINER$ $SYNTAX$ $ARGUMENTS$ $RETURNS$ $DESCRIPTION$ $END$ */ /* $CLASSDOC$ $DATA$ $CATEGORY$ $ONELINER$ $DESCRIPTION$ $END$ */ Source : https://github.com/harbour/core/blob/master/doc/class_tp.txt
Harbour Quotations
SP_BLANKREC
BLANKREC() Short: ------ BLANKREC() Blanks out a record Returns: -------- <lSuccess> => success, or not Syntax: ------- BLANKREC([nTries,lInteractive,cMessage]) Description: ------------ Attempts to lock the record <nTries> (default 5), and if no luck and <lInteractive> (default False), prompts the user with <cMessage> (default "Unable to lock record. Keep trying?") If a lock is gotten, the record is replaced with blank values. See BLANKFIELD() Examples: --------- DELETE BLANKREC() Notes: ------- Use ISBLANKREC() to locate blank records. This is a good way to re-use records, rather than using PACK. Source: ------- S_BLANK.PRG
SP_BLANKFIELD
BLANKFIELD() Short: ------ BLANKFIELD() Returns a blank value corresponding to a field Returns: -------- <expBlank> => blank value, corresponding to a field Syntax: ------- BLANKFIELD(cField) Description: ------------ <cField> is the name of the field. Can also include the alias A blank value is returned: For: Character Spaces the length of the field Numeric Zero Logical .f. Date Blank date Memo "" Examples: --------- // fill an array with matching blanks for the record - good // for an ADD routine aAdds := array(fcount()) for i = 1 to fcount() aAdds[i] := BLANKFIELD(field(i)) next Source: ------- S_BLANKS.PRG
SP_BIGELEM
BIGELEM() Short: ------ BIGELEM() Returns length of longest string in an array Returns: -------- <nLength> => Length of longest string in an array Syntax: ------- BIGELEM(aTarget) Description: ------------ Determines the length of the longest string element in <aTarget> Array may have mixed types Examples: --------- ?BIGELEM( {"1","22","333"} ) => returns 3 Notes: ------- This was a C function in previous SuperLibs Source: ------- S_BIGEL.PRG
SP_BEGEND
BEGEND() Short: ------ BEGEND() Determines beginning or end of week,month or quarter Returns: -------- <dReturnDate> => Date of beginning/end of week,month,qtr Syntax: ------- BEGEND(dStartDate,nBeginEnd,nWkMnthQtr,[nDayofWeek]) Description: ------------ Returns date which is beginning (1) or end (0) <nBeginEnd> of week (1) , month (2) ,or quarter (3) <nWkMnthQtr> in which input date <dStartDate> falls. If week, [nDayofWeek] is day of the week beginning or end of the week <nBeginEnd> falls on, with Sunday being 1 and Saturday being 7. Examples: --------- dDate := ctod("01/15/90") bow := BEGEND(dDate,1,1,1) // beg of week eow := BEGEND(dDate,0,1,7) // end of week bom := BEGEND(dDate,1,2) // beg of month eow := BEGEND(dDate,0,2) // end of month boq := BEGEND(dDate,1,3) // beg of quarter eoq := BEGEND(dDate,0,3) // end of quarter Source: ------- S_BEGEND.PRG
SP_AVARIANCE
AVARIANCE() Short: ------ AVARIANCE() Determines the variance of an array with condition Returns: -------- <nVariance> => Array variance Syntax: ------- AVARIANCE(aTarget,[bCondition]) Description: ------------ <aTarget> is the target array. Normally an array of numeric values. [bCondition] is an optional codeblock used to select a subset of the array. This could be used to filter out 0's or non-numeric elements. The block must accept an array element as a parameter, and return true or false <expL> to determine if this element is part of the desired subset. Examples: --------- v := AVARIANCE(aSales) v := AVARIANCE(aSales,{|e|valtype(e)=="N".and.e<>0}) Source: ------- S_ASTATS.PRG
SP_AUPDATED
AUPDATED() Short: ------ AUPDATED() Determines if array contains updated values for record Returns: -------- <lUpdated> => Are the values in the array updated Syntax: ------- AUPDATED(aCheck) Description: ------------ Compares the values in an array (usually created with a call to DBF2ARRAY() ) <aCheck> with the current values in the current record. The order of the array is presumed to match the order of the fields, and to be of length fcount(). If the values in the array are updated (changed), True is returned. Examples: --------- use customer a := DBF2ARRAY() // store values for i = 1 to len(a) @0+i,0 get a[i] next read // edit them if AUPDATED(a) // if they were updated from the DBF values ARRAY2DBF(a) // save them endif Source: ------- S_DBARR.PRG