How to stop an ASL script

One question keeps coming up in Smarts (aka IONIX) ASL training sessions and discussions... "When do you need to use the 'stop' function?".

The answer is quite simple but does highlight one of the oddities (features ?) of the language, which is that it is designed to consume an input stream and take actions on what it finds there. However it is not always used in quite this way.

Rather than just giving the one-line answer of  "make sure you consume your input and use 'stop' to abort on errors", I think a little explanation is in order.

(Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime.)

The oddity

Before getting into the suggestions of when to use (and not to use) the stop function, it might be helpful to try to show where the confusion arises for many people. Take a look at this little script...

What would you expect it to do? and what does it actually do when you run it?

START do {
        print("hello");
}

If you are new to ASL, you might expect it to print "hello" and terminate. However you'd be surprised to find that what it actually does is to print the word "hello" over and over again until it is killed. For that kind of behavior  you'd really expect the script to have a loop construct of some sort if you're used to programming in other languages.

So what is going on and how do we fix this? One way is to add a "stop" command to the script like this ('stop' stops the program)..

START do {
        print("hello");
        stop();
}

In this particular case it works just fine, but there is a problem with this approach, illustrated by this program..

START do {
        print("hello");
        a = 2 / 0;
        stop();
}

This does something even odder. It prints "hello", and then an error message complaining about an attempt to divide by zero. But it doesnt do this just once - it repeats this output until it is killed. This may seem like a bug in the language, but actually it's as per design.

A common solution

The most commonly used way to prevent both of these looping behaviours is to add a match pattern rule of '.. eol' near the top of the script, like this..

START { .. eol } do {
        print("hello");
}

In fact, most of the "well behaved"  simple query scripts you see will start with that magic syntax, namely: "START { .. eol } do {". But why.....?

Why the solution works

It's all very well to present you with a template solution to the issue, but I'm afraid it isn't always going to be appropriate - and some explanation of why it works is in order. We may even discover a little more about the fundamentals of ASL along the way.

Let's begin by understanding that every ASL script has an input stream which it reads. This might be a file that you specify using the "-f" option of the sm_adapter command line, or an event subscription stream defined using "--subscribe", or a number of other possibilities. If you don't specify any input at all, the script still has one anyway - it's a dummy file consisting of one empty line.

The syntax between the "{" and "}" that come after the "START" rule name and before the "do" word tells the rule how to consume it's input. ASL runs the START rule repeatedly until there is no input left. The basic idea is that "START" will read a line and take some action on it, though things can get more complex than this. The main thing to note is that if the START rule doesn't consume any input then there is still input left when it finishes, which means it will be run again .. and again .. and again.

The input consuming part of the rule is very powerful and it is beyond the scope of this note to explain it in detail - that's a subject for another posting. For now we'll keep it really simple.

Here's a script that reads it's input a line at time and prints out what it sees..

START { x: { .. eol } } do {
        print(x);
}

You'd run this using a command line like..

sm_adapter -f input-file.txt script-name.asl

From these examples, I hope you can see that what our fixed "hello" script actually does is to print the word "hello" once for every line in it's input file. And if we dont specify an input file then it uses the dummy one-line file and so prints "hello" just once.

This also explains why the "divide-by-zero" script above runs repeatedly even though it has a call to the "stop" function. The problem is that the the first call to START hits the divide by zero error and aborts - never hitting the "stop". But: since there is still input to be processed it re-runs the START rule .. and so ad infinitum.

The function of the "stop" command is to break out of this read-something-and-process-it loop and has the effect of forcing the script to terminate - but it can only happen if the function actually gets executed (and errors can prevent this if you're not careful).

So what have we learned?

  • If you begin simple query scripts with "START { .. eol } do" (and don't give them an input stream), you wont have to worry about unexpected loops.
  • However, the above isn't appropriate for scripts that read real files or subscription streams.
  • The START rule is called to consume input by design, and not doing so has the side effect of making the script loop for ever.
  • Use of "stop" is usually only needed when an input-processing script needs to abort early. This is often in the event of an error condition that the script detects.
  • Errors cause the START rule to stop processing the current line (or input fragment), but ASL continues to read and process the remaining input - re-running START to do so.

One additional note:

  • The behaviour of ASL when it encounters errors can be modified using the question-mark syntax (see asl_ref.pdf for details).
Scroll to Top