This chapter explains the strategies available in the library for controlling file and console I/O.
The need for traditionally file I/O is somewhat diminished for typical applications of Stratego. Normally, Stratego programs are designed to work together connected by Unix pipes. The programs employ
io-wrap (or similar strategies) that automatically take care of the input and output.
The primitive layer of Stratego I/O inherits its characteristics from Unix. The basic I/O strategies recognize the special files
stderr. Streams are opened by
fopen and closed with
fclose On top of this, a collection of more convenient strategies have been built.
6.1. Console I/O¶
The basic strategies for console I/O
printnl are used to write terms to
stderr (or any other opened file). They both take a tuple. The first element of the tuple is the file to write to, the second is a list of terms. Each term in the list be converted to a string, and and these strings will be concatenated together to form the resulting output. The
printnl will also append a newline to the end of the resulting string.
The following module should be compiled with strc, as usual.
module example imports libstratego-lib strategies main = <print> (stdout, ["baz"]) ; <printnl> (stdout, [ "foo", 0, "bar" ])
After compiling this file, running it will give the following result:
$ ./example bazfoo0bar $
Notice how the string
baz will be written without a newline (or other space). Also, notice how the terms in the list argument were concatenated.
When using these strategies in the Stratego Shell, some care must be taken when using the
std* files, as the following example shows.
stratego> <printnl> (stdout(), [ "foo", 0, "bar" ]) foo0bar
The shell requires that you put an extra parenthesis after the
error are convenience wrappers around
printnl. They will always write their result to
error strategy is defined as:
error = where(<printnl> (stderr, <id>))
It is used similarly to the
stratego> <error> ["foo", 0, "bar"] foo0bar
debug strategy accepts any term, i.e. not only lists of terms. The term will be written verbatim:
stratego> <debug> [ "foo", 0, "bar" ] ["foo",0,"bar"]
6.2. Path and Directory Operations¶
The library provides a small set of simple file and directory manipulation operations. Assume the directory
/tmp only contains the files
baz. Elementary directory operations can be done as illustrated below:
stratego> <readdir> "/tmp" ["foo","bar","baz"] stratego> <rename-file> ("/tmp/foo", "/tmp/bax") "/tmp/bax" stratego> <remove-file> "/tmp/baz"  stratego> <link-file> ("/tmp/bar", "/tmp/foo") "/tmp/foo" stratego> <link-file> ("/tmp/bar", "/tmp/foo") "/tmp/foo" stratego> <new-temp-dir> "/tmp" "/tmp/StrategoXTnsGplS"
The library contains a family of strategies which must be applied to a
File, and will return information about it. these include
islnk which are predicates checking if a file is a directory, TTY, FIFO or a symbolic link, respectively. To obtain a
File object in the first place, we should call
file-exists followed by
filemode. Thus, checking if
/etc is a directory is done as follows:
stratego> <file-exists ; filemode ; isdir> "/etc"
The library also has another family of strategies for getting information about files. These must be applied to a string containing the filename. The family includes
stratego> <is-executable> "/bin/bash" "/bin/bash"
Finally, the directory strategies also include the usual suspects for dealing with paths.
stratego> <is-abspath> "../foo" command failed stratego> <dirname> "/foo/bar/baz" "/foo/bar" stratego> <base-filename> "/foo/bar/baz" "baz" stratego> <get-extension "/tmp/foo.trm" "trm" stratego> <abspath> "../foo" /home/karltk/source/oss/stratego/strategoxt-manual/trunk/../foo
There are also a few strategies for finding files. We shall describe
find-file(s). The other variants of
find-file are described in the library documentation. The strategy
find-file(s) finds one file with a specific file extension in a list of directories. It takes a two-element tuple. The first element is a file name as a string, then second element is a list of paths, i.e.
(f, [d*]). The extension of
f will be replaced by what is produced by
s, and the directories given in
[d*]. Consider the code below.
stratego> <find-file(!"rtree")> ("file.str", ["."])
This snippet will consider the filename
file.str, replace its extension with
rtree and look through the directories in the list
["."]. Effectively, it will search for
file.rtree in the current directory.
6.3. File and Text I/O¶
Opening a file is done with the
fopen strategy. It takes a two-element tuple, the first element is the filename as a string, the second is the open mode, which is also a string. The most important modes are read (
r); write (“w”) which opens and empty file for writing, truncating any existing file with the same name; and append (
a) which appends to the file if it already exists. After all file operations stream have been finished, it should be closed with
fclose, which will flush and close the file. Explicit flushing can also be done with
It should be pointed out that reading and writing text files with Stratego is rather rare. Normally, text files are read with a parser generated from an SDF description and written using a pretty-printer defined in the Box formalism. In rare cases, this may turn out be too heavy handed, especially if the file format is simplistic and line-based. In this instance, we can come up with an easier solution using
Assume the file
/tmp/foo contains the following lines:
one two three
We can read this file in one big chunk into a string with the
read-text-file strategy, which must be applied to a filename:
stratego> <read-text-file> "/tmp/foo" "one\ntwo\nthree\n"
Alternatively, for example if the file is large, we can read it line by line. In this scenario, we must open the file and get a handle to a stream.
stratego> <fopen> ("foo.txt", "r") => inp Stream(136788400) stratego> <read-text-line> inp "one"
6.4. Term I/O¶
The primary form of file I/O you will be using in Stratego is reading and writing terms. As explained earlier, the terms are stored on disk as either binary, compressed text or plain text ATerms. Reading a term, no matter which storage format, is done with the
ReadFromFile strategy. It is applied to a filename.
stratego> <ReadFromFile> "/tmp/foo.trm" Foo(Bar)
To write a term to file, you can use
WriteToBinaryFile. The binary format is approximately eight times more space-efficient on average. Both strategies take a two-element tuple where the first element is the filename and second is the term to write. Writing the current term requires a minor twist, which is shown here:
stratego> <WriteToBinaryFile> ("/tmp/bar.trm", <id>) Foo(Bar)
It is also possible to read and write terms from and to strings, using
write-to-string. The chapter on Strings contains explanation of how these strategies work.
The strategies for logging are used pervasively throughout the Stratego toolchain. They are easy to use in your own applications, too. The logging system is built on top of the
log(|severity, msg) and
log(|severity, msg, term) strategies. It is possible to use these directory, as the following example demonstrates.
stratego> import util/log stratego> log(|Error(), "my error")
However, it is preferable to use the high-level wrapper strategies
notice-msg(|msg). Except for
fatal-err-msg, these strategies will return with the current term untouched, and write the message as a side effect. The
fatal-err-msg strategy will also terminate the program with error code
1, after writing the message.