throw

control structure

Syntax
throw catch-name catch-parameter*


Purpose

You can use throw to change the flow of your program. Throw causes execution of your program to jump to the catch named in the throw statement. As execution is transferred to the catch, OmniMark closes local scopes and releases their resources, ensuring a smooth transfer of control from the throw to the catch.

The named catch must exist in a scope that is currently executing. OmniMark will look for the named catch first in the current scope. If the named catch does not exist in the current scope, OmniMark looks for it in the parent scope, and so on. Once the named catch is found, OmniMark closes the current scope and all intervening scopes as required to reach the catch. Execution is then transferred to the catch.

If any of the scopes closed on the way to a catch contain an always block, the always block will be executed before the scope is closed.

If the named catch is not found, OmniMark raises an error which will be caught by the first available catch of #program-error. If there is no catch of #program-error in the execution scope, the program will terminate.

  declare catch my-catch
  
  process
     submit "kqaqpzq"
     catch #program-error
        output "Rats!%n"
  
  find "a" [ \ "z"]* => some-stuff "z"
     submit some-stuff
        catch my-catch
           output "Gotcha!%n"
  	
  find letter => my-letter
     throw my-catch when my-letter = "q"
     catch #program-error
        output "Ooops!%n"

The program above will output the following:

  Ooops!
  Gotcha!
  Ooops!

Each line in the output is generated as a result of the attempt to throw when the letter "q" occurs in the scanned data. The first time it occurs, the find rule is executing as a direct result of the submit in the process rule. The "catch my-catch" statement is not in scope, which creates an error that is caught by the "catch #program-error" in the next line, resulting in the output of "Ooops!"

The second time "q" is matched, it is as a result of the submit in the first find rule. Because "catch my-catch" is part of the same scope as that submit, the catch is in scope when the second find rule matches it. Thus the throw succeeds and the catch block outputs "Gotcha!".

The final time "q" is matched, it is once again as a result of the original submit and the catch is no longer part of the execution scope. Thus we get "Ooops!" output again.

If we edit the program to remove the catch of #program-error in the second find rule, the behavior will be different:

  declare catch my-catch
  
  process
     submit "kqaqpzq"
     catch #program-error
     output "Rats!%n"
  
  find "a" [ \ "z"]* => some-stuff "z"
     submit some-stuff
     catch my-catch
        output "Gotcha!%n"
  	
  find letter => my-letter
     throw my-catch when my-letter = "q"

The program above will output the following:

  Rats!

The reason we get only a single "Rats!" and no "Gotcha" is that the OmniMark-initiated throw to #program-error closes down all enclosing scopes, including the scope in which the catch occurs. So after the first "q" is matched the rest of the submit is aborted.

(Obviously this simple example would be better handled with find rules alone. Catch and throw is intended for large-scale flow control. We use simple examples to illustrate the syntax. We do not recommend using throw and catch where simpler structures would do as well.)

Throw parameters

A catch may be declared to allow you to pass various parameters to the catch when you make a throw:

  declare catch vowel value string the-vowel
  
  process
     submit "kqaqpzq"
     catch vowel the-vowel
     output "Hey, " || the-vowel || " isn't a consonant!%n"
  
  find letter => my-letter
     do unless my-letter matches (ul ["aeiou"])
        output "Consonant!%n"
     else	
        throw vowel my-letter
     done	

You are not required to catch all the thrown parameters.

Limits and interactions

If an always block contains a throw, and that always block is being executed as part of the closing of a scope in the process of executing a previous throw, both throws will be completed. The resulting catches will be executed in the order in which they occur in the unwinding of execution scopes, without regard to the order of throws. If the two catches occur in the same lexical scope, the order in which they are executed is indeterminate. You cannot use a new throw to cancel the execution of a throw in progress.

If you throw in a markup rule and that throw is not caught in the same markup rule, the entire parse will be aborted. The next point at which the throw can be caught is in the scope that initiated the parse.