Skip to main content
The Squirrel Programming Guide

Program Control

Contents

Introduction

Squirrel provides a number of structures that allow you to control the flow of a program: they allow you to set the program to follow one a number of possible paths. Which path the program takes is selected by examining a value, typically stored in a variable, and comparing it to another value. These values can represent almost anything that’s relevant to your application, and upon whether the values are the same or different at that moment, will the operations the program performs depend.

Branching Structures

Branching structures are the embodiment of this idea. They give your program a means to perform a comparison and take one of several code paths according to the result of that comparison.

if... else if... else

The most basic branching structure is based on the keyword if, followed by a condition and some further instructions. If the condition evaluates to true, those extra instructions are performed, otherwise they are ignored. The condition is placed within round brackets (( and )), and the further instructions within braces ({ and }) — ie. as a discrete block of code. Here's a very simple example:

local someValue = 1;
if (someValue == 1) {
    server.log("The value is 1");
}

What is happening here? We create a variable, someValue, and give it the value 1. We then ask if the value of someValue equals 1 and provide some code for Squirrel to run if the answer to our question is yes. It does, of course, so the code in braces is executed and we see the words The value is 1 displayed in the log.

The == symbol is called the ‘equality comparison operator’. It tells Squirrel to compare the values on either side of it (in this case, the value of someValue and the literal 1) and to answer true or false if the two values are, respectively, equal or different. If we wanted to test whether the two values are different, we could use the ‘inequality comparison operator’, !=, which is the opposite of ==: it answers true if the two values are different, or false if they are the same. Answering the question posed by the condition is called evaluating the condition; if the if statement’s condition evaluates to true, the code block is executed, otherwise it is ignored.

Squirrel provides a raft of operators which you can include in if conditions. Some apply to certain variable types, others to all or a number of different types. Some allow you to combine simple conditions into complex ones (if this condition is true and this other condition is true and if either of these next two conditions are also true...) which in turn give you incredible flexibility when it comes to making decisions in your code and acting accordingly.

The example above has only one code path; you can extend it by adding another path:

local someValue = 1;
if (someValue == 1) {
    server.log("The value is 1");
} else {
    server.log("The value is not 1");
}

What we have done here is add a second Squirrel keyword, else, and provided a second block of code. You should be able to see what all this does. As before, if someValue equals 1, the first code block is executed and an appropriate message is logged; the second block of code is ignored. But if someValue does not equal 1, then the first block of code is ignored and the second block of code is executed instead. We have executed a branch: depending on the value of someValue, branch to one of two possible code paths.

Branches need not go one of two ways: you can extra paths, as may as you need, by including further conditions using else if:

local someValue = 1;
if (someValue == 1) {
    server.log("The value is 1");
} else if (someValue == 2) {
    server.log("The value is 2");
} else {
    server.log("The value is neither 1 nor 2");
}

This time, Squirrel evaluates the first condition; if the evaluation is true, it runs the first block of code and then jumps to whatever code follows the if... else if... else structure. However, if the evaluation is false, Squirrel moves on to the second condition and evaluates that. Again, if the evaluation is true, the second branch is followed and at its end, Squirrel moves on. But if the evaluation results in false, the third branch is taken.

Of the three branches shown in the example above, only one can be taken; the choices are mutually exclusive. If someValue equals 1, then the second condition, or any that follow it, will never be tested; Squirrel instead moves on to whatever code follows the structure’s final closing brace (}). If you need to include tests that are not mutually exclusive, just add further if... else if... else structures after this one. These will be evaluated and executed whatever path Squirrel takes in this one.

Short Circuits

Squirrel processes multiple tests using the logical operators && and || in left-to-right order and, like C, will ‘short circuit’ the overall test at the first failure. For example, the following code will not trigger an error:

local data = {};
if ("value" in data && (data.value == 42 || data.value == 84)) server.log("Ha!");

The table, data, has no member value, so the the sub-test data.value == 42 should trigger an ‘index not found’ error. This does not happen because Squirrel evaluates the lefts side of the test statement first. This fails and so Squirrel never evaluates the left side of the OR statement.

If we swap the two sides of the && comparison, then the error will be triggered — the order of the sub-tests is crucial.

Short-circuiting saves you from having to nest separate comparison statements in cases where subsequent tests are dependent on an earlier one.

switch... case

Sometimes you can end up with a very long if... else if... else structure, typically when you compare the same variable to a large sequence of possible values, each with its own block of code. Squirrel provides an alternative structure to use in these instances called switch... case that provides a more clear means of enumerating the possible values and the code run if the variable has that value. This is best shown with an example:

local someValue = 1;
switch (someValue) {
    case 1:
        server.log("'someValue' is 1");
    case 2:
        server.log("'someValue' is 2");
    default:
        server.log("'someValue' is neither 1 nor 2");
}

This works in a slightly different way to if... else if... else. This time, the condition evaluates not to true or false but to a value. The structure should include a number of case statements for all of the possible values that we’re interested in. The default keyword marks the code that will be run if none of the case statement cases are met.

When it encounters a switch... case structure, Squirrel evaluates the switch statement then works through each case value until it reaches one that matches the result of the evaluation. When it does, however, Squirrel will run all of the blocks that follow. If you run the code above, for example, you will see three log statements, not one (as was the case with the if... else if... else examples). This is because having matched at case 1: (ie. someValue equals 1), it executes all of code below it.

The reason for this behavior is that it allows you to group values and code paths; the following example extends the one above to demonstrate this:

local someValue = 5;
switch (someValue) {
    case 1:
    case 2:
    case 3:
        server.log("'someValue' is between 1 and 3");
    case 4:
    case 5:
    case 6:
        server.log("'someValue' is between 4 and 6");
    default:
        server.log("'someValue' is outside the range 1-6");
}

This time we see (correctly) The value is between 4 and 6 in the log, because someValue matches against the case 5:. This saves you from having to duplicate code across multiple cases which you want to lead to the same outcome.

break

However, we again see the unwanted The value is outside the range 1-6 printed in the log. Had we set someValue to 1, 2 or 3 in the first line, we would have had three messages logged. How do we avoid this? We add Squirrel’s break keyword to wherever we want the sequence of instructions to stop:

local someValue = 1;
switch (someValue) {
    case 1:
    case 2:
        server.log("'someValue' is 1 or 2");
        break;
    case 3:
        server.log("'someValue' is 3");
        break;
    case 4:
    case 5:
    case 6:
        server.log("'someValue' is between 4 and 6");
        break;
    default:
        server.log("'someValue' is outside the range 1-6");
}

Change the value of someValue and see what happens. Whatever value you choose, you will only get one message.

You might wonder why we haven’t included a break on the last line of the default block. It’s because we don’t need one: as the last item in the sequence, it’s always going to be followed by the next instruction in the program. You only need break when there are subsequent lines within the switch... case structure that you want Squirrel to step over.

You can include you case statements in any order. That said, Squirrel will always match against them in the order in which they appear, and this is much clearer to see when the order of the cases follows the order of the values they contain. It’s traditional to put default at the end of the sequence, but it’s not mandatory. Just bear in mind that it will match against any value, so will always be called, possibly to the exclusion of other blocks, if positioned first on the list.

The examples above use integers as case values, but they can be of any type that you can express as a literal: strings, floats and booleans are valid too. But you should be consistent within the structure: the type of value after each case should match that generated by the switch evaluation. There’s no point trying to match an integer against a string; the match will never be made. If you need to do that, consider converting the integer value to a string in the switch statement, or vice versa. We’ve used nothing but a variable name in the switch statement examples above; this will always evaluate to the variable’s value. However, we don’t have to be limited to that: we might use the value to perform a sum whose result we're interested in:

local someValue = 100;
switch (someValue.tostring()) {
    case "100":
        ...
}

or we might use it to access another variable:

local someValue = 1;
local possibleValues = ["zero", "one", "two"];
switch (possibleValues[someValue]) {
    case "zero":
        ...
}

Here the switch statements evaluates to the element in the array possibleValues is at index someValue. The result is a string, so we match against as sequence of string cases.

Loop Structures

The examples above show how Squirrel provides ways to branch along one of a number of possible paths, but what if we want to go back and repeat a block of code, perhaps a number of times, before we continue? This is process is called looping, and Squirrel has a number of means to achieve it.

do... while

This is the simplest of Squirrel’s loop structures. It marks out a block of code (placed within braces) which is run over and over again for as long as a condition, included at the end of the do... while statement, is met.

local index = 0;
local spaces = "";
do {
    spaces = spaces + "*";
    index = index + 1;
} while (index < 32);
server.log(spaces);

This do... while loop runs until index equals 32; index starts at 0, but increases by 1 each pass through the loop. The string spaces grows by a single asterisk through the loop; we're using the loop to assemble a string of 32 stars to write out to the log, which we do when the loop operation has completed.

As we saw with if... else if... else, the condition (in this case, does index equal 32?) evaluates to true or false, and the loop only stops when the condition evaluates to false. Because the condition is evaluated at the end of the each pass through the loop, the loop will always run at least once (set index to equal any number 32 or greater to see this), but whether the loop code performs any action, will depend on the loop itself:

local index = 0;
do {
   local alarm = alarms[index];
   if (alarm.done) {
       // Alarm complete, so remove it from the list
       alarms.remove(i);
   } else {
       index++;
   }
while (index < alarms.len());

This snippet won’t run as it stands, but it illustrates how looping used to step through all the alarm records in the array alarms and removes any that are marked as done. If no alarms are done, none will be removed.

With do... while loops it is very important to verify that the loop’s condition can evaluate to false, otherwise the loop will continue forever... or until you power cycle the imp. While the loop is running the imp cannot receive updated application code or impOS updates. For this reason, do... while loops are generally discouraged; the imp API provides much ‘safer’ loop methods which you should use instead. You can find out more about these better looping approaches in the guide How to Write Imp Loop Structures.

for

By themselves do... while loops do not track the number of times the loop code is executed. In the examples above, we added a variable, index, to do this, but it was separate from the loop structure and we had to remember to update it on each pass through the loop. If we had forgotten to do so, the value of index would never have reached the point where it triggered the loop exit condition.

As an alternative do... while, Squirrel provides loop structures which do manage loop pass counting for you, and the first of these is the v loop. This time, you specify a variable which will be used by the loop to count the number to times the loop is run; you specify a start value and the amount by which that values increases or decreases each time the loop cycles. You also provide a condition which Squirrel evaluates to see whether to stop looping. Looping ends when the condition evaluates false.

All of these elements are included at that the start of the for statement. This means that the loop condition is evaluated before loop code is executed (compare that to do... while which checks at the end of every cycle) and, as a result, it is possible that the loop code may never be run.

The configuration details are placed in round brackets; each component is separated by a semi-colon. For example:

local spaces = "";
for (local index = 0 ; index < 32 ; index = index + 1) {
    spaces = spaces + "*";
    server.log(i);
}
server.log(spaces);

Let’s look at the loop constructor first. We define a local variable, index and set it to zero. We then specify that the loop will continue for as long as index is less than 32. Finally, we specify that at the start of each pass through the loop, index will be increased by 1. Notice that the increment is specified as an operation Squirrel will perform, not as a plus or minus value.

Like an earlier example, this one just keeps adding stars to the string spaces on each pass through the loop and logging the loop counter. Again, the loop code is bracketed within braces.

Because we choose the loop variables start value and per-loop increment, or decrements, we can count up or down, and in steps as large or as small as we need. The three elements in the for statement are related but essentially separate operations, so, for example, the exit condition doesn’t actually need to makes use of the loop variable’s current value.

Like the switch... case structure (and, indeed, do... while), for loops can make use of the break keyword if they need to exit early. For example, you might loop through the contents of an array, looking for a specific item whose index you don’t know. You will potentially need to look at every element in the array in order to find the right one, but when you do, there is no need to look at any further entries, so you can break out of the loop to avoid performing unnecessary operations, which could take some considerable time depending on your application:

for (local index = 0 ; index < aList.len() ; index += 1) {
    local item = aList[index];
    if (item == mySavedItem) {
        break;
    }
}

continue

As before, break causes Squirrel to jump to the next program line after the end of the loop block. But what if you just want to skip the immediate loop code and go on to the next value in the loop variable’s sequence? For this you need the continue keyword:

for (local index = 0 ; index < aList.len() ; index++) {
    local item = aList[index];
    if (item == mySavedItem) {
        continue;
    }
    item.canBeRemoved = true;
}

Here, instead of breaking out of the loop, we jump back to the start of the loop and continue with the next value of index (if there is one; the exit condition may cause the loop to end at this point). So all items in the array aList will have their canBeRemoved set to true except for the item that’s also referenced by mySavedItem.

Note The two examples above introduce two further Squirrel operators, += and ++. Both of these are ‘increment operators’. The first adds the value after the operator to the value in front of it: index += 1 is the same as index = index + 1. The second of the new operators just adds one to variable before it. ++ can only ever add 1 to the variable, but += allows you to choose the increment you need. There are equivalent decrement operators, -= and --, which subtract the specified value or 1 from the named variable.

foreach

This is a special variation on the for loop that Squirrel includes specifically for iterating through collection data types: strings, blobs, arrays and tables. Each of these data types comprises zero or more component parts: characters in the case of strings, bytes for blobs, items for arrays, and slots for tables. foreach steps through these for you and ensures you don't step beyond the number of elements they contain. To do so would trigger an error, and that’s possible to do with for if you’re not careful, but impossible with foreach.

Note If the collection is a table, do not add or remove keys within the foreach loop. If you want to do so for the purpose of finding elements to delete, for example, you should record references to those elements (eg. store the keys in an array) and remove them after exiting the foreach loop.

The general form for foreach statement is ‘element in collection type’. So for characters in a string, you might use:

foreach (character in myString) {
    if (character == 32) {
        character = 42;
    }
}

This runs through all the characters in myString and converts spaces (Ascii code 32) to asterisks (Ascii code 42). The equivalent code for an array (of numeric strings) might be:

foreach (item in myArray) {
    if (item == "32") {
        item = "42";
    }
}

If you need to know where you are in the loop sequence, Squirrel exposes its loop variable as a variable in the foreach statement:

foreach (index, character in myString) {
    if (character == 32) {
        // Convert spaces to stars
        character = 42;
    }

    if (index % 5 == 0) {
        // Every fifth pass through the loop, set the character to a '*'
        character = 42;
    }
}

The exception to this is iterating through a table’s slots. That first variable (index in the example above) takes instead the slot’s key, and the second variable the slot’s value:

foreach (key, value in myTable) {
    server.log("Key \"" + key + "\" has the value " + value);
}

Again, we can use break to get out of the loop early, and continue to jump to the next item in the collection.

Catching Errors In Code

The Squirrel program control structures discussed here so far are all used to select blocks of code that will run when the program is running correctly. But what if it is not running correctly? What if an error occurs? Are we limited to the error causing the imp or its agent to restart? The final structure considered here is intended to allow you to provide two code paths: one which contains operations your program will perform, the other actions that will take place if the first fails at any point.

It is called a try... catch structure, and it is used to trap and handle exceptions that may be thrown by the code placed within it. This is best understood with an example:

local aTable = {};

try {
    // This will force an error: we are mis-assigning a new table slot
    // The next line should be 'aTable.newSlot <- 1234'
    aTable.newSlot = 1234;
} catch(exception) {
    server.error(exception);
    // Displays "ERROR: the index 'newslot' does not exist" in the log
}

Squirrel processes the code with the braces following the try keyword as normal. However, if an exception is thrown within the block, it will immediately be passed to the catch block via the exception parameter. In the code above, an exception is sure to take place: we have used the assignment operator when we should have used the new slot operator. When the code is run, the exception is caught and then logged. No other action is taken; the program continues to run at whatever lines follow the try... catch structure. Your code might use the catch block to provide a default value that can be used by later code, or it might simply ignore the error entirely. Crucially, program execution will not be interrupted.

Exceptions can be issued manually with the throw keyword. The command takes a single value of any type. It also causes Squirrel execution to halt and the Squirrel virtual machine to be restarted, just as an uncaught exception does. It is possible to catch an exception within, say, a class method and throw it for a high level catch to receive. However, the exception is presented simply as a string and as such lacks state data.

In addition, the assert() system function can be used to trigger an exception if an expression evaluates to false.

Global Error Handling

impOS provides the method imp.onunhandledexception() to allow you to register a function that will be executed whenever an uncaught exception occurs within your program. Unlike a try... catch structure, this function can’t prevent the exception from triggering a Squirrel restart, but it does provide your code with a means to perform tasks before the restart takes place. You might use it to update your device state record, for example.

imp.onunhandledexception() takes a function with a single parameter of its own into which the otherwise uncaught exception is passed. As soon as the registered function returns, or throws an exception itself, the Squirrel VM will be torn down and restarted.

There are two exceptions to this rule. Electric Imp’s impCentral™ uses #require to load code libraries and #line to set the line number of the code following the directive, to aid debugging. For example:

#line 5000
throw ("Error!");

will generate the following output in the log:

2018-01-15 14:30:03.205 "Action": [agent.error] ERROR: Error!
2018-01-15 14:30:03.205 "Action": [agent.error] ERROR:   in main agent_code:5000

This directive allows you to mark sections of your code by line number, the better to help you locate the code causing errors.


Back to the top