Codeblocks

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

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

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