OmniMark distinguishes the act of creating output from the act of selecting the output destination, meaning that
the stream that receives the output data does not have to be lexically in scope for you
to output to it. Instead, a stream or a string sink
can be placed in an output execution
scope. While a stream is in the current output scope, all output statements will output to it
regardless of the lexical scope they occur in.
You can use the keywords using output as
to create an output scope and to place a stream variable
into that output scope. Like any other kind of scope, output scopes can be nested:
process local stream foo open foo as file "foo.txt" using output as foo do output "<rhyme>" submit "Mary had a little lamb" output "</rhyme>" done find ("Mary" | "lamb") => person local stream foo reopen foo as file "foo2.txt" using output as foo output "<person>" || person || "</person>"
Here a new output scope is established in the find rule, causing the material output in the find rule to be sent to a different destination. This output scope is nested inside the output scope created in the process rule. This scope becomes the current output scope again as soon as the find rule exits.
You can also place a stream into the current output scope, without creating a new output scope, using
output-to
:
global stream foo global stream bar process open foo as buffer open bar as buffer using output as foo submit "Mary had a little lamb" close foo close bar output "Foo contains: " || foo output "%nBar contains: " || bar find " a " output-to bar
This program outputs the following:
Foo contains: Mary had Bar contains: little lamb
The output-to in the find rule resets the destination of the output scope established by the using output
as
in the process rule. Thus the rest of the text goes to the new destination.
In general, you should use using output as
rather then output-to
, but
output-to
is useful in certain situations, especially when the destination of data is determined by
examining the data itself.
Consider a piece of XML that might be used to send files across a network. It encapsulates the name of the file
and its contents inside "name" and encapsulates "data" elements inside a "file" element:
<file> <name>myfile.txt</name> <data>The content of the file.</data> </file>
We can process this with the following program:
global stream file-data process do xml-parse document scan file "files.xml" suppress done element file suppress close file-data element name open file-data as file "%c" output-to file-data element data output "%c"
Here the entire processing of the XML is done in a single output scope, but every time we find a filename in the
input we change the destination of the current output scope. It would be difficult to do the same thing with
using output as
, because the "data" element is not nested inside the "filename" element, so an output
scope established in the "name" element would have expired before the "data" element was processed.
(By the way, the example shows poor XML language design. It would have been better to make the filename an attribute of the "file" element. But you can't always control the format of the data you have to process.)
If you use output-to
, note that placing a stream into an output scope does not exempt it from the
rules of lexical scoping when it comes to the life span of variables. Local variables are created when their
lexical scope enters execution scope and destroyed when their lexical scope leaves execution scope. It is an error
to allow a local variable to go out of execution scope while it is still in an output scope. You will avoid this
problem if you stick to using using output as
to create output scopes.