HOME | COMPANY | SOFTWARE | DOCUMENTATION | EDUCATION & TRAINING | SALES & SERVICE

    "The Official Guide to Programming with OmniMark"

Site Map | Search:   
OmniMark Magazine Developer's Forum   

  International Edition   

OmniMark® Programmer's Guide Version 3

11. Cross-Referencing and Hypertext Linking

Detailed Table of Contents

Previous chapter is Chapter 10, "Accessing the External World".

Next chapter is Chapter 12, "Functions".

Referents in OmniMark provide direct support for hypertext links. A referent is a data structure that can be used to manipulate values that may be determined later.

For instance, when a reference (or link) to another part of the document is encountered, the program can output a referent at that point. When that part of the document is encountered, the referent can be initialized. These two activities can take place in either order. OmniMark remembers every place where a referent was written, and replaces the referent with its final value when the processing of the document has been completed. A referent's value can change many times over the course of processing the program. Only the final value will appear in the output of the program.

The process of replacing each referent with the final value of that referent is referred to as resolving the referent.

In order to support server-based programs, the programmer can designate a sub-part of the program that uses its own referents. Those referents will be resolved when that part of the program completes.

Some examples of hypertext link processing are:

11.1 Creating and Using Referents

Although there are many advanced features that give programmers control over how referents are used, just the simple ability to create and use them provides a great deal of functionality.

A simple example of cross-referencing using referents is:

   DOWN-TRANSLATE

   GLOBAL COUNTER chapter-number INITIAL { 0 }

   ELEMENT ref
      OUTPUT REFERENT "%v(id)"

   ELEMENT chapter
      INCREMENT chapter-number
      OUTPUT "%c"

   ELEMENT title WHEN PARENT IS chapter
      LOCAL STREAM content

      SET content TO "Chapter %d(chapter-number): %c%n"
      OUTPUT content
      SET REFERENT (ATTRIBUTE id OF PARENT) TO content

In this example, references to chapters are made using a ref element. The ref element links to the chapter with the same id. References are replaced with the chapter number and chapter title. All references are assumed to be valid. (A technique for dealing with undefined referents is described in Section 11.4.6, "Iterating Over the Current Referent Set".)

11.1.1 Local Cross-References

Referents are not limited to processing cross-references that can span the entire document. It is often the case that a small region of the document may be heavily cross-referenced within itself.

An example of this is a procedure which describes a list of steps to perform a given task. Often a step will have to refer to other steps in the same procedure. This cross-referencing can also be done with referents.

   GLOBAL COUNTER list-number
   GLOBAL COUNTER step-number
   ...
   ELEMENT list
     SET step-number TO 1
     OUTPUT "%c"
     INCREMENT list-number

   ELEMENT step
     SET REFERENT "%d(list-number)/%v(id)" TO "%d(step-number)"
                   WHEN ATTRIBUTE id IS SPECIFIED
     OUTPUT "%c"
     INCREMENT step-number

   ELEMENT step-ref
     OUTPUT REFERENT "%d(list-number)/%v(id)"
     SUPPRESS

Assigning each list in the document a unique number allows the referents to have unique names even if steps in different list have the same name. Cross-references to steps will always be to steps in the current list.

Another way to implement local cross-references is to use nested referents, as described in Section 11.3, "Server Applications: Local Referent Sets".

11.1.2 Initializing a Referent

The "SET REFERENT" action is similar to "SET BUFFER", in that it opens a stream, attaches the stream to a named referent, writes the specified text to the stream, and then closes the stream. Its form is:

Syntax

   SET REFERENT string-expression  TO string-expression

where the first string-expression contains the name of the referent, and the second is the value to store in that referent. An example of this is the ELEMENT title rule from the above example:

   ELEMENT title WHEN PARENT IS chapter
      LOCAL STREAM content

      SET content TO "Chapter %d(chapter-number): %c%n"
      OUTPUT content
      SET REFERENT (ATTRIBUTE id OF PARENT) TO content

This is an example that maps the values of cross-references to particular sec1 and sub-sec1 titles. The referents are called "sec-1", "sec-2", etc., in order of their appearance. Referring to a referent named "sec-n" produces the title associated with it.

The string-expression is only evaluated as needed. This means that if the string-expression consists of any of the following alone or in a JOIN, they are processed "as needed". This means that if it contains:

11.1.3 Writing Out a Referent

The syntax to PUT or OUTPUT a referent is:

Syntax

   PUT stream-name indexer? open-modifier*
      (& stream-name indexer? open-modifier*)*
      REFERENT string-expression

or

Syntax

   OUTPUT REFERENT string-expression

where stream is a stream name, indexer is an optional "@" (ITEM), "^" (KEY), or LASTMOST phrase, open-modifiers is an optional list of stream modifiers, and string-expression contains the referent name.

For instance, the ELEMENT ref rule in the above example writes a referent to the current output streams set.

   ELEMENT ref
      OUTPUT REFERENT "%v(id)"

Referents cannot be written to every stream. They can only be written to streams which were opened with a REFERENTS-ALLOWED modifier. By default, the #MAIN-OUTPUT stream in any program that uses referents is treated as if it were opened with REFERENTS-ALLOWED.

Setting the REFERENTS-ALLOWED modifier when opening streams is discussed in Section 11.2, "Allowing Referents to be Written to a Stream". Changing the REFERENTS-ALLOWED modifier of #MAIN-OUTPUT is discussed in Section 11.2.4, "Allowing Referents in the Main Output".

Programmers are required to explicitly control where referents are written because:

Using the keyword REFERENT never produces the "current" value of a referent, only a reference to some value which may be determined in the future. For this reason, a referent cannot be used in all the same ways that a string expression is used. (For instance, it cannot be used as a key value, or passed as an argument to a function.) It can only be written to streams.

11.1.4 Writing Data to a Referent in a Piecewise Fashion

If data is to be written to a referent over a period of time, then a referent can be attached to a stream, and data written to that stream in the normal fashion. Referents are opened with an action of the form:

Syntax

   OPEN stream index? open-modifier* AS REFERENT string-expression

where, as in other forms of the OPEN action, stream is a stream name, indexer is an optional "@" (ITEM) or "^" (KEY) phrase, open-modifiers is an optional list of modifiers, and referent is the referent name, which is given as a string expression. Any stream modifiers may be used to open a referent.

For instance,

   LOCAL STREAM chapid
   ...
   OPEN chapid AS REFERENT "%v(id)"

opens the stream chapid and attaches it to the referent whose name is the value of the attribute named id. Notice that the name of the referent has been determined from the document.

11.1.4.1 Attaching Referents to Streams

A referent can only be attached to one stream at a time. When the stream is closed, it still retains the attachment to that referent.

Attaching the same referent to a different stream snaps the previous attachment. At this point, REOPEN and "NAME OF" can no longer be used with the previous stream.

The only difference between using a "SET REFERENT" action and an OPEN-PUT-CLOSE sequence of actions is that the referent is not attached to any stream after the "SET REFERENT" action.

While attached to a stream, the contents of a referent can be accessed with the "%g" format item in the same manner as other buffers. The content of the referent will be its content at the time it is accessed. Unlike other buffers, however, referents can be "attached" to other streams (by OPEN or REOPEN). When a referent is attached to a stream it is "detached" from whatever stream previously opened it. When this happens a "%g" using the previous stream is no longer allowed.

11.1.4.2 Other Operations on Streams Attached to Referents

The CLOSE, REOPEN, and DISCARD actions can also refer to streams attached to referents.


11.2 Allowing Referents to be Written to a Stream

A stream is normally opened such that any attempt to write a referent to it is in error. OmniMark allows the programmer to specify whether or not referents are allowed for a stream, and what is done with them, as follows:

Note that there are two ways of writing a referent to a stream:

All three referent-controlling stream modifiers can be applied to an OPEN action or a REOPEN or SET stream action.

11.2.1 Writing Referents to Different Types of Streams

Almost any type of stream may have the REFERENTS-ALLOWED modifier set. The REFERENTS-ALLOWED open modifier is allowed in an OPEN action or SET (or "SET STREAM") action for a BUFFER, REFERENT, FILE or "EXTERNAL OUTPUT FUNCTION". It is also allowed on a REOPEN action.

Streams to which referents have been written cannot be used as string values in an OmniMark program, because they do not take on their final value (i.e. are resolved) until the end of the program, or the end of the "referent scope" in which they are opened (see Section 11.3, "Server Applications: Local Referent Sets").

A file to which referents have been written does not overwrite the original file until all referents are resolved. An attempt to access a file that has had referents written to it ignores the version of the file written with referents. The current version of the file (if it exists at this point) is accessed.

For buffers and referents that are REFERENTS-ALLOWED and which have been closed, their values can only be copied to streams with either the REFERENTS-ALLOWED modifier enabled (in which case the referents are simply copied over, see Section 11.2.3, "Copying Referents") or the REFERENTS-DISPLAYED modifier enabled (in which case the referents are converted to text as described below).

Whether streams can be used as "ordinary" string values, or whether they can only be copied to other streams with REFERENTS-ALLOWED depends on whether the streams were opened with REFERENTS-ALLOWED themselves, not on whether or not referents have actually been written to them.

REFERENTS-ALLOWED streams that are opened in "nested referent scopes" are coerced to the equivalent non-REFERENTS-ALLOWED streams at the end of the nested referent scope, and, once closed, can be used as string values after that point (see Section 11.3, "Server Applications: Local Referent Sets").

11.2.2 The Format of Displayed Referents

Referents written to any stream to which REFERENTS-DISPLAYED currently applies, are formatted in a special way. They are output in the form of an SGML processing instruction. For example, a referent named "chap 1 title" with the value "Overview" will be displayed as:

   <?REF "chap 1 title"="Overview">

The information provided in the displayed form depends on whether the referent is defined or not, or in the process of being defined, and whether it is currently attached to a stream. The referent name is always displayed in (double) quotation marks, as above.

This special processing of referents for these streams is intended to improve their utility in debugging OmniMark programs. The programmer can apply this processing to other streams using the REFERENTS-DISPLAYED stream modifier. See Chapter 11, "Cross-Referencing and Hypertext Linking".

11.2.3 Copying Referents

A buffer or referent that has REFERENTS-ALLOWED can be written to any stream that has REFERENTS-ALLOWED or REFERENTS-DISPLAYED. When a buffer or referent with REFERENTS-ALLOWED is written to a stream with REFERENTS-ALLOWED, OmniMark simply copies across the referents. e.g., in either of

   PUT foo STREAM bar
   PUT foo "%g(bar)"

if "bar" allowed referents when it was open, and had referents written to it and "foo" allows referents, then the referents from "bar", and not their "resolved" values are written to "foo". Those referents will be resolved in "foo" when "foo" needs to be written out to a file, or when a stream to which "foo" itself is written out, or never, if "foo" is never written out.

When a buffer or referent which contains referents is written to another stream that allows referents, the text in that buffer or referent is copied with all processing done in the context of the stream from which it is copied except that:

All other processing, including line breaking, is done at the time of the original write to the stream. Maximum line length checking is always done in the context of the stream to which referent-containing text is finally written.

If a buffer containing referents is written to a stream in which referents are allowed, it must be done with no format modifiers applied to the source stream, as those format modifiers require processing of the buffer's contents prior to copying them over. So the following would be in error:

   PUT foo "%ug(bar)"

A stream that has REFERENTS-ALLOWED, but which is attached to a FILE or to an externally-defined output stream, cannot be copied over to another stream with REFERENTS-ALLOWED. This restriction is because OmniMark does not have any guarantee that it has all of the file or externally-defined output stream content available to it.

Referents can be written to referents, because referents are buffers. The only restriction is that a referent cannot be written to itself, directly or indirectly. This later condition is detected at run-time when a stream to which the referent has been written is resolved.

A buffer or referent to which referents have been written can also be written to a stream that has REFERENTS-DISPLAYED. In this case all the referents in the buffer or referent are converted to "displayed" text prior to being copied over, so that the target stream only has text written to it.

The current value of a referent that contains referents can be written to any other type of stream, in the same manner as can the text of any other buffer.

11.2.4 Allowing Referents in the Main Output

The #MAIN-OUTPUT stream normally has REFERENTS-ALLOWED applied to it if referents are used in the OmniMark program (see Section 11.1.4.1, "Attaching Referents to Streams" for particularities). Especially with the introduction of local referent scopes this may often be inappropriate. It may often be the case that the main output has no referents written to it and should not be buffered in a manner that allows referents to be written to it, even though there are other streams to which referents are being written.

OmniMark provides programmer control over how #MAIN-OUTPUT is opened with regards to referent use with the "DECLARE #MAIN-OUTPUT" declaration. The programmer can specify one of the following:

   DECLARE #MAIN-OUTPUT HAS REFERENTS-ALLOWED
   DECLARE #MAIN-OUTPUT HAS REFERENTS-NOT-ALLOWED
   DECLARE #MAIN-OUTPUT HAS REFERENTS-DISPLAYED

If a program contains a "DECLARE #MAIN-OUTPUT" declaration then it determines how #MAIN-OUTPUT is opened. A program can contain more than one "DECLARE #MAIN-OUTPUT" declaration, but they must all agree on their choice.

If a program does not contain a "DECLARE #MAIN-OUTPUT" declaration, then the program is examined for any use of:

If the program uses any of these keywords #MAIN-OUTPUT is opened with REFERENTS-ALLOWED. If the program does not use any of these keywords #MAIN-OUTPUT is opened with REFERENTS-NOT-ALLOWED.

11.2.5 Suppressed Referents

The #SUPPRESS stream has REFERENTS-DISPLAYED by default. As the information written to the #SUPPRESS stream is discarded immediately, the referents, along with the text, are discarded and no attempt is actually made to "display" their values. The #PROCESS-OUTPUT and #ERROR streams have the REFERENTS-DISPLAYED stream modifier enabled for them by default. See Section 11.2, "Allowing Referents to be Written to a Stream".


11.3 Server Applications: Local Referent Sets

Server applications cannot wait until the server terminates to resolve referents. Applications that perform parsing/transformation tasks for clients must be able to resolve the referents on completion of the client task.

This can be accomplished by creating a local referent scope for the duration of the task. When the task is complete and the local referent sets terminates, all of the referents created during that task are resolved.

Local referent sets have application wherever a subtask with its own internal cross-referencing is performed, including:

Nested referents allow the programmer to say to OmniMark: "I want to use referents just for parsing this particular document, and I want the referents all resolved when I've finished this transaction so I can send all the output back to the client."

11.3.1 The Current Referent Set

At any point in the running of an OmniMark program there is a current referent set. Any referent written to a stream (with REFERENTS-ALLOWED) is associated with the current referent set. The name of the referent written to the stream is associated with the definition that has that referent name as its key in the current referent set. Any referent that is OPENed or SET creates or updates the definition of the referent with the referent name in the current referent set.

11.3.2 Creating A New Referent Set

Syntax

   USING NESTED-REFERENTS action

A new set of referent definitions can be created for the duration of an action by prefixing it with "USING NESTED-REFERENTS".

The current referent set is saved away on entry to the action, and a new referent set is created. The new referent set will be used for all actions and rules up to the end of that action.

The new referent set initially has no definitions. At the end of the action:

  1. the referents are resolved,
  2. the referent set is discarded, and
  3. the saved referent set is restored.

Referents are resolved by replacing the referent with its final value in every stream that it was written to. If a referent written to a stream has no value when it is resolved, then OmniMark signals a fatal error.

If any stream was opened within the scope of a "USING NESTED-REFERENTS" prefix, then the stream is "flushed" after the referent resolution. That is, the text in the stream is written to its final destination (a buffer, referent, a file, or an externally-defined output stream), maximum line-length checking is performed, and the REFERENTS-ALLOWED modifier is removed from the stream.

It is an error for a stream attached to a referent to be open when the referent is resolved. (Streams are automatically closed when the scope in which they were declared ends. If the end of the stream's scope happens before the end of the referent's scope, then the stream will be automatically closed before the referent is resolved. Otherwise, it is the programmer's responsibility to close the stream before the referent resolution. )

The following example is a simple case of using a nested referent set. An element link is found in an SGML document, and the program's response is to parse and output a document described by the link element's attribute:

   ELEMENT link
      ; An element that links to another document that needs processing.
      DO SGML-PARSE DOCUMENT WITH SCAN FILE "%v(docname).doc"
         USING NESTED-REFERENTS
            USING GROUP linked-doc
               SET FILE "%v(docname).out" WITH REFERENTS-ALLOWED
                   TO "%c"
      DONE
      SUPPRESS

In this example, the input and output files' names are based on the attribute's value, and a special group of (ELEMENT) rules is used to process the other document (which may very well require different processing than the document containing the link element). The "USING NESTED-REFERENTS" informs OmniMark that, by the end of the parsing of the linked document, the output file should have all of its referents processed and replaced by their definition values.

11.3.2.1 Local Referent Sets and Domains

Although referents from the referent set can be created and output in both domains, the "USING NESTED-REFERENTS" prefix can only be used in one domain at a time.

If a new referent set has been created in one domain, the "USING NESTED-REFERENTS" prefix cannot be used in the other domain until the new referent set has been resolved.

This ensures that local referent sets are always destroyed in the reverse order that they were created in.

11.3.2.2 The Global Referent Set

Outside of any "USING NESTED-REFERENTS", there is a global referents set. This is the initial referent set in any program using referents, and the only one if the program doesn't use "USING NESTED-REFERENTS".

The referent set established by "USING NESTED-REFERENTS" exists for the duration of the action which it prefixes. The global referent set exists throughout the OmniMark program as a whole.

The global referent set differs from the referent sets established by "USING NESTED-REFERENTS" only in that all streams are local to the "global set", and consequently, all buffers and referents are discarded at the end of the global referent set.

This means that referents can be left undefined, without error, if they are associated with the global referent set and written only to buffers or referents which are never themselves written to a file or to the built-in stream #MAIN-OUTPUT (even indirectly).

11.3.3 Final Destinations for Referents

The following are the possible final destinations for which all referents written to them must be resolvable:

In the last case, the BUFFER loses its REFERENTS-ALLOWED property. It will become either REFERENTS-NOT-ALLOWED or REFERENTS-DISPLAYED as described in Section 11.3.5, "Reopening a Referent in a Different Scope". = There are two cases in which a stream's attachment is not written to its final destination:

Both these cases are characterized by the fact that they both become "invisible" outside of their local scope or referent set -- and there is therefore no need to resolve them. A referent's contents (a.k.a. "effective value") may be used in the resolution of some other stream, but a referent itself ceases to exist outside of its own scope.

11.3.4 Referent Set Nesting Depth

"USING NESTED-REFERENTS" can be nested within other "USING NESTED-REFERENTS" -- and in any case a "USING NESTED-REFERENTS" is nested within the referent set created for the program as a whole (which is provided by OmniMark by default). Any stream that is OPENed within a referent set, with REFERENTS-ALLOWED, has with it associated a nesting depth which is the current nesting depth at the time the stream was opened.

The nesting depth of a referent that is identified using the REFERENT or REFERENTS keywords is always the current nesting depth. The nesting depth of a referent identified by the name of a stream to which the referent is attached may be something other than the current nesting depth.

The fact that referents and streams exist at different levels imposes some constraints on which referents can be written to which streams. In general, although the following regulations may seem a bit convoluted, they all come down to "you can't grab the value of a referent before it's defined".

The provisions made above for local referent sets also apply to the "global referent set":

11.3.5 Reopening a Referent in a Different Scope

A stream opened and attached to a REFERENT can, in OmniMark, be dynamically reopened by using the REOPEN action without an attachment (the AS part). Such a REOPEN simply prepares the already created referent to receive more text and (if the REFERENT was opened with REFERENTS-ALLOWED) referents.

If the referent attached to a closed stream belongs to an outer "referent scope" from the current one, then, even though it is reopened in the inner scope it keeps its original identity and referent scope. For example, in the following, following the REOPEN, both s1 and s2 are attached to referents called "A", but they are two different referents, distinguished by being in different referent scopes.

   OPEN s1 AS REFERENT "A"
   CLOSE s1
   USING NESTED-REFERENTS
   DO
      OPEN s2 AS REFERENT "A"
      REOPEN s1
      ...

If the referent attached a closed stream belongs to an inner "referent scope" from the current one, then any attempt to open it is in error, because it becomes "unattached" when the inner referent scope is exited, and cannot have any more operations performed on it (see Section 11.3, "Server Applications: Local Referent Sets").


11.4 The Referents Shelf

OmniMark provides a method of working with all the referents defined in a system by treating them as if they belonged to a shelf called REFERENTS. Most of the operations that are available for programmer-defined shelves are available for the REFERENTS shelf.

REFERENTS behaves like a programmer-defined shelf in that:

It is different from programmer-defined shelves in that:

The following subsections describe the various types of operations that can be explicitly performed on the REFERENTS shelf.

11.4.1 Getting The Current Value of a Referent

A referent's current value can be directly accessed from the REFERENTS shelf:

Syntax

   REFERENTS ^ string-expression

where string-expression is a string expression giving the name of the referent to access. This itself returns a string expression containing whatever value has been assigned to the referent named by string-expression.

The keyword KEY can be used as a synonym for the "^" operator.

It is an error if the referent named string-expression does not exist, or if it exists but is currently open.

11.4.2 Checking Whether A Referent Has Been Used

A test can be made to see if a particular referent exists, with the test:

Syntax

   REFERENTS (HAS|HASNT) KEY string-expression

where string-expression is a string expression giving the name of a referent. This test succeeds if one of the items in the REFERENTS shelf has a name equal to string-expression, and fails otherwise.

A referent will have an entry on the REFERENTS shelf if it has been:

11.4.3 Indexing The Referents Shelf

The key of a particular referent can be obtained:

Syntax

   KEY OF REFERENTS @ numeric-expression

where numeric-expression gives the position of the referent to access.

In a simple program, the nth value on the REFERENTS shelf is the nth mentioned in the program. However, if a stream is bound to a referent and then discarded while open, that referent is removed from the shelf, and the positional values of some of the referents may change.

11.4.4 The Number Of Referents

The number of referents that currently exist can be accessed:

Syntax

   NUMBER OF REFERENTS

11.4.5 Checking Whether A Referent Is Defined

Syntax

   REFERENTS indexer (IS|ISNT) ATTACHED

Syntax

   THIS REFERENT (IS|ISNT) ATTACHED

A referent is attached when its contents are being defined or have been defined. However, if a particular referent's contents have been given, the referent has been closed, and then the stream bound to that referent is discarded, the referent is no longer attached. The following example illustrates this:

   MACRO attach-check TOKEN ref-name IS
     DO WHEN REFERENTS ^ ref-name IS ATTACHED
       OUTPUT "attached%n"
     ELSE
       OUTPUT "not attached%n"
     DONE
   MACRO-END

   OUTPUT REFERENT "x"        ; referent "x" is not attached
   attach-check "x"
   OPEN S AS REFERENT "x"     ; referent "x" is attached now
   attach-check "x"
   PUT S "some contents%n"    ; still attached
   attach-check "x"
   CLOSE S                    ; still attached
   attach-check "x"
   DISCARD S                  ; referent "x" is no longer attached
   attach-check "x"
   SET REFERENT "x" TO "more contents%n"
   attach-check "x"           ; attached now so we end up with a value

11.4.6 Iterating Over the Current Referent Set

It is also possible to walk over the REFERENTS shelf with the "REPEAT OVER" action.

Syntax

   REPEAT OVER REFERENTS
      local-declaration*
      action*
   AGAIN

A "REPEAT OVER REFERENTS" selects each referent in turn. On each iteration, the selected referent is identified as "THIS REFERENT" in the body of the "REPEAT OVER" action, as described in Section 11.4.7, "The USING Prefix and the Referents Shelf".

The following example shows how OmniMark can be used to easily find all of the undefined referents, and list them in the output file. By default, OmniMark complains about every unresolved referent after the end of the last DOCUMENT-END or FIND-END rule has been processed, and writes out a null string in its place. Null strings can be particularly difficult to find when looking for the context of the undefined referents. Instead, we can report the undefined referent and give it a value that will make it easy to find in the output. Once we know why the referent is undefined, it should be relatively easy to fix the problem.

   DOCUMENT-END
    LOCAL SWITCH first-time INITIAL {TRUE}

    ; All output in this block goes to the error stream.
    USING OUTPUT AS #ERROR
     REPEAT OVER REFERENTS
       ; Process each undefined referent,
       ; which has been mentioned but never defined.
       DO WHEN THIS REFERENT ISNT ATTACHED

         ; Print a header only if we need it.
         OUTPUT "Unattached referents: %n" WHEN first-time
         DEACTIVATE first-time

         ; Show the undefined referent
         OUTPUT "%_%_" || KEY OF THIS REFERENT || "%n"

         ; Define the referent as its name, surrounded by |< and >|
         ; This is easier to find in the final output file
         SET REFERENT KEY OF THIS REFERENT TO
                      "|<" || KEY OF THIS REFERENT || ">|"
       DONE
     AGAIN

The REFERENTS shelf can be combined with programmer-defined shelves in a "REPEAT OVER" (if they have the same number of items), in the manner described in Section 7.6, "Repeating Actions on Each Item of a Shelf".

11.4.7 The USING Prefix and the Referents Shelf

It is possible to set a default item on the REFERENTS shelf in a way similar to the method described in Section 7.5, "Specifying a New Default Selected Item". This has the form:

Syntax

   USING REFERENTS indexer

where indexer is an index phrase, giving either the key or the index of the referent to use.

References to the referent selected by a USING prefix are different from those involving other shelves such as counters or attributes. With other shelves, the name of a shelf inside the body of the USING prefix refers to the selected item. But the REFERENTS shelf does not have a name. The selected referent is referred to by the keywords "THIS REFERENT".

The keywords "THIS REFERENT" can be used:


11.5 Default Referent Definitions

There are a couple of ways in which an OmniMark programmer can avoid having to provide a definition for every referent written out: the referent definition can be defaulted or "made silent".

11.5.1 Specifying Default Referent Definitions

A DEFAULTING part can be specified with REFERENTS-ALLOWED in an OPEN action. It has two possible forms. It can be specified with either one string expression or two, and provides an effective value for referents as follows:

The DEFAULTING part, if any, used to provide the effective value of an undefined referent is the DEFAULTING part associated with the "final destination" of the referent. This has a number of consequences:

11.5.2 Default Referent Definitions for a Reopened Stream

DEFAULTING is also allowed on a REOPEN action, but only one that has an AS part -- i.e one that explicitly specifies a stream attachment.

A REFERENTS-ALLOWED in an OPEN action, in a SET (or "SET STREAM") action or in a REOPEN action that has an explicit stream association (i.e. AS) can have a DEFAULTING part also, that describes the default referent value for the stream.

On the other hand, a "dynamic" REOPEN action, although it can specify REFERENTS-ALLOWED (if it was originally opened with REFERENTS-ALLOWED), cannot specify DEFAULTING. In this case the originally specified DEFAULTING, if any, on the OPEN action that created the stream continues to apply. If there was no DEFAULTING specified on the original OPEN action, then the stream has no DEFAULTING, period.

11.5.3 "Silent" Referents

Normally, in OmniMark, when a referent is written to a REFERENTS-ALLOWED stream, its existence is also noted on the REFERENTS shelf. This means that, whether or not a value is assigned to the referent, its existence can be detected using "REPEAT OVER REFERENTS" (and whether or not it has a value detected using "IS ATTACHED").

For those cases in which the detection of undefined referents is not required, OmniMark allows referents to be written without their existence being noted on the REFERENTS shelf. Instead of doing an output of REFERENT something to a REFERENTS-ALLOWED stream, you can use SILENT-REFERENT something, as in:

   OUTPUT SILENT-REFERENT "table mark %d(table-mark-count)"

A SILENT-REFERENT, when resolved, takes on its effective value, if any. If the stream to which the SILENT-REFERENT was written has no applicable DEFAULTING value, then it is an error to attempt to resolve the value of the SILENT-REFERENT, in the same manner that it is an error to attempt to resolve the value of an unattached referent when there is no applicable DEFAULTING value.

The main advantage of using SILENT-REFERENT comes when there needs to be placed in the output a large number of "marked points", at which later-determined text may need to be placed, but for most of which no extra text is required. In this case the applicable DEFAULTING value should be set to the zero-length string, and only the marked points with text defined.

Note that if a referent is given a value, then its existence is noted on the REFERENTS shelf whether or not it has been used as a REFERENT or SILENT-REFERENT. Likewise a referent can be used as both a REFERENT and a SILENT-REFERENT, in which case its use as a REFERENT will note its existence on the REFERENTS shelf.


11.6 Referents and Line-Breaking

When output lines are broken with REPLACEMENT-BREAK and INSERTION-BREAK lines, break positions are calculated with the text at hand. If referents are being written, OmniMark does not know the length of the final value of the referent, and ignores its length. This may result in lines that are either much shorter than the stated preferred width, or much longer, even if more optimal line lengths could have been selected.

Lines that exceed the maximum width after referents have been replaced with their final value will be reported.

The following example shows how referents and line-breaking interact:

   CROSS-TRANSLATE

   BREAK-WIDTH 20

   REPLACEMENT-BREAK '%_' '%n'

   FIND-START
     LOCAL STREAM s
     OUTPUT "first%/ line%/ with%/ no%/ referents,%/ breakable%/ spaces.%n"
     OUTPUT "second%/ line%/ with%/ "
     OUTPUT REFERENT "1"
     OUTPUT "%/ a referent.%n"
     OUTPUT "third%/ line,%/ also%/ with%/ "
     OUTPUT REFERENT "2"
     OUTPUT "%/ referents.%n"
     OPEN s AS REFERENT "1"
     PUT s "|referent 1|"
     CLOSE s
     OPEN s AS REFERENT "2"
     PUT s "|referent 2|"
     CLOSE s

With no input, the following output is produced:

   first line with no                        ; 1
   referents, breakable                      ; 2
   spaces.                                   ; 3
   second line with |referent 1| a referent. ; 4
   third line, also                          ; 5
   with |referent 2| referents.              ; 6

The first line is correctly broken, giving output lines 1 through 3. The second line is not broken because both the part before the referent and after are less than twenty characters long. The final line is broken before the referent, giving line 5, but the part after is not broken. No errors were reported here because no maximum width was specified.

Next chapter is Chapter 12, "Functions".

Copyright © OmniMark Technologies Corporation, 1988-1997. All rights reserved.
EUM27, release 2, 1997/04/11.

Home Copyright Information Website Feedback Site Map Search