Demystifying WMLScript (part 2)

Loops, operators, and some pre-defined WMLScript functions.

A Little Bit Of Logic

After last week's introduction to the world of WMLScript, you should have a clearer idea of how WMLScript variables are created and manipulated in the wireless space. In this concluding article, we'll be exploring WMLScript's loops, functions and in-built browser libraries, together with a look at some simple WMLScript applications that should get you started on the road to building cool toys for all the yuppies to play with on their cellphones.

Before we begin with loops, however, you need to know about the three important logical operators supported in WMLScript. They are:

the || operator - used to indicate an OR condition

the && operator - used to indicate an AND condition

the ! operator - used to indicate a NOT condition

These operators come in very handy when building compact conditional expressions.

For example, consider this:

if (a == 0 || a == 1)
    {
    do this!
    }

In English, this would translate to "if a equals zero OR a equals one, do this!"

But in the case of

if (a == 0 && b == 0)
    {
    do this!
    }

the "do this!" statement would be executed only if a equals zero AND b equals zero.

The third logical operator, the NOT operator, is usually indicated by a prefixed exclamation mark[!], and is used to reverse the sense of a conditional expression. Consider the two examples below:

if (a)
    {
    do this!
    }

In English, "if a exists, do this!"

if (!a)
    {
    do this!
    }

In English, "if a does NOT exist, do this!"

Got that? All righty, let's move on to loops!

Round And Round....

As you may already know, a loop is a simple programming construct which allows you to repeat a set of program statements over and over again; the number of times the statements are repeated is either dependent on the fulfillment of a pre-defined condition, or fixed in advance. WMLScript allows both types of contortions via the "while" and "for" loops (incidentally, unlike "real" programming languagss like C and Perl, these are the only two loops the language supports!)

The syntax of the "while" loop is as follows:

while (condition)
    {
    do this!
    }

Or, to make the concept clearer,

while (rich Aunt Petunia's in a good mood)
    {
    hit her for a loan
    }

The "condition" here is a standard conditional expression, which evaluates to either true or false.

In order to demonstrate this better, take a look at our next example, which asks for a number and calculates the factorial of that number (for those of you who flunked math class, the factorial of a number is the product of that number and all the numbers preceding it, all the way down to 1)

[fact.wml]

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">

<wml>
<card id="Factorial" title="Factorial">
<p>
Gimme a number:
<input format="N*" name="number" />
<br/>
The factorial of the number $(number) is $(answer)
<do type="accept" label="Calculate">
<go href="fact.wmls#factorial($(number))"/>
</do>
</p>
</card>
</wml>

The WML deck above sets up the page which asks the user to enter a number. Once the number has been entered and the user has hit the ACCEPT button, the WML page will call the factorial() function in the file "fact.wmls"

[fact.wmls]

// function to calculate factorial
extern function factorial(number)
{

    var tempfact = 1;

    while(number != 1 && number != 0)
    {
    tempfact = tempfact * number;
    number--;
    }

    WMLBrowser.setVar("answer",tempfact);
    WMLBrowser.go("fact.wml#Factorial");
}

The factorial() function accepts a number and uses a "while" loop to count down from that number until it reaches 1. The product is then passed back to the browser via the standard WMLScript libraries.

Note the conditional expression, which ensures that a factorial is not calculated if the number entered is either 0 or 1, and the autodecrement operator --, which is used to reduce the value of the variable "number" by 1 during each iteration of the loop.

For Pete's Sake

WMLScript also supports that all-time favourite of programmers everywhere, the "for" loop, which is used to execute program statements a specific number of times.

for (initial value of counter; condition; update counter)
    {
    do this!
    }

Doesn't make any sense? Well, the "counter" here refers to a regular variable that is initialized to a specific numeric value [usually 0 or 1]; this counter is used to keep track of the number of times the loop has been executed.

Each time the loop is executed, the "condition" is tested for validity. If it's found to be valid, the loop continues to execute and the value of the counter is updated appropriately; if not, the loop is terminated and the statements following it are executed.

In the following example, we've rewired the code above to calculate factorials using the "for" loop.

// function to calculate factorial
extern function factorial(number)
{

    var tempfact = 1;
    var counter;

    for(counter = number; counter != 1; counter--)
    {
        tempfact = tempfact * counter;
    }

    WMLBrowser.setVar("answer",tempfact);
    WMLBrowser.go("fact.wml#Factorial");
}

The "for" loop also comes with two additional constructs designed to provide programmers with additional flexibility: the "break" statement, which is used to terminate the loop and execute the statements immediately following the loop; and the "continue" statement, which is used to skip a particular iteration of a loop and jump to the next iteration.

Something Fishy

Now that loops are out of the way, it's time to tackle user-defined functions, sometimes known as subroutines.

As you've seen thus far, WMLScript requires that all code be placed within functions before it can be used in your WML decks. Here's the typical syntax for such a WMLScript function:

if (a == 0 || a == 1)
    {
    do this!
    }

As you can see, functions are defined with the "function" keyword; this keyword is followed by the name of the function. All the program code attached to that function is then placed within a pair of curly braces - this program code could contain loops, conditional statements, or calls to other functions.

Now, that's fine and dandy - but you're probably wondering what the "extern" keyword is doing hanging off the beginning of the definition. Allow us to enlighten you.

Since all WMLScript functions have to be placed in a separate file, it is essential to differentiate between functions which are available to the WML deck, and functions which are not. This is accomplished via the "extern" keyword; a function defined with "extern" can be called from any WML deck, while a function without "extern" can only be called from other functions in the same WMLScript file.

Consider this simple example.

if (a == 0 || a == 1)
    {
    do this!
    }

We've defined two functions in the above example, one with "extern" and one without. The function something_fishy() can be called from any WML deck; however, the function not_really() cannot be invoked from anywhere outside the file which contains the functions.

WMLScript also provides for the "return" statement, which is used to return a specific value from a function. The following example demonstrates this more clearly:

if (a == 0 || a == 1)
    {
    do this!
    }

In this case, function fish() receives a number and passes it to the check_size() function. The check_size() function evaluates the number and returns a response to the fish() function via an explicit "return" statement; this response value is then passed back to the WML browser.

What You Get For Free

As you've seen thus far, WMLScript comes with standard libraries which provide pre-defined functions to the programmer. A number of such standard functions are available, broken down into various categories, and they come in very handy when carrying out string and mathematical operations, displaying messages to the user and performing other basic functions.

Here's a quick list of the important functions available in the standard libraries, together with brief descriptions - you've already used some of these, while others will become clear to you in the examples on the next page.

Core functions: WMLBrowser.getVar(delta) - get the value of the variable "delta"

WMLBrowser.setVar(delta, something) - assign the value "something" to variable "delta"

WMLBrowser.go(delta) - load WML deck/card "delta"

WMLBrowser.prev() - return to previous card in deck

WMLBrowser.refresh(delta) - refresh WML deck/card

Message functions: Dialogs.alert(phi) - display message "phi"

Dialogs.prompt(phi, default) - display prompt with message "phi" and default input "default"

Dialogs.confirm(phi, yes-message, no-message) - display confirmation dialog box with message "phi" and accept/cancel button text

Math functions: Lang.abs(alpha) - return absolute value of number "alpha"

Lang.min(alpha, beta) - return the smaller of two numbers "alpha" and "beta"

Lang.max(alpha, beta) - return the larger of two numbers "alpha" and "beta"

Lang.isInt(alpha) - returns whether "alpha" is an integer

Lang.isFloat(alpha) - returns whether "alpha" is a floating-point number

Lang.random(alpha) - returns a positive random number between 0 and "alpha"

Float.int(alpha) - returns the integer part of "alpha"

Float.round(alpha) - returns the rounded-off value of "alpha"

Float.sqrt(alpha) - returns the square root of "alpha"

String functions: String.length(gamma) - returns the length of the string "gamma"

String.isEmpty(gamma) - returns whether or not the string "gamma" is empty

String.charAt(gamma, n) - returns the nth character from the string "gamma"

String.substring(gamma, n, m) - returns a substring of length "m" starting at character "n" from the string "gamma"

String.find(gamma, pattern) - search for "pattern" in string "gamma"

String.replace(gamma, pattern, newpattern) - replace "pattern" in string "gamma" with "newpattern"

String.trim(gamma) - remove leading and trailing whitespace from string "gamma"

URL functions: URL.isValid(omega) - returns whether or not URL "omega" is valid

URL.escapeString(omega) - URL-encode URL "omega"

URL.unescapeString(omega) - URL-decode URL "omega"

This is not an exhaustive list, just the most important functions. You can get a complete list, with examples, from the WMLScript standard libraries specification, available at the WAP Forum Web site at http://www.wapforum.org

Checking Your Mail

Now that you have some idea of the functionality available to you, it's time to start using it - and so, we've put together a couple of simple WMLScript applications that demonstrate the kinds of things you can do with the language.

First up, a simple email address validator. Here's the form which sets up the input fields...

if (a == 0 || a == 1)
    {
    do this!
    }

...and here's the function that validates the address (note that this is very basic validation, and is intended for demonstration purposes only)

if (a == 0 || a == 1)
    {
    do this!
    }

In this case, once the function receives a string, it first checks to make sure that the string is not empty. Once this test is satisfied, it parses the string character by character (using a "for" loop) in search of the "@" and "." characters, and displays an appropriate message on the screen.

Note the manner in which the various string functions have been used to perform the validation.

The Money Market

And finally, one more simple - and very useful - application: a currency converter that allows you to convert dollar amounts to any of three other currencies. Check it out, and feel free to add more currencies to the list!

if (a == 0 || a == 1)
    {
    do this!
    }
if (a == 0 || a == 1)
    {
    do this!
    }

Here, too, a list box is used to provide the user with a list of currencies - the selected currency and the amount entered are then passed to the calculate() function, which sets up conversion rates and performs the conversion based on the currency selected. A new card is then loaded from the deck with the result of the conversion.

And that's about it for this article. Keep checking back for more from the wireless world!

This article was first published on11 Oct 2000.