You will use BASIC code to write event handlers and other routines. You will manipulate components, numbers, and strings.
Before we discuss everything you can do with BASIC, let's take a look at a couple of typical routines and see what they're made of.
The rest of this section covers the following topics:
Use the REM keyword to add comments to your code. REM is short for REMark. The NEWBASIC interpreter ignores everything after the REM on a line of program code. This allows you to leave notes about how a routine works, set aside a line for use by a revision control system, or leave notes useful for others who might wish to improve the routine in the future.
If you define a string constant which includes REM , the REM won't act as a comment marker, but will just appear in the string.
You may use variables to store data. You may retrieve the value of the data in a variable by using the variable name in an expression. You may assign a new value to a variable in a number of ways.
You may use constant expressions to make your BASIC code more readable.
Each variable has a type. The following types are available:
integer
A signed integral number between -32768 and 32767. Each integer takes up two bytes of memory.
When expressing integer constants, you may use either decimal, hexadecimal, or octal notation. Use &H to specify that the number is in hexadecimal notation, &O to specify octal notation.
You may use exponential notation to express numerical constants.
If you attempt assign a non-integral number to an integer variable, everything after the decimal will be ignored. For example, if you attempt to assign 3.14 to an integer variable, it will get the value 3.
long
An integral number between -2,147,483,648 and 2,147,483,647. Each long integer takes up four bytes of memory. As with integers, you may use &H and &O notation to specify hexadecimal and octal constant values.
float
A floating-point number; i.e., a number which need not be an integer. Each floating point number takes up four bytes of memory. A floating-point value can have about seven decimal digits of precision. The magnitude of a float may range from 3.4 x 10-38 to 3.4 x 1038. Because floating point numbers may be very large or small, exponential notation is often used when expressing floating point number constants.
oneOverTwo = 0.5
oneOverRootTwo = 7.07107E-1
string
A piece of text. All strings consist of Unicode text.
When specifying string constants, the following tokens represent special characters:
To concatenate two strings, use the + operator.
array
An array of data structures; i.e., a group of data structures organized into a table in which you may refer to a member of the group by number. The array may have up to three dimensions--three numbers by which one looks up numbers in the table.
The elements of an array may be integers, longs, floats, strings, STRUCTs, complex, component, or module. They may not be arrays themselves.
REM We are pre-calculating two tables of numbers.
REM First, we will calculate the values
REM of n-factorial where n ranges from 0... 9.
REM Next, we will use our array of factorials to
REM construct a portion of Pascal's Triangle--a
REM two-dimensional array of numbers useful for
REM computing certain probabilities.
factorial[0] = 1
theNumber = 1
FOR count = 1 TO 9
theNumber = count * theNumber
factorial[count] = theNumber
NEXT count
DIM pascal[6, 5] AS integer
FOR i = 0 TO 5
FOR j = 0 TO 4
pascal[i, j] = factorial[i + j] / factorial[i] / factorial[j]
NEXT j
NEXT I
You may not assign to arrays, though you may assign to their elements.
DIM a[10] AS integer
a[0] = b[0] REM this is fine |
Use the REDIM command to re-size an array, or to clear out the data in an array. You may not change the number of dimensions, or the type of the array. To clear out the data from an array and re-size it, use a statement with the syntax:
To preserve the data in an array and re-size it, use a statement with the syntax:
REDIM PRESERVE array-name[new-size]
If the array becomes smaller, then some data will still be lost as the last few elements are truncated.
Using REDIM PRESERVE, you may only change the size of an array's last dimension. Thus:
DIM Income[12, 5] AS integer
REDIM PRESERVE Income[12, 4] REM This is OK
REDIM PRESERVE Income[11, 4] REM This is not OK
STRUCT
A type built up from other types. You may use STRUCTs to develop your own types. The fields may be of any simple type; they may not be arrays. If your application uses a STRUCT that you define, the STRUCT definition must appear in the module_init() routine. If your STRUCT definition appears in a routine other than module_init(), there may be no immediate problem; yet, this behavior is unsupported.
For example, you might define a structure to hold employee data:
STRUCT emplrecord |
To reference fields of the structure, use the caret ("^") or period (".").
When you assign a structure to a variable, the assignment is by reference . This means that if you change a field of the structure (either of the original structure variable, or the assigned structure variable), the change will appear in both the original and the assigned variable.
DIM origPoint AS STRUCT point
DIM samePoint AS STRUCT point
samePoint = origPoint
origPoint^x = 5
samePoint^y = 17
REM Both origPoint and samePoint are now (5, 17)
In the above example, both origPoint and copyPoint became (5, 17)--by assigning origPoint to copyPoint, the program made them both references to the same structure. If you wish to make a separate copy, you must copy each field separately.
complex
Some components make use of complex data. This data might contain, for example, formatted text or graphics information. Generally, there isn't any way to examine the contents of this sort of data with BASIC code: they should be treated as opaque. However, you can declare complex variables to hold this complex data. To test a complex data variable to see if its contents are empty, use the IsNullComplex() function.
Sometimes you will want to create a null complex value. To do this, DIM a complex variable and don't assign a value to it. For example, the following snippet of BASIC code would clear the picture1 component's graphic data:
DIM myNullComplex AS complex |
component
Components are the gadgetry which make up NEWBASIC programs. You can use variables of type component to keep track of components. You may use the variable name within expressions where you might otherwise use a component name.
DIM theDialog AS component
theDialog = OKButton.parent
theDialog.caption = "OK"
module
It is possible to organize your program such that its parts are in more than one .BAS file. If you do so, each of these files will manifest as a module. This is discussed in more detail in
See Loading Modules
, but for now we will just point out that one can declare variables of type module, which may then be used to refer to one of a program's modules. Module variables may be compared for equality.
To declare a variable, use the DIM keyword.
DIM x AS integer |
Variable names may be up to 128 characters long; the first character must be an alphabetic character, and all other characters must be either an alphanumeric character or an underline. For examples of declaring array variables, see See array An array of data structures; i.e., a group of data structures organized into a table in which you may refer to a member of the group by number. The array may have up to three dimensions--three numbers by which one looks up numbers in the table. .
Depending on the context in which you declare a variable, it may or may not be defined in other contexts. If the variable is defined in the module_init() routine, it is a global variable, and will be defined for all routines in the module. If the variable is defined elsewhere (i.e., in a routine other than module_init()) then it is defined only in that routine, and is called a local variable.
SUB module_init()
DIM X AS integer
REM Because we define X in module_init(),
REM it's a global variable.
X = Factorial(5)
END SUB
FUNCTION Factorial( n AS integer ) AS long
REM Factorial()
REM This function returns n * (n-1) * (n-2) * ... * 2 * 1
Factorial = 1
DIM count AS integer
FOR count = 1 TO n
Factorial = Factorial * count
NEXT count
REM count is a local variable.
REM It is only defined within the
REM Factorial() function.
REM
REM The Factorial variable is a special local
REM variable defined only within the Factorial()
REM function. It holds the function's
REM return value.
END SUB
You may specify that a routine's variables should be global by declaring the routine with the GLOBAL keyword, which should appear after the parenthesized arguments list.
SUB InitializeArrays() GLOBAL |
Some programs are organized into several .BAS files, known as modules. By default, global variables are only defined within their module. It is possible to export global variables so that they can be accessed by a module that load's the variable's module. This is done by means of the EXPORT keyword, which has the following syntax:
In this syntax, variable is the name of a global variable.
SUB module_init() |
For an explanation of exported variables and modules, see See Loading Modules .
Function arguments and return values behave something like local variables. They are defined only within the body of the routine. The syntax used to define them is special. They are also special in that they are used to pass and return values to and from the routine.
Instead of using the DIM statement, define the names and types of arguments in a routine definition by means of the argument list:
FUNCTION Squared( x AS integer )
SUB HashInsert( data AS string, bucket AS integer)
SUB QuickSort( theList[] AS string )
The initial value of these arguments is set by the caller of the routine.
The return value of a function behaves like a local variable of that function. The name of the return value variable is the same as the name of the function.
FUNCTION Squared( x AS float )
Squared = x * x REM `Squared' is return value
END FUNCTION
To make the type of the return value explicit, name the type after the argument list:
FUNCTION Factorial(n AS integer) AS long
FUNCTION GetRecord(SSN AS long) AS STRUCT emplrecord
Functions may not return arrays, though they may alter the contents of arrays passed to them as arguments.
For more information about functions and subroutines, see See Functions and Subroutines: The Distinction .
Component properties are similar syntactically to variables as they appear in expressions. They are referred to by the name of the component and the name of the property, separated by a period:
IF (PlayerNameEntry.text <> "" )
HighScoreNameEntry.text = PlayerNameEntry.text
HighScoreNum.value = Score
END IF
You can define custom component properties by assigning values to them in expressions. There is no way to explicitly DIM a component property--they may only be defined implicitly through assignment.
sub module_init()
CountButton.count = 0
end sub
SUB CountButton_pressed(self AS component)
self.count = self.count + 1
END SUB
The name of a custom component property can be at most 128 characters long; it must start with an alphabetic character and all other characters must be either alphanumeric or underlines.
You may also refer to properties using expressions in place of the property name--if the expression is enclosed within parentheses. Each of the following would change button1's caption property.
button1.caption = "Normal" REM The usual syntax
button.("caption") = "Weird"
button.("cap" + "tion") = "Strange"
DIM s AS string
s = "caption"
button.(s) = "Stranger"
button.(Left("captionX", 7)) = "Really ugly"
Constants allow you to make your code more readable by giving names to numbers and strings. You may then use the constant names in your code. When your program is compiled, the compiler will substitute the constant values for the constant strings.
Define constants using the following syntax:
CONST constant-name constant-expression
...where constant-name is a valid name and constant-expression is a simple expression containing no variables or routine calls: only simple values or constants.
IF telnetResult = TRANSFER_DONE THEN |
|
The CONST definition must appear before any use of the constant name in expressions--perhaps in an earlier routine. For this reason, most programs keep their CONST expressions in their module_init() routine.
The following would be illegal:
x = LATE REM This will generate an error...
CONST LATE 6 REM ...because LATE was defined too late.
Constant expressions allow you to give descriptive names to numbers within your program without requiring the lookup time overhead associated with variables.
Expressions in NewBASIC are built up from value, variables, and operators. The available operators are listed in precedence order below.
* / MOD
The multiply, divide, and modulo operators.
+ - The plus and minus operators. + is also the string concatenation operator.
< <= > >=
The inequality comparison operators: less than, less than or equal to, greater than, greater than or equal to.
= <>
The equality comparison operators: equal to, not equal to.
BITAND
The bitwise-and operator.
BITXOR
The bitwise-exclusive-or operator.
BITOR
The bitwise-or operator.
AND
OR
XOR
The logical-and, logical-or, and logical-exclusive-or operators.
Assignments are a special kind of expression. They assign a value to a variable or component property. The assignment is made up of the name of the variable or component property, an equals sign, and an expression specifying the value.
There are three types that represent numbers: integer, long, and float. If you assign a number of one type to another, some information may be lost or an error may occur:
DIM x AS integer
x = 7.8 REM this sets x to 7 -- the .8 is truncated
x = 700000 REM this generates an Overflow error
In general, if you try to assign a value to a variable of the wrong type, a run-time error will occur. For example:
Note that assigning structures is a special case. They are assigned by reference . For the implications, see the discussion of STRUCTs, starting on See STRUCT A type built up from other types. You may use STRUCTs to develop your own types. The fields may be of any simple type; they may not be arrays. If your application uses a STRUCT that you define, the STRUCT definition must appear in the module_init() routine. If your STRUCT definition appears in a routine other than module_init(), there may be no immediate problem; yet, this behavior is unsupported. .
You may not assign to arrays, though you may assign to their elements. For example:
DIM a[10] AS integer |
The following constructs allow you to control the flow of execution within a routine. We will discuss each of these constructs in detail.
In an If-then construct, you may specify that one block of code or another should be executed, depending on the value of an expression at run-time.
In its simplest form, the construction appears:
IF
condition
THEN
statements
END IF
In this case, the condition expression is evaluated. If condition doesn't evaluate to zero, then the block of statements between the THEN and END IF will be executed. If condition is zero, then the statements will be skipped. (The THEN keyword is optional.)
IF (numPlayers = 1) THEN |
You may specify a second block of statements to be executed if the condition is false by means of an ELSE clause. A construct with an ELSE clause has the following form:
IF
condition
THEN
statements
ELSE
else-statements
END IF
As above, if condition evaluates to a non-zero value, then statements will be executed. However, if condition evaluates to zero, then the else-statements will be executed.
IF (Product >= 0) THEN |
You may include any number of ELSE IF clauses in an IF construct. An IF construct with one ELSE IF clause has the form
IF
condition1
THEN
statements
ELSE IF
condition2
else-statements
END IFYou may have an
ELSE
clause in an IF- construct containing
ELSE IF
clauses, but the
ELSE
clause must appear after all
ELSE IF
clauses. Here's an example that uses both
ELSE IF
and ELSE:
A For-next loop allows you to repeat a group of statements a set number of times. The simple form of the For-next loop is shown below.
FOR
count
=
start
TO
stop
statements
NEXT
count
There is a special count variable. In the simple form of the For-next construction, one is added to the count variable after each execution of the loop's statements . The start value determines count's starting value. The looping continues until count goes beyond stop . Count must be an integer or a long value. Two simple examples:
By using the optional STEP keyword, you can ask that the count variable change by a number other than one. The syntax is:
FOR
count
=
start
TO
stop
STEP
add
statements
NEXT
count
With each execution of statements, add is added to count . Execution will stop when count goes beyond stop . For a countdown effect, use a negative add .
FOR Year = 1900 TO 1995 STEP 5
FOR i = numCards TO NewSlot + 1 STEP -1 |
The add, start, and stop values are evaluated only once, before the looping starts.
To repeat execution of a group of statements until some condition is met, use some variation of a DO- loop. There are a few variations of the DO- loop:
Perhaps the most straightforward DO- loop construct, the DO WHILE loop executes a set of statements until some condition is met. It has the following syntax:
DO WHILE
condition
statements
LOOP
If the condition expression evaluates to a non-zero value, then the statements are executed. Then the condition is evaluated again, and again if it evaluates to a non-zero value, the statements are executed. This cycle continues until the condition evaluates to zero.
DO WHILE Max > Min |
The DO-UNTIL variation is slightly different. Its syntax is
DO
statements
LOOP UNTIL
condition
The statements are executed, then condition is evaluated. If condition evaluates to zero, then the statements are executed again, and condition is evaluated again. This process repeats until condition evaluates to a non-zero value.
DO |
The EXIT DO variation of the DO- loop is the most free-form. It allows you to use the EXIT DO statement within the body of statements. If this EXIT DO statement is executed, the DO- loop finishes. This variation has the following syntax:
DO
statements
EXIT DO
statements
LOOP
There may be any number of EXIT DO statements between DO and LOOP . There should probably be at least one EXIT DO statement if the loop is ever to finish, however.
In a Select-case construct, a single expression is evaluated. Depending on the value of the expression, a different block of code will be executed. They follow the syntax:
SELECT CASE
expression
CASE
value-list
statements
CASE
value-list
statements
...
END SELECT
The expression is evaluated. The resulting value is compared with the first value-list . If expression falls within value-list , then the first set of statements is executed and then execution resumes after the END SELECT . Otherwise, the next value-list is tested-if expression falls within that value-list , then the corresponding set of statements is executed. Each value-list is tried until a value-list is found that expression falls within. Perhaps there will not be any matching value-list; in this case, no set of statements will be executed.
Each value-list is a comma-separated list of values and/or ranges of values. Values may be constants. You can define numerical ranges by two constants separated by the TO keyword. Here are some sample value-lists:
CASE 1, 4, 6, 8 TO 10, 12, 14 TO 16, 18, 20 TO 22
You can set up a CASE ELSE clause in the Select-Case construct to make sure that there's a group of statements executed to handle any cases you don't test for. When using this sort of clause, follow the syntax:
SELECT CASE
expression
CASE
value-list
statements
CASE
value-list
statements
...
CASE ELSE
else-statements
END SELECT
The CASE ELSE line should occur after all other CASE lines in the construct: if it is reached, the else-statements block of statements will be executed and execution will jump down to the END SELECT line.
SELECT CASE Rank |
NewBASIC BASIC supports GOTO statements, which cause execution to jump to a label. The GOTO statement and label must be within the same function.
To define a label, use a line containing the label name followed by a colon. The name must be a valid name, unique within the routine. For example:
If you will do any error handling in the routine, you probably don't want to have a label named "next."
The GOTO statement consists of the GOTO keyword followed by the label name.
Normally, GOTO statements are used to skip large blocks of BASIC code that are not needed under certain circumstances. C-language programmers who like to "#ifdef out" code use IF...THEN GOTO in BASIC to skip over unwanted code.
In the following example, we use a GOTO to skip over the main body of code under certain conditions:
FUNCTION Factorial(x AS integer) AS integer |
Be careful when using GOTO. Some confusing situations can arise if you use GOTO to enter or exit a FOR/NEXT loop or SELECT/CASE construct.
If you've tried running any BASIC code, you've probably encountered errors. Some of these errors get caught before your program starts running, but some crop up in the middle of your tests. Perhaps you attempted to divide by zero, perhaps you tried to reference an out-of-range array element. Your program halted, an error dialog box appeared, and you knew it was time to fix your bug.
For advice about what to do when your program encounters an error, see page See Testing the Program with the Builder for the most common approach, or see the Debugging chapter, which starts on See Debugging .
However, it is possible to have your program handle errors itself. Using the constructs described below, you can "trap" errors and handle them as you see fit. Also, you may raise errors yourself.
By using an ONERROR GOTO construct, you may specify that a place in your routine is an error handler, and that if your program generates an error, execution should jump to the specified label in your program. To turn off error handling, you would use the special ONERROR GOTO 0 statement.
Within the body of your error-handling code, you'll probably want a RESUME statement. This clears the error within this routine, and lets execution resume within the main part of your routine. (You may specify that execution should resume where the error occurred, or that it should jump to a specified label.) The following RESUME options are available:
The following example shows code with a simple error handler:
The code above generates an error when it tries to compute 1/0: there is a divide-by-zero error. When the error occurs, execution jumps to the errorCatcher label. The RESUME NEXT statement causes execution to jump to the statement after the one that generated the error--with some exceptions.
If the error generating statement was in an...
IF condition
If an error occurs the IF construct's conditional, execution will continue after the END IF statement.
IF z = x/y THEN REM An error here would mean execution would resume
REM at line A
y = y - 1
z = x/y REM An error here would mean execution would resume
REM at line B
y = 7 REM line B
END IF
x = 3 REM line A
SELECT-CASE construct
Similar to the behavior of an IF...THEN clause--if an error occurs in the evaluation of the SELECT expression, execution will continue after the END SELECT; if an error occurs in the execution of one of the CASE bodies, execution will resume after the line generating the error as normal.
FOR-NEXT construct
Similar to the behavior of an IF...THEN clause--if an error occurs in the evaluation of the FOR construct's start, stop, or step expressions, execution will continue after the NEXT; if an error occurs in the body of the loop, execution will resume after the line generating the error as normal.
DO LOOP construct
Similar to the behavior of an IF...THEN clause--if an error occurs in the evaluation of the construct's condition expression, execution will continue after the LOOP; if an error occurs in the body of the loop, execution will resume after the line generating the error as normal.
Your routine may include more than one error handler. When your code generates an error, execution will jump to the label specified in the most recent ONERROR GOTO statement:
SUB ChangeCaptions( x AS integer )
ONERROR GOTO buttonErrorCatcher
button.caption = Str( 1 / x )
ONERROR GOTO dialogErrorCatcher
dialog1.caption = Str( 1 / (x-1) )
ONERROR GOTO 0
label1.caption = Str( 1 / (1 + (2 * x))
GOTO done
buttonErrorCatcher: REM we hit error setting the button caption
button1.caption = "Infinity"
RESUME NEXT
dialogErrorCatcher: REM we hit error setting the dialog caption
x = 7
RESUME
If you want your routine to totally ignore all errors, use an ONERROR RESUME NEXT statement. This special statement is equivalent to an error handler which just does a RESUME NEXT--all errors are ignored.
SUB RunWild()
ONERROR RESUME NEXT
...
END SUB
SUB RunWild()
ONERROR GOTO errorHandler
...
GOTO done
errorHandler:
RESUME NEXT REM Ignore error and keep going!
done:
END SUB
Each of the various error types has a number associated with it. You may want your error handler to react differently depending on what sort of error was generated. To find out the error number of the current error, call GetError().
You can generate errors by means of the RaiseError() routine. This allows you to generate an error just as if you had called some BASIC code that resulted in an error.
If you are writing a module of shared BASIC code that is to generate custom errors, use the RaiseError() command to generate these errors.
Complicated applications may have their own custom error numbers--these are useful for communicating error conditions from low-level routines to those routines which called them. Such application-custom error numbers should be above 32000. This range has been reserved for such errors.
The standard BASIC run-time errors have the following numbers:
To call a function or subroutine, use the name of the routine followed by a parenthesized comma-separated list of arguments:
RoutineName ( arg1 , arg2 , ... )
A call to a subroutine should appear on a line of itself; i.e., it shouldn't be part of an expression or assignment.
Each function has a return value. The return value's type determines the context in which a function call may appear. For example, if a function returns a string, then a call to that function may appear where a string was expected.
Component actions may be invoked in the same way as routines, except that you must also specify the component. The syntax for invoking a component action is
componentName.ActionName( arg1, arg2, ... )
For example, given a text component called text1, to invoke its DeleteRange() action, you would use the syntax:
You may also invoke an action using a string expression in the place of its name--enclose the expression in parentheses. For example, each of the following would invoke text1's DeleteRange() action.
text1.DeleteRange(9,10) REM The normal syntax
text1.("DeleteRange")(9,10) |
There are many built-in routines which perform useful operations upon strings or numbers. These routines as well as reference information about the built-in operators are listed here in alphabetical order.
This function returns the ASCII value of the first character of a string. If it were written in BASIC , it might look like
FUNCTION Asc( x AS string) AS integer
firstChar = Left( x, 1 )
SELECT CASE firstChar
...
CASE "A"
Asc = 65
CASE "B"
Asc = 66
CASE "C"
Asc = 67
...
END SELECT
END FUNCTION
ASCII value of the first character of the passed string; this will be an integer between 0 and 127.
The arc tangent of passed number
x
. The returned angle measure, which is expressed in radians, between
and
.
This function returns the type of a variable or expression. It will return one of the following strings: "integer," "long," "float," "string," "array," "struct," "complex," "module," or "component." It is useful for dealing with values whose type you do not know.
To get more specific type information about a complex value, use the Type() function.
x
???
Some value of some type. If you knew which type, you wouldn't need to use this function.
A string describing the value's type. It will return one of the following strings: "integer," "long," "float," "string," "array," "struct," "complex," "module," or "component."
This function returns a one-character long string, the string consisting of the character with the passed ASCII value. If this function were written in BASIC , it might look like
FUNCTION Chr( a AS integer) AS string
SELECT CASE a
...
CASE 65
Chr = "A"
CASE 66
Chr = "B"
CASE 67
Chr = "C"
...
END SELECT
END FUNCTION
The cosine of the passed angle . This will be a floating point value between negative one and one.
This function returns the module value of the current module (i.e., the module from which the function was called). It is not very useful, and is normally used only for internal testing. For information about how one may use module values, see See Loading Modules .
This function takes a Date structure and a TimeOfDay structure and packs their data into a long value. The resulting value may be decoded later via the LongToDateTime() subroutine. When this function stores the seconds of the time, it's only accurate to two seconds.
The fields of the structures must fall into the following ranges, or else overflow errors or a malformed long value may result-if the long value is malformed, it will be incorrectly decoded by LongToDateTime() . The function does not check the passed structure for valid values.
LongToDateTime(), DateToInteger(), TimeToLong()
The function packs the values of the passed Date structure into an integer. This function comes in handy for storing date information in a database. It's also useful if you will be comparing dates many times: the smaller the value returned by this function, the earlier the passed Date occurs. The resulting integer may be decoded back to a Date structure by means of the IntegerToDate() routine.
The function doesn't do any bounds checking on the passed Date; it is only guaranteed to work if the fields of the Date structure fall within the bounds:
Using values outside of these ranges may result in overflow errors or an integer that will be incorrectly interpreted by the IntegerToDate() routine.
e power , where e is the base of the natural logarithms (approx. 2.71828). This number will be positive.
This function returns the number of dimensions in the passed array. For example, the following code would check to make sure that the passed array was one-dimensional:
This function returns the size of one of an array's dimensions.
DIM appointment[12, 31] AS string |
For example, the following code would search an array of strings for a target string:
dimension
integer (0- dimensions-1)
Which dimension of the array to examine. Use the
GetArrayDims()
function to find out how many dimensions the array has.
This routine returns a number which corresponds to the current error, or zero if there is no current error. It is normally only called in error handlers (see See Error Handlers ).
This routine returns true (i.e., non-zero) if the passed component comp has the passed property prop . Otherwise, it returns false (i.e., zero). This can come in handy if you're unsure as to the component's type, or if you're using some components with custom component properties (See See Component Properties for information about defining custom component properties).
IF HasProperty( countButton, "count" ) THEN |
This function does not detect the presence of array properties.
This routine returns true (i.e., non-zero) if the passed component comp has the passed property prop . Otherwise, it returns false (i.e., zero).
This function returns a string containing the hexadecimal representation of the passed number x .
This function searches a string to see if it contains another string. If string big doesn't contain string little , then InStr() returns zero. If string big does contain string little , then InStr() returns the offset of the first occurrence of string little within string big . Thus:
If InStr() were written in BASIC , it might look like:
FUNCTION InStr( big AS string, little AS string) AS integer
lenBig = Len(big)
lenLittle = Len(little)
InStr = 0
count = 1
DO WHILE count <= (lenBig - lenLittle)
IF Mid(big, count, lenLittle) = little THEN
InStr = count REM We found a match! Set return value.
count = lenBig REM EXIT DO
END IF
count = count + 1
LOOP
END FUNCTION
If string little is found within string big , then InStr() returns the offset of the first occurrence of little within big ; for example, InStr("beagle", "e") returns 2. If string little is of zero length, then InStr() returns one. If string little cannot be found within string big , then InStr() returns zero.
This routine returns the floor of the passed number x , i.e., the greatest integral number which is less than or equal to x . (Note that this number might be returned in float format, and might be too large to fit in an integer or long value!)
If this function were written in BASIC and didn't need to handle numbers of large magnitude (i.e., numbers that would overflow a long integer), it might look like
FUNCTION Int( x AS float ) AS float
DIM floor AS long
floor = x
Int = floor
END FUNCTION
The floor of the passed number. This will be an integral value.
The subroutine unpacks an integer into the fields of a Date structure. This subroutine expects the passed integer to have been created by means of the DateToInteger() function. This subroutine has no explicit return function. You must pass a Date structure to it; the subroutine will fill in the fields of the passed structure.
The subroutine will fill in the structure with values in the following bounds:
The subroutine expects the integer to be formed as a bitfield:
DateToInteger(), LongToDateTime ()
This function checks the passed value to see if it is null. Exactly what "null" means depends on the value's type.
One if the passed complex variable is empty; zero if it is full.
String containing the first (leftmost) n characters of string s . If n is greater than the length of string s , then Left() returns the entire string s .
This function returns the length of a string; i.e., the number of characters in a string.
The number of characters in the string. This will be a non-negative integer.
This program loads a compiled Basic file as a module of the program. For details about how to use a module so loaded, see See Loading Modules .
Finance = LoadModule("DOS://~D/CALCFRMS/FINANCE") |
fileName
string
The path and name of the file to load. This string should be in all upper case. There are some shortcuts for specifying file locations in standard NEWBASIC directories:
"~D" is shorthand for the DOCUMENT directory.
"~A" is shorthand for the WORLD directory.
"~S" is shorthand for the SYSTEM directory.
"~T" is shorthand for the Top-level NEWBASIC directory.
A module. The module's UI gadgetry will appear. The code in the module's module_init() subroutine, if any, will be executed.
This program loads a compiled Basic file as a module of the program. If the module has already been loaded as shared, this function won't load it again. For details about how to use a module so loaded, see See Loading Modules .
Finance = LoadModuleShared("DOS://~D/CALCFRMS/FINANCE") |
fileName
string
The path and name of the file to load. This string should be in all upper case. There are some shortcuts for specifying file locations in standard NEWBASIC directories:
"~D" is shorthand for the DOCUMENT directory.
"~A" is shorthand for the WORLD directory.
"~S" is shorthand for the SYSTEM directory.
"~T" is shorthand for the Top-level NEWBASIC directory.
A module. If the module is being loaded now (if it has not already been loaded shared), then the module's UI gadgetry will appear. The code in the module's module_init() subroutine, if any, will be executed.
This subroutine unpacks a long value into Date and TimeOfDay structures, decoding a value created by the DateTimeToLong() function (or perhaps a timestamp value as used by a database component). Though this routine has no explicit return value, it takes a Date structure and a TimeOfDay structure and fills in their fields with data extracted from the passed long value.
The fields of the structures will fall into the following ranges.
This subroutine expects the passed long value to be one of those created by DateTimeToLong() (or perhaps a timestamp value as used by a database component). It treats the long value as a bitfield:
DateTimeToLong(), LongToTime(), IntegerToDate()
This function takes a number and unpacks it into a TimeOfDay structure--it assumes that the number was formed by the TimeToLong() function, and has been laid out with bits as follows:
It does not return anything explicitly; instead, it takes a long number and a TimeOfDay structure as arguments. The subroutine will fill in the fields of the TimeOfDay structure with the proper values.
TimeToLong(), LongToDateTime()
This function creates a component of the requested type, placing the component within the program' s component hierarchy as a child of the passed parent component.
FunButton = MakeComponent( "button", FunForm ) |
type
string
A string consisting of the name of a component type.
parent
component
A component to act as the new component's parent. It must be of a type able to act as the new component's parent, of course. Also, you may pass the string "top" to use the module's top object as the parent, or use the string "app" to use the application's top object as the parent; if your application consists of a single module, as most do, then passing "top" will have the same effect as passing "app." For more information about parent components, see
See Component Tree: Parents & Children
.
This function returns part of a string s . You may specify start , the offset at which to start copying, and length , the number of characters to copy. If start is greater than the length of string s , then the empty string is returned. If start + length is greater than the length of string s , then fewer than length characters will be returned: the returned string will start with the character at offset start and continue to the end of string s .
If this function were written in BASIC , it might look like:
FUNCTION Mid( s AS string, start AS integer, length AS integer ) AS string
DIM lengthOfS AS integer
DIM tailLength AS integer
DIM r AS integer
lengthOfS = Length(s)
tailLength = lengthOfS - ( start - 1 + length )
r = length + tailLength
IF r > 0 THEN
Mid = Right( s, r )
ELSE
Mid = ""
END IF
Mid = Left(length, Mid)
END FUNCTION
s
string
The string from which to copy.
start
integer
The offset at which to start copying. To start from the first character, pass 1.
The string derived by starting at offset start in string s and getting length characters. If start is greater than the length of string s , then the empty string is returned. If start + length is greater than the length of string s , then fewer than length characters will be returned: the returned string will start with the character at offset start and continue to the end of string s .
base float
The number to be raised to a power.
power float
The exponent to which to raise base.
This function generates a run-time error. For a discussion about how this might be useful, see See Generating Errors .
errorNumber integer
The number of the error to raise.
This function returns the last (rightmost) length characters in a string s. If it were written in BASIC, it might look like:
FUNCTION Right( s AS string, length AS integer ) AS string
start = Len(s) - length + 1
IF start > 1 THEN
Right = Mid( s, start, length )
ELSE
Right = Mid( s, 1, length )
END IF
END FUNCTION
The last (rightmost) length characters of string s . If there are fewer than length characters in string s , then string s will be returned.
This function returns a random number; specifically, a random fraction between zero and one.
FUNCTION Sgn( x AS float ) AS integer
This function returns information about the sign of a number. If the passed number x is positive, Sgn() returns 1; if x is 0, then Sgn() returns 0; if x is negative, then Sgn() returns -1. It is always true that Sgn(x) * Abs(x) = x.
If this function were written in BASIC , it might look like:
FUNCTION Sgn( x AS float ) AS integer
IF x THEN
Sgn = x / Abs(x)
ELSE
Sgn = 0
END IF
END FUNCTION
One of {-1, 0, 1}. If the passed number x is negative, the function will return -1; if the passed number x is zero, the function will return zero; if the passed number x is positive, then the function will return 1.
The sine of the passed angle. This will be a floating point value between negative one and one.
This function returns a string of n space characters. If it were written in BASIC, it might look like:
FUNCTION Space( n AS integer ) AS string
DIM count AS integer
FOR count = 1 TO n
Space = Space + " "
NEXT count
END FUNCTION
String consisting of n spaces.
This function returns the square root of a number.
This function compares two strings to determine which comes first in a lexical (alphabetic) ordering. You may conduct a case-sensitive or case-insensitive comparison: set nocase to a non-zero value for a case-insensitive comparison, zero for case-sensitive.
nocase
integer (0-1)
Set non-zero to conduct a case-insensitive comparison; zero to conduct a case-sensitive search.
It takes a TimeOfDay structure and packs the structure's data into a long number with bits as follows:
The function does not check the fields of the passed structure for validity. You may fill in the structure's fields with values that do not correspond to a valid time of day. Note that if you pass values that are outside the ranges mentioned in the list above, the result may be an overflow error or a malformed number which will be incorrectly decoded by the LongToTime() routine.
LongToTime() , DateTimeToLong( )
This function returns the type of a variable or expression. It will return one of the following strings: "integer," "long," "float," "string," "array," "struct," "graphic," "module," or "component." It is useful for dealing with values whose type you do not know.
This function is similar to the BasicType() function; BasicType() just returns "complex" when it is passed a complex value; this function returns "graphic" or "integer" to specify the exact complex type.
x
???
Some value of some type. If you knew which type, you wouldn't need to use this function.
A string describing the value's type. It will return one of the following strings: "integer," "long," "float," "string," "array," "struct," "graphic," "module," or "component."
This routine attempts to unload the module and free up its associated resources. If modl is a shared module (loaded via LoadModuleShared() ), then it will only be unloaded if no other applications are still using it.
If you unload a module that has loaded other modules, those other modules will be unloaded automatically.
As each module is unloaded, it's module_exit() routine will be called.
In general, it's not a good idea for a module to use this routine upon itself.
This routine pauses execution of the program's BASIC code until the program's on-screen UI has had a chance to update. Note that events may take place during a call to Update(), as NewBASIC components have a chance to respond to input from the user and the operating system. If your code contains event handlers that modify global variables or component properties, you should not rely on these values remaining constant across a call to Update().
This function converts a string s to a numeric value. The string s should contain the textual representation of a number. Val() translates as much of the string as it can, starting at the beginning of the string. Once it encounters a character that it does not recognize as part of a number, it will ignore that character and all characters following.
Val() recognizes scientific notation.
Val() does not recognize the &H or &O prefixes.
This function determines whether the component parent would be able to accept component child as a child. There are various reasons that the component might not accept the child.
For an overview of the parent/child component hierarchy, see See Component Tree: Parents & Children .
For information about restrictions on a component's children, see the documentation for the type of the parent component and the type of the child component. See See Component Types for more information about these.
parent
component; the potential parent. Or you may pass "app" to specify the top level of the application or "top" to specify the top component of an aggregate component.
We've discussed the syntax related to components in other places. We will present a summary of the syntax here.
The syntax for accessing a component property is
Component properties may appear in expressions and assignments in the same places that variables can. You may implicitly declare a new property for a component by assigning a value to it. For further discussion and examples, see See Component Properties .
Declaring a component event handler is just writing a routine with a special name that takes the proper arguments. The event handler routine name is
To change the component name used in the construction of a component's event handler names, change its proto property. If you set button1's proto property to "FunButton," then it will generate FunButton_pressed() events instead of button1_pressed() events. Be careful, however.
Components created via the MakeComponent() function don't have their proto property set by default; you must set it yourself.
DIM FunButton AS component |
Because the proto property will form the first part of one or more routine names, make sure that it would be a legal beginning for a routine name. Its first character should be a letter; other characters should be letters, numbers, or underscore characters ("_").
For any event handler, the first argument will be a component called self. This will be the component handling the event.
Invoking component actions is much like calling routines. The syntax for a component action invocation is
Some component actions have return values; invocations of such actions may appear within expressions just as function calls may. Some component actions do not have return values; results of such component actions may appear in the same contexts as subroutine calls: normally they will appear on their own line. Further discussion and examples of the calling conventions associated with functions and subroutines appears in See Organization of BASIC Code .
You can organize your program so that it is made up of more than one file. Each file in the program is known as a module. A module may load another module (provided it has been compiled), and may then refer to certain variables and routines within the loaded module. To load a module, use the LoadModuleShared() routine or the LoadModule() routine. LoadModule() loads a module specified by file location and returns a module value. LoadModuleShared() will only load a module if it's not already loaded--it then returns the module value of the loaded module.
You may reference routines and exported variables in loaded modules using the following syntax:
For example, suppose there were a module providing financial functions. The following sample code loads the module, sets the value of a variable provided by that module, then calls a function in the module.
DIM Finance AS module |
You may only reference variables which the loaded module has exported. Thus, when writing a module that is to be loaded, you must export all variables which will be referenced by loaders of the module. To export a variable, use the EXPORT keyword. It has the following syntax:
Thus, in the above example, the FINANCE.BAS file must include the following code:
SUB module_init()
FUNCTION Payment(r AS float, nPs AS integer, amt AS float) AS float |
There is a limitation that arises when using an EXPORTed component variable--the interpreter will not know the component type of the component. You may still call the component's actions and access its properties.
The EXPORT keyword reserves a location for the variable in the module header when the module gets compiled, allowing other modules to access the variable at runtime.
Note that .BAS files must be built to be loadable by a LoadModuleShared() or LoadModule() call. That is, if you will load the module with a program running under the Builder, you will need to compile your module for NewBASIC.
To unload a module, call UnloadModule() . To totally unload a shared module that was loaded by means of LoadModuleShared() , call DestroyModule().
If your module is an application, then there are some special things you can do to affect how the System Launcher program will interact with your application.
...then the System Launcher program will not try to unload your application unless the system is running desperately low on memory.
sub module_init() |
The system and the System Launcher will call certain routines in your application when the user
These routines are described here.
Called by the interpreter after the module is first loaded and after all components have been initialized. Any initialization code needed for the module can go here. Variables declared here will be global to the entire module.
Called by the interpreter just before the module is unloaded. Any special shutdown work desired can go here.
The System Launcher program calls your module_show() routine when the user is switching to your application from another application.
The System Launcher program calls your module_hide() routine when the user is switching to another application. This is a chance to set forms and dialogs not visible, saving memory in certain situations.
sub module_goTo(context as string)
A call to this routine is a request for the module to go to a new context. As a convention, the null string should be used to indicate the startup state. GoTo context changes are usually requested in two situations:
function module_getContext() as string
Returns the current context for the module. The returned string should be sufficient to restore the state of the module if passed to the module_goTo() function. The context will generally be requested for one of two reasons: