Table of Contents
Last update: 29.01.2025.
Command line programs often require some output to describe the actions of the program, or read and write some files, without all of this being explicitly part of the result that the program computes. Such things are called side effects. They do not exist in pure functional languages, but they can be of great use in command-line programs and scripts.
Some of the functions presented in Command Line Library create side
effects. For example, the input
function and all
echo
functions. These functions focus on communication, and
controlling the side effects is their secondary task. There are two
functions whose main task is to control the side effects:
return
and aside
.
Function / Type and Description
return
('1 * '2 -> '2)
Evaluates both arguments,
discards the 1st result and returns 2nd one.
aside
('1 * ('1 -> '2) -> '1)
Applies 2nd arg. to
1st, discards the result and returns 1st arg:
aside(x,fn) == fn(x).return(x)
The function return
has two arguments. It evaluates both
in the specified order and returns the second one. It is assumed that
the first argument only has a side effect role and its value is
discarded.
It can be misleading to think of this function in the usual form
return(x,y)
, as it is normally used in a different way. It
is used almost exclusively in the form
...exp... .return(y)
, where the expression
...exp...
is evaluated and discarded and then
y
is evaluated and returned. Only the side effects of the
expression ...exp...
are important, while the result is
discarded.
"Good morning!"
return( "Good afternoon!" ) .
Good afternoon!
The function return
is useful, for example, after
writing a file, traversing a directory and some other operations with
side-effects, when there is no need to return the specific results. For
example, the following expression performs some processing, then
discards the current result and returns the message “OK” as confirmation
that the program has finished:
... some processing ...echoTxt('--------\n')
.return( "OK" ) .
...whatever the processing output is...
--------
OK
"Good morning!"
echoLn()
.echoTxt( '-------------------\n' )
.return( "Good afternoon!" ) .
Good morning!
-------------------
Good afternoon!
The function return
is not always the best solution, as
it not only discards the result of the first argument, but may also
discard an error report. You should therefore use it with caution.
The function aside
is similar to return
-
it evaluates two arguments in the specified order, but then returns the
first and discards the second. It assumes that the current result is
still needed, but performs some intermediate processing. It is typically
used to output a report on the current progress or result status, as in
the following example:
"Good morning!"
aside(\x:
."Before saying: \"" + x + "\", bring a cup of coffee first!").echoLn()
( )
Before saying: "Good morning!", bring a cup of coffee first!
Good morning!
In some simple cases, it is often sufficient (and better) to use
echo
functions (the echoFn
is perfect in the
previous example), but if the reporting involves a file creation or a
database update, then aside
is a much better solution.
The function aside
can be used to prepare some temporary
resources for processing or to perform a cleanup after the processing.
For example, consider the following code:
... begin ...aside(\x: ... create some tmp files ... )
.
... processing ...aside(\x: ... delete tmp files ... )
. ... finalize ...
Having discussed list processing, we now have a broader picture of common programming techniques in functional programming languages. The computation is implicitly controlled by some higher order functions and by the processed data itself. This approach is general and flexible enough to solve practically any problem. In some cases, however, the creation and processing of additional lists burdens the computation without this really being necessary.
For thos reason, the Wafl core library contains some so-called iterative functions. Iterative functions are used as a tool to control the iterative computation, but based on integers and conditions, rather than on sequences.
Function / Type and Description
iterate
('1 * Int * Int * ('1 * Int -> '1) -> '1)
Left associative folding of specified integers range:
iterate(zero,2,4,fn) = fn(fn(fn(zero,2),3),4)
= zero.fn(2).fn(3).fn(4)
= (2..4).foldl(fn,zero)
iterateBy
('1 * Int * Int * Int * ('1 * Int -> '1) -> '1)
Left associative folding of specified integers range by step:
iterateBy(zero,2,6,2,fn) = fn(fn(fn(zero,2),4),6)
= zero.fn(2).fn(4).fn(6)
= [2,4,6].foldl(fn,zero)
repeatUntil
('1 * ('1 -> '1) * ('1 -> Bool) -> '1)
Repeats the function evaluation until the condition is met:
repeatUntil(x,fn,cond) =
if cond(x) then x
else fn(x).repeatUntil(fn,cond)
The function iterate(zero,from,to,fn)
computes a left
associative folding of the specified integer range. It is functionally
equivalent to imperative for-loop, like the next one in C++:
auto result = zero;
for( int i=from; i<=to; i++ )
= fn( result, i );
result return result;
The function iterate(zero,from,to,fn)
is equivalent to
expression (from..to).foldl(fn,zero)
, but performs the
computation without creating the list from..to
, so that it
is more efficient. If the range is in descending order, the negative
step is used:
{#iterate( '#', 1, 10, \s,x: s + '-' + x.asString() ),
iterate( '#', 10, 1, \s,x: s + '-' + x.asString() ),
1..10).map(sum(1,_)),
(1..10).map(fact)
(
#}where {
sum(n,m) = iterate( 0, n, m, operator+ );
fact(n) = iterate( 1, 1, n, operator* );
}
{# '#-1-2-3-4-5-6-7-8-9-10', '#-10-9-8-7-6-5-4-3-2-1', [1, 3, 6, 10, 15, 21, 28, 36, 45, 55], [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] #}
The function iterateBy(zero,from,to,step,fn)
is a more
general form of iterate
. If iterate
is used,
the step is always 1 or -1. With iterateBy
, the step may be
any integer.
{#iterateBy( '#', 1, 10, 2, \s,x: s + '-' + x.asString() ),
iterateBy( '#', 10, 1, -3, \s,x: s + '-' + x.asString() )
#}
{# '#-1-3-5-7-9', '#-10-7-4-1' #}
The function repeatUntil(x,fn,cond)
controls the
iteration based on the given condition. It repeats the iterative
transformation of x
until the condition is fulfilled.
Again, it can be useful to describe this in an imperative way:
auto result = x;
while(!cond(result))
= fn(result);
result return result;
In the next example, the given number is multiplied by itself until the result is greater than or equal to 1000:
2..10).map( enlarge )
(where {
enlarge(n) = n.repeatUntil(
*(n,_),
operator>=(_,1000)
operator;
)}
[1024, 2187, 1024, 3125, 1296, 2401, 4096, 6561, 1000]
Function / Type and Description
fileRead
(String -> String)
Reads entire file to a
string.
fileReadPart
(String * Int * Int -> String)
Reads a part of
the file, from given 0-based position and with given length in bytes:
fileReadPart( fname, pos, bytelen )
fileSize
(String -> Int)
Returns the file size.
The fileRead(filepath)
function reads the file and
returns its content as a string. The entire file is read, regardless of
its size. This approach should not be used for very large files due to
the high memory consumption.
For very large files, the function
fileReadPart(filepath,pos,bytelen)
should be used. It reads
a part of the file that starts at the given position pos
(in bytes) and has the given length bytelen
.
The function fileSize(filepath)
returns the size of the
specified file.
fileRead( 'src/txt/en/title.md' )
# Wafl Tutorial
*Wafl* is a strongly typed functional programming language, with implicit type inference. It was originally designed as an experimental application of FP languages in web development. A few years later, however, Wafl became a simple and very fast scripting language, with a simple and efficient interface to databases.
The Wafl Tutorial introduces the language constructions, the use of the command line interpreter, elements of the core library and many examples.
Welcome to *Wafl*!
*Saša Malkov*
fileReadPart( 'src/txt/en/title.md', 100, 100 )
inference. It was originally designed as an experimental application of FP languages in web developm
fileSize( 'src/txt/en/title.md' )
530
Function / Type and Description
fileWrite
(String * String -> String)
Writes string to a
file and truncates old content:
fileWrite( fname, content )
fileWriteAppend
(String * String -> String)
Writes string to a
file by appending to the file end.
fileWriteAppend( fname, content )
fileWriteTo
(String * String -> String)
Writes string to a
file and truncates old content:
fileWriteTo( content, fname )
fileTruncate
(String -> Bool)
Truncates a file. Returns false
if truncate fails or file does not exist.
The function fileWrite(filename,s)
writes the given
character string s
to the file and discards the old file
contents, if present. The function fileWriteTo(s,filename)
does the same, only with the order of the arguments changed. It is good
to have both functions because of the dot-syntax.
If an empty string s
is specified, the file is
truncated. If there is no such file, a new empty file is created.
The functions fileWriteAppend
and
fileWriteAppendTo
have the same arguments, but append the
given string s
to the end of the file and do not discard
the previous file contents.
Each of the functions returns the string s
as a
result.
The function fileTruncate
truncates the file. If the
file does not exist, a new empty file is created. This is exactly the
same behavior as when writing an empty string with
fileWrite
or fileWriteTo
.
{#fileWrite( "tmp.txt", "One sentence. " ),
fileWrite( "tmp.txt", "Another sentence. " )
#}
{# 'One sentence. ', 'Another sentence. ' #}
fileRead( 'tmp.txt' )
Another sentence.
{#fileWrite( "tmp.txt", "One sentence. " ),
fileWriteAppend( "tmp.txt", "Another sentence. " )
#}
{# 'One sentence. ', 'Another sentence. ' #}
fileRead( 'tmp.txt' )
One sentence. Another sentence.
Function / Type and Description
fileExists
(String -> Bool)
Checks whether a file with the
given name exists.
fileOrDirExists
(String -> Bool)
Checks whether a file or a
directory with the given name exists.
fileDelete
(String -> Bool)
Deletes a file.
fileNewTempName
( -> String)
Generates a new temporary file
name.
The functions fileExists
and
fileOrDirExists
check and return whether a file or
directory with the specified name exists.
The function fileExists
checks the files and if it
returns true
, then there is a file with the given name. It
can be used to check before reading or deleting a file.
{#fileExists( 'tmp.txt' ),
fileExists( 'tmp2.txt' ),
fileDelete( 'tmp.txt' ),
fileExists( 'tmp.txt' )
#}
{# true, false, true, false #}
The function fileOrDirExists
checks both the files and
directories and returns true
if there is a file or a
directory with the given name. It can be used to check before creating a
file or a directory.
The function fileNewTempName()
returns a possible name
for a new temporary file. The file name is located in the directory for
temporary files, which depends on the respective platform. When the
program is terminated, all files with the names returned by this
function are deleted.
Function / Type and Description
dirCreate
(String -> Bool)
Creates a directory.
dirDelete
(String -> Bool)
Deletes a directory, if it is
empty
dirExists
(String -> Bool)
Checks whether a directory with
the given name exists.
dirFiles
(String -> List[String])
Returns the list of
files that fulfill the given filter.
dirSubdirs
(String -> List[String])
Returns the list of
subdirs that fulfill the given filter.
fileOrDirExists
(String -> Bool)
Checks whether a file or a
directory with the given name exists.
The functions dirFiles(filter)
and
dirSubdirs(filter)
return lists of files and directories
that match the given filter. The paths returned are relative to the
current working directory.
"Directories:\n "
+ dirSubdirs( '*' ).strJoin('\n ')
+ "\nFiles:\n "
+ dirFiles( '*' ).strJoin('\n ')
Directories:
.vscode
bld
code
design
lib
resources
src
Files:
bldall.cmd
bldsite.cmd
pandocHtml.cmd
pandocPdf.cmd
prepmd.cmd
run_local_http_server.cmd
site.cmd
test.wafl
todo.notes.txt
Tutorial - Site Version.lnk
Tutorial - Standalone Version.lnk
WaflTutorial.md
The function dirExists
is similar to
fileExists
and fileOrDirExists
, but it only
checks whether a directory with the specified name exists. It can be
used before creating, deleting or traversing a directory.
The regex support in Wafl uses the ECMAScript syntax for regular expressions, as defined and implemented in the C++ standard library from C++11.
By default, all quantifiers are greedy (they match as many characters
as possible). This can be overridden to ungreedy (match as few
characters as possible) by adding a question mark ?
after
the quantifier.
Function / Type and Description
regexMatch
(String * String -> Bool)
Checks whether a
string matches given regular expression.
regexMatchI
(String * String -> Bool)
Checks whether a
string matches given regular expression. Ignores upper and lower
case.
The regex matching functions regexMatch(s,r)
and
regexMatchI(s,r)
check whether the given string
s
matches the given regular expression r
. The
match is performed for the entire string s
. The only
difference is that regexMatch(s,r)
ignores upper and lower
case.
{#regexMatch("ab.*AB"),
s.regexMatch(".*ab.*AB.*"),
s.regexMatch(".*ab.*cd.*"),
s.regexMatch(".*AB.*ab.*"),
s.regexMatchI(".*AB.*ab.*")
s.
#}where {
s = "abcabcABCABC";
}
{# false, true, false, false, true #}
Function / Type and Description
regexPos
(String * String -> Int)
Finds the first regex
matching position in the given string.
regexPosI
(String * String -> Int)
Finds the first regex
matching position in the given string. Ignores upper and lower case.
regexPosAll
(String * String -> List[Int])
Finds all regex
matching positions in the given string.
regexPosAllI
(String * String -> List[Int])
Finds all regex
matching positions in the given string. Ignores upper and lower
case.
The regex functions regexPos(s,r)
and
regexPosI(s,r)
search for the first position of a substring
of the given string s
that matches the given regular
expression r
.
{#regexPos("ab.*AB"),
s.regexPos("ab.*cd"),
s.regexPos("AB.*ab"),
s.regexPosI("AB.*ab")
s.
#}where {
s = "abcabcABCABC";
}
{# 0, -1, -1, 0 #}
The regex functions regexPosAll(s,r)
and
regexPosAllI(s,r)
search for the starting positions of all
substrings of the given string s
that match the given
regular expression r
.
{#regexPosAll("ab.*AB"),
s.regexPosAll("ab.*cd"),
s.regexPosAll("AB.*ab"),
s.regexPosAllI("AB.*ab"),
s.regexPosAllI("AB.*?ab"),
s.regexPosAllI("a.*c"),
s.regexPosAllI("a.*?c")
s.
#}where {
s = "abcabcABCABC";
}
{# [0], [], [], [0], [0, 6], [0], [0, 3, 6, 9] #}
Function / Type and Description
regexReplace
(String * String * String -> String)
Replaces
all matching of a regex with the given string.
regexReplaceI
(String * String * String -> String)
Replaces
all matching of a regex with the given string. Ignores upper and lower
case.
The regex functions regexReplace(s,r,x)
and
regexReplaceI(s,r,x)
search for the positions of all
substrings that match the given regular expression r
, and
replace them with the given string x
.
{#regexReplace("ab.*AB", "#"),
s.regexReplace("ab.*?AB", "#"),
s.regexReplace("ab.*cd", "#"),
s.regexReplace("AB.*ab", "#"),
s.regexReplaceI("AB.*ab", "#"),
s.regexReplaceI("AB.*?ab", "#"),
s.regexReplaceI("a.*c", "#"),
s.regexReplaceI("a.*?c", "#")
s.
#}where {
s = "abcabcABCABC";
}
{# '#C', '#CABC', 'abcabcABCABC', 'abcabcABCABC', '#C', '#c#C', '#', '####' #}
If it is necessary to preserve a part of the found substring, you can
use the capture groups. A capture group is a part of the regular
expression enclosed in parentheses, except when the first character in
the parentheses is a question mark. In the replacement string
x
, you can use $n
to copy a capture group,
where n
is an integer 1-based index of the corresponding
capture group. In the following example, the capture groups are used to
insert a dash -
between two lower case letters:
{#regexReplace("([a-z])([a-z])", "$1-$2"),
s.regexReplace("([a-z])([a-z])", "$1-$2")
s.regexReplace("([a-z])([a-z])", "$1-$2")
.
#}where {
s = "abcabcABCABC";
}
{# 'a-bc-ab-cABCABC', 'a-b-c-a-b-cABCABC' #}
Function / Type and Description
regexSearch
(String * String -> List[String])
Searches for
the regex matching in the given string.
regexSearchI
(String * String -> List[String])
Searches for
the regex matching in the given string. Ignores upper and lower
case.
regexSearchAll
(String * String -> List[List[String]])
Searches
for all regex matchings in the given string.
regexSearchAllI
(String * String -> List[List[String]])
Searches
for all regex matchings in the given string. Ignores upper and lower
case.
The regex search functions regexSearch(s,r)
and
regexSearchI(s,r)
search for the first substring of the
given string s
that matches the given regular expression
r
and return all matched sub-elements. The returned value
is a list of strings. The first element of the list is the complete
matched substring, and the following elements correspond to the
sub-patterns (capture groups) of the regular expression.
The following example searches for the pattern
<name>=<n>
:
{#regexSearch("([a-z]+)\\s*=\\s*([0-9]+)")
s.
#}where {
s = "a=12 ;bcd = 24; ef =354";
}
{# ['a=12', 'a', '12'] #}
The first element of the returned list is the first matching
substring. The second element is the corresponding part
name
(the first part of the regular expression enclosed in
the parentheses ([a-z]+)
), and the third element is the
corresponding part n
(the second part of the regular
expression enclosed in the parentheses ([0-9]+)
).
The functions regexSearchAll
and
regexSearchAllI
do the same, but for all matching
substrings. The result is therefore not a single list, but a list of
string lists - one list for each matching substring. The elements of the
lists are the same as in the result of the regexSearch
function:
{#regexSearchAll("([a-z]+)\\s*=\\s*([0-9]+)")
s.
#}where {
s = "a=12 ;bcd = 24; ef =354";
}
{# [['a=12', 'a', '12'], ['bcd = 24', 'bcd', '24'], ['ef =354', 'ef', '354']] #}
The functions for command line support are specific to the command line Wafl interpreter. They are not normally available in other versions. The main purpose is to support the read and write operations of the command line terminal.
The command line program interpreter supports the command line program arguments. There are some functions and special operators for working with command line arguments:
Function / Type and Description
$*
List[String]
Returns the list of command line
arguments.
$#
Int
Returns a number of command line arguments.
$ A
(Int -> String)
Return a command line argument:
$1 = cmdLineArgs()[1]
cmdLineArgs
( -> List[String])
Return a list of command line
arguments.
cmdLineArgsCount
( -> Int)
Return a number of command line
arguments.
The constant operator ‘$*
’ is used to get the list of
all command line arguments. The synonym, in the form of a function, is
the function cmdLineArgs()
. For example, if a program is
started with the following command:
clwafl program.wafl a b c "abcde"
both $*
and cmdLineArgs()
return the
following list:
['program.wafl', 'a', 'b', 'c', 'abcde']
The number of arguments can be computed as the size of the list of
command line arguments (size($*)
) or by using the constant
operator $#
, or its synonym function
cmdLineArgsCount()
.
Command line programs return an integer code to the caller (operating system or an application). If everything is OK, console programs are expected to return zero. In the event of an error, the programs return some program-specific positive values.
The function cmdSetExitCode(s,code)
sets the program
exit code to the specified integer code
and returns
s
.
Function / Type and Description
cmdSetExitCode
('1 * Int -> '1)
Set program exit code to second
argument and return the first one.
if $# > 1
then $1
else "No argument" .cmdSetExitCode(1)
No argument
The output to the command line terminal is provided by the echo functions.
Function / Type and Description
echo
('1 -> '1)
Write the argument’s string
representation to the console and return the argument:
echo(x) == echoTxt( x, x.asString() )
echoLn
('1 -> '1)
Write the argument’s string
representation and new line to the console and return the argument:
echoLn(x) == echoTxt( x, x.asString() + '\n' )
echoTxt
('1 * String -> '1)
Write the second argument to
the console and return the first argument.
echoFn
('1 * ('1 -> String) -> '1)
Apply the
function to the 1st arg. and write the result to the console:
echoFn( x, fn ) == echoTxt( x, fn(x) )
Each of the echo functions outputs something to the standard console output and returns its first argument.
The function echo(x)
outputs the given value
x
converted to a string and returns the original value
x
. The function echoLn(x)
does the same, but
outputs an additional new line symbol at the end.
"This is a string"
echo()
.echoLn() .
This is a stringThis is a string
This is a string
The functions echoTxt
and echoFn
write some
other strings to the command line output, but return the first argument
in the same way as echo
and echoLn
. The
function echoTxt(x,s)
writes s
and returns
x
. The function echoFn(x,fn)
writes
fn(x)
and returns x
:
"This is a string"
echoTxt("Output from echoTxt\n")
.echoFn(\x: 'Output "' + x + '" from echoFn\n' ) .
Output from echoTxt
Output "This is a string" from echoFn
This is a string
Function / Type and Description
input
(String -> String)
Write a string to the
console, wait for input and return it.
The function input(s)
writes the given string
s
to the command line output, waits for the user to enter a
line into the console, and returns the entered string.
"Hello "
+ input( "What is your name? " )
+ "!"
What is your name? Marvin Hello Marvin!
Function / Type and Description
cmdExecute
(String -> String)
Execute the command in the
active console and return the result.
cmdShellExecute
(String -> String)
Open new shell, execute the
command in it and return the result.
cmdLastError
( -> Int)
Get integer code of last command
status.
The functions cmdExecute(cmd)
and
cmdShellExecute(cmd)
execute the given shell command string
and return the results. They return the entire command output as a
result. Both functions support the console input. The difference between
the two functions is that cmdShellExecute
sends the entire
output to the console output, while cmdExecute
outputs just
a single lines one over another.
The function cmdLastError()
returns the exit code of the
last shell command executed. As usual, if everything is OK, it returns
zero.
The HTTP and web functions of the Wafl library are divided in two parts:
Function / Type and Description
httpGetSize
(String -> Int)
Get WWW content length using
HTTP/HTTPS HEADER method.[Deprecated. Use ‘Curl’ library.]
httpGet
(String -> String)
Get WWW content using
HTTP/HTTPS GET method.[Deprecated. Use ‘Curl’ library.]
httpGet_callback
(String * (Int * Int -> Int) -> String)
Get
WWW content using HTTP/HTTPS GET method, with progress
callback[Deprecated. Use ‘Curl’ library.]
The httpGet(uri)
function sends a GET
HTTP
request with a given uri
, waits for a response and returns
the content of the response. It can be used for both HTTP and HTTPS
requests.
httpGet( 'http://www.informatics.rs/~smalkov/wafl/index.html' )
size() .
0
The function httpGetSize(uri)
sends a
HEADER
HTTP request with a given uri
, waits
for a response and returns the reported size of the response content. It
can be used for both HTTP and HTTPS requests. If a server does not
respond to the HEADER request, the function returns -1. If a server
responds to the request but returns an invalid header or a header
without content size information, the function returns -2.
httpGetSize( 'http://www.informatics.rs/~smalkov/wafl/index.html' )
-1
The httpGet_callback(uri,fn)
function sends a
GET
HTTP request with a given uri
, uses the
fn
function of type (Int * Int -> Int)
to
process intermediate callbacks, waits for a response and returns the
reported response content. The result value should be the same as in the
case of httpGet
. The function
fn(received,expected)
can perform some intermediate
processing, such as progress reporting. The argument
received
is the total number of bytes received, and the
argument expected
is the total number of bytes expected.
The expected
should match the result of the
httpGetSize(uri)
and can be a negative value if the server
does not deliver the expected size.
httpGet_callback(
'http://www.informatics.rs/~smalkov/wafl/index.html',
\r,t: t.echoTxt( r.asString() + ' / ' + t.asString() + '\n')
)size() .
0
httpGet_callback(
'http://poincare.matf.bg.ac.rs/~smalkov/images/h2.png',
\r,t: t.echoTxt( r.asString() + ' / ' + t.asString() + '\n')
)size() .
1161 / 7024
7024 / 7024
7024
Function / Type and Description
Form
( -> Map[String][String])
Get form variable
set.
FormValue
(String -> String)
Get value of a given form
variable.
Session
( -> Map[String][String])
Get session variable
set.
SessionValue
(String -> String)
Get value of a given session
variable.
Service
( -> Map[String][String])
Get service variable
set.
ServiceValue
(String -> String)
Get value of a given service
variable.
The functions Form()
, Session()
and
Service()
return the corresponding web variable sets - the
current form content received from a client, the current set of session
variables and the current service configuration. The functions
FormValue(varname)
, SessionValue(varname)
and
ServiceValue(varname)
return the value of a variable with
the given variable name.
Function / Type and Description
httpHost
( -> String)
Get HTTP host for current
request.
httpPathInfo
( -> String)
Get HTTP path info for current
request.
httpScript
( -> String)
Get HTTP script for current
request.
The functions httpHost()
, httpPathInfo()
and httpScript()
return the corresponding parts of the
currently processed URI.
Function / Type and Description
ask
(String -> Map[String][String])
Ask a question
by sending the given page content to the client. Returns the next
client’s request.
answerAction
( -> String)
Automatic action URI generator for
forms in questions.
The ask(pageContent)
function sends the given page
content to the client and waits for the returned answer. All links and
form actions on the page that begin with a relative link generated by
answerAction()
, represent the alternative answers
for the given page and return to this specific function. The
answerAction()
function returns a result that identifies
the specific current program evaluation so that the program can continue
after an appropriate answer is received from the client.
Wafl has two functions for exporting Wafl values to JSON.
Function / Type and Description
toJSON
('1 -> String)
Converts a value to a string in
JSON format.
toFmtJSON
('1 -> String)
Converts a value to a string in
pretty JSON format.
Both functions can be applied to any Wafl value type. The function
toJSON(x)
converts the value x
into the JSON
format in the as compact a form as possible. This is useful if the
exported string only needs to be processed by machine.
The function toFmtJSON(x)
does essentially the same
thing, but exports the value in a better formatted JSON format. This is
better if the exported string needs to be processed by humans.
1..5).map(\n:{
(: n,
n: 's'+n$,
s: {# n, 's'+n$ #}
t
})toJSON() .
[{'n':1,'s':'s1','t':[1,'s1']},{'n':2,'s':'s2','t':[2,'s2']},{'n':3,'s':'s3','t':[3,'s3']},{'n':4,'s':'s4','t':[4,'s4']},{'n':5,'s':'s5','t':[5,'s5']}]
1..5).map(\n:{
(: n,
n: 's'+n$,
s: {# n, 's'+n$ #}
t
})toFmtJSON() .
[ {
'n': 1,
's': 's1',
't': [
1,
's1'
]
}, {
'n': 2,
's': 's2',
't': [
2,
's2'
]
}, {
'n': 3,
's': 's3',
't': [
3,
's3'
]
}, {
'n': 4,
's': 's4',
't': [
4,
's4'
]
}, {
'n': 5,
's': 's5',
't': [
5,
's5'
]
} ]
Function / Type and Description
cmdWafl
(String * List[String] -> String)
Run Wafl
program file in a command line shell with given arguments and return the
result. All arguments are passed to Wafl program. No arguments apply to
interpreter. The output is redirected to the caller’s output stream.
cmdWaflSrc
(String * List[String] -> String)
Run Wafl
program source in a command line shell with given arguments and return
the result. All arguments are passed to Wafl program. No arguments apply
to interpreter. The output is redirected to the caller’s output
stream.
cmdWaflX
(String * RecordX[]['1] -> String)
Run Wafl
program file in a command line shell with given options and return the
result. Options are specified as a record fields:
cmdLineArgs - the list of command line arguments to pass
to the program (List[String]);
coutFile - the full filename of the output redirection file;
if '*null*' is specified, output is discarded;
if empty string is specified (default),
the caller's output stream is used .
These functions carry out the evaluation of Wafl programs. The
function cmdWafl(prgpath,args)
evaluates a Wafl program in
the specified file. The function cmdWaflSrc(src,args)
evaluates the Wafl program written in the string. Both functions send
the specified arguments as command line arguments to the evaluated
program and return the program output and the result as a return
value.
cmdWaflSrc('2+3',[])
5
"A controlled error:\n"
+ cmdWaflSrc( 'an error',[])
A controlled error:
--- Loading from command line
Parser error
Unexpected text at the end of the file! [err 1201:6]
Line 1: an error
___/
--- End loading from command line
Each Wafl program evaluation is performed in a separate evaluation environment, but in the same process. This means that most errors do not propagate to the parent program (the one in which one of these functions is used), but if the memory usage becomes too high or the process fails, this can have an impact on the parent program.
It is even possible that such an evaluated program evaluates another Wafl program:
cmdWaflSrc( 'cmdWaflSrc("2+3",[])',[])
5