throw

action

Syntax
throw catch-name catch-parameter-list


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

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

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

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

Consider the following program:

  declare catch my-catch
  
  process
     submit "kqaqpzq"
  
   catch #program-error
     output "Rats!%n"
  
  find "a" [any except "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 clause is not in scope, which creates an error that is caught by 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 clause 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 #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.

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

Limits and interactions

If an always clause contains a throw, and that always clause 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.