swirl
Guide to OmniMark 7   OmniMark home
docs home 
IndexConceptsTasksSyntaxLibrariesLegacy LibrariesErrors
 
Prerequisite Concepts     Related Syntax  

Functions

Like most languages, OmniMark supports functions. Unlike many languages, an OmniMark program is not simply a hierarchy of functions. Rules are the principal structural element of OmniMark programs. Functions are supplementary structures. Functions cannot contain rules (though they can invoke them through submit, do xml-parse, do sgml-parse, or do markup-parse). You can use functions to encapsulate code you use commonly within different rule bodies.

There are four types of functions in OmniMark:

Value returning functions

A function that returns a value can be defined as follows:

  define integer function add
     (value integer x,
      value integer y)
     as
     return x + y

The return type of the function is declared following the define keyword. It may be any OmniMark variable type, any record type, or any OMX component type. The value is returned using the return keyword. return exits the function.

Here is how the "add" function can be called:

  process
     output "d" % add(2,3)

Input functions

An input function is one that generates input for the statement that calls it. It is similar to a stream-returning function in that it returns a stream of data. However, input functions can be simpler to write and more efficient than stream returning functions. Input functions can be particularly valuable when you need to process an input stream before sending it to a parser. See Input functions and the markup parser for details.

An input function is defined by putting the keyword input in the place of the return type. This establishes a new output scope that is the return value of the function. All output generated while the input function is being executed will become the return value of the function (unless the output scope is changed).

This input function escapes the "<" ">" and "&" characters with character entities. (This is useful when you want to insert a piece of text into an XML element.)

  define input function escape-text
   value stream original
   as
     repeat scan original
        match [\"&<>"]+ => ordinary
           output ordinary
        match "&"
           output "&amp;"
        match "<"
           output "&lt;"
        match ">"
           output "&gt;"
     again

You call an input function just as you would call a stream-returning function. For example, the function above might be called as follows, in code that copies a database field into an XML document:

  output "<description>"
      || escape-text db.reader of record-set{"Description"}
      || "</description>"

Input functions are generally to be preferred over stream-returning functions, especially when the output of the function is complex to build. For contrast, here is the same function written as a stream-returning function:

  define stream function escape-text
   value stream original
   as
     local stream temp
     open temp as buffer
     using output as temp
      repeat scan original
        match [\"&<>"]+ => ordinary
           output ordinary
        match "&"
           output "&amp;"
        match "<"
           output "&lt;"
        match ">"
           output "&gt;"
      again
     close temp
     return temp

Streaming properties of input functions

Another way in which input functions are superior to stream returning functions is that their behavior is streaming. That is, their output is streamed incrementally to their calling environment as it is generated. Where the calling environment is capable of accepting incrementally streamed data, this is more efficient than building a temporary buffer to hold a copy of the entire return value. This streaming behavior is supported in two contexts:

In all other cases, the return value is buffered automatically by OmniMark.

Action functions

An action function does not return a value. Rather, it performs an action. Here is how an action function might be defined. Note that it has no return type in the definition and no return is required:

  define function clear-flags
      (modifiable switch the-flags
      ) as
      repeat over the-flags
        set the-flags to false
     again

This function clears all the switches on a switch shelf that is passed to it as a modifiable argument.

Action functions can generate output. The following function outputs characters in a specified range:

  define function output-character-range
   value stream start
   to value stream end
   as
     repeat for integer i
      from  binary start
      to binary end
        output "b" % i
     again

  process
     output-character-range "A" to "M"

While this type of function is permitted, it is generally preferable to write such functions as input functions. This will improve the readability of your code and increase the generality of the functions. The function is changed to an input function simply by adding input to the definition and changing the function name from output-character-range to character-range:

  define input function character-range
   value stream start
   to value stream end
   as
     repeat for integer i
      from binary start
      to binary end
        output "b" % i
     again

  process
     output character-range "A" to "M"

Here the normal OmniMark keyword output can be used in the function call, enhancing the clarity of the program. In addition, the input function can be used in a wider range of contexts such as:

  submit character-range "A" to "Z"

You can also write functions that both return a value and do output:

  define integer function add
     (value integer x,
      value integer y
     )as
     output "I will add %d(x) and %d(y)%n"
     return x + y

  process
     local integer z
     set z to add(2,3)
     output "%d(z)%n"

While it is certainly possible to program like this, we recommend that you avoid writing functions that both do output and return a value. Not only do they make it hard to follow your code, but they can have unexpected results. In particular, if the return value is directed to current output, you may not get the function's return values and output in the order you expected.

Pattern matching functions

You can use functions as pattern matching functions.

You can also use input functions or stream-returning functions to dynamically define the text to be matched in a pattern.

Records and functions

Because record variables are references, they behave slightly differently when passed to functions. In particular, the value of a record passed to a function as a value argument can be changed, since it is the reference that is passed by value, not the record itself. For the same reason, if the value of a record passed to a function as a value argument is changed, its value will also be changed in the calling environment, since it is the same record.

Functions and the rules that call them

Whatever you can do in the rule that calls a function, you can do in the function itself. For example, a function can use the %c operator, or the %v or %q format items when called from a rule that supports them. The most useful application of this feature is the ability to write a function that does generic processing on a class of markup element types.

Note that if you call a function from a context that does not support the actions that it contains, a runtime error will result.

Recursion

You can call OmniMark functions recursively. The following program calculates the factorial of a number using a recursive function:

  define integer function factorial (value integer n) as
      do when n <= 0
          return 1
      else
          return n * factorial(n - 1)
      done

  process
      output "d" % factorial (7)   

Overloading

Functions can be overloaded. See Functions: overloaded for details.

Side effects

The principal job of a function is to encapsulate a discrete operation. However, a function may have side effects on the global state of the program. While writing functions with side effects is appropriate in some situations, you should exercise caution when using this technique as it can lead to programs that are difficult to debug and hard to read and maintain.

Functions isolate sections of code, but don't isolate you from the current environment, in particular the current output scope. Output generated in an action function goes to the current output scope. If a function changes the destinations of the current output scope (with output-to), this carries over to the calling environment.

Function side effects can be particularly problematic with functions used in patterns and in the guards of rules. To allow for optimization of pattern matching routines, OmniMark does not define whether a pattern or the guard on a pattern is executed first (a pattern is itself a kind of guard on a statement, so this is sensible). You should never write a program that depends on the order in which a pattern and a guard on that pattern are executed.

In the case of patterns that fail, OmniMark does not guarantee that all parts of the pattern will be tried, or that the same parts will be tried in all circumstances. This allows OmniMark to optimize pattern matching. You should never write a program that depends on the side effects of a function called in a pattern that fails.

Prerequisite Concepts
     Rule-based program, basic structure
   Rules
   Scopes
 
  Related Syntax
   define function
   define infix-function
   export, opaque
 
 

Top [ INDEX ] [ CONCEPTS ] [ TASKS ] [ SYNTAX ] [ LIBRARIES ] [ LEGACYLIBRARIES ] [ ERRORS ]

OmniMark 7.1.2 Documentation Generated: June 28, 2005 at 5:44:36 pm
If you have any comments about this section of the documentation, send email to [email protected]

Copyright © Stilo Corporation, 1988-2005.