|
|||||
|
|||||
Related Syntax | |||||
Output |
Output in OmniMark is part of the streaming architecture of the language. Data is processed as it streams and all streaming data has a source and a destination. Within an OmniMark program, the source of streaming data is always the current input scope, and the destination is always the current output scope. To direct output to a particular destination, such as a file, you must attach the file to an OmniMark stream variable and make that stream variable part of the current output scope.
In order to direct output to a particular destination, therefore, you must do the following:
Once you do this, all output in your program will go to the destination attached to your stream. This program shows how this works:
process local stream out-file open out-file as file "out.txt" using output as out-file submit "in.txt" find "$" digit+ => dollars "." digit{2} => cents output dollars || "," || cents || "$"
Here is how this code accomplishes the steps outlined above:
local stream out-file
creates a stream variable called "out-file".
open out-file as file "out.txt"
attaches the file "out.txt" to the stream out-file.
using output as out-file
creates an output scope and places the stream out-file in that scope.
The output scope created by using output as out-file
exists for all code governed by the using
statement. In this case, that code is the submit
statement on the next line. The submit
initiates scanning by find
rules. Therefore, any data that streams to output during the submit flows to that output scope. Any find
rules that fire as a result of the submit
are inside the output scope of the submit, so any output they generate goes to the output scope.
The find rule itself does not specify where its output goes. It simply goes to the current output scope.
This separation between the business of generating output and the business of deciding where output goes is fundamental to OmniMark. It allows you to write code to create output and call that code from many different places. Each place you call that code from can first establish an appropriate current output scope. The code that generates output is highly reusable because it operates completely independently of where the output is going.
Notice in the code above that the open
statement is opening a stream and attaching that stream to a file. It is not opening the file. It is OmniMark's job to actually open the file and write to it. In some cases, OmniMark may not open and write to the file until much later. Data destinations are always abstracted either by OmniMark itself or by an OMX component. In your program you write only to OmniMark streams (by making them part of current output and writing to current output). You never write directly to a destination. You do not need to know any of the details of how a stream is attached to a destination or what kind of destination it is attached to.
The consequence of this is that there is exactly one output mechanism in OmniMark that applies to all output operations. This means that all OmniMark keywords that create output use the same mechanism and thus can all work on the full range of output destinations from buffers, to files, to network data streams.
OmniMark has three operations that create output:
You can use any one of them to write to any output destination. For example, you can use set
to place a value in a file:
set file "mary.txt" to "Mary had a little lamb"
Conversely, you can use output
to write a value to a variable:
open Mary as buffer using output as Mary do output "Mary had a little lamb" output "Its fleece was white as snow" output "And everywhere that Mary went" output "The lamb was sure to go" done
This is much more efficient than writing:
set Mary to "Mary had a little lamb" set Mary to Mary || "Its fleece was white as snow" set Mary to Mary || "And everywhere that Mary went" set Mary to Mary || "The lamb was sure to go"
This is a powerful feature of OmniMark. It enables you to choose the type of data assignment mechanism appropriate to the scale of operation you want to perform. You can use set
for any kind of small-scale assignment, whether to a file or a variable, without any of the bother of opening files or buffers. For large-scale operations, you can use output
and perform multiple updates without the need to specify the destination, or even worry about the kind of destination involved. Choosing the method appropriate to the scale of operation you are performing will greatly simplify your code.
An OmniMark program starts with a default output scope with a default stream attached to a default destination. The name of the default stream is #main-output
and its default attachment is standard output (stdout). stdout is the screen, unless it is redirected by a calling process, such as a web server (which binds stdout to itself when launching a CGI script).
This means that if you create an OmniMark program that does not direct its output in any way, the output will appear on the screen:
process output "Hello world%n"
You can change the attachment of #main-output
on the command line using the -of command line option, which is described in the OmniMark Server Engine documentation. This attaches #main-output
to the file named in the -of
parameter. This allows you to write programs in OmniMark without specifying their output, and specify the output file on the command line when you run the program.
If #main-output
is redirected in this way, you still have access to stdout through the built-in stream #process-output
, which is permanently attached to stdout.
The statement using output as
both establishes a new output scope and places streams in that output scope. However, you can change the streams in the current output scope without creating a new output scope. To do this, you use the output-to
command:
process local stream foo-file local stream bar-file open foo-file as file "foo.txt" open bar-file as file "bar.txt" using output as foo-file do output "one" output-to bar-file output "two" done
The code above changes the stream in the output scope created by the using output as
statement from foo-file
to bar-file
.
It is possible to send output to multiple destinations simultaneously by placing more than one stream in the current output scope:
global stream my-file global stream my-buffer process open my-file as file "myfile.txt" open my-buffer as buffer using output as my-file & my-buffer submit "Mary had a little lamb"
You can refer to the streams in the current output scope with the keyword #current-output
. For instance, this code uses #current-output to add a new stream to an existing output scope:
global stream my-file global stream my-buffer process open my-file as file "myfile.txt" using output as my-file submit "Mary had a little lamb" find "had" open my-buffer as buffer output-to my-buffer & #current-output output "I've been had!"
This code will place "I've been had!" in both the file "myfile.txt" and the variable my-buffer.
Related Syntax open output output-to put using output as |