HotBasic Compiler Tips
Updated: Sep 24, 2005Tips
.bat files: You can insert a program to run before compiling in your hb.bat file. E.g., to encrypt resources (see hotcrypt.bas). To decrypt resources, one can extract to a MEMORY or STRING var and use the .Decrypt method. Optionally, then the .SaveToFile method can be used to complete the transfer of the decrypted resource to a file.
$INCLUDE cannot be nested. HotBasic considers your source as a sequence of STATEMENTS so $INCLUDE can be used anywhere at any time. It is suggested that long source code efforts be broken up into smaller files and those be accessed with $INCLUDE. Personally, I find this helps to avoid editing errors.
API calls: All arguments generally are DWORD, INTEGER or LONG. However, HotBasic converts As STRING and As TYPE into pointers.
Strings and TYPES may always be passed by using VARPTR or @. E.g., @mystring$ results in a number -- a "long pointer" and therefore the Declare for the API might be "lpstring As LONG" or "As DWORD", etc.
Since API function names are case-sensitive, $UPPERCASE may have to be OFF. Note: -u switch turns it ON.
Application STACK: All applications in any language use a CPU register to maintain an application stack for temporary storage of limited amounts of information and for storage of arguments in subroutine and function calls. Get out your notepad:
HotBasic does NOT use the application stack for arguments for calls to SUBs and FUNCTIONs defined in your own source code. Rather the argument value is copied to the corresponding variable in the SUB or FUNCTION DECLARE statement. Thus, when program flow arrives at SUB or FUNCTION code, the variables named in the Declare statement have all the values you expect them to have -- the values of their arguments in the calling statement. Again, the arguments are NOT loaded on the stack.
Why is HotBasic designed like this? Easy. To be fast and efficient.
However, API calls are a different story. To call any external .obj or .dll module, the arguments are put on the application stack as required.
ARRAYS: Please see POSITION. For ARRAY A, SIZEOF(A) returns item size and A.Length returns total ARRAY size.
The size of each string in a string ARRAY defaults to 256 but may be modified using [* value] in the DIM statement. E.g.,
DIM A(20,100) AS STRING*32 'each string 32 bytes.
The TO keyword is used to DIM ranges of ARRAY subscripts if you need a non-zero-based range. E.g.,
DIM A(20, 1 TO 100) As LONG 'first subscript is zero-based; second not
CASE: Keyword TO not implemented.
COLOR, LOCATE: Use only two arguments, not three.
CONST For RapidQ compatibility, numeric constants default to DOUBLE unless you change the default like this: $OPTION DIM LONG. This $OPTION may be used at any time. Another route is to change all CONST to DEFDWORD or other DEF... statements as desired or to use $DEFINE OR $MACRO, which can define constants and use text substitution during compiling to place their values in source code. As in all cases of text substitution, avoid cases where a "ReplaceThis" string is embedded in any other source code text.
DECLARE: Presently, SUBs and FUNCTIONs need Declare statements. Use of GOSUB avoids need for DECLARE and passing arguments -- which only makes a copy of your variables (not so in API calls).
DEFxxx: A value may be assigned only to the last item in a comma-delimited list.
DIM, STATIC: Variables dimensioned in SUBS or FUNCTIONS are local in the sense that the same symbols can be used in more than one procedure. HotBasic renames those symbols as the procedure name plus the symbol name in user code, as will be shown in the Symbol Table. However, these variables may be accessed by code outside the procedure if this composite name is used.
ENVIRON: The argument is one string (HB), not two (RQ).
EXTRACTRESOURCE: Please see RESOURCE.
FOR NEXT parameters: For increased loop speed, the FOR index variable should be DWORD, INTEGER or LONG. Theoretical maximum loop speed is obtained if the keyword STEP is not used (STEP assumed to be 1).
FUNCTION: By definition, a function reference is a source value. If a return value is not wanted, DECLARE a SUB instead.
GOSUB: Can be used to avoid DECLARE for both SUBS and FUNCTIONS. Do not let your code "fall into" a GOSUB routine. Most SUB and FUNCTION declarations do not need arguments -- the routine can just access any dimensioned variables.
Most FUNCTIONs do not need to be functions either. You can write to any dimensioned variable for your RESULT values. If source code is cleaned up to remove unnecessary passing of arguments, your programs will run faster. FUNCTION has the limit of one "official" return value. However, a FUNCTION, SUB or GOSUB, you can write to any global variables for multiple "return values".
Hint: Put all your GOSUB procedures after the END statement. (filerec.bas, hottest.bas, etc)
Declared SUBs can be called in the usual manner or GOSUB can be used to enter any procedure at any line label in your code -- multiple entry points.
GOTO can be used to jump from code in one SUB to code in another SUB or GOSUB.
KEYWORDS that are not used, are no-ops and skipped and may be ommitted:
KEYWORDS that provoke WARNING in compilation:
BYREF in DECLARE statements (to alert user where a pointer may be required as an argument)
LIBRARYINST: Seldom used for API calls to .dlls. The LIB keyword in DECLARE statements automatically loads the .dll.
LIST: This needs an "index array" to be automatically created internally for each dimensioned LIST (QStringList). As it is now, it works. But access time for .Item(n) method is the sum of two times: (1) general procedure time and (2) an extra time proportional to (a) number of items in LIST and (b) average LIST item length.
As database managers know, an internal index array maintained silently would completely eliminate time (2) above. Meanwhile, LIST does work, but a slowdown may be noticed for longer lists where .Item(n) is used.
Hint: for sequential access from the beginning, .ReadLine is faster, because the delimiter for LISTs is CRLF and if MyList.Position is set to 0 first. When done, be sure to restore .Position (MyList.Position = MyList.Length) so that other methods like .AddItems will work properly.
LEN: About the only time HotBasic internally uses the string LEN function is when strings are obtained from external sources, such as the OS. Otherwise, HotBasic already knows the length of every string and it need not be evaluated again by a LEN call. .Length is a property of all stream objects including ARRAYs. E.g., i=A.Length or i=s$.Length which is always faster than i=LEN(s$).
LOCAL variables: All variables dimensioned in SUBs/FUNCTIONSs have "local scope" (please see DIM, STATIC above) by creation of unique user names (please see Symbol Table). A compiler error may signal duplicate names.
To create stack-like variables in SUBS or FUNCTIONS, there are two options.
(1) simulate a stack in the basic language as explained in RapidQ tips.
(2) use PUSH and POP. Imagine you want to use v1, v2, v3, etc over and over again in recursive calls:
DefInt v1,v2,v3 'non-float 4-byte signed values
'now inside your procedure code
PUSH v1: PUSH v2: PUSH v3
'saves their values so now you can use them any way you want.
'there is no way out of your procedure except at end:
POP v3: POP v2: POP v1 'note reversed order
'now your END SUB, RETURN or END FUNCTION here
POSITION property: All stream objects including ARRAY's maintain a .Position property. .Position always points to the next byte after the item last accessed. For speed, usage of ARRAY subsripts should be minimized where sequential access, by .Read, .Write or .ReadNum, etc, can be done. To "find your place", a single reference will "set the position" for the next item: x=A(i,j,k); or you can set the .Position by assignment at any time.
PRINT: Avoid concatenation of strings in PRINT statements. This is waste:
PRINT "Average = "+STR$(xmean)+"; "+STR$(n)+" trials"This is better: PRINT "Average = "; STR$(xmean); "; "; STR$(n); " trials"SUB: By definition, a SUB reference occupies the initial position in a statement. Needs to be declared. Use GOSUB to avoid declare statements.
TRUE/FALSE: In HotBasic, true is not equal to 0 and false = 0. The HotBasic versions of many functions, e.g., FILEEXISTS, etc, return true/false values. In HotBasic, IF statements can therefore be simplified, omitting the ">0" (testing for TRUE), for example.
HotBasic has numeric functions TRUE (alias for ONE) and FALSE (alias for ZERO) which are convenient. E.g., MyEdit.Visible = true
However, keep in mind the distinction between the function TRUE (one) and logical true (any value other than zero).
WARNINGS: allow compliation to proceed.
$INCLUDE "RapidQ.inc" is the kiss of death, not to mention "windows.inc". RQ uses these files to clean up unresolved symbols. HB compiles it all!
WHITE SPACE: At present, space characters should not precede "(" in SUB/FUNCTION calls, ";" or ",". It is suggested that code be edited as necessary to follow this syntax, but a work-around may be: $MACRO " (" ( and $MACRO " ," ,
However, these MACROS will alter quoted text in your source as well.
Tabs in source code are automatically replaced with space.
WITH works for destination objects or types. (see hotudt.bas)
With your help, the hope is that HotBasic can be one of the best compilers around -- for everything from games to business to science.Thank you very much for trying HotBasic, James J Keene PhD
Copyright © 2003-2006 James J Keene PhD
HotBasic™ is a trademark of James J Keene
Original Publication: Aug 30, 2003