The Language

You will use BASIC code to write event handlers and other routines. You will manipulate components, numbers, and strings.

Typical Routine Components

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.

Topics covered in this section

The rest of this section covers the following topics:

Topic

Description

Page

Comments

Remarks ignored by BASIC but useful to people

See Comments

Variables, Constants, and Types

Places to store data, and types of data that is stored

See Variables, Constants, and Types

Assignments and Expressions

Computing values by combining values and storing those values in variables

See Expressions and Assignments

Flow Control Constructs

Branching and looping constructs

See Flow Control Constructs

Calling Functions and Subroutines

Calling functions and subroutines: both those you've written, and those built in

See Calling Functions and Subroutines

Built-in Routines

Routines provided by NewBASIC

See Built-in Routines and Operators

Components

Summary of syntax for manipulating components, properties, actions, and events

See Components: Summary of Syntax

Modules

Managing programs with source code in more than one file

See Loading Modules

Comments

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.

FUNCTION Squared ( x AS float )
REM Squared()
REM This function takes a number and returns
REM the number raised to the second power.

Squared = x * x REM return value = x * x

REM
REM JEAN--would it be better to optimize for the case where
REM x is zero? Try it out.
REM                 --JORDAN
REM
END FUNCTION REM Squared

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.

Status.caption = "Patient has achieved REM sleep."

Variables, Constants, and Types

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.

Available Types

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.

For example:

number1.value = -&Ha1 REM -161
cr = &O15 REM 13

You may use exponential notation to express numerical constants.

For example:

number1.value = -2.73E2 REM -273

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.

For example:

s = "B" + "end" REM s is "Bend"

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.

DIM factorial[10] AS LONG

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.

For example:

DIM a[10] AS integer
DIM b[10] AS integer

a[0] = b[0] REM this is fine
a = b REM this results in a syntax error

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:

REDIM array-name[new-size]

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
DIM lastname AS string
DIM firstname AS string
DIM middlename AS string
DIM SSN AS long
END STRUCT

To reference fields of the structure, use the caret ("^") or period (".").

A simple example:

DIM FTPoomm AS STRUCT emplrecord
FTPoomm^lastname = "Poomm"
FTPoomm^firstname = "Franklin"
FTPoomm^middlename = "Tiberius"
FTPoomm^number = 560793409
...
label1.caption = FTPoomm.firstname + " " + FTPoomm.lastname

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.

A simple example:

DIM origPoint AS STRUCT point
DIM copyPoint AS STRUCT point
origPoint.x = -26
origPoint.y = 23
copyPoint.x = origPoint.x
copyPoint.y = origPoint.y
copyPoint.x = 69
REM origPoint is ( -26, 23 ), but copyPoint is ( 69, 23 )

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
picture1.graphic = myNullComplex REM clear graphic data

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.

Declaring Variables

To declare a variable, use the DIM keyword.

For example:

DIM x AS integer
DIM theText AS string, theDate AS STRUCT Date, times[4] AS STRUCT TimeOfDay

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. .

Variable Scope

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.

For example:

SUB InitializeArrays() GLOBAL
DIM PlayerInfo[NumPlayers] AS STRUCT PlayerStruct REM will be global
END SUB

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:

EXPORT variable

In this syntax, variable is the name of a global variable.

A simple example:

SUB module_init()
  DIM bestGuess AS float
  EXPORT bestGuess
END SUB

For an explanation of exported variables and modules, see See Loading Modules .

Routine Arguments and Return Values

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

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

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.

CONST TELNET_EOF

236

CONST TRANSFER_DONE

TELNET_EOF

CONST STATUS_STRING_DONE

"Telnet finished transfer successfully."

CONST PAPER_WIDTH

8.5 * 72

CONST MARGIN_SIZE

1 * 72

CONST DOCUMENT_WIDTH

PAPER_WIDTH - (2 * MARGIN_SIZE)

IF telnetResult = TRANSFER_DONE THEN

entry3.text = STATUS_STRING_DONE

END IF

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 and Assignments

Expressions

Expressions in NewBASIC are built up from value, variables, and operators. The available operators are listed in precedence order below.

NOT
The logical-not operator.

*  /  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

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.

Some examples:

Area = Length * Width

Remainder = Numerator.value MOD Denominator.value

NumPlayers.value = 2

numEmployees = numEmployees + 1

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:

DIM x AS integer
x = "wrong" REM this generates an error

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
DIM b[10] AS integer

a[0] = b[0] REM This works
a = b REM This is a syntax error

Flow Control Constructs

The following constructs allow you to control the flow of execution within a routine. We will discuss each of these constructs in detail.

Construct

Description

Page

IF-THEN

constructs allow for simple branching

See IF-THEN-ELSE IF-ELSE-END IF

FOR-NEXT

loops allow you to repeat a group of BASIC statements a set number of times

See FOR-TO-STEP-EXIT FOR-NEXT-

DO-LOOPS

allow you to repeat a group of statements until some condition is met

See DO-WHILE-EXIT DO-LOOP-UNTIL-

SELECT-CASE

constructs allow you to execute one of several groups of statements based upon the value of some variable

See SELECT CASE-CASE-CASE ELSE-END SELECT

GOTO

jumps to a label within the routine

See GOTO ...

IF-THEN-ELSE IF-ELSE-END IF

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.

Simplest if-then

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.)

A simple example:

IF (numPlayers = 1) THEN
  Player[1].name = "Computer"
  Player[1].hiScore = 0
END IF

ELSE statement

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.

A simple example:

IF (Product >= 0) THEN
  Answer.value = Sqr(Product)
  Status.text = "Done."
ELSE
  Answer.value = 0
  Status.text = "Error #6: Bad Inputs"
END IF

Complex ELSE

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:

FUNCTION Plural( noun AS string ) AS string
REM This function takes a word and returns the
REM plural of that word.
REM Plural("win") yields "wins".
REM Plural("whinny") yields "whinnies".
REM Plural("way") yields "ways".
REM Plural("wish") yields "wishes"
REM Plural("watch") yields "watches"
REM Plural("wife") yields "wives"
REM Plural("wolf") yields "wolves". .
REM Plural("sheep") yields "sheeps". Hey, nobody's perfect.

  tail = Right( noun, 2 )
  IF tail = "sh" OR tail = "ch" THEN
    Plural = noun + "es"
  ELSE IF Right( tail, 1) = "y" THEN
    IF (tail<>"ay") AND (tail<>"ey") AND (tail<>"oy") AND (tail<>"uy") THEN
     l = Len( noun )
      Plural = Left( noun, l-1 ) + "ies"
    END IF
  ELSE IF tail = "fe" THEN
    l = Len( noun )
    Plural = Left( noun, l-2 ) + "ves"
  ELSE IF tail = "lf" THEN
    Plural = Left( noun, l-1 ) + "ves"
  ELSE
    Plural = noun + "s"
  END IF
END FUNCTION

FO R-TO -STEP-EXIT FOR-NEXT-

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

Count variable

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:

FOR Player = 0 TO NumPlayers -1
  PlayerRecord[Player].score = 0
  PlayerRecord[Player].time = 0
  DrawBeast(Player)
NEXT Player

FOR y = 1 TO gadget1.height-1
gadget1!DrawLine( 1, y, gadget1.width, y, BLACK)
r = Rnd() * 2
IF r THEN
y = y * 2
END IF
NEXT y

Using the STEP keyword

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 .

Two simple examples:

FOR Year = 1900 TO 1995 STEP 5
  ChartDraw(Year, Profits[Year-1900])
NEXT Year

FOR i = numCards TO NewSlot + 1 STEP -1
  CardHandles(i) = CardHandles(i-1)
NEXT i

The add, start, and stop values are evaluated only once, before the looping starts.

EXIT FOR statement

To force the loop to finish early, you can use an EXIT FOR statement:

FOR charNum = 0 TO Len(theString)-1
IF Mid(theString,charNum,1) = "*" THEN
successFlag = 1
EXIT FOR
END IF
NEXT charNum

DO- WHILE-EXIT DO-LOOP-UNTIL-

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:

DO WHILE

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.

A simple example:

DO WHILE Max > Min
  NewGuess = Int( (Max + Min) / 2 )
  Result = TestGuess(NewGuess)
  IF Result < 0 THEN
    Min = NewGuess
  ELSE
    Max = NewGuess
  END IF
LOOP

DO UNTIL

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.

A simple example:

DO
  NewGuess = Int( (Max + Min) / 2 )
  Result = TestGuess(NewGuess)
  IF Result < 0 THEN
    Min = NewGuess + 1
  ELSE
    Max = NewGuess - 1
  END IF
LOOP UNTIL Max <= Min

You may combine the DO-WHILE and LOOP-UNTIL constructs:

DO WHILE Max <= Min
...
LOOP UNTIL TestGuess(NewGuess)

EXIT 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.

A simple example:

DO
  NewGuess = Int( (Max + Min) / 2)
  Result = TestGuess(NewGuess)
  IF Result = 0 THEN
    Success = 1
    EXIT DO
  END IF
  IF Min = Max THEN
    Success = 0
    EXIT DO
  END IF
  IF Result < 0 THEN
    Min = NewGuess
  ELSE
    Max = NewGuess
  END IF
LOOP

SELECT CASE-CAS E-CASE ELSE-END SELECT

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.

Value lists

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

CASE 1, 4, 6, 8 TO 10, 12, 14 TO 16, 18, 20 TO 22

CASE 0.0 TO 0.5

CASE "cookie"

A simple example of a Select-Case statement:

SELECT CASE LEFT(TheSymbol, 1)
 CASE "a", "e", "i", "o", "u"
    Category = "vowel"
 CASE "y"
    Category = "sometimes y"
 CASE "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", \
"r", "s", "t", "v", "w", "x", "z"
    Category = "consonant"
END SELECT

CASE Else

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

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.

A simple example:

SELECT CASE Rank
  CASE 11, 12, 13 REM Jack, Queen King
    Value = 10
  CASE 1 REM Ace
    Value = 11
    Aces = Aces + 1
  CASE ELSE REM Number card
    Value = Rank
END SELECT

GOTO ...

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:

myLabel:

If you will do any error handling in the routine, you probably don't want to have a label named "next."

Examples

The GOTO statement consists of the GOTO keyword followed by the label name.

For example:

GOTO myLabel

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
Factorial = 1
IF x < 2 THEN
GOTO done
END IF
DIM count AS integer
FOR count = 1 TO x
Factorial = Factorial * count
NEXT count
done:
END FUNCTION

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.

  • The compiler will catch some of these and force you to change them; it may not detect all of them.
  • For example, the following would be dangerous, since it's not obvious what to do at the NEXT statement:

    GOTO abunaiNoLabel REM This causes an error.
    FOR count = 1 TO 10
    x = 2
    abunaiNoLabel:
    NEXT count

Errors: Handling, Ignoring, and Raising

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.

Error Handlers

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:

SUB module_init()
DIM x AS integer
ONERROR GOTO errorCatcher REM turn on errorCatcher error handler
label1.caption = Str(1/x)
ONERROR GOTO 0 REM turn off error handling
GOTO done
errorCatcher: REM Handles error setting
REM label1.caption to 1/0
label1.caption = "Infinity"
RESUME NEXT
done:
END SUB

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.

Exceptions to the resume command

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.

Multiple error handlers

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

done:
END SUB

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

...would be equivalent to...

SUB RunWild()
ONERROR GOTO errorHandler
...
GOTO done
errorHandler:
RESUME NEXT REM Ignore error and keep going!
done:
END SUB

Getting the Current Error

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().

Here is an example using GetError():

SUB ChangeCaption( x AS integer )
DIM error AS integer
ONERROR GOTO errorTrap
label1.caption = Str( 1 / x )
GOTO done
errorTrap:
error = GetError()
SELECT CASE error
CASE 30 REM we tried to divide by zero
label1.caption = "Infinity"
CASE ELSE
label1.caption = "Unknown Error"
END SELECT
RESUME NEXT
done:
END SUB

Generating Errors

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.

Error Numbers

The standard BASIC run-time errors have the following numbers:

Error Numbers

2 "Bad parameter type"

3 "Bad return type"

4 "Array reference before dim"

5 "Bad module"

6 "Bad module reference"

7 "Type mismatch"

8 "Bad type"

9 "Bad array reference"

10 "Bad opcode"

11 "Bad rval"

29 "Loop overflow. Try using DO WHILE"

30 "Overflow"

31 "Call to function expected"

32 "Call to procedure expected"

33 "Bad string index"

34 "Null struct or component"

35 "GET PAUL; Pass by reference not currently supported"

36 "Invalid parent"

37 "Stack Overflow (check for infinite recursion)"

12 "Bad number of arguments"

13 "Argument not a number"

14 "Argument not a string"

15 "Argument is of invalid type"

16 "Divide by zero"

17 "Incompatible types"

18 "Unknown component class"

19 "Invalid action"

20 "Readonly property"

21 "Unknown property"

22 "Wrong value type for property"

23 "Wrong size for property"

24 "The property has not been set"

25 "Wrong number of arguments to action"

26 "Wrong type"

27 "Specific property error"

28 "Function not found in module"

38 "CompInit (1) is an undocumented feature and (2) can't be used with aggregate components"

39 "Array too big"

40 "You are exiting a routine that has a pending error."

41 "You cannot use RESUME if there is no pending error."

42 "Negative dimension in DIM or REDIM"

43 "quiet exit (you should never see this)"

44 "Module already being unloaded"

45 "Unexpected end of loop (cannot GOTO inside a for loop from outside)"

46 "Unexpected end of routine (cannot GOTO outside a for loop from inside)"

47 "Requested component library not found"

48 "Module does not export any aggregate components"

49 "Confused -- invalid runtime error"

Calling Functions and Subroutines

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.

Calling Component Actions

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:

text1.DeleteRange( 5, 10 )

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)
text1.("DeleteR" + "ange")(7,8)
DIM s AS string
s = "DeleteRange"
text1.(s)(5,6)

Built-in Routines and Operators

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.

Abs()

FUNCTION Abs( x AS float ) AS float

This function returns the absolute value of a number. If it were written in BASIC , it might look like

FUNCTION Abs( x AS float ) AS float
  IF ( x >= 0 )
    Abs = x
  ELSE
    Abs = -x
END FUNCTION

A simple example:

ManhattanDistance = Abs(x1 - x2) + Abs(y1 - y2)

Pass:

x float

Return:

The absolute value of x .

See Also:

Sgn()

AND Operator

p AS long AND q AS long

The logical-and operator. If p is zero or q is zero, this will evaluate to zero; if both p and q are non-zero, it will evaluate to a non-zero value. For example:

  • 0 AND 15 is 0.
  • 0 AND 0 is 0.
  • -37 AND 37 is non-zero.

Asc()

FUNCTION Asc( x AS string) AS integer

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

A simple example:

doubleQuote = Asc( &H22 ) REM " character

Pass:

x string

Return:

ASCII value of the first character of the passed string; this will be an integer between 0 and 127.

See Also:

Chr()

Atn()

FUNCTION Atn( x AS float ) AS float

This function returns the arc tangent of a number. The returned angle measure is expressed in radians and is between and .

A simple example:

IF width = 0 THEN REM Avoid divide by zero!
  angle = 1.57080 REM pi/2 is approx. 1.57080
ELSE
  angle = Atn( length/width )
END IF

Pass:

x   A number.

Return:

The arc tangent of passed number x . The returned angle measure, which is expressed in radians, between and .

See Also:

Sin(), Cos()

BasicType()

FUNCTION BasicType( x ) AS string

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.

A simple example:

SUB DisplayData( theData )
SELECT CASE BasicType( theData )
CASE "integer", "long", "float"
label1.caption = Str( Int( theData ))
CASE "string"
label1.caption = theData
CASE "array"
SELECT CASE GetArrayDims( theData )
CASE 1
DisplayData(theData[0]
CASE 2
DisplayData(theData[0,0])
CASE 3
DisplayData(theData[0,0,0])
CASE ELSE
label1.caption = ""
END SELECT
END SUB

To get more specific type information about a complex value, use the Type() function.

Pass:

x   ???
Some value of some type. If you knew which type, you wouldn't need to use this function.

Return:

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."

See Also:

Type()

BITAND Operator

x AS long BITAND y AS long

The bitwise-and operator. For example:

  • 13 BITAND 0 is 0.
  • 13 BITAND 10 is 8.
  • 13 BITAND 21 is 9.

BITOR Operator

x AS long BITOR y AS long

The bitwise-or operator. For example:

  • 13 BITOR 0 is 13.
  • 13 BITOR 10 is 15.
  • 13 BITOR 21 is 29.

BITXOR Operator

x AS long BITXOR y AS long

The bitwise-exclusive-or operator. For example:

  • 13 BITXOR 0 is 13.
  • 13 BITXOR 10 is 7.
  • 13 BITXOR 21 is 24.

Chr()

FUNCTION Chr( a AS integer) AS string

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

A simple example:

digitChar = Asc(digit + 48)
REM "0" is ASCII 48

Pass:

a   integer (0-127)
An ASCII value (an integer between 0 and 127).

Return:

A single-character string consisting of the character with the passed ASCII value.

See Also:

Asc()

Cos()

FUNCTION Cos( angle AS float) AS float

This function returns the cosine of an angle .

A simple example:

x = xOrigin + ( Cos(vectAngle) * vectAmplitude )

Pass:

angle   An angle measure expressed in radians.

Return:

The cosine of the passed angle . This will be a floating point value between negative one and one.

See Also:

Sin(), Atn()

CurModule()

FUNCTION CurModule() AS module

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 .

DateTimeToLong()

FUNCTION system:DateTimeToLong( date AS STRUCT Date, time AS STRUCT TimeOfDay ) AS long

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.

  • year (1980-2107)
  • month (0-15)
  • day (0-31)
  • hour (0-31)
  • minute (0-63)
  • second (0-62)

The resulting long value consists of a bitfield:

  • Top 7 bits: (year-1980), with the top bit inverted
  • Next 4 bits: month
  • Next 5 bits: day of the month
  • Next 5 bits: hour
  • Next 6 bits: minute
  • Bottom 5 bits: seconds/2

See Also:

LongToDateTime(), DateToInteger(), TimeToLong()

DateToInteger()

FUNCTION system:DateToInteger( date AS STRUCT Date ) AS integer

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:

  • year: 1980 -2107
  • month: 0 - 15
  • day: 0 -31

Using values outside of these ranges may result in overflow errors or an integer that will be incorrectly interpreted by the IntegerToDate() routine.

The integer is formed as a bitfield:

  • Top 7 bits: (year-1980) with the top bit inverted.
  • Next 4 bits: month
  • Bottom 5 bits: day

See Also:

IntegerToDate(), DateTimeToLong()

DestroyModule()

SUB DestroyModule( m AS module )

This subroutine unconditionally unloads a module, even if that module is still being used by other modules. It is a dangerous subroutine; you will probably never have reason to use it. You probably want to use UnloadModule() instead.

Pass:

m module
The module to destroy.

See Also:

UnloadModule()

Exp()

FUNCTION Exp( power AS float) AS float

This function returns epower.

A simple example:

FUNCTION TenToThe( power AS float ) AS float
REM Returns 10^power
  y = power * 2.3025851 REM y = power * log(10)
  TenToThe = Exp( y )
END FUNCTION

Pass:

power   float

Return:

e power , where e is the base of the natural logarithms (approx. 2.71828). This number will be positive.

See Also:

Log()

GetArrayDims()

FUNCTION GetArrayDims( arrayName ) AS integer

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:

FUNCTION Search( target AS string, array[] AS string ) AS integer
REM Search the array of strings for the passed string

REM Make sure the array is one-dimensional
IF GetArrayDims( array ) <> 1 THEN
Search = -99 REM Error !
GOTO done
END IF

...
Do search
...

done:
END FUNCTION

Pass:

arrayName  
An array.

Return:

The number of dimensions in the array.

See Also:

GetArraySize()

GetArraySize()

FUNCTION GetArraySize( arrayName, dimension AS integer ) AS integer

This function returns the size of one of an array's dimensions.

For example...

DIM appointment[12, 31] AS string
x = GetArraySize( appointment, 0 ) REM x is 12
x = GetArraySize( appointment, 1 ) REM x is 31

For example, the following code would search an array of strings for a target string:

FUNCTION Search( target AS string, array[] AS string ) AS integer
REM Search the array of strings for the passed string

REM Search the table
DIM numElems AS integer REM number of elements in array
numElems = GetArraySize( array, 0 )

DIM count AS integer REM looping variable

FOR count = 0 TO numElems-1
IF target = array[count] THEN
Search = count
EXIT FOR
END IF
NEXT count

END FUNCTION

Pass:

arrayName  
An array

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.

Return:

The number of elements in that dimension of the array.

See Also:

GetArrayDims()

GetError()

FUNCTION GetError() AS integer

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 ).

Pass:

Nothing.

GetMemoryUsedBy()

FUNCTION GetMemoryUsedBy( modl AS module ) AS integer

This routine computes the amount of memory currently in use by the specified module.

GetSource()

FUNCTION GetSource( modl AS module ) AS string

This routine returns the string that was passed to LoadModuleShared() / LoadModule() when loading in module modl.

HasProperty()

FUNCTION HasProperty( comp AS component, prop AS string ) AS integer

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).

A simple example:

IF HasProperty( countButton, "count" ) THEN
countButton.count = countButton.count + 1
ELSE
countButton.count = 2
END IF

This function does not detect the presence of array properties.

Pass:

comp component
Component to check.

prop string
Property to check for.

Return:

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).

Hex()

FUNCTION Hex( x AS long ) AS string

This function returns a string containing the hexadecimal representation of the passed number x .

  • Hex( 42 ) returns "2A."
  • Hex( 10.5 ) returns "A."
  • Hex( -42 ) returns "-2A."
  • A simple example:

    statusLabel.caption = "Data: " + Hex( data[0] ) + " " + Hex( data[0] )

Pass:

x   long

Return:

This function returns a string containing the hexadecimal representation of the passed number x .

See Also:

Oct(), Str()

InStr()

FUNCTION InStr( big AS string, little AS string ) AS integer

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:

  • InStr("headache", "he") yields 1
  • InStr("headache", "she") yields 0
  • InStr("headache", "ada") yields 3

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

A simple example:

IF InStr( article, "Kibo" ) THEN
  KiboAlertDialog.Show()
END IF

A more complicated example:

FUNCTION DelSpaces( original AS string ) AS string
REM Takes a string and returns the string without
REM spaces. DelSpaces("Who? Oh. ") returns "Who?Oh."

  theString = original
  find = InStr( original, " " )
  DO WHILE find
    DelSpaces = DelSpaces + Left( original, find-1 )
    theString = Mid( theString, find+1, 60000 )
    find = InStr( original, " " )
  LOOP
  DelSpaces = DelSpaces + theString
END FUNCTION

Pass:

big string
The string within which to search.

little string
The string to search for.

Return:

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.

See Also:

Mid()

Int()

FUNCTION Int( x AS float ) AS float

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!)

  • Int(-3.5) returns -4.
  • Int(-3) returns -3.
  • Int(3.5) returns 3.
  • Int(3) returns 3.

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

A simple example:

FUNCTION RollDice()
REM Returns the total roll of a pair of
REM virtual dice.
  die1 = 6 * Rnd()    REM between 0 and 5.999...
  die1 = Int( die1 )  REM one of 0,1,2,3,4,5
  die1 = die1 + 1     REM one of 1,2,3,4,5,6
                     REM Finished rolling first die
  die2 = Int( 6 * RND() ) + 1
                     REM Finished rolling second die
  RollDice = die1 + die2
END FUNCTION

Pass:

x float

Return:

The floor of the passed number. This will be an integral value.

IntegerToDate()

SUB system:IntegerToDate( in AS integer, out AS STRUCT Date )

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:

  • year: 1980 -2107
  • month: 0 - 15
  • day: 0 -31

The subroutine expects the integer to be formed as a bitfield:

  • Top 7 bits: (year-1980) with the top bit inverted.
  • Next 4
  • 5 bits: day

See Also:

DateToInteger(), LongToDateTime ()

IsNull()

FUNCTION IsNull( x AS ... ) AS integer

This function checks the passed value to see if it is null. Exactly what "null" means depends on the value's type.

  • Complex : The function returns true if the passed value is empty; else it returns false.
  • Module : The function returns 1 if the passed value is null (does not contain a loaded module); else it returns zero.
  • String : The function returns 1 if the passed string is null; else it returns zero. The null string is not quite the same as the empty string, though null strings and empty strings behave in the same way in all contexts outside of this function. To test for the empty string, compare it with "".

IsNullComplex()

FUNCTION IsNullComplex( compl AS complex) AS integer

This function returns true if the passed complex variable is empty.

Pass:

compl complex

Return:

One if the passed complex variable is empty; zero if it is full.

IsNullComponent()

FUNCTION IsNullComponent( comp AS component ) AS integer

This function returns true (i.e., non-zero) if there is no component assigned the passed component comp . Otherwise, it returns zero.

Left()

FUNCTION Left( s AS string, n AS integer ) AS string

This function returns the first (left-most) n characters in a string s . If it were written in BASIC, it might look like:

FUNCTION Left( s AS string, n AS integer ) AS string
  Left = Mid( s, 1, n )
END FUNCTION

A simple example:

MiddleInitial = Left( MiddleName, 1)

Pass:

s   string
The original string.

n integer
The number of characters to extract. This should be a non-negative integer.

Return:

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 .

See Also:

Mid()

Len()

FUNCTION Len( s AS string ) AS integer

This function returns the length of a string; i.e., the number of characters in a string.

A simple example:

FUNCTION UpperCase( s AS string ) AS string
REM Returns the upper-case equivalent of a string.
REM E.g. UpperCase("UpperCase") yields "UPPERCASE".

  FOR c = 1 TO Len( s ) REM for each character in string s...
    l = Mid( s, c, 1 )
    SELECT CASE l
      CASE "a" TO "z"
        a = Asc(l) - 32
        UpperCase = UpperCase + Chr(a)
      CASE ELSE
        UpperCase = UpperCase + l
     END SELECT
  NEXT c
END FUNCTION

Pass:

s   string
The string to measure.

Return:

The number of characters in the string. This will be a non-negative integer.

LoadModule()

FUNCTION LoadModule( fileName AS string ) AS module

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 .

A simple example:

Finance = LoadModule("DOS://~D/CALCFRMS/FINANCE")
interest = Finance:IPmt( 12, .08, 10000 )

Pass:

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.

Return:

A module. The module's UI gadgetry will appear. The code in the module's module_init() subroutine, if any, will be executed.

LoadModuleShared()

FUNCTION LoadModuleShared( fileName AS string ) AS module

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 .

A simple example:

Finance = LoadModuleShared("DOS://~D/CALCFRMS/FINANCE")
interest = Finance:IPmt( 12, .08, 10000 )

Pass:

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.

Return:

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.

Log()

FUNCTION Log( x AS float ) AS float

This function returns the natural logarithm of a number.

A simple example:

FUNCTION LogBase10( x AS float ) AS float
REM Returns the 10-base logarithm of a number.
  l = Log(x)
  LogBase10 = l / 2.3025851  REM Divide by LOG(10)
END FUNCTION

Pass:

x float
A positive number.

Return:

The natural logarithm of x .

See Also:

Exp()

LongToDateTime()

SUB system:LongToDateTime( in AS long, date AS STRUCT Date, time AS STRUCT TimeOfDay )

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.

  • year (1980-2107)
  • month (0-15)
  • day (0-31)
  • hour (0-31)
  • minute (0-63)
  • second (0-62)

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:

  • Top 7 bits: (year-1980), with the top bit inverted
  • Next 4 bits: month
  • Next 5 bits: day of the month
  • Next 5 bits: hour
  • Next 6 bits: minute
  • Bottom 5 bits: seconds/2

See Also:

DateTimeToLong(), LongToTime(), IntegerToDate()

LongToTime()

SUB system:LongToTime( in AS long, out AS STRUCT TimeOfDay )

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:

  • Top 15 bits: zero
  • Next 5 bits: hour (0-31)
  • Next 6 bits: minute (0-63)
  • Next 6 bits: seconds (0-63)

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.

See Also:

TimeToLong(), LongToDateTime()

MakeComponent()

FUNCTION MakeComponent( type AS string, parent AS component ) AS component

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.

A simple example:

FunButton = MakeComponent( "button", FunForm )
FunButton.caption = "Press for More Fun"
FunButton.proto = "FunButton"
FunButton.visible = 1

Pass:

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 .

Return:

A new component of type type .

See Also:

ValidParent()

Mid()

FUNCTION Mid( s AS string, start AS integer, length AS integer ) AS string

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 .

  • Mid( "Matisse", 1, 3 ) returns "Mat"
  • Mid( "Matisse", 4, 2 ) returns "is"
  • Mid( "Matisse", 10, 3 ) returns ""
  • Mid( "Matisse", 3, 10 ) returns "tisse"

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

A simple example:

offset1 = InStr( theString, " " )
tailString = Mid( theString, offset1+1, 65535 )
offset2 = InStr( tailString, " " )
theWord = Mid( theString, offset1+1, offset2-1)

A complicated example:

FUNCTION InitializeWordArray()
REM We're going to break up the string into an
REM array of words. We'll stop when we run out
REM of string or we run out of space in the array.
  DO
    offset = InStr( theString, " " )
    SELECT CASE offset
      CASE 0
        l = Len( theString )
        IF l THEN
     Words[numWords] = theString
          EXIT DO
        ELSE
          EXIT DO
END IF
      CASE 1
        TheString = Mid(TheString, 2, 65535)
      CASE ELSE
       Words[numWords] = Left(theString, offset-1)
        numWords = numWords + 1
        IF numWords = MAX_WORDS
          EXIT DO
        ELSE
          theString = Mid(theString,offset+1,65535)
        END IF
    END SELECT
  LOOP
END FUNCTION

Pass:

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.

length integer
How many characters to copy.

Return:

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 .

See Also:

Left(), Right(), Len()

MOD Operator

x AS long MOD y AS long

The modulo operator. MOD computes the remainder of x divided by y. For example:

  • 26 MOD 2 is 0.
  • 26 MOD 10 is 6.
  • 26 MOD 50 is 26.

NOT Operator

NOT x AS long

The logical-not operator.

  • NOT 0 returns a non-zero value.
  • NOT of a non-zero value returns zero.

Oct()

FUNCTION Oct( x AS integer ) AS string

This function returns a string containing the octal representation of the passed number x .

A simple example:

Status.caption = "Unrecognized character &O" + Oct( nextToken )

Pass:

x A number.

Return:

String containing octal representation of the passed number x .

See Also:

Hex(), Str()

OR Operator

p AS long OR q AS long

The logical-or operator. If p is non-zero or q is non-zero, this will evaluate to a non-zero value; if both p and q are zero, it will evaluate to zero. For example:

  • 0 OR 15 is non-zero.
  • 0 OR 0 is 0.
  • -37 OR 37 is non-zero.

Pow()

FUNCTION Pow( base AS float, power AS float ) AS float

This function computes basepower.

Pass:

base float
The number to be raised to a power.

power float
The exponent to which to raise base.

RaiseError()

SUB RaiseError (errorNumber AS integer)

This function generates a run-time error. For a discussion about how this might be useful, see See Generating Errors .

Pass:

errorNumber integer
The number of the error to raise.

Right()

FUNCTION Right( s AS string, length AS integer ) AS string

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

A simple example:

FUNCTION Plural( noun AS string ) AS string
REM This function takes a word and returns the
REM plural of that word.
REM Plural("win") yields "wins".
REM Plural("whinny") yields "whinnies".
REM Plural("way") yields "ways".
REM Plural("wish") yields "wishes"
REM Plural("watch") yields "watches"
REM Plural("wife") yields "wives"
REM Plural("wolf") yields "wolves". .
REM Plural("sheep") yields "sheeps". Hey, nobody's perfect.

  tail = Right( noun, 2 )
  IF tail = "sh" OR tail = "ch" THEN
    Plural = noun + "es"
  ELSE IF Right( tail, 1) = "y" THEN
    IF (tail<>"ay") AND (tail<>"ey") AND (tail<>"oy") AND (tail<>"uy") THEN
      l = Len( noun )
      Plural = Left( noun, l-1 ) + "ies"
    END IF
  ELSE IF tail = "fe" THEN
    l = Len( noun )
    Plural = Left( noun, l-2 ) + "ves"
  ELSE IF tail = "lf" THEN
    Plural = Left( noun, l-1 ) + "ves"
  ELSE
    Plural = noun + "s"
  END IF
END FUNCTION

Pass:

s string
The original string.

length integer
How many characters to return from the end of the string.

Return:

The last (rightmost) length characters of string s . If there are fewer than length characters in string s , then string s will be returned.

See Also:

Left(), Mid(), Len()

Rnd()

FUNCTION Rnd() AS float

This function returns a random number; specifically, a random fraction between zero and one.

A simple example:

FUNCTION RollDice()
  die1 = 6 * RND()   REM between 0 and 5.999...
  die1 = INT( die1 ) REM one of 0,1,2,3,4,5
  die1 = die1 + 1    REM one of 1,2,3,4,5,6
                     REM Finished rolling first die
  die2 = INT( 6 * RND() ) + 1
                     REM Finished rolling second die
  RollDice = die1 + die2
END FUNCTION

Pass:

Nothing.

Return:

A floating point value less than one but greater than or equal to zero; that is 0 <= x < 1.

See Also:

Int()

Sgn()

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

A simple example:

SELECT CASE Sgn( area )
  CASE -1
    Result.value = 0
    Status.text = "Error: Bad inputs."
  CASE 0
    Result.value = 0
    Status.text = "Warning: Suspicious inputs."
  CASE 1
    Result.value = Sqr( area )
    Status.text = "Done."
END SELECT

Pass:

x float

Return:

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.

See Also:

Int()

Sin()

FUNCTION Sin( a AS float ) AS float

This function returns the sine of an angle.

A simple example:

y = yOrigin + ( Sin(vectAngle) * vectAmplitude )

Pass:

angle  float
An angle measure expressed in radians.

Return:

The sine of the passed angle. This will be a floating point value between negative one and one.

See Also:

Cos(), Atn()

Space()

FUNCTION Space( n AS integer ) AS string

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

A simple example:

FUNCTION Pad( s AS string, desiredLength AS integer ) AS string
REM Pad a string with spaces, right-justifying it to fill up
REM a space desiredLength characters long. If the string is longer
REM than the desiredLength, then truncate it to fit.
DIM length AS integer
  length = Len( s )
  IF length > desiredLength THEN
    Pad = Left( s, desiredLength )
  ELSE
    Pad = Space( desiredLength - l ) + s
  END IF
END FUNCTION

Pass:

n integer
The number of spaces desired. This should be a non-negative number.

Return:

String consisting of n spaces.

Sqr()

FUNCTION Sqr( x AS float ) AS float

This function returns the square root of a number.

A simple example:

SELECT CASE Sgn( area )
  CASE -1
    Result.value = 0
    Status.text = "Error: Bad inputs."
  CASE 0
    Result.value = 0
    Status.text = "Warning: Suspicious inputs."
  CASE 1
    Result.value = Sqr( area )
    Status.text = "Done."
END SELECT

Pass:

x float
A non-negative number.

Return:

The square root of the passed number x .

Str()

FUNCTION Str( x AS float ) AS string

This function returns a string containing the textual representation of the passed number x .

A simple example:

label1.caption = "Stopped at record " + Str( recordNum ) + "."

Pass:

x float
 A number.

Return:

String containing textual representation of the passed number.

See Also:

Hex(), Oct(), Val()

StrComp()

FUNCTION StrComp( s1 AS string, s2 AS string, nocase AS integer) AS integer

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.

A simple example:

FUNCTION Lookup( word AS string ) AS integer
REM Lookup word in the alphabetized word table
REM via a simple binary search.
REM If word found, return the index in the table.
REM Otherwise, return (-1).
  Lookup = -1
DIM min AS integer
DIM max AS integer
DIM index AS integer
DIM cmp AS integer
  min = 0
  max = NumEntries - 1
  DO
    index = INT ( (min + max) / 2 )
    cmp = StrComp( word, WordTable[index], 1 )
    SELECT CASE cmp
      CASE 0
        Lookup = index
      CASE -1
        max = index - 1
      CASE 1
        min = index + 1
    END SELECT
  LOOP UNTIL ( Lookup > 0 ) OR ( max < min )
END FUNCTION

Pass:

s1   string

s2 string

nocase integer (0-1)
Set non-zero to conduct a case-insensitive comparison; zero to conduct a case-sensitive search.

Return:

A number signaling the comparison result:

-1 s1 < s2

0 s1 = s2

1 s1 > s2

See Also:

InStr()

TimeToLong()

FUNCTION system:TimeToLong( time AS STRUCT TimeOfDay ) AS long

It takes a TimeOfDay structure and packs the structure's data into a long number with bits as follows:

  • Top 15 bits: zero
  • Next 5 bits: hour (0-31)
  • Next 6 bits: minute (0-63)
  • Next 6 bits: seconds (0-63)

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.

See Also:

LongToTime() , DateTimeToLong( )

Type()

FUNCTION Type( x ) AS string

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.

A simple example:

SUB DisplayData( theData )
SELECT CASE Type( theData )
CASE "integer", "long", "float"
gadget1!DrawText( Str( Int( theData )), 0,0, BLACK, "", 12, 0 )
CASE "string"
gadget1!DrawText( theData, 0,0, BLACK, "", 12, 0 )
CASE "graphic"
gadget1!DrawImage( theData, 0,0 )
CASE "array"
SELECT CASE GetArrayDims( theData )
CASE 1
DisplayData( theData[0] )
CASE 2
DisplayData( theData[0,0] )
CASE 3
DisplayData( theData[0,0,0] )
END SELECT
END SELECT
END SUB

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.

Pass:

x   ???
Some value of some type. If you knew which type, you wouldn't need to use this function.

Return:

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."

See Also:

BasicType()

UnloadModule()

SUB UnloadModule( modl AS module )

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.

Update()

SUB Update()

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().

Pass:

Nothing.

Return:

Nothing.

UseLibrary()

UseLibrary( m AS module )
UseLibrary( s AS string )

UseLibrary() is a rather esoteric function. It may be used to load a program.

If passed a string, UseLibrary() will try to load the program named by the string. Do not use this option.

Val()

FUNCTION Val( s AS string ) AS float

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( "451F" ) returns 451.
  • Val( "Fahrenheit 451" ) returns 0. The beginning character was not recognized as a number, thus it and all characters following were ignored.
  • Val( "41 * 11" ) returns 41. The "*" was not recognized as a number: it and all characters following were ignored.
  • Val( "-451F" ) returns -451.
  • Val ( "4-51" ) returns 4.

Val() recognizes scientific notation.

  • Val( "6.32E8" ) returns 632,000,000.
  • Val( "7.01e-03" ) returns 0.00701.

Val() does not recognize the &H or &O prefixes.

A simple example:

SUB ZipField_entered()
REM The user has entered a value in the zip code
REM field; we'll check to make sure it's a five-
REM digit number.
REM This routine truncates those zip+presort zips.
DIM zipNumber AS integer
  zipNumber = Val( ZipField.text )
  IF zipNumber > 99999 OR zipNumber < 0 THEN
    ZipField.text = "Error"
  ELSE
    ZipField.text = Str( zipNumber )
    DO WHILE Len(ZipField.text) < 5
      ZipField.text = "0" + ZipField.text
    LOOP
  END IF
END SUB

Pass:

s string
 The textual representation of a number.

Return:

A number recognized within the passed string s.

See Also:

Str()

ValidParent()

FUNCTION ValidParent( parent AS component, child AS component ) AS integer

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.

  • Components of some types only accept children of a particular type.
  • Components of some types might accept only a limited number of children.
  • Components of some types don't accept children at all. For example, Buttons cannot have children.

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.

Pass:

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.

child
A component; the potential child.

Return:

The function returns a non-zero value if the parent component would be a valid parent for the child component; otherwise it returns zero.

XOR Operator

p AS long XOR q AS long

The logical-exclusive-or operator. If both p and q are zero, it evaluates to zero; if both p and q are non-zero, it evaluates to zero; if p is zero but q isn't or vice versa, then it evaluates to non-zero. For example:

  • 0 XOR 15 is non-zero.
  • 0 XOR 0 is 0.
  • -37 XOR 37 is 0.

Components: Summary of Syntax

We've discussed the syntax related to components in other places. We will present a summary of the syntax here.

Component property syntax

The syntax for accessing a component property is

component . propertyName

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 .

Event handler

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

componentName _ eventName

Proto property

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.

A simple example:

DIM FunButton AS component
FunButton = MakeComponent("button", form1)
FunButton.caption = "Press for More Fun"
FunButton.proto = "FunButton"

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

component . ActionName ( action-args-list )

Component actions

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 .

Loading Modules

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.

Syntax

You may reference routines and exported variables in loaded modules using the following syntax:

module : thing

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
Finance = LoadModule("DOS://~D/CALCFRMS/FINANCE")
Finance:Precision = 3
Result = Finance:Payment(.07, 360, 100000)

Exporting variables

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:

EXPORT variable

Thus, in the above example, the FINANCE.BAS file must include the following code:

SUB module_init()
...
  DIM Precision AS integer
  EXPORT Precision
...
END SUB

...

FUNCTION Payment(r AS float, nPs AS integer, amt AS float) AS float
...
END FUNCTION

...

Limits on exporting

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.

However, the following code would not work:

DIM b AS button
b = MyModule:TheButton REM Generates Type Mismatch error

Instead, you would use a variable of type "component":

DIM b AS component REM We know it's a button;
b = MyModule:TheButton REM ...but the interpreter doesn't know.

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.

Compiling .BAS files

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().

Applications

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.

preventUnload

If your application...

...then the System Launcher program will not try to unload your application unless the system is running desperately low on memory.

For example:

sub module_init()
DIM preventUnload AS integer
EXPORT preventUnload
preventUnload = 1
end sub

Special Routines

The system and the System Launcher will call certain routines in your application when the user

These routines are described here.

sub module_init()

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.

sub module_exit()

Called by the interpreter just before the module is unloaded. Any special shutdown work desired can go here.

sub module_show()

The System Launcher program calls your module_show() routine when the user is switching to your application from another application.

sub module_hide()

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:

when restoring an application to its former state, by the environment/system module.
by an application having stored the context as a "Hyperlink" that it now wishes to follow.

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:

The environment/system module is about to unload an application in order to make room for a new module to be loaded.
The module that loaded this module is fulfilling its own "getContext" request.