What's new in PB/CC 3.0
Error Code Changes (3.00):
==========================
* Compiler error code 514 has been added "Enclosing <**> angle brackets
expected"
* Compiler error code 531 has been added "Object variable expected"
* Compiler error code 532 has been added "Variant variable expected"
* Compiler error code 533 has been added "Dispatch Object variable expected"
* Compiler error code 549 has been added "ByVal required with pointers"
* Compiler error code 552 has been added "TRY expected"
* Compiler error code 553 has been added "CATCH expected"
* Compiler error code 554 has been added "END TRY expected"
* Compiler error code 555 has been added "ON ERROR not allowed here"
* Compiler error code 557 has been added "Macro too long/complex"
* Compiler error code 558 has been added "MACRO expected"
* Compiler error code 559 has been added "END MACRO expected"
* Compiler error code 560 has been added "BLOCK expected"
* Compiler error code 561 has been added "END BLOCK expected"
* Compiler error code 562 has been added "INTERFACE expected"
* Compiler error code 563 has been added "END INTERFACE expected"
* Compiler error code 564 has been added "MACROTEMP not allowed here"
New Features - PB/CC 3.00
=========================
* COM: The COM acronym represents the "Component Object Model", which
describes a method of building and accessing "Components". These
are reuseable chunks of code and associated data, which may be
accessed by "COM-Aware" applications. One of the most frustrating
things about this technology has been the ever-changing list of
buzz-words used to describe it. We've evolved through OLE, VBX,
and OCX, to the more current COM and ActiveX. Though nuances of
difference abound, the important thing to remember is that COM and
ActiveX describe a means of accessing code and data. COM+ refers
to some extensions which are specific to Win2000. Throughout this
discussion, we'll use the terms COM Object and ActiveX Object to
describe components: reuseable chunks of code and associated data.
First and foremost, some buzz-word definitions are in order:
ActiveX Client: An application which uses the services offered
by an ActiveX Server (Component). A client is also referred
to as an ActiveX Controller because it controls the server:
It chooses which services of the component will be utilized,
when they will be utilized, and the purpose of each reference.
ActiveX Container: An application which can accept an ActiveX
Control (with a visual user interface) or an Activex Document
Object.
ActiveX Control: A special form of ActiveX component which
offers a visual user interface to work like a Windows control.
ActiveX Component: A COM Object, acting as a server, which can
be used by other programs to provide needed functionality.
ActiveX Document Object: A document which can be embedded into
an ActiveX Container.
ActiveX Server: A COM Object (Component) which can be used by
other programs to provide needed functionality. In most cases,
the terms ActiveX Component and ActiveX Server may be used
interchangeably.
A COM Object may reside in a DLL, in which case it is known as an
"In-Process" server because it shares address space with the client,
or controlling application. It may reside in an EXE, in which case
it is known as an "Out-of-Process" server because it does not share
address space. A COM Object may even reside on another computer,
even if it has a different architecture and/or operating system.
In that case, communication between client and server, over a local
network or even the Internet, is handled automatically by Windows,
through a transparent process known as DCOM or Distributed COM.
COM Objects may be referenced from any Win32 PowerBASIC program using
OLE Automation and the Dispatch interface to that object. An object
could be described simplistically as a set of functions and some
associated data. The internal implementation of the functions, and
the variables they use, are hidden from the outside world by this
concept of encapsulation. A COM Object is known as a server, because
it publishes an interface of functions through which other programs
may communicate. The client application, which is also known as the
controller, communicates with the server by calling one or more of
the published functions. It is never possible to reference object
data directly, as it is only assigned or retrieved through a call to
an object function.
A new class of variables is introduced. They are known generally as
object variables, and may only contain a reference to a COM Object,
and no other value of any kind. An object variable must be declared
by its specific class, which could be the generic "Dispatch" class,
or one which you explicitly define with an INTERFACE structure. An
object variable may only be used in specific situations, such as
execution of an object method. It is never legal to reference them
in normal numeric or string expressions, nor is it possible to even
PRINT their value without the use of the special new functions like
OBJPTR().
A Dispatch object is one which communicates through the IDispatch
interface, which is the most flexible method, as it supports both
named and optional parameters. While an object may support hundreds
of functions (known as Methods or Properties) in Dispatch Objects,
they are all accessed through a single entry point, called Invoke.
So how does the object tell one from another? By encoding every
function (Method or Property), and every named parameter as a special
numeric, long integer value known as a Dispatch ID, or DispID for
short. When a Dispatch Client calls a method in a Dispatch Object,
it passes one or more DispID's to tell the object precisely which
Method and which parameters it expects to utilize. Luckily,
PowerBASIC handles most all of these messy details for you, so all
you really need to know are the names of the functions and params.
Dispatch objects require that all parameters, return values, and
assignment values be passed only as Variant Variables.
PowerBASIC can access Dispatch Objects through "Early Binding" or
"Late Binding". Late binding, the simpler method, offers the most
flexibility. You don't need to declare Methods, Properties, and
parameters in advance, as everything is resolved at the time the
program is executed. A Dispatch Object knows a lot about itself,
including information about all of its member functions. However,
for that very reason, the validity of these references is not
checked at compile time. Virtually everything is done at run time.
To access a COM Object through late binding, you must first declare
a variable to be of the generic object class "Dispatch". Then,
you use the SET statement to create an instance of the Dispatch
Object in a specific server by referencing its unique ProgID. At
this point, you can finaly use the OBJECT statement to communicate
with the COM Server:
Dim OWord as Dispatch
Dim DocNum as Variant
Set OWord = Dispatch in "Word.Application.8"
Object Get OWord.Documents.Count to DocNum
Object Call OWord.Quit
Set OWord = Nothing
The above code snippet shows how you can access an active instance
of Microsoft Word, to ascertain the number of documents which are
currently open. When PowerBASIC executes the OBJECT GET statement,
this is a general idea of what happens "Behind the Scenes"...
1- The Object variable OWord is checked to see that it references
an instance of Microsoft Word.
2- The server is asked to verify that it supports a "Documents"
collection of Document Objects.
3- The server responds affirmatively and returns a Dispatch ID of
the value 6 for the client to use to identify it.
4- The server is instructed to execute function 6. It responds
by creating a Document object, returning a reference to it.
5- The server is asked to verify that it supports a "Count"
property within the Document object.
6- The server responds affirmatively and returns a Dispatch ID of
the value 2 for the client to use to identify it.
7- The server is instructed to execute function 2. It responds
by returning a Variant variable which contains that value.
8- The temporary Document object is destroyed.
An involved process? You bet it is, but it does offer functionality
which couldn't readily be duplicated in any other way. However, did
you notice that there are some ways this functionality could be
optimized? One thing is obvious... Why not look up the values of
the Dispatch ID's ahead of time? That certainly works, and it's
known as "Dispatch Early Binding". It takes a little more work
from you, but it offers a big boost in performance. All you have to
do is determine the value of the needed DispID's, and tell PowerBASIC
about it in an INTERFACE DISPATCH structure...
Interface Dispatch MSWord
Member Get Documents<6>() as Document
End Interface
Interface Dispatch Document
Member Get Count<2>()
End Interface
You can list every Method/Property in the interface, or just the
ones you reference in your code. They can appear in any sequence.
The only thing we're doing here is associating each Method/Property
name, and each Named Parameter with the appropriate DispID value,
and identifying any property which returns an object to be used in
a nested object reference like "Document". You can look up the
DispID's of your COM Servers using an Object Browser, or by reading
the object documentation. If a Member offers named parameters,
they're declared in a similar fashion...
Member Call Insert<4>(Line<5>)
Finally, you can even insert some additional information about each
parameter, the internal type, and the Member return value for your
own reference, even though the compiler doesn't use it. Namely,
each parameter may be preceded with: Optional, In, Out, InOut)
Member Call Insert<4>(In Line as String<5>) as String
Put it all together, and you have...
Interface Dispatch MSWord
Member Get Documents<6>() as Document
End Interface
Interface Dispatch Document
Member Get Count<2>()
End Interface
Dim OWord as MSWord
Dim DocNum as Variant
Set OWord = MSWord in "Word.Application.8"
Object Get OWord.Documents.Count to DocNum
Object Call OWord.Quit
Set OWord = Nothing
The source code looks surprisingly similar, but in many cases,
you'll find that the execution speed is remarkably faster.
* The compiler accepts Long File Names for both source and object files,
as well as quoted file/path names on the command line. Since Long
File Names may contain conflicting characters (like blank spaces,
semi-colons, etc.) quotes will often be required to avoid ambiguity:
PBCC "Go To The Store.Bas" /i"First;Path";"\Any Path";\Root;
* The compiler accepts a new command line option: /cfilename.any which
specifies a text file which contains the complete command line.
This may be optionally used to specify a very long command line,
up to 1024 bytes long, in excess of the operating system limitation.
* PB/CC has allowed a pointer variable to be declared as a Sub/Function
parameter (x as byte ptr), but it has always been forced to BYVAL
internally, even though the BYVAL modifier may have been omitted.
It has never been possible to pass a pointer BYREF as a parameter
and an attempt to do so will still generate error code 549: "ByVal
option is required". This behavior is not consistent with the
normal default to BYREF, and will most certainly change in future
versions of PowerBASIC. We strongly encourage you to modify all
source code to explicitly declare pointer parameters as ByVal, such
as, for example: Sub abc(ByVal CmdLine as asciiz ptr)
* When a Sub/Function definition specifies either a Byref parameter or
a pointer variable parameter, the calling code may freely pass a
Byval Dword or a Pointer instead. While the use of the explicit
BYVAL override in the calling code is optional, it is recommended
for clarity.
* Sub/Function parameter variables passed by reference may be freely
substituted between both long/dword and integer/word variables, as
the sizes are identical. It is the programmer's responsibility to
ensure that the value is useable in the new context. For example,
passing a word value of 65535 to a function expecting an integer
will be seen as -1 to the function receiving it.
* Declare, Sub, and Function statements may specify one or more
parameters as optional by preceding the parameter with either the
word OPTIONAL, or the abbreviation OPT. Optional parameters are
only allowed with CDECL or SDECL calling conventions, not BDECL.
When a parameter is declared OPTIONAL, all remaining parameters
are OPTIONAL as well, whether or not they specify an explicit
OPTIONAL directive. Optional ByVal Strings are now supported.
Function ABC&(aaa&, Optional ByVal bbb&, Opt ByVal ccc&)
When optional parameters are omitted, the stack area normally
reserved for that parameter is zero-filled, so ByVal parameters
default to the value 0 or a null string. However, care should
be exercised with Optional ByRef parameters, as an attempt to
reference them through the null pointer will certainly cause a
fatal exception (GPF) to occur. If ByRef parameters are used,
it is the programmer's responsibility to avoid this situation
by passing other information which defines which optional
parameters, if any, are passed as optional null pointers. The
OPTIONAL directive provides the same functionality as the older
syntax using square brackets "[]". Square brackets will not be
supported in future versions of the compiler, so source code
should be updated to the use of OPTIONAL as soon as possible.
* Sub/Function parameters may now include Fixed Strings, Asciiz Strings,
and User-Defined Types which are passed ByVal. This capability
should normally be limited to smaller sizes because of the overhead
of copying data to the stack, and there is an absolute limit of 64k
for all paramters in one function combined, which is imposed by the
Intel cpu. You may interchange any of these three parameter types,
and still maintain maximum efficiency, as long as the length of the
variable is identical to that of the parameter. You may also pass
a dynamic string, a string expression, or a variable of a different
length, but that will impose at least a small additional overhead.
* Assignment of a value to a String Equate may now include a calculated
string expression. This includes concatenation of string literals,
other string equates, CHR$(), GUID$(), SPACE$(), and STRING$()
functions. For example...
$AnEquate = "Much " & CHR$(9) & " more flexibility" &
CHR$(13,10)
All features of these functions may be used, even ranges and strings
with CHR$(), although all function parameters must be expressed as
literal values.
* Expressions involving Numeric Equates and conditional compilation may
include the relational operators +,-,>,<,>=,<=,<>,=.
%xyz = (%abc = 3) or #IF %abc = 77
* Expressions involving Numeric Equates and conditional compilation may
include the CVQ() function. This allows you to easily assign numeric
values to an equate, based upon a meaningful mnemonic. In this
context, the CVQ expression is limited to a length of eight bytes.
%mode = CVQ("DEBUG") or %style = CVQ("Cool")
* The capacity for programs with larger code and/or data size has been
substantially increased.
* User-Defined Type or Union definitions may be repeated any number of
times within source code, as long as the definitions are identical.
* Structures (Type/Union) may be embedded within another structure, for
simplification in referencing deeply nested items, by simply stating
the structure name alone at the appropriate position. The internal
alignment of the member structure is precisely maintained, regardless
of other alignment specifications, to foster inheritance issues.
Type ABCD3 Type ABCD2 Union ABCD1
a as long d as dword f as dword
ABCD2 e as double g as long
c as long ABCD1 h as single
End Type End Type End Union
In this case, you could access the lone single float member of this
structure very simply. Assuming DIM X AS ABCD3, you could print the
single precision Union Member with the statement PRINT X.H, instead
of the extended syntax ABCD3.ABCD2.ABCD1.H
* GUID variables are supported, and are used to contain a 128-bit
Globally Unique Identifier, primarily for use with Com Objects.
Generally speaking, a GUID variable is assigned a value with the
GUID$() function, or with a string equate, and that value usually
remains constant throughout the program. The GUID variable is
typically used only as a parameter, rather than as a term in an
expression. GUID variables must be explicitly declared with DIM,
LOCAL, etc., and are used in much the same way as a 16-byte fixed
length string or a user-defined type of that size. A GUID variable
is only valid as a parameter when it contains precisely 16 bytes of
data in an appropriate format. For example...
$idNull = guid$("{00000000-0000-0000-0000000000000000}")
Dim abc as local GUID : Dim xyz as global GUID
abc = $idNull
abc = guid$("{00000000-0000-0000-C000000000000046}")
xyz = abc
Print GuidTxt$(xyz)
* Variant variables are supported, but their use is limited to that of a
parameter, assignment for conversion of data, and compatibility with
other languages and applications. Although notoriously lacking in
efficiency, Variants are commonly used as Com Object parameters due
to their flexibility. You can think of a Variant as a kind of
container, which can hold a variable of most any data type, numeric,
string, or even an entire array. This simplifies the process of
calling procedures in a Com Object Server, as there is little need
to worry about the myriad of possible data types for each parameter.
This flexibility comes at a great price in performance, so PowerBASIC
limits their use to data storage and parameters only. You may assign
a numeric value, a string value, or even an entire array to a Variant
with the LET statement, or its implied equivalent. In the same way,
you may assign one Variant value to another Variant variable, or even
assign an array contained in a Variant to a compatible PowerBASIC
array, or the reverse. You may extract a simple scalar value from a
Variant with the VARIANT#() function for numeric values (regardless
of the internal numeric data type), or with the VARIANT$ function
for string values. You may determine the type of data a Variant
variable contains with the VariantVT() function. You may not use a
Variant in an expression, PRINT it, or name it as a member of a
structure like a Type/Union, etc. Instead, you must first extract
the value with one of the above conversion functions, and use that
acquired value for calculations. A Variant is always 16 bytes in
size, and may be passed as either a ByVal or ByRef parameter, at the
programmer's discretion. However, when a ByRef Variant is required
as a parameter, only a an explicit Variant variable may be passed by
the calling code -- a ByCopy expression is not allowed. All dynamic
strings contained in a Variant must be Wide/Unicode, and PowerBASIC
handles these conversions automatically. There may be some cases
where you wish to manipulate the internal structure of a variant
directly. Though possible, you must exercise caution or a serious
memory leak could occur. Since a variant could be the owner of a
string, array, etc., you must always reset a variant (Var=EMPTY)
prior to manipulation with POKE, pointers, etc. When you use the
standard PowerBASIC assignment syntax: [LET] Var = 21, all this
"housekeeping" is automatic and handled by the compiler. Every
Variant variable must be explicitly declared with an appropriate
statement such as: DIM xyz as Variant or LOCAL xyz as Variant
* Additional predefined numeric equates are included to represent each
of the PowerBASIC intrinsic data types. At this time, the primary
purpose is for use with the ARRAYATTR() function. You should always
use these predefined equates, rather than their literal values, as
the equated values are guaranteed to change in future versions.
They include %varclass_byt, %varclass_wrd, %varclass_dwd,
%varclass_int, %varclass_lng, %varclass_qud, %varclass_sng,
%varclass_dbl, %varclass_ext, %varclass_cur, %varclass_cux,
%varclass_vrnt, %varclass_ifac, %varclass_guid, %varclass_type,
%varclass_asc, %varclass_fix, %varclass_str.
* Additional predefined numeric equates are included to represent each
of the PowerBASIC variant data types. They include %vt_empty,
%vt_null, %vt_i2, %vt_i4, %vt_r4, %vt_r8, %vt_cy, %vt_date, %vt_bstr,
%vt_dispatch, %vt_error, %vt_bool, %vt_variant, %vt_unknown, %vt_i1,
%vt_ui1, %vt_ui2, %vt_ui4, %vt_i8, %vt_ui8, %vt_int, %vt_uint,
%vt_void, %vt_hresult, %vt_ptr, %vt_safearray, %vt_carray,
%vt_userdefined, %vt_lpstr, %vt_lpwstr, %vt_filetime, %vt_blob,
%vt_stream, %vt_storage, %vt_streamed_object, %vt_stored_object,
%vt_blob_object, %vt_cf, %vt_clsid, %vt_vector, %vt_array, %vt_byref.
* Additional predefined numeric equates are included to represent each
of the PowerBASIC Run-Time Error Codes. They include %err_NoError,
%err_IllegalFunctionCall, %err_Overflow, %err_OutOfMemory,
%err_SubscriptPointerOutOfRange, %err_DivisionByZero,
%err_DeviceTimeout, %err_InternalError, %err_BadFileNameOrNumber,
%err_FileNotFound, %err_BadFileMode, %err_FileIsOpen,
%err_DeviceIoError, %err_FileAlreadyExists, %err_DiskFull,
%err_InputPastEnd, %err_BadRecordNumber, %err_BadFileName,
%err_TooManyFiles, %err_DeviceUnavailable, %err_CommError,
%err_PermissionDenied, %err_DiskNotReady, %err_DiskMediaError,
%err_RenameAcrossDisks, %err_PathFileAccessError, %err_PathNotFound,
%err_FarHeapCorrupt, %err_StringSpaceCorrupt
* Predefined String Equates include: $NUL, $BEL, $BS, $TAB, $LF, $VT,
$FF, $CR, $CRLF, $EOF, $ESC, $SPC, $DQ.
* #BLOAT nexp allows the programmer to create a bloated target program,
one which is artificially increased in size to match or exceed that
generated by competing BloatWare compilers. The nexp specifies the
total desired size of the compiled program, but is ignored if it is
smaller than the actual program size. While #BLOAT adds no true
merit to the technical efficiency of the compiled code, there are
several possible reasons for its use:
1- To wean new PowerBASIC programmers off of BloatWare.
2- To impress managers with the volume of executable code created.
3- To allay the fears of uninformed customers who may mistakenly
infer "That tiny program couldn't possibly do everything that..."
* #STACK nexp allows the programmer to set the maximum potential stack
size. The literal numeric expression is expressed in bytes, and
is rounded up to the next 64k boundary. The minimum allowable
stack size is 128k, and a typical stack size of at least 1 Megabyte
(the default) is usually recommended. Upon program startup, an
initial block of 128k of physical memory is allocated to the stack.
As the stack grows, additional memory is automatically added, as
necessary, up to the specified maximum. Since physical memory is
only committed as required, it is usually prudent to overestimate
potential stack needs. In most cases, the default stack size is
acceptable, so the need for #STACK usage is generally rare.
* #TOOLS On|Off meta-statement allows integrated development tools like
Trace, Profile, and CallStk to be readily disabled, ensuring that
extra code and data is not compiled into the final version of an
application. #Tools defaults to On, and may appear only once in
your source, before any statements which generate executable code.
* ACODE$(unicode$) function returns the ansi, multi-byte equivalent of
the unicode string expression
* ARRAYATTR(x$(), i) returns various descriptive attributes of an
array, depending upon the value of i:
i=0, returns -1 if the array is currently dimensioned, 0 if not.
i=1, returns the data type as a numeric value equal to one of the
predefined numeric equates from %varclass_byt to %varclass_str.
i=2, returns -1 if it is an array of pointers, 0 if not.
i=3, returns the number of dimensions in the array.
i=4, returns the total number of elements in the array.
i=5, returns the size of each item in the array.
* BIT CALC var&, bitexpr, calcexpr allows you to set or reset a
bit in an integer class variable, or bit array, based upon a
value which is calculated at run time. The value bitexpr defines
the specific bit to be addressed, and may range from zero, for
the first bit, through the size of the variable or bit array.
The value calcexpr determines the action: if zero, the bit is
reset -- if one, the bit is set.
* CALLSTK "fname" causes extra code to be generated which allows
you to capture a representation of all of the stack frames which
exist above the one which includes the CallStk Statement. When
the CallStk Statement is executed, a standard sequential file
(of the specified file name) is created. It contains a list of
every call to a Sub/Function, and the associated parameter values,
which are currently defined on the application stack. In a test
or debugging situation, TRACE, CALLSTK, and CALLSTK$(n) allow
you to easily answer the age-old programming question "How did I
get here?". If PbMain() calls the Sub aaa(x&) which calls the
Sub bbb(y&), the CallStk from within bbb(y&) might look something
like:
PbMain()
aaa(77)
bbb(-1)
Later, if bbb(y&) exited, then aaa(x&) called ccc(z&), the updated
CallStk from within ccc(z&) might then appear as:
PbMain()
aaa(77)
ccc(33)
Numeric parameters are displayed in decimal, while pointer and
array parameters display a decimal representation of the offset of
the target value. CallStk can be invaluable during debugging,
but it generates substantial additional code which should be
avoided in a final release version of an application. If the
source code contains #TOOLS OFF, then all CallStk Statements
which remain in the program are ignored. The CALLSTK statement
is "Thread-Aware", displaying only stack frame details from the
thread in which it was executed.
* CALLSTK$(n) causes extra code to be generated which allows you to
capture a representation of any one of the stack frames which exist
above the one which includes CallStk$(n). CallStk$(1) returns
the name of the current Sub or Function, and the value of each of
the parameters at the time it was called. CallStk$(2) returns
the name of the Sub or Function which called the current one, as
well as its parameters. Likewise, CallStk$(3) returns the one
above it, and so forth. If the CallStk$(n) parameter is outside
the range of one through the number of stack frames, a null string
is returned. Numeric parameters are displayed in decimal, while
pointer and array parameters display a decimal representation of
the offset of the target value. The CallStk$() Function can be
invaluable during debugging, but it generates substantial extra
code which should be avoided in a final release version of an
application. If the source code contains #TOOLS OFF, then all
CallStk$(n) Functions which remain in the program return a null
string. The CALLSTK$(n) function is "Thread-Aware", returning only
stack frame details from the thread in which it was referenced.
* CALLSTKCOUNT causes extra code to be generated in association with
the CallStk Statement and CallStk$(n) function. CallStkCount
returns a long integer value which represents the number of stack
frames which currently exist on the application stack.
* CHOOSE(index&, choice1, choice2...) returns one of several values, based
upon the value of an index. CHOOSE() expects choices of any numeric
type. CHOOSE&() expects choices optimized for long integer type.
CHOOSE$() expects choices of string type. If the index evaluates to
one, choice1 is returned, if two, choice2 is returned, etc. If the
index is less than one or greater than the number of choices provided,
zero or a nul$ is returned. CHOOSE%() is recognized as a valid
synonym for CHOOSE&(). Contrary to the dubious implementation in MS
Visual Basic, only the selected choice is evaluated at run-time, not
all of them. This ensures optimum execution speed, as well as the
elimination of unanticipated side effects.
* CHR$() function may take a single parameter, multiple parameters, a
range of parameters, or a string parameter. Parameter types may be
intermixed within a function reference, so that CHR$("abc",13,10) is
legal. The purpose of this extended definition is to simplify
encoding of a multi-byte string, and avoid the need for concatenation
operations. CHR$(x& TO y&) returns a sequence of all characters from
CHR$(x&) through CHR$(y&), as long as x& <= y&. If x& > y&,
a null
string is returned. CHR$(65 to 70) returns "ABCDEF". Parameters may
be expressed as literals or as expressions.
CHR$("Line1",13,10,"Line2")
Line1" & chr$(13) & chr$(10) & "Line2"
The above two lines are functionally equivalent, and return the same
string result. The CHR$() function treats a numeric parameter of -1
as nul, in order to provide a natural complement to the ASC() function.
CHR$(-1)="" CHR$(65,-1,66)="AB"
* CLSID$(program_id$) looks up the text ProgID (the unique programmatic
identifier as a text name) in the registry, and returns the CLSID
associated with it, as a 16-byte string in GUID format. If the ProgID
cannot be found, or any error occurs, a null string is returned.
PROGID$() is the natural complement to CLSID$().
* CONSHNDL function returns the handle of the Console Window.
* CSET [ABS] var = $exp [USING $exp] centers a string within the space
of another string or a user-defined type. If the string expression
is shorter than the string/type variable, it is padded on both sides
with the first character of the "using" string, or spaces if not
specified. If the ABS option is included, and the "using" string is
null, the "padding" positions are left unchanged. CSET may be used
to assign the value of a user-defined type to a different class of
user-defined type.
* CSET$($exp, strlen& [USING $exp]) function returns a string expression
$exp, centered in a field of strlen& characters. The $exp is padded
to the requested length with the first character of the "using"
string, or spaces if not specified.
* DIM Statement may be used to declare a variable of the class of
Variant or Dispatch.
* DIR$ CLOSE will cause the operating system FindNext handle to be
closed. Each time a new DIR$() sequence is initiated within a
thread, or DIR$() returns a null string, PowerBASIC automatically
closes the FindNext handle to avoid overuse of system resources.
However, in unusual circumstances (such as a recursive directory
scan with delete), it may be necessary to close the FindNext
handle sooner, through the use of this explicit statement, so that
a directory can be removed. It is never necessary to execute
DIR$ CLOSE to simply avoid a "System Handle Leak".
* EOF(file#) function may be used with COMM LINE and TCP LINE to detect
that an incomplete line was received. Normally, these statements
read data until a cr/lf is found, and in that case, EOF() will
return false (zero). However, even if no cr/lf has been found, the
statements will end when no additional data is available. In that
case, they will return whatever data has already been accumulated,
and set EOF() as true (minus one). In many cases, it would be
prudent to test EOF() after every COMM LINE and TCP LINE to verify
that a full line has been received. In some cases, you may wish to
execute the statement one or more additional times, combining the
data, in order to obtain a full line of text.
* ERROR$(errnum&) function returns a string containing the name of
the PowerBASIC Run-Time Error Code specified by errnum&. If the
ERROR$ function is referenced with no parameter, PowerBASIC uses
the current value contained in ERR as the default. Run-Time
Error Codes are always smaller than 256.
* FILESCAN #fnum, RECORDS TO x&[, WIDTH TO y&] is used to rapidly scan
a file opened for input or binary mode in order to obtain size
information about variable length string data. It assigns a count
of the lines/records/strings to x& and the length of the longest
string to y&. In input mode, it is assumed the data is standard
text, delimited by a cr/lf or eof (1A hex). In binary mode, it is
assumed the file was written in the PowerBASIC and/or VB packed
string format using PUT of an entire string array: If a string is
shorter than 65535 bytes, a 2-byte length word is followed by the
string data. Otherwise, a 2-byte value of 65535 is followed by a
length dword, then finally the string data. If FILESCAN is applied
to other file formats, the results are undefined.
* FORMAT$(nexpr, fmtmask$) formats a numeric expression based upon the
specifications given in the fmtmask$. The fmtmask$ expression may
contain one, two, or three sub-masks, each of which are delimited
from the others by semicolons (;). If one mask is given, it is
used for all values of the numeric expression. If two masks are
given, the first is used for positive values, and the second for
negative values. If three masks are given, the first is used for
values greater than zero, the second for values less than zero,
and the third for a zero value. The format mask may contain any
text, which is included as-is in the returned string, and the
following special characters which determine the characteristics
of the formatted numeric value.
0 a digit is placed here, replaced by a number zero (0) if
there is no digit for this particular position.
*? two digits are placed here, replaced by the user-specified
characters if there are no digits for either of these
particular positions. The replacement character is that
which immediately follows the asterisk (shown as ? here).
Subsequent # fields are treated as part of the *? field,
but 0 fields are not.
# a digit is placed here, replaced by nothing ("") if there is
no digit for this particular position. If a user-specified
replacement character has been declared, then that character
is used instead of nothing ("").
. the decimal point is placed here.
% multiply the numeric expression by 100, and display a
trailing % sign.
, delimit thousands by including a comma every 3 digit columns.
\ escape character. Do not interpret the next character, but
include it as-is in the returned string.
" quoted strings. Do not interpret characters enclosed in
quotes, but include them as-is in the returned string.
E+ scientific notation using either + or - sign.
e+ interpreted as E+.
E- scientific notation using nul or - sign/
e- interpreted as E-.
The comma and percent characters may be present anywhere in the
fmtmask$, but must immediately follow a field character (0,#,*?)
to be recognized. The comma is considered a flag, and its
position in the format string has no relevance to its position
in the formatted output. Scientific notation requires at least
one digit field to the left and right of the E+- characters. The
fmtmask$ (and the formatted output string) may be up to 255 bytes
in length. Characters that are not valid format characters are ignored
and will not appear in the format string (unless quoted or escaped),
with the exception of "(", ")", "+", "-",
"$" and " " (CHR$(32)).
* FUNCNAME$ function returns the name of the Sub/Function in which it
is located. It returns the ALIAS name, if one has been defined, or
the primary name if there is no alias. May be particularly useful
for global error handlers.
* GET #1, seek, x$() [Records 100] [To x&] reads a file opened for
binary, assigning data to each element of the array. It attempts
to read the number of elements specified in the Records option, or
the number of elements in the array, whichever is smaller. The
actual number of elements read is assigned to the variable
specified in the optional To clause. With dynamic strings, it is
assumed the file was written in the PowerBASIC and/or VB packed
string format using PUT of an entire string array: If a string is
shorter than 65535 bytes, a 2-byte length word is followed by the
string data. Otherwise, a 2-byte value of 65535 is followed by a
length dword, then finally the string data. With other data types,
the entire data area is read as a single block. In either case, it
is presumed the file was created with the complementary Put Array
statement. EOF() is set just as with other Get statements.
* GETSTDKBD function returns the handle of the keyboard device. File
redirection can cause this to be different from the handle returned
by the GETSTDIN function.
* GETSTDVID(page&) function returns the handle of the video page given
by the parameter page&. If page& is omitted, or equal to zero, then
the handle of the active page is returned.
* GUID$() function with no parameter (or a null, zero-length string
parameter) will return a new, unique 16-byte string GUID (Global
Unique Identifier) which may be used as a new Class Identifier or
an Interface Identifier.
* GUID$(guidtxt$) function examines a text string, and converts the first
GUID it finds (in standard text display format) into a 16-byte string
which contains the internal GUID representation as a 128-bit data
item. The guidtxt$ string must contain exactly 32 hexadecimal digits,
optionally delimited by spaces or hyphens, which must be enclosed
overall by curly braces: "{01234567-89AB-CDEF-FEDC-BA9876543210}".
If any errors are found, GUID$() returns a null, zero-length string
instead of the 16-byte GUID string. The GUID$() function is the
logical complement to the GUIDTXT$() function.
* GUIDTXT$(guid$) function converts the binary, 128-bit GUID into the
the standard text display format for a GUID. The guid$ parameter
must be exactly 16-bytes long, and is converted into a 38-byte
string of the format: "{01234567-89AB-CDEF-FEDC-BA9876543210}".
If any errors are found, GUIDTXT$() returns a null, zero-length
string instead of the 38-byte GUID text string. The GUIDTXT$()
function is the logical complement to the GUID$() function.
* HIINT(long) extracts the most significant (high) word from a long
integer or dword value, and returns it as a signed integer value.
* IIF(expr, truepart, falsepart) returns one of two values, based upon
a true/false condition. IIF() expects parts of any numeric type.
IIF&() expects parts optimized for long integer type. IIF$()
expects parts of string type. If expr evaluates to True, the
truepart is returned, else the falsepart is returned. The expr is
evaluated as a typical PowerBASIC boolean expression, which offers
short circuit expression evaluation as needed. IIF(1 and 2, 3, 4)
would return the truepart (3) because both terms in expr are true.
To force a bitwise evaluation of expr, enclose it in parentheses.
IIF$( (1 and 2), "True", "False") would return "False".
IIF%() is
recognized as a valid synonym for IIF&(). Contrary to the dubious
implementation in MS Visual Basic, only the selected expression
(truepart or falsepart) is evaluated at run-time, not both. This
ensures optimum execution speed, as well as the elimination of
unanticipated side effects.
* INTERFACE DISPATCH Structure is used to declare DispID's for a COM
interface, so that PowerBASIC can utilize the more efficient calling
conventions known as "Dispatch Early Binding".
Interface Dispatch AnyObject
Member Get Documents<6>() as Document
Member Call Insert<4>(Line<5>)
Member Call Delete<7>(Line as String<8>) as String
End Interface
You can list every Method/Property in the interface, or just the
ones you reference in your code. They can appear in any sequence.
The only thing we're doing here is associating each Method/Property
name, and each Named Parameter with the appropriate DispID value,
and identifying any property which returns an object to be used in
a nested object reference. You can look up the DispID's of your
COM Servers using an Object Browser, or by reading your object
documentation. You can even insert additional information about
the types and return value for your own reference, even though the
compiler doesn't use it.
* ISNOTHING(objectvar) tells the current status of a particular object
variable. This is particularly useful is determining the success
or failure of a SET statement. It returns true (-1) if the object
variable contains nothing, or false (0) if it contains a valid
current reference to an object interface.
* ISOBJECT(objectvar) tells the current status of a particular object
variable. This is particularly useful is determining the success
or failure of a SET statement. It returns true (-1) if the object
variable contains a valid current reference to an object interface
or false (0) if it contains nothing.
* JOIN$(array$(),delim$) returns a dynamic string consisting of all of
the strings in array$(), each separated by the delimiter specified
in delim$, which may be any length. If the delimiter expression is
a zero-length string, then no separators are inserted between the
string sections. If the delimiter expression is the 3-byte value
of "," (which may be expressed in your source code as the string
literal ""","""), then a leading and trailing double quote
is added
to the returned string to ensure standard, comma-delimited quoted
fields for easy parsing. The array specified by array$() may be a
dynamic string array, a fixed-length string array, or an asciiz
string array. The JOIN$() function is the natural complement to
the PARSE statement.
* LET statement, as well as the implied form, has been expanded to
include assignment to Variant variables. There are 5 added forms:
[LET] variant = variant
[LET] variant = EMPTY
[LET] variant = ERROR numr
[LET] variant = expression [AS vartype]
[LET] variant = array()
[LET] array() = variant
With the first form, a Variant variable is duplicated, with all
necessary internals managed appropriately. The second form allows
you to set a Variant to EMPTY, in which case it is considered to
contain no value at all. The third form permits you to assign a
specific ERROR number, while the fourth form allows assignment of
a new value, numeric or string, to a Variant variable. PowerBASIC
automatically analyzes the expression and chooses an appropriate
numeric or string data type for the internal representation of the
Variant. However, you can specify a particular preferred format
by adding the optional AS vartype clause:
LET xyz = 21.34 AS LONG
In the above example, the Variant would actually contain the value
21, as it was forced to Long Integer representation by the programmer.
The fifth and sixth forms allow you to assign an entire array to a
variant, or convert it back to a PowerBASIC array. In the case of a
dynamic string array, the appropriate ansi/unicode conversions are
handled automatically. This array assignment is always subject to
the type compatibility of each component. For example, you cannot
assign an extended float array to a variant, nor can you assign an
array with more than eight dimensions to a PowerBASIC array.
* LINE INPUT [prompt] x$ the prompt to be display may be specified
by either a quoted string literal or a string equate.
* LINE INPUT #1, x$() [Records 100] [To x&] reads a file opened for
input, assigning full lines of text to each element of the array.
It is assumed the data is standard text, delimited by a cr/lf or
eof (1A hex). It attempts to read the number of lines specified
in the Records option, or the number of elements in the array,
whichever is smaller. The actual number of lines read is assigned
to the variable specified in the optional To clause. Filescan is
useful in conjunction, to determine the dimensioned size of the
string array. EOF() is set just as with single Line Input.
* LOINT(long) extracts the least significant (low) word from a long
integer or dword value, and returns it as a signed integer value.
* LSET [ABS] var = $exp [USING $exp] left-justifies a string within
the space of another string or a user-defined type. If the string
expression is shorter than the string/type variable, it is padded
on the right with the first character of the "using" string, or
spaces if not specified. If the ABS option is included, and the
"using" string is null, the trailing "padding" positions are left
unchanged. LSET may be used to assign the value of a user-defined
type to a different class of user-defined type.
* LSET$($exp, strlen& [USING $exp]) function returns a string expression
$exp, left-justified in a field of strlen& characters. The $exp is
padded to the requested length with the first character of the
"using" string, or spaces if not specified.
* MACRO name [(prm1,prm2...)] = replacement text...
* MACRO name [(prm1,prm2...)]
MACROTEMP ident1, ident2...
replacement text...
EXIT MACRO
replacement text...
END MACRO
* MACRO FUNCTION name [(prm1,prm2...)]
MACROTEMP ident1, ident2...
replacement text...
EXIT MACRO
replacement text...
END MACRO = return expression
Macro is a powerful text substitution construct which may take a
single-line or multi-line form. It generates absolutely no
executable code unless it is referenced, and effectively allows
the programmer to design a part of the PowerBASIC language to his
own needs and requirements. A macro must always be defined before
it is referenced, and the parameter count must always match the
definition. When a macro is referenced, the occurrence of the
name is replaced by the defined replacement text, expanded with
parameter substitution. A single line macro, or a Macro Function,
may be referenced at any source code position which, when expanded,
will be syntactically correct. For a simplistic example...
MACRO concatenate(prm1,prm2) = prm1 & prm2
PRINT concatenate("Hello ","World")
During compilation, PowerBASIC would translate this internally to:
PRINT "Hello " & "World"
A multi-line macro, while more powerful in terms of coding, may
usually be referenced only in the "statement" position, which is
the first position on a line. That single reference is expanded
into mutiple lines of inline code to perform a complex task:
MACRO print9times(prm1)
PRINT prm1 : PRINT prm1 : PRINT prm1
PRINT prm1 : PRINT prm1 : PRINT prm1
PRINT prm1 : PRINT prm1 : PRINT prm1
END MACRO
print9times "This is very cool..." would do just that.
The MACROTEMP statement may be used to specify a list of one or more
identifiers which are automatically made unique to each expansion
of a multi-line macro by PowerBASIC. This is done by appending the
digits 0001, 0002, etc. to the identifier upon each expansion of the
macro. A text identifier may represent a variable, label, or any
other word which expands appropriately to avoid a duplicate name
conflict in your code. For example:
MACRO CopyUntilNul(ptr1,ptr2)
MACROTEMP LoopPoint, ByteVar
Dim ByteVar as Byte
LoopPoint:
ByteVar = @ptr1
@ptr2 = ByteVar
Incr ptr1
Incr ptr2
If ByteVar <> 0 then GoTo LoopPoint
END MACRO
Therefore, CopyUntilNul(Source, Dest) would expand to:
Dim ByteVar0001 as Byte
LoopPoint0001:
ByteVar0001 = @Source
@Dest = ByteVar0001
Incr Source
Incr Dest
If ByteVar0001 <> 0 then GoTo LoopPoint0001
If the MACROTEMP option were not utilized, serious naming conflicts
would occur most any time that a macro was expanded more than once
in a program. MACROTEMP may appear 0, 1, or more times in a macro
definition, but they must always precede any other text in the
macro. Generally speaking, MACROTEMP should be used with any label
in a macro which may be expanded more than once in a program, and
any variable which should not be shared with other expansions of
the macro.
EXIT MACRO may be used to terminate execution of code in the current
Macro Expansion. It is functionally identical to the imaginary
concept of GOTO End-Macro.
A macro definition may contain replacement text up to approximately
4000 characters. Macros may specify up to 240 parameters which may
occupy up to approximately 2000 bytes total expanded space per macro.
Macro Function substitutions are limited to an expanded total of
approximately 16000 characters per line of original source code.
Macros may be nested.
* MAKINT(lobyt&,hibyt&) combines two byte values (the least significant
byte of 32-bit values) into a 16-bit signed integer.
* MAKWRD(lobyt&,hibyt&) combines two byte values (the least significant
byte of 32-bit values) into a 16-bit unsigned integer.
* OBJACTIVE(ProgID$) function returns a true/false value to tell if
the program identified by the string expression ProgID$ is
currently running, initialized with OLE. This information may
prove useful in determining whether to use the NEW option with
the SET Statement.
* OBJECT Statement allows communication with a COM Object through the
Dispatch Interface. There are four general forms:
Object Get x.FileName (User=Vrnt) To Vrnt
Object Let x.Visible (Day=Vrnt) = Vrnt
Object Set x.MyProp (Much=Vrnt) = ByRef Object Vrnt
Object Call x.Quit (Class=Vrnt) [To Vrnt]
All parameters, return values, and assignment values must be in
the form of a Variant Variable. Dispatch object method calls
are bound at runtime, and require no declaration of properties
and methods. However, for this reason, the validity of these
references is also not checked at compile time.
The OBJECT statement can use both positional and named parameters,
but you should keep in mind that not while most Dispatch Servers
support named parameters, it is not universal. A positional param
is simply a Variant Variable containing an appropriate value. A
named param consists of a parameter identifier, an equal sign, and
a Variant Variable containing an appropriate value. Positional
parameters must precede any and all named parameters, but named
parameters may be specified in any sequence.
* OBJPTR(objectvar) function returns the object pointer contained in
the specified object variable, as a dword value.
* OBJRESULT function returns the most recent hResult value returned by
an object, resulting from the execution of an OBJECT statement.
* PAGEACTIVE returns the current active page number in the range 1-8.
* PAGEVISIBLE returns the current visible page number in the range 1-8.
* PARSE main$, array$() [,ANY delim$] parses the entire string specified
by main$, assigning each delimited sub-string to a successive element
of array$. The array specified by array$() may be a dynamic string
array, a fixed-length string array, or an asciiz string array. The
field delimiter is defined by delim$, which may be one or more
characters long. To be valid, the entire delimiter must match
exactly, but the delimiter itself is never assigned as a part of the
delimited field. If the ANY option is chosen, then each appearance
of any single character comprising delim$ is considered a valid
delimiter. If delim$ is not specified or nul, then standard comma-
delimited (optionally quoted) fields are presumed. In this case only,
the following parsing rules apply. If a standard field is enclosed
in optional quotes, they are removed. If any characters appear
between a quoted field and the next comma delimiter, they are
discarded. If no leading quote is found, then any leading or trailing
blank spaces are trimmed before the field is returned. It is usually
advantageous to dimension array$() to the correct size with the use of
the PARSECOUNT() function. The JOIN$() function is the natural
complement to the PARSE statement. The PARSE statement is typically
much more efficient, as a whole, than repeated use of the PARSE$()
function when it is necessary to parse the entire string expression.
* PRINT #1, x$() When Print to a file specifies an array name with
empty parentheses, the entire array is written as text strings,
each element delimited by a cr/lf. Numeric items are converted
to the ascii text equivalent, but arrays of user-defined types
may not be used in this context.
* PROFILE "fname" causes extra code to be generated which allows you
to capture a profile of your executing program. At the time the
Profile Statement is executed, a standard sequential file (of the
specified file name) is created, which contains a list of every
Sub/Function within the same EXE or DLL, the number of times it
was called, and the total elapsed time (in milliseconds) spent
executing all instances of that procedure. Subs/Functions in an
external EXE or DLL are not profiled. Of course, the time resolution
is limited to that supported by the operating system (Win95/98 is
54msec, and WinNT/2K is 10 msec), and could also be affected by any
other applications which run concurrently. Nonetheless, it can offer
great insight as to which code may be wasting the most time, and
where your efforts at additional optimization should be concentrated.
Profile only describes Subs/Functions which physically reside within
the EXE or DLL where Profile is located. It is highly recommended
that you close all other applications when profiling a PowerBASIC
application. When an application is being profiled, PowerBASIC must
generate a considerable amount of extra code to gather all of the
needed information. This extra code is generated whenever it notes
that a legal PROFILE statement appears in your program, regardless
of whether it is actually executed. Always be certain that every
PROFILE statement is commented out of your code for final versions
(or #TOOLS OFF included) to ensure the highest performance levels.
* PROGID$(class_id$) looks up the CLSID (a 16-byte string in GUID format)
in the registry, and returns the ProgID (the unique programmatic
identifier as a text name) associated with it. If the ProgID cannot
be found, or any error occurs, a null string is returned. PROGID$()
is the natural complement to CLSID$().
* PUT #1, seek, x$() When Put to a binary file specifies an array
name with empty parentheses, the entire array is written. With
dynamic strings, the file is written in the PowerBASIC and/or VB
packed string format: If a string is shorter than 65535 bytes, a
2-byte length word is followed by the string data. Otherwise, a
2-byte value of 65535 is followed by a length dword, then finally
the string data. With other data types, the entire data area is
written as a single block. In either case, it is presumed the
file will be read with the complementary Get Array statement.
* REGEXPR mask$ IN target$ [AT start&] TO posvar& [,lenvar&]
Evaluates a regular expression to determine whether the "wild card"
string specified by mask$ can be found within target$. The search
begins at the position stated by the optional parameter start&, or
else defaults to position one. If found, the variable posvar&
contains the position of the match (indexed to one), and the
optional variable lenvar& contains the length of the match, if it
is included. If no match is found, both variables are set to zero.
The regular expression specified by mask$ may contain a combination
of standard text characters and/or the metacharacters which are
defined below. While it is possible for more than one match to be
found in a particular target string, REGEXPR first selects one or
more matches which start at the leftmost possible position, then
returns the longest of those.
. the period matches any character, but not end-of-line.
[ ] identifies a user-defined class of characters, any of which
will match: [abc] will match a, b, or c. Only three special
metacharacters are recognized within a class definition, the
caret ^ for complemented characters, the hyphen - for a range
of characters, or one of the following \ backslash escape
sequences: \\ \- \] \e \f \n \q \r \t \v \x##
Any other use of a backslash within a class definition yields
an undefined operation which must be avoided.
[^] the caret, when it appears as the first item in a class,
identifies a complemented class of characters, which will
not match: [^abc] matches any character except a, b, or c.
A caret located in any position other than the first is
treated as a literal character.
[-] identifies a range of characters to match. [a-f] will
match a, b, c, d, e, or f. [f-a] matches nothing. Lists
of characters, and one or more ranges of characters, may be
intermixed. The start and end of a range may be specified
by a literal character, or one of the \ backslash escape
sequences: \\ \- \] \e \f \n \q \r \t \v \x##
Any other use of a backslash within a class definition yields
an undefined operation. [a-d2-5] matches a, b, c, d, 2, 3,
4, or 5. [a\-c] is a list, not a range, and matches a, -,
or c due to the \ backslash escape sequence.
() parentheses are used to match a Tag, or sub-pattern, within
the full search pattern, and remember the match. The matched
sub-pattern can be retrieved later in the mask, or in a replace
operation, with \01 through \99, based upon the left-to-right
position of the opening parentheses. Parentheses may also be
used to force precedence of evaluation with the alternation
operator. For example, "(Begin)|(End)File" would match either
"BeginFile" or "EndFile", but without the Tag designations,
"Begin|EndFile" would only match either "BeginndFile" or
"BegiEndFile". Parentheses may not be used with ? + * as
repetition could cause the tag value to be ambiguous. To
match repeated expressions, use parentheses followed by \01*.
\ the backslash is the escape operator, which causes the
following special character to be treated as a literal,
rather than interpretation as a special character. Note that
the following character must actually be a special character.
\b is interpreted as a "word boundary", the start or end of a
word. A word consists of any number of letters, digits, or
underscores.
\c forces a case-sensitive search. Without it, the default is
to ignore case when matching. Unlike some othe limited
implementations of regular expressions, case-insensitivity
is recognized in all operations, even a range of characters
such as "[6-Z]". It may appear at any position in the mask.
\e is interpreted as an escape code: chr$(27)
\f is interpreted as a formfeed code: chr$(12)
\n is interpreted as the line-feed or new-line code: chr$(10).
\q is interpreted as a double quote (") for ease of inclusion in
a literal string with source programs.
\r is interpreted as the carriage-return code: chr$(13).
\s causes the shortest matching string to be returned, rather
than the longest (the default). For example, when searching
for the mask "abc.*abc" in "abcdabcabc", the default setting
would return position 1 and length 10. With the \s switch
set, it returns position 1 and length 7. This option may
cause a slight increase in processing time.
\t is interpreted as a tab code: chr$(9).
\v is interpreted as a vertical tab: chr$(11).
\x is interpreted as an evaluated character code in hex, defined
by the two following hex digits: \xFF = chr$(255).
\##(## could be 01 through 99) is evaluated as the characters
matched by tag number ##. Tags are implicitly numbered from
01 through 99, based upon the left-to-right position of the
left parentheses. "(...)w\01" would match "abcwabc" or
"456w456". If a reference is made to any Tag which is not
yet completely defined, a non-match is presumed.
^ the caret matches the beginning-of-line position.
$ the dollar matches the end-of-line position, but line delimiter
characters (cr/lf) are not included with the returned text.
| the stile specifies alternation (the OR operator), so
that an expression on either side can match - precedence
is left-to-right as encountered in the expression.
? the query specifies that zero or one match of the
preceding sub-pattern is allowed.
+ the plus specifies that one or more matches of the
preceding sub-pattern are allowed.
* the star specifies that zero or more matches of the
preceding sub-pattern are allowed.
* REGREPL mask$ IN main$ WITH new$ [AT start&] TO posvar&, newmain$
evaluates a regular expression, just as with REGEXPR, but the
matched text in main$ is replaced by new$ and the entire text
is assigned to newmain$. Main$ remains unchanged and posvar&
reflects the position immediately after the replaced text, so
the operation can be repeated, if desired. If no text is replaced,
then posvar& returns zero. The expression new$ may contain any
text, as well as the tags specified by \##. Each tag from \01
through \99 is replaced by the text actually matched for that tag.
\00 is replaced by the entire matched text. The only way to
specify a literal backslash is with \\.
* RETAIN$(main$,[ANY]match$) returns a string consisting of zero or
more copies of the complete expression match$ which are found in
main$. All other characters are removed. If the ANY option is
included, match$ specifies a list of single characters to be
retained, if they are found in main$.
* RSET [ABS] var = $exp [USING $exp] right-justifies a string within
the space of another string or a user-defined type. If the string
expression is shorter than the string/type variable, it is padded
on the left with the first character of the "using" string, or
spaces if not specified. If the ABS option is included, and the
"using" string is null, the leading "padding" positions are left
unchanged. RSET may be used to assign the value of a user-defined
type to a different class of user-defined type.
* RSET$($exp, strlen& [USING $exp]) function returns a string expression
$exp, right-justified in a field of strlen& characters. The $exp is
padded to the requested length with the first character of the
"using" string, or spaces if not specified.
* SCREENATTR(row,col) returns a composite screen attribute. The screen
attribute is an integer value in the range of 0 through 255 (&hFF).
The lowest 4-bits (low nybble) contains the foreground attribute, in
the range of 0-15, and the next 4-bits (high nybble) contains the
background attribute, in the range of 0-15. If the row/col params
are specified as 0,0 then the default screen attribute (that which
will be used for subsequent Print statements) is returned. If the
row/column parameters represent a valid screen location, 1 through
ScreenX/ScreenY, then the screen atrribute for that particular
position is returned.
* SELECT CASE [AS] [LONG|CONST|CONST$] expr offers three optional
modifiers to provide highly optimized code generation for specific
circumstances. By default, numeric expressions are evaluated as
floating point values to offer the widest range of compatibility
for any possible circumstance. Further, string expressions are
evaluated dynamically to allow virtually any data. However, if
the programmer finds he can limit the type and range of the data
used for comparision, performance can be dramtically enhanced.
AS LONG: In this case, the controlling expression and the case
expressions must evaluate in the range of a long integer. Each
of these expressions are calculated dynamically, so all of the
normal operators are still available. Performance is enhanced
by the integer class of code generation, rather than floating
point.
AS CONST: In this case, the controlling expression must evaluate
in the range of a long integer. However, each of the case values
must be strictly specified by a numeric literal in the range of a
long integer. Multiple case values may be given (CASE 2,3,7), but
operators and ranges of values are not allowed. Performance is
enhanced by creating a vector jump table, one entry for each number
from the smallest to the largest case value. While this form of
the structure offers the utmost perfomance possible, the execution
speed must be carefully weighed against the increased program size,
particularly when using sparse case values. For example, with just
two case values of 2 and 1000, the generated jump table would need
999 table entries which is 3996 bytes in size. The largest allowed
jump table for this form is approximately 3200 entries (12K bytes).
If exceeded, an error 402 is generated (Statement too long/complex).
AS CONST$: In this case, the controlling expression must evaluate
to a dynamic string of length zero through 255. However, each of
the case values must be strictly specified by a string literal (a
quoted string, or a string equate). Multiple case values may be
given (CASE "a","Bob",$value), but operators and ranges of values
are not allowed. Performance is enhanced by creating a vectored
scan table, eight bytes for each case value specified.
* SET Statement is used to assign a reference to an object variable.
Set obja = New Dispatch in "ProgID" ' creates a new instance
of the object identified by "ProgID". It opens a Dispatch
Interface on the object, and assigns that reference to the
object variable obja. The object can then be accessed using
the Object statement. If the interface can not be opened
(for example, if the Dispatch interface is not supported in
"ProgID"), the object variable obja is set to nothing. You
can test for success or failure with the IsObject(o) or
IsNothing(o) functions.
Set obja = Dispatch in "ProgID" ' creates an interface to a
running application which was initialized with OLE, and
assigns that reference to the object variable obja. The
object can then be accessed using the Object statement.
If the interface can not be opened (for example, if the
application is not running, or the Dispatch interface is
not supported in "ProgID"), the object variable obja is
set to nothing. You can test for success or failure with
the IsObject(o) or the IsNothing(o) function.
Set y = x ' Assuming both variables have been dimensioned as a
Dispatch Object variable, an object copy is created. You
can test for success/failure with the IsObject(o) or the
IsNothing(o) functions.
Set vrnt = obja ' Attempts to open an IDispatch interface,
else an IUnknown interface on the object of obja, and
assigns that reference to vrnt. Variant variables can not
contain references to custom interfaces, only IDispatch or
IUnknown. If the assignment is successful, VariantVT(vrnt)
will return either vt_unknown or vt_dispatch. If it is
unsuccessful, vrnt is set to vt_empty.
Set obja = vrnt ' opens an interface of the appropriate class
for obja on the object of vrnt, and assigns that reference
to obja. It assumes that vrnt contains a reference to an
object of type vt_unknown or vt_dispatch. If the desired
interface can not be opened, the object variable obja is
set to nothing. You can test for success/failure with the
IsObject(o) or the IsNothing(o) functions.
Set a = Nothing ' destroys an object variable, which in turn
allows the object to be destroyed when all variables which
reference it no longer exist.
* SWITCH(expr1, value1, expr2, value2...) returns one of a series of
values, based upon the true/false condition of a corresponding
series of expressions. SWITCH() expects values of any numeric type.
SWITCH&() expects values optimized for long integer type. SWITCH$()
expects values of string type. If expr1 evaluates True, value1 is
returned, if expr2 evaluates True, value2 is returned, etc. Each
control expr in the series is evaluated as a typical PowerBASIC
boolean expression, which offers short circuit expression evaluation
as needed. To force a bitwise evaluation of an expr, enclose it in
parentheses. If more than one expr is true, SWITCH() returns the
value from the first one found. SWITCH%() is recognized as a valid
synonym for SWITCH&(). Contrary to the dubious implementation in
MS Visual Basic, only the selected value (value1,value2,value3...)
is evaluated at run-time, not all. This ensures optimum execution
speed, as well as the elimination of unanticipated side effects.
* TAB$(x$,y) returns the string x$, with all tab characters, CHR$(9),
expanded with spaces to the tabstop specified by y. If the tabstop
is less than 1 or greater than 256, the string is returned unchanged.
* THREADCOUNT function returns the number of active threads which exist
in an application or DLL.
* THREADID function returns the thread identifier, a long integer, of
the thread which is currently executing.
* TRACE Statement causes extra code to be generated which allows you to
capture a representation of the precise flow of execution in either
an application or a DLL. There are five general forms to TRACE:
TRACE NEW "filename expression"
TRACE ON
TRACE PRINT string_expr
TRACE OFF
TRACE CLOSE
TRACE NEW x$ causes causes a standard sequential Trace File (of
the specified file name) to be created, deleting any previous file
by the same name. When a subsequent TRACE ON is then executed,
PowerBASIC begins to write pertinent trace information to the
Trace File. It will contain a chronological list of every call to
an internal Sub/Function, the associated parameter values, and the
point at which it was exited. Further, it will list a label name
each time that program execution flows through the label position.
In a test or debugging situation, TRACE, CALLSTK, and CALLSTK$(n)
allow you to easily answer that age-old programming question,
"How did I get here?". TRACE details the entry and exit of every
Sub or Function in your program, while CALLSTK simply lists the
stack frames which now exist above the current level. TRACE is
particularly valuable in pinpointing the area of a program where
a fatal machine crash occurs.
TRACE PRINT string_expr causes the value of string_expr to be
output to the Trace File. The string_expr might record variable
values or other information of importance to the programmer.
TRACE OFF causes output to the Trace File to be temporarily
paused, but it may be subsequently restarted with TRACE ON. An
implied TRACE OFF is performed when you exit the Sub or Function
in which the current TRACE ON was first executed. TRACE CLOSE
permanently detaches the Trace File from the stream of trace data.
In the course of TRACE report generation, each time that an error
is generated, PowerBASIC records a text line to describe it. This
may aid in locating elusive exception problems.
If PbMain() calls the Sub aaa(x&), which calls the Sub bbb(y&),
which calls the Sub ccc(z&), and PbMain() contained TRACE NEW
and TRACE ON, the Trace File might look something like:
AAA(3) (Err=0)
BBB(4) (Err=0)
CCC(5) (Err=0)
TRACE PRINT expr$ printed this data from CCC (Err=0)
CCC Exit (Err=5)
BBB Exit (Err=0)
AAA Exit (Err=0)
The TRACE Statement can easily create a huge file, so caution
must be exercised. Use TRACE ON at the lowest Sub or Function
level possible, to keep the output size within reason. Numeric
parameters are displayed in decimal, while pointer and array
parameters display a decimal representation of the offset of the
target value. TRACE can be invaluable during debugging, but it
generates substantial additional code which should be avoided in
the final release version of an application. If the source code
contains #TOOLS OFF, then all Trace Statements which remain in
the program are ignored. The Trace Statement is "Thread-Aware",
displaying only Sub, Function, or Label details from the thread
in which it was executed. You can execute TRACE multiple times,
or even in multiple concurrent threads. However, you must use
caution to ensure that each TRACE uses a unique name for its
Trace File.
* TRY, CATCH, FINALLY, END TRY offers a structured method of trapping
and responding to system exceptions:
Try
Print #1, "This is a very good compiler."
Catch
if err=%DiskFull then
close #1
print "Buy a larger hard drive!"
end if
Finally
Beep
End Try
Statements in the "Try" section are executed normally. The first
time an error occurs, control is transferred to the "Catch" section.
If no errors are generated, the "Catch" section is skipped entirely.
Then, regardless of error status, the "Finally" section is executed,
if it is present. Error trapping and control transfer are disabled
in the "Catch" and "Finally" sections, so you would normally use
"If Err = ..." to test the success of error-prone operations. Try
structures can be nested to any level, so that is another option
available to you within these clauses. "Catch" is a mandatory part
of this structure, while "Finally" is optional. You must exit the
structure normally through END TRY, or use EXIT TRY as the only
alternative. Because of the nesting requirements, the ERR value
is local to the TRY structure. Upon exit, the prior ERR value is
restored, so be sure to save the value of ERR if it will be needed
outside of the TRY structure. ON ERROR GOTO is invalid within a
TRY structure, but may be used within the same Sub/Function.
* TYPE SET var = type/$exp [USING $exp] assigns the value of a user
defined type (or a string expression), left-justified, to another
user-defined type (or string variable). If the assigned data is
shorter than the string or type variable, it is padded on the
right with the first character of the "using" string, or binary
zeros if not specified. If the "using" string is null (length=0),
any trailing "padding" positions are left unchanged. TYPE SET is
primarily designed to assign the value of a user-defined type to
a different class of user-defined type.
* TYPE definitions may optionally specify an alignment of BYTE (the
default), WORD, DWORD, or QWORD, as well as FILL characteristics.
Note that while QWORD alignment is included for compatibility with
Windows, it can not be fully implemented in a 32-bit operating
system. With QWORD, individual members are 64-bit aligned for the
appropriate structure size, but variables of that type may only be
aligned on 32-bit boundaries, as stack pointer alignment is not
guaranteed. With standard alignment, each member of a Type Structure
will be located on the specified boundary. For example, with DWORD,
up to 3 bytes may be skipped between members to accomplish alignment.
However, when a user-defined type is defined as a member of a larger
user-defined type, this "sub-type" retains the original size and
alignment, just as first declared. If the FILL option is specified,
such as TYPE xxx DWORD FILL, then the following rules apply:
1- No bytes are skipped if the next member of the Type will fit
entirely into that space to be skipped.
2- Fixed-length strings are considered to be an array of bytes, so
no bytes are skipped preceding them.
3- The total size of an array is considered to determine if FILL
should affect its placement within the structure. For example,
with DWORD FILL, an array of two integers would be started on
a 4-byte boundary, even if two or three byte must be skipped.
Microsoft Visual Basic uses an alignment style which may be emulated
with DWORD FILL.
* UCODE$(ansicode$) function returns the unicode equivalent of the ansi,
optionally multi-byte, string expression.
* USING$(fmtmask$, expr [,expr]) function formats one or more expressions,
string or numeric, based upon the contents of the format mask string.
The rules of formatting are based upon the PRINT USING statement
supported in many Dos versions of Basic, including PowerBASIC/Dos.
However, since it is implemented as a function, it allows far more
versatility in that it is not necessary to PRINT a value to gain the
benefit of this unique functionality.
When expr is a string, the following format codes apply:
! - the first character of the string is returned.
& - the entire string is returned.
\\ - two characters are returned.
\ \ - if backslashes enclose n spaces, n+2 characters are returned.
When expr is numeric, the following format codes apply:
# A numeric digit position , which is space-filled to the left,
and zero-filled to the right of the decimal point. If the
number is negative, a minus sign occupies a digit position.
. The decimal point is placed at this position.
, A numeric digit position, which signifies that whole number
digits should be displayed with a comma each three digits.
$$ Two numeric digit positions which cause a dollar sign to be
inserted immediately before the number.
* Specifies a new fill character. Any leading spaces in the
field are replaced by the character following the asterisk.
For example, "**" specifies asterisk fill, and "*0" can be
used to specify leading zeros on non-negative numbers.
+ A plus at the start of the field causes the sign of the
value (+ -) to be inserted before the number. A plus at
the end of the field causes the sign of the value (+ -) to
be added after the number.
- A minus at the end of the field causes a minus signed to be
added after a negative number, or a space to be added after
a positive number. A minus at the start of the field is
treated as a literal character, which is always inserted.
^ Numbers can be formatted in scientific notation by including
three to six carets (^) in the format string. Each caret
corresponds to a numeric digit position in the exponent,
one for E, one for the exponent sign, and one to four for
the actual digits of the exponent value.
All characters in the format mask not identified above are copied
into the output string just as they are encountered. You can
override any special format code by preceding it with an underscore
character (_) and it will be copied as any other literal. The
format codes "0" and ";" are reserved for future implementation,
and should be preceded with an underscore for compatibility. The
output string is limited to an absolute length limit of 1024.
* VARIANT#(variant) function returns the numeric value contained in the
Variant variable. The value returned may be any range from BYTE to
DOUBLE/QUAD/CURRENCY, depending upon the internal representation used
in the Variant. While Variant variables, by definition, do not offer
support for Extended Precision Float data types, you should note that
it is possible for a QUAD or CURRENCY value to exceed the precision
level offered by a DOUBLE. You should therefore use some judgement
in deciding on the numeric variable type to be used as the destination
of this function, based upon the expected return values, and the
internal representation, which you can obtain with VariantVT(). It
is presumed that a valid numeric value is present (not an array), but
the value zero is otherwise returned.
* VARIANT$(variant) function returns the dynamic string value contained in
the variable, automatically converted from Wide/Unicode format. It is
presumed that a valid string value is present (not an array), but a
nul (zero-length) string is otherwise returned.
* VARIANTVT(variant) function returns the internal VT data type stored in
the variant. The entire range of VT_**** values are documented by the
OLE specification and Win32api.inc. The most important values in
this limited context might be %vt_empty=0 and %vt_bstr=8, since most
of the others are numeric formats automatically resolved by the LET
statement and VARIANT#() function. If a variant contains a complete
array, the variant type is determined by adding the base type to the
array modifier. That is, for a string array, it would be %vt_bstr
plus %vt_array, or &H2008.