/**
* This module contains a strategies for workin on the XTC
* universe. This includes composing components, passing data
* between components, and basic file handling.
*
* The XTC model is based around Unix pipes and ATerm stored
* as (temporary) files on disk. Each XT component is a
* an executable program, which reads an ATerm as input
* (either from stdin or from a file), processes it, and then
* writes the result to another file (or stdout).
*
* The lowlevel details of creating temporary files, opening
* pipes, and executing components have been hidden behind the
* strategies found in this module. You will find many examples
* of wrapper strategies, which perform bookkeeping of temporary
* files, opened file descriptors, and similar, to ensure that
* these are removed and freed upon program termination.
*
* @author Eelco Visser <visser@acm.org>
* @author Karl Trygve Kalleberg <karltk@cs.uu.nl>
*/
module stratego/xtc/Proc
imports
stratego/xtc/Register
signature
constructors
FILE : String -> File
DIR : String -> File
TEMP : XtcKey
TempFiles : Table
TempDirs : Table
strategies
/**
* Reads a term from a stdin. The input on stdin is assumed to be
* an ATerm. It will be parsed, and returned as a term.
*
* @type stdin -> t
*/
read-from :
stdin() -> t
where <ReadFromFile> stdin() => t
/**
* Write a term to a temporary file, in binary. The name of the file
* is generated automatically, the term is written to it in the
* binary ATerm format (BAF), and a handle is returned by this
* strategy.
*
* @type t -> FILE(name)
*/
write-to :
t -> FILE(name)
where xtc-new-file => name
; <WriteToBinaryFile> (name, t)
/**
* Write a term to a temporary file, as text. The name of the file
* is generated automatically, the term is written to it in the
* textual ATerm format (TAF), and a handle is returned by this
* strategy.
*
* @type t -> FILE(name)
*/
write-to-text :
t -> FILE(name)
where xtc-new-file => name
; <WriteToTextFile> (name, t)
/**
* Writes a (list of) string(s) to a temporary file. A single
* string, or a list of strings is written to a text file. The
* file name is generated automatically, and returned by this
* strategy.
*
* @type String or List(String) -> FILE
*/
print-to :
strings -> FILE(name)
where
xtc-new-file => name
; <fopen> (name, "w") => stream
; if <is-string> strings then
![strings]
else
!strings
end
; <fprint> (stream, <id>)
; <fclose> stream
/**
* Searches for an XT component, by name. The input to this
* strategy is the name of the component, as registered in
* the XTC registry. If found, a FILE representing its
* location (path) is returned.
*
* @type String -> FILE(String)
*/
xtc-find-file =
!FILE(<xtc-find>)
strategies
/**
* Renames one file to another, on disk. If the new name
* is identical to the old, no action is taken. The old
* file can also be 'renamed' to stdout, in which case
* the content of the old file is written to stdout
* before the file is removed.
*
* @param name _ -> newname
* @type FILE(oldname) -> FILE(newname) or stdout
*/
rename-to(name) :
FILE(oldname) -> FILE(oldname)
where name => oldname
/**
* @inc rename-to
*/
rename-to(name) :
FILE(oldname) -> FILE(newname)
where name => newname
; not(stdout() + ?oldname)
; <rename-file> (oldname, newname)
/**
* @inc rename-to
*/
rename-to(name) :
FILE(oldname) -> stdout()
where name => stdout()
; <copy-file> (oldname, stdout())
; <remove-file> oldname
strategies
/**
* Copies one file to another. If the new file has the same name
* as the old, no action is taken. In addition to a real file,
* the new file can be stdout (or stderr). In this case, the contents
* of the old file will be written to stdout (or stderr) before the
* file is removed.
*
* @param name _ -> b
* @type FILE(a) -> FILE(b)
*/
copy-to(name) :
FILE(oldname) -> FILE(oldname)
where name => oldname
/**
* @inc copy-to
*/
copy-to(name) :
FILE(oldname) -> FILE(oldname)
where name => newname
; not(stdout + stderr + ?oldname)
; <copy-file>(oldname, newname)
/**
* @inc copy-to
*/
copy-to(name) :
FILE(oldname) -> FILE(oldname)
where name => file
; (stdout() + stderr())
; <copy-file> (oldname, file)
strategies
/**
* Succeeds if the input term is a FILE
*
* @type FILE -> FILE
*/
xtc-ensure-file =
?FILE(_)
/**
* Succeeds if the input term is stdin, and it can be successfully
* copied to a FILE.
*
* @type stdin -> FILE
*/
xtc-ensure-file =
?stdin()
; where(<xtc-new-file> () => f)
; <copy-file> (stdin(), f)
; !FILE(f)
strategies
/**
* Writes a FILE to the output of this component. The output
* is file is retrieved from the -o option given to the
* program; if -o was not given, stdout is used.
*
* @type FILE -> FILE
*/
xtc-write-output =
?FILE(_); copy-to(<get-config> "-o" <+ !stdout())
/**
* @type stdin -> FILE
*/
xtc-write-output =
?stdin(); <copy-file> (stdin(), <get-config <+ !stdout()> "-o")
/**
* XTC-TEMP-FILES : scoped temporary files
*/
strategies
// xtc-new-file
/**
* Removes all temporary files created by this program. Each
* time the <tt>xtc-*</tt> strategies create a new temporary
* file, it will be tracked. Running this strategy will remove
* all files created so far.
*
* @type _ -> _
*/
xtc-remove-temporaries =
where(
<table-get>(XTC, TEMP) => fs
; map(try(remove-file))
)
/**
* Wrapper strategy that automatically cleans all temporary files
* created by s. This strategy is used to wrap a strategy <tt>s</tt>,
* which can create one or multiple temporary files. All temporary
* files will be cleaned up automatically, and the result of
* <tt>s</tt> will be returned.
*
* @param s t -> u
* @type t -> u
*/
xtc-temp-files(s) =
begin-scope(!TempFiles)
; restore-always(s,
where((<table-get>(TempFiles, Scopes) <+ ![[]]) => [scope | scopes]
; <map(try(remove-file))> scope)
; end-scope(!TempFiles)
)
/**
* Clean up temporary files and exit program. In normal operation,
* XT components create temporary files, which should be cleaned
* up on program exit. Calling this strategy instead of
* <tt>exit</tt> ensures that no files are left behind.
*
* @type Integer -> _
*/
xtc-exit =
?status
; repeat(
where(<table-get>(TempFiles, Scopes) => [scope | scopes]
; <map(try(remove-file))> scope)
; end-scope(!TempFiles)
)
; exit
// XTC-IO : 'read' input file, transform, and 'write' to output file
/**
* High-level wrapper that reads a term from a file, transforms it
* using s, then writes the output back to a file. The input file
* is taken from the command line options given to the program
* (the -i option), and the output is taken from the -o option.
* If -i is not given, stdin is used. If -o is not given, stdout
* is used
*
* @param s u -> v
* @type FILE(a) -> FILE(a)
*/
xtc-io(s) =
xtc-temp-files(
(!FILE(<get-config> "-i") <+ !stdin())
; s
; xtc-write-output
<+ prim("SSL_stacktrace_get_all_frame_names")
; report-failure(xtc-exit)
)
// stop in the middle of a transformation pipeline, copying the
// current file to the output file.
/**
* Terminate the running program safely by flushing the current
* working file to output. The output file is determined from the
* -o option on the command line; if not given, stdout will be
* used. All temporary files will also be removed.
*
* @type t -> _
*/
xtc-io-exit =
xtc-write-output
; <xtc-exit> 0
// XTC-INPUT : 'read' input file, transform
xtc-input(s) =
xtc-temp-files(
(!FILE(<get-config> "-i") <+ !stdin)
; s
)
/**
* Applies the strategy s to the current term, then writes the result
* to output. The output file is taken from the -o option on the command
* line, if given, or defaults to stdout, if not.
*
* @param s a -> FILE(c)
*/
xtc-output(s) =
xtc-temp-files(
s
; copy-to(<get-config> "-o" <+ !stdout)
)
// io-wrap for xtc tools: 'read' -i option, transform, and 'write' to -o option
xtc-io-wrap(s) =
xtc-io-wrap(fail, s)
xtc-io-wrap(extra-opts, s) =
option-wrap(extra-opts <+ io-options, xtc-io(s))
xtc-io-wrap(extra-opts, deps, s) =
xtc-io-wrap(extra-opts, system-usage, system-about, deps, s)
xtc-io-wrap(extra-opts, usage, about, deps, s) =
option-wrap(extra-opts <+ io-options <+ check-options(deps), usage, about, id,
xtc-check-dependencies
; xtc-io(s)
)
xtc-iowrap(s) =
xtc-io-wrap(s)
xtc-iowrap(extra-opts, s) =
xtc-io-wrap(extra-opts, s)
/**
* Option parsing wrapper for XTC programs that only read data. This
* strategy functions like xtc-io-wrap, except that it only exposes
* the -i option, and will not write any output to pipe (or file).
*
* Typically, s will start with a call to read-from.
*
* Example: xtc-input-wrap(read-from ; id)
*
* @param s FILE(a) -> b
* @type List(option) -> _
*/
xtc-input-wrap(s) =
xtc-input-wrap(fail,s)
/**
* Option parsing wrapper for XTC programs that only read data. This
* strategy functions like xtc-io-wrap, except that it only exposes
* the -i option, and will not write any output to pipe (or file).
*
* Typically, s will start with a call to read-from.
*
* Example: xtc-input-wrap(ArgOption(....), read-from ; id)
*
* @param extra-options _ -> Option or ArgOption
* @param s FILE(a) -> b
* @type List(option) -> _
*/
xtc-input-wrap(extra-options, s) =
option-wrap(extra-options <+ input-options, xtc-input(s))
/**
* Option parsing wrapper for XTC programs that only generate data.
* This strategy functions like xtc-io-wrap, except that it only
* exposes the -o option, and will read any output from stdin
* (or file).
*
* Typically, s will will end with a call to write-to.
*
* Example: xtc-output-wrap(!"foo" ; write-to)
*
* @param s a -> FILE(b)
* @type List(option) -> _
*/
xtc-output-wrap(s) =
xtc-output-wrap(fail,s)
/**
* Option parsing wrapper for XTC programs that only generate data.
* This strategy functions like xtc-io-wrap, except that it only
* exposes the -o option, and will read any output from stdin
* (or file).
*
* Typically, s will end with a call to write-to.
*
* Example: xtc-output-wrap(!"foo" ; write-to)
*
* @param extra-options _ -> Option or ArgOption
* @param s a -> FILE(b)
* @type List(option) -> _
*/
xtc-output-wrap(extra-options, s) =
option-wrap(extra-options <+ output-options, xtc-output(s))
// check tools upon which this tool depends
/**
* Used to add the --check option to your XT component. This
* strategy should be composed with the other command line
* options of your program, as given to io-wrap, xtc-io-wrap
* or option-wrap. It will add a --check option to the command
* line. It is intended to be used with xtc-check-dependencies.
*
* The deps parameter should be a list of components (names)
* required by your program. This will be used by
* xtc-check-dependencies.
*
* @param deps _ -> List(String)
* @type _ -> Option
*/
check-options(deps) =
Option("--check"
, where(<set-config>("--check", <deps>))
, !"--check check tool dependencies"
)
/**
* Succeeds if all necessary components for the running program
* are available, and the user has specified --check. The
* check-options strategy must have been added to the command line
* options of the running program, see check-options for details.
* Additionally, xtc-check-dependencies will only run if the user
* specified --check on the command line when invoking the program.
*
* @type _ -> _
*/
xtc-check-dependencies =
if check := <get-config> "--check" then
<filter(not(xtc-find-silent))> check
; if not(?[]) then
debug(!"No XTC registration found for: ")
; <exit> 1
else
<echo> "All tools available"
; <exit> 0
end
end
strategies
/**
* Transform character chunks in text file. This strategy
* inherits the semantics of char-io/filter-text-file,
* where more is applied to each line (and must always
* suceed), while done must fail when the processing
* should stop.
*
* @param
* @type FILE(a) -> FILE(b)
*
* seealso char-io/filter-text-file
*/
xtc-filter-text-file(more, done) :
FILE(f) -> FILE(g)
where <xtc-new-file> f => g
; <filter-text-file(more, done)> (f, g)