CodeWorker must be seen as a script interpreter that is intended to parse and
to generate any kind of text or source code. This interpreter admits some options
on the command line. Some of them look like those of a compiler.
CodeWorker doesn't provide any Graphical User Interface, but a console
mode allows interactivity with the user.
1 Command line of the interpreter
The leader script is the name given to the script that is executed first by the interpreter.
It exists six ways to pass this leader script to the interpreter via the command line:
the script describes all the processing tasks for parsing text, decorating the graph and
generating code ; the option of the command line is -script to execute the script,
the script describes an extended BNF grammar ; the option of the command line is
-parseBNF for executing the script and parsing the source file,
the script describes how to generate code ; the option of the command line is
-generate to execute the script and to generate the output file,
the script describes how to expand a file ; the option of the command line is
-expand to execute the script and to expand the output file into its markups,
a file contains embedded scripts driving their own expansion ; the option of the command line is
-autoexpand to execute embedded scripts located below each markups, expanding the output file on markups,
the script describes a source-to-source translation ; the option of the
command line is -translate to execute the script and to translate the source
file to the output file,
To find easier a file to open for reading among some directories, the option -I
specifies a path to explore. It gives more flexibility in sharing input files (both scripts
and user files, excepting generated or expanded files) between directories, and it avoids
relative or absolute paths into scripts.
It is possible to define some properties on the command line, thanks to option -define
(or -D). These properties are intended to be exploited into scripts.
It is recommended to specify a kind of working directory with option -path.
The assigned value is accessible into scripts via the function getWorkingPath(). This
working directory generally indicates the output path for copying or generating
files. The developer of scripts decides how to use it.
CodeWorker interprets scripts efficiently for speed. However, it is more convenient to run
a standalone executable, instead of the interpreter and some script files. Moreover, once
scripts are stable, why not to compile them as an executable to run the project a few times
faster? Option -c++ allows translating the leader script and all its dependencies to
C++ source codes, ready-to-compile.
To facilitate the tracking of errors, an integrated debugger is called thanks to the
option -debug. It runs into the console, and some classical commands allow taking
the control of the execution and exploring the stack and the variables.
Here are presented all switches that are allowed on the command line:
Switch
Description
-args[arg]*
Pass some arguments to the command line.
The list of arguments stops at the end of the command line or as soon as an option
is encountered. The arguments are stored in a global array variable called _ARGS.
-autoexpandfile-to-expand
The file file-to-expand is explored
for expanding code at markups, executing a template-based script inserted
just below each markup.
It is identical to execute the script function autoexpand(file-to-expand, project).
-c++generated-project-path CodeWorker-path
To translate the leader script and all its
dependencies in C++ source code, once the execution of the leader script has
achieved (same job as compileToCpp()compileToCpp()). The CodeWorker-path
is optional and gives the path to includes and libraries of the software.
-c++externalfilename
To generate C++ source code for implementing
all functions declared as external into scripts.
-commentBeginformat
To specify the format of a beginning of comment.
-commentEndformat
To specify the format of a comment's end.
-compilescriptFile
To compile a script file, just
to check whether the syntax is correct.
-commandscommandFile
To load all arguments processed ordinary on the command-line.
It must be the only switch or else passed on the command-line.
-console
To open a console session (default mode if no script
to interpret is specified via -script or -compile or
-generate or -expand.
-debug
To debug a script in a console while executing it.
-define VAR=value
or -D ...
To define some variables, as
when using the C++ preprocessor or when passing properties to the JAVA compiler.
These variables are similar to properties, insofar as they aren't exploited during
the preprocessing of scripts to interpret. This option conforms to the format
-define VAR when no value has to be assigned ; in that case,
"true" is assigned by default to variable VAR. The script
function getProperty("VAR") gives
the value of variable VAR.
-expandpattern-script file-to-expand
Script
file pattern-script is executed to expand file file-to-expand
into markers.
It is identical to execute script function expand(pattern-script, project, file-to-expand).
-fast
To optimize speed. While processing generation, the output
file is built into memory, instead of into a temporary file.
Switch
Description
-generatepattern-script file-to-generate
Script
file pattern-script is executed to generate file file-to-generate.
It is identical to execute script function generate(pattern-script, project, file-to-generate).
-genheadertext
Adds a header at the beginning of all generated
files, followed by a text (see procedure setGenerationHeader()setGenerationHeader()).
-help or ?
Help about the command line.
-Ipath
Specify a path to explore when trying
to find a file while invoking include or parseFree
or parseAsBNF or generate or expand or ... This option may be
repeated to specify more than one path.
-insertvariable_expression value
Creates a new node in
the main parse tree project and assigns a constant value to it. It is identical
to execute the statement insertvariable_expression= "value" ;.
-nologo
The interpreter doesn't write the copyright in the shell at the beginning.
-parseBNFBNF-parsing-script source-file
The script
file BNF-parsing-script parses source-file from an extended BNF grammar.
It is identical to execute the script function parseAsBNF(BNF-parsing-script, project, source-file).
-pathpath
Output directory, returned by the script
function getWorkingPath(), and used ordinary to specify where
to generate or copy a file.
-quantify [outputFile]?
To execute scripts into quantify mode that consists of
measuring the coverage and the time consuming. Results are saved to HTML file outputFile or
displayed to the console if not present.
-scriptscript-file
Defines the leader script, which will be
executed first.
-stackdepth
To limit the recursive call of functions, for avoiding an
overflow stack memory. By default, the depth is set to 1000.
-stdinfilename
To change the standard input for reading from an
existing file. It may be useful for running a scenario.
-time
To display the execution time expressed in milliseconds, just
before exiting.
Script
file translation-script processes a source-to-source translation.
It is identical to execute the script function translate(translation-script, project, source-file, file-to-generate).
-varexist
To trigger a warning when the value of a variable that doesn't
exist is required into a script.
-versionversion-name
To force interpreted scripts as written in a
precedent version given by version-name.
Note that the interpreter proposes a convenient way for running a common script with arguments:
A console mode is launched when the command line is empty. The console only accepts
scripts written in the common syntax, with common functions and procedures. So, parsing
and generation scripts aren't typed directly on the console.
2 Syntax generalities and statements
A script in CodeWorker consists of a series of statements that are organized into
blocks (also known as compound statements). A statement is an instruction
the interpreter has to execute.
A single statement must close with a semicolon (';'). A compound statement is defined
by enclosing instructions between braces ('{}'). A block can be used
everywhere you can use a single statement and must never end with a semicolon after the
trailing brace.
Comments are indicated either by surrounding the text with '/*' and '*/'
or by preceding the rest of the line to ignore with a double slash ('//').
It exists three families of scripts here. To facilitate their syntax highlighting in editors,
or to indicate briefly the type of the script, we suggest to employ some file extensions,
depending on the nature of the script. The next table exposes the different extensions used
commonly in CodeWorker.
Extension
Description
".cwt"
a template-based script, for text generation
".cwp"
a extended-BNF parse script, for parsing text
".cws"
a common script, none of the precedent
The structure of the grammar is so rich that it is a challenge to find an editor, which
offers a syntax highlighting engine powerful enough. JEdit proposes the writing of
production rules to describe it, so it is possible to express the syntax highlighting of
the scripting language.
You'll find a package dedicated to JEdit on the Web site, for the inclusion of these new
highlighting modes. Many thanks to Patrick Brannan for this contribution.
2.1 preprocessor directives
A preprocessor directive always starts with a '#' symbol and is followed by the name
of the directive.
2.1.1 Including a file
The #includefilename directive tells the preprocessor to replace
the directive at the point where it appears by the contents of the file specified by the
constant string filename. The preprocessor looks for the file in the current
directory and then searches along the path specified by the -I option on the command
line.
2.1.2 Extending the language via a package
A package is an extension of the scripting language that allows adding new
functions in CodeWorker at runtime. A package is implemented as an executable module,
which exports all new functions the developer wants to make available in the interpreter.
Loading of a package
The preprocessor directive #use tells the interpreter that it must extend itself
with the functions exposed by a package.
The syntax is:
#usepackage-name
Loading a package more than once has no effect.
The name of the package must prefix the name of the function, when calling it:
package-name::my-function(parameters...)Example:
#use PGSQL PGSQL::connect("-U pilot -d emergencyDB");
local sRequest = "SELECT solution FROM average_adjustment WHERE damage = 'broken wing'";
local listOfSolutions; PGSQL::selectList(sRequest, listOfSolutions);
if listOfSolutions.empty()
traceLine("No solution. Suggestion: parachute jump?");
else {
traceLine("Solutions:");
foreach i in listOfSolutions
traceLine(" -" + i);
} PGSQL::disconnect(); // if the plane hasn't crashed yet
The PGSQL package serves here for connecting to and querying a PostGreSQL database.
For this example, the package exports three functions: PGSQL::connect,
PGSQL::selectList and PGSQL::disconnect.
The executable moduleCodeWorker expects a dynamic library, whose name is deduced from the package name and from
the platform the interpreter is running to.
The short name of the dynamic library concatenates "cw" at the end of the
package name. The extension of the dynamic library must be ".dll" under
Microsoft Windows, and ".so" under Linux.
You must put the dynamic library at a place where CodeWorker will find it at runtime. Microsoft Windows proceeds in the following order to locate the library:
The directory where the executable module for the current process is located.
The current directory.
The Windows system directory (not recommended - it concerns CodeWorker only).
The Windows directory (not recommended - same reason).
The directories listed in the PATH environment variable.
Under Unix, a relative path for the shared object refers to the current directory
(according to the man description of dlopen(3C)).
So, when CodeWorker reads #use PGSQL, it searches a dynamic library
called "PGSQLcw.dll" under Windows or "PGSQLcw.so"
under Linux.
Building a package
This section is intended to those that want to build their own packages, for binding to
a database or to a graphical library ... or just for gluing with their own libraries.
When the interpreter find the preprocessor directive #usepackage-name
in a script, it loads the executable module and executes the exported C-like function
CW4DL_EXPORT_SYMBOL void package-name_Init(CW4dl::Interpreter*).
The preprocessor definition CW4DL_EXPORT_SYMBOL and the namespace CW4dl are
both declared in the C++ header file "CW4dl.h". This header file is located in
the "include" directory if you downloaded binaries, and at the root of the project
if you downloaded sources.
The C-like function 'package-name_Init()' MUST be present!
C-like means that it is declared extern "C" (done by CW4DL_EXPORT_SYMBOL).
Initializing the module that way is useful for registering new functions in the engine,
via the function createCommand() of the interpreter (see the header file "CW4dl.h"
in the declaration of the class Interpreter for learning more about it).
Every function to export must start its declaration with the preprocessor definition
CW4DL_EXPORT_SYMBOL (means 'extern "C"', but a little more under Windows).
Up to 4 parameters, the signature of such a function looks like:
where selectList is a function expecting 2 parameters.
The initializer PGSQL_Init() in our example informs the engine about the existence
of this function selectList in the package:
which means that selectList expects a string followed by a tree.
In the body of the function 'selectList(...)', the C++ binding is obtained easily
by a cast of CW4dl::Parameter:
(const char*) p1 for the value parameter p1,
(CW4dl::Tree*) p2 for the node parameter p2,
if a function contains strictly more than 4 parameter, its signature changes
and requires a variable number of parameters:
CW4DL_EXPORT_SYMBOL const char*
myFunction(CW4dl::Interpreter*, int nbParams, CW4dl::Parameter* tParams);
where tParams is an array of parameter types, and where 'nbParams' gives
the size.
The initializer PGSQL_Init() informs the engine about the existence
of this function in the package differently too:
createCommand("myFunction", 6, tParams);
which means that myFunction has 6 parameters whose types are provided
in tParams.
Note that every function returns const char*.
The interpreter CW4dl::Interpreter represents the runtime context of CodeWorker.
It is the unavoidable intermediary between the module you are building and CodeWorker.
Use it for:
registering new functions into the CodeWorker's engine,
throwing an error,
handling parse trees,
The #line directive forces to another number the line counter of the script file
being parsed. The line just after the directive is supposed to be worth the number specified after
#line.
2.1.3 Changing the syntax of the scripting language
The #syntax directive tells the preprocessor not to parse the following
instructions as classical statements of the scripting language, but as conforming to another
syntax. It allows adapting the syntax to what you are programming:
If you are programming a kind of makefile logic, where you have to check
whether a file has been changed before another or not (using the function
fileLastModification()fileLastModification() for example), it is clear
that you would prefer to implement it in a makefile-like syntax rather than
in the scripting language's syntax,
If you are programming a kind of shell logic, where you have to copy files
and directories, or re/move them, you would prefer to implement it in a shell-like
syntax rather than in the scripting language's syntax. For instance:
The directive admits the following writing: "#syntax" [parsing-mode [':'BNF-script-file]? | BNF-script-file]
How does it work? The piece of source code, which doesn't conform to the syntax of the script
language, is put between the directives #syntax ... and #end syntax. If the
trailing directive isn't found, the remaining of the script is considered as written in a
foreign syntax. Be careful that the trailing directive must start at the beginning of the line
necessary to be recognized and that no spaces are allowed between # and end.
At runtime, the famous piece of source code is parsed and processed via the BNF script file.
Note that it is possible to attach an identifier (called parsing-mode above) to a
script file, and to specify later, in any other script, the parsing mode only;
CodeWorker will find the corresponding BNF script file. It avoids to handle a physical name
of the BNF parsing file, where a logical name of parsing mode is more convenient.
Example:
// the first time, a parsing mode may be attached to the BNF script file
#syntax shell:"TinyShell.cwp"
...
#end syntax
// at the second call, it isn't recommended to use the path of the parsing file
// it is better to use the parsing mode registered previously
#syntax shell
...
#end syntax
// here, I know that I'll call it once only, so I don't care about a parsing mode
#syntax "MakeFile.cwp"
...
#end syntax
where the parsing script "TinyShell.cwp" might be worth:
Of course, the parsing and the processing are implemented in the scripting language, so changing
the syntax will be slower than keeping the default one. However, it allows writing a code
easy to support and to understand.
2.1.4 Managing changes in a multi-language generation
The directives #reference and #attach serve to be notified when a change has
been made into a script for generating in a given language, but not taken back in another
language. For example, you are writing a framework both in C++ and JAVA. You are adding some
new features in C++ or correcting some mistakes. One day, you'll be care not to forget to
update the JAVA generation. In fact, thanks to these directives, a warning will be produced
up to changes will have been put in the other script.
How does it work? Directives must delimit the piece of script you have changed: "#reference"key
... "#end"key
The key is an identifier that allows putting more than one reference area into a
script file. A #reference area might cover one or more #reference
directives, without confusing about boundaries. The directive must be put at the beginning of
the line.
Here are the directives delimiting the piece of script that should be updated later in
another file: "#attach"reference-file':'reference-key
... "#end"reference-key
A #attach area might cover one or more #reference or #attach
directives, as a #reference area. The directive must be put at the beginning of the
line.
The first time CodeWorker will encounter the reference script file, it will compute
a number that depends on the content of the area. The first time CodeWorker will encounter an
attached script file, it will get back the magic number of the reference
area, found both by the file name and the key of the reference. And then, at the beginning,
the reference and attached areas are considered as similar. CodeWorker stores
the magic number of the reference just behind the #attach directive: "#attach"reference-file':'reference-key','reference-number
In fact, a script file that must be updated, so as to store the magic numbers for some attached
areas, takes into account the modifications at the end of the parsing, and only if no error
was encountered. If the writefileHook() function (see writefileHook) is implemented,
it is called and the script file doesn't change if it returns false. If the script file
is read-only, the corresponding readonlyHook() function is called (see readonlyHook).
If it isn't possible to save the script file, an error is thrown.
When a change occurs in the reference area, the next time CodeWorker will encounter
it, the magic number will be recomputed. When an attached piece of script is encountered after
the change, the old magic number of the reference is compared to the new one. If they aren't
the same, a warning is displayed to notify that the attached area hasn't been updated yet.
Once the changes have been taken back into the attached area, the magic number of the reference
must be cut (don't forget the comma too!). And so, the next time this attached area will be
encountered by the interpreter, it will get back the magic number of the reference
area. And then, the reference area and the attached area are considered as
similar once again.
Of course, the use of these directives is quite constraining. However, it is the only way in
CodeWorker to assure that features and corrections have been taken back in all generated
languages.
2.2 Constant literals
CodeWorker handles all basic types as strings, and doesn't distinguish a double from a boolean or a date.
A string literal is a sequence of characters from the source character set enclosed in double quotation marks (" ").
String literals are used to represent a sequence of characters which, taken together, form a null-terminated string.
The interpretation done of the data depends on the context: function
increment(index)
expects that its argument index contains a number, but stored as a string.
Floating-point numbers are represented as they are commonly admitted into programming languages: 3.141592 or 5.5E+6.
Integers are represented without the dot, 64 for instance.
A character literal is represented between single quotes as in C or JAVA; it admits
classical escape characters.
Bytes are represented as a couple of hexadecimal digits. The 4D byte is the
ASCII of the letter N.
About boolean types, an empty string "" means false, and any kind of sequence of characters means true, such as "1" or "raspberries".
Two constant literals are provided: keyword true is worth
"true" and false is an empty string.
dates are written according to a format that looks like 24sep2002, where:
day takes 2 digits
month is represented as the 3 first letters of the corresponding english word ; aug as august and may as may, for instance
year takes 4 digits: 2002 but never 02.
the time representation conforms to the format: HH:MM:SS.millis
A constant tree describes a tree as a list of constant trees and expressions, intended
to be assigned to a variable. Example:
local aVariable = {"a", {"yellow", "red"}, "submarine"};
You'll find more information in the sub section Scope below.
2.3 Variables, declaration and assignment
Variables serve as containers for the data you use into scripts. Data type is a tree that may
be reduced to a leaf node, which contains a value and that's all.
2.3.1 Declaring variables
It isn't necessary to declare a variable before using if for the first time. A variable that
is assigned without being declared is understood as a new sub-node to be added to the current
tree context. The current context is obtained by the read-only variable called this.
It corresponds to the main parse tree whose root name is project when you are into
the leader script, and to the variable passed by parameter when calling a parsing or pattern
script.
A variable that is read without being declared returns an empty string, but doesn't cause the
creation of a sub-node. The danger is that you aren't safe from a spelling mistake. To prevent
it, put the option -varexist on the command line and use the function existVariable()
to check whether a variable exists or not.
2.3.2 Scope
When you declare a local variable, it is valid for use within a specific area of code, called
the scope. When the flow of execution leaves the scope, the content of the variable,
a subtree specially allocated during its declaration, is deleted and disappears forever from
the stack. A scope is delimited by a block.
To declare a variable to the stack, use the following declaration statement: local-variable-statement ::= "local"local-variable-declaration';' local-variable-declaration ::= variable [ '='assignment-expression ]? assignment-expression ::= constant-tree | expressionconstant-tree ::= '{' [assignment-expression [','assignment-expression]* ]? '}'
An extension of the syntax allows the declaration of more than one variable in one shot. A comma separates the
variable declarations: local-variable-statement ::= "local"local-variable-declaration [ ','local-variable-declaration ]* ';'
The local variable points to a new empty tree, pushed into the stack.
If an expression is present after the local declaration, it is evaluated and
the string result is assigned to the new local variable.
If a constant tree is present after the local declaration, it is assigned to
the new local variable. Example:
local aVariable = {"a", {"yellow", "red"}, "submarine"};
where pushItem means that a new item has to be added in the array owned
by aVariable, and where #last means accessing to the last item
of the array.
To assign a reference to another variable, instead of either the result of
evaluating an expression or a constant tree, use rather the following declaration statement: local-ref-statement ::= "localref"local-ref-declaration [ ','local-ref-declaration ]* ';' local-ref-declaration ::= variable'='reference
In the case of a CodeWorker version strictly older than 1.13, local
variables that are declared in the body of a script or in the scope of a function may
be accessed further in the scope of functions during their timelife. So a different behaviour
may occur with a more recent CodeWorker interpreter.
This stack management had historical reasons, but it is now obsolete and often reflects an
implementation's error. To preserve you from this kind of mistake, a warning may be displayed,
so that scripts strictly older than version 1.13 may continue to run. Specify a
version strictly older than 1.13 to the command line (option -version) for
reclaiming that CodeWorker checks and generates a warning.
To correct this kind of mistake in old scripts, the variable should be propagated in an
argument for functions that refer to it.
To declare a global variable, use the global statement. The declaration of a global
variable can be specified anywhere in scripts. The first time the declaration of a global
variable is encountered, the interpreter registers it as accessible from any point into
scripts. The second time the interpreter encounters a global declaration for the variable,
the latter remains global but its content is cleared.
Note that if a local variable or an attribute of the current node (this) is identical
to the name of an existing global variable, the global variable remains hidden while
the flow of control hasn't left the scope that contains the homonym.
the global declaration statement looks like: global-variable-statement ::= "global"global-variable-declaration [ ','global-variable-declaration ]* ';' global-variable-declaration ::= variable [ '='assignment-expression ]?
2.3.3 Navigating along branches
It is possible to navigate along a branch of the subtree put into the variable. A branch
points to a node of the subtree. The syntax looks generally like: branch ::= variable ['.'sub-node]*
If the branch isn't known before runtime, it may be build during the execution.
Example: while parsing an XML file, each time an XML attribute is encountered, one creates the
corresponding attribute into the parse tree. But the name of the attribute is discovered
during the parsing. The directive #evaluateVariable(expression) allows doing it.
expression is evaluated at runtime and provides a branch:
#evaluateVariable("a.b.c")
will resolve the path "a.b.c" at runtime and
navigate from a to textit{c}.
A node may contain an array of nodes, which are indexed by a key that is a constant string.
A branch allows navigating through arrays, and the definitive syntax of branches conforms
to: branch ::= "#evaluateVariable"'('expression')'
::= variable ['.'sub-node | array-access]* array-access ::= '['expression']'
::= '#' ["front" | "back" | "parent"]
::= '#' '['integer-expression']'
We see that there are some ways to access an item node of an array or to change how to
navigate from nodes to nodes:
sub-node'['expression']' means that
we'll access the node item associated to the string key resulting of the
expression's evaluation,
sub-node'#' ["front" means that the first item
node of the array is required. If the array is empty, an error occurs.
sub-node'#' ["back" means that the last item
node of the array is required. If the array is empty, an error occurs.
sub-node'#' ["parent" means that one comes back
up the parent's node of sub-node.
sub-node'#' '[' <integer-expression> ']' means
that we'll access the node item located at the position given by the evaluation
of the expression. The position starts counting to 0. An error is raised if the
position is out of bounds.
2.3.4 Assignments
CodeWorker provides some different ways to put a data into a variable or into the node
pointed to by a branch:
setvariable-branch ['=' | '+='] assignment-expression :
the expression is evaluated and the resulting string value is assigned to the
variable, or concatenated if the operator '+=' was required. Keyword
set may be omitted. The node to assign is supposed existing yet. If not,
the assignment is done, but it causes a warning to prevent a spelling mistake on
the variable's name.
insertvariable-branch [['=' | '+='] assignment-expression]? :
it works like the set assignment, except that it is the preferred mode
to add a new node when the variable doesn't exist yet. If the node already exists,
of course it isn't added twice, and the assignment if done as expected. If no
assignment is specified after the variable's name, nothing is assigned to the node.
So, if the node wasn't existing yet, it contains an empty string. Otherwise,
the ancient value isn't changed.
refvariable'='existing-variable-or-branch :
the variable to assign will refer to an existing node. Inspecting the variable
will cause inspecting the referenced existing node. If the referenced node doesn't
exist, an error occurs. If you apply the reference to a variable that already
refers a node, this link is broken instead of propagating the reference to the
referred node. This operator is very useful during the decoration of the
parse tree, and leads to transform the tree as a freely-oriented graph.
Be careful not to keep a reference to a local variable once the flow of execution
has left its scope: the local variable is deleted, and so, the reference points
to a corrupted part of the memory.
If you intend to assign a reference to a variable into a function and that the
variable is passed by parameter, don't forget to take the reference parameter
mode:
function badFunction(myVar : variable) {
...
// myVar will keep up a reference to aNode
// up to the end of the function:
ref myVar = aNode;
...
// myVar is passed as variable, so the
// reference is cancelled once the function is left!
}
// To keep the reference after leaving the function, change the parameter
// mode to reference:
function goodFunction(myVar : reference) {
...
// myVar will keep up a reference to aNode
// up to the end of the function:
ref myVar = aNode;
...
// myVar is passed as reference, so the
// reference is kept once the function is left!
}
setallvariable-branch'='existing-variable-or-branch :
value, attributes and array of the variable to assign are purged, and the subtree,
to which the existing variable points, is copied integrally to the node to
assign.
mergevariable-branch'='existing-variable-or-branch :
the subtree, to which the existing variable points, is copied integrally to the
node to assign, preserving the attributes and the arrays of the assigned node,
which are updated or completed.
pushItemvariable-branch ['='expression]? :
a new item node is added at the end of the variable's array, whose key is worth
its position, starting at 0. If the expression exists, then after evaluating it,
the result is assigned to the item node as a value. If no array was previously
existing, the item becomes its first component.
The boolean operators & or && resolve the logical AND.
The boolean operators | or || resolve the logical OR.
The comparison operators do the classical resolution, working on the lexicographical
order. Note that different of may be written != or <> and
that the equality is written= or ==. If the comparison succeeds, it
returns "true", otherwise, it returns an empty string. See section
arithmetic expressions to discover an escape mode for writing arithmetic
comparisons.
A special comparison operator in checks whether the left-hand side
member belongs to a set of constant strings or not. Example:
sHTMLTagin { 'i', "kbd" }
returns true if sHTMLTag is worth 'i' or "kbd" and
false in all other cases.
The operator + serves to concatenate two string. Becareful not to use this
operator to ask for an arithmetic addition! See the function add() to do
that or the section arithmetic expressions to discover an escape mode for
writing arithmetic operators.
A constant string must be written between double quotes and escape characters
are expected as in C, starting with a back-slash.
The syntax of a number is the one admitted commonly. The number is then converted
to a string.
Constant true is worth the constant string "true", and false
is worth the constant string "false".
expressions with parentheses are allowed.
The unary ! operator resolves the logical NOT, such as an empty value gives
"true" and any kind of other value gives an empty string.
Function calls are described into the section function.
The escape mode for arithmetic expression is set/unset via the '$' symbols
and allows interpreting arithmetic/comparison operators as usual ('+' as an
addition, '<' as a numerical comparison). See section
arithmetic expressions for more information about this escape mode.
some arithmetic operators enable to handles bits: the unary bitwise operator
~ and the shift operators << (left shifting)
and >> (right shifting).
The preprocessor expressions give information about the source script:
#FILE: returns the file name of the script, or an empty
string if the source script wasn't coming from a file,
#LINE: returns the line number where the directive is
located into the source script,
2.4.2 Arithmetic expressions
The classical syntax of the interpreter forces expressions to work on sequences of characters.
So, comparison operators apply the lexicographical order and the '+' operator concatenates
two strings and the '*' operator doesn't exist.
Of course, it exists some functions to handle strings as number and to execute an arithmetic
operation (the 'add()' or 'mult()' functions for instance) or a comparison (the
'isPositive()' or 'inf()' functions for instance).
However, it appears clearly more convenient to write arithmetic operations and comparisons in a
natural way, using operators instead of the corresponding functions. So, CodeWorker provides
an escape mode that draws its inspiration from LaTeX to express mathematical formulas:
the arithmetic expression are delimited by the symbol '$'.
Example:
local a = 11;
local b = 7;
traceLine("Classical mode = '"
+ inf(add(mult(5, a), 3), sub(mult(a, a), mult(b, b))) + "'");
traceLine("Escape mode = '" + $5*a + 3 < a*a - b*b$ + "'");
Output:
Classical mode = 'true'
Escape mode = 'true'
2.5 Common statements
2.5.1 The 'if' statement
The BNF representation of the while statement is: if-statement ::= "if"expressionthen-statement
["else"else-statement]?
The if statement evaluates the expression following immediately. The expression
must be of arithmetic, text, variable or condition type. In both forms of the if
syntax, if the expression evaluates to a nonempty string, the statement dependent on the
evaluation is executed; otherwise, it is skipped.
In the if...else syntax, the second statement is executed if the result of evaluating
the expression is an empty string. The else clause of an if...else statement is
associated with the closest previous if statement that does not have a corresponding
else statement.
2.5.2 The 'while'/'do' statements
The BNF representation of the while statement is: while_statement ::= "while"expressionstatement
The while statement lets you repeat a statement or compound
statement as long as a specified expression becomes an empty
string. The expression in a while statement is evaluated before
the body of the loop is executed. Therefore, the body of the loop
may be never executed. If expression returns an empty string,
the while statement terminates and control passes to the next
statement in the program. If expression is non-empty, the process
is repeated. The while statement can also terminate when a
break, or return statement is executed within the statement
body. When a continue statement is encountered, the control breaks
the flow and jumps to the evaluation of the expression.
Note that the break and continue statements apply to
the first loop statement (foreach/forfile/select, do/while) they encounter
while leaving instruction blocks.
The BNF representation of the do statement is: do_statement ::= "do"statement"while"expression';'
The do-while statement lets you repeat a statement or compound
statement until a specified expression becomes an empty string. The
expression in a do-while statement is evaluated after the body of the
loop is executed. Therefore, the body of the loop is always executed at
least once. If expression returns an empty string, the do-while
statement terminates and control passes to the next statement in the
program. If expression is non-empty, the process is repeated.
The do-while statement can also terminate when a break, or return
statement is executed within the statement body. When a continue
statement is encountered, control is transferred to the evaluation of
the expression.
2.5.3 The 'switch' statement
The BNF representation of this statement is: switch_statement ::= "switch" '('expression')' '{' (label_declaration)* ("default" ':'statement)? '}' label_declaration ::= ["case" | "start"] constant_string':'statement
The switch statement allows selection among multiple sections of
code, depending on the value of an expression. The expression enclosed
in parentheses, the controlling expression, must be of string type.
The switch statement causes an unconditional jump to, into, or past
the statement that is the switch body, depending on the value of the
controlling expression, the constant string values of the case or start labels,
and the presence or absence of a default label. The switch body is
normally a compound statement (although this is not a syntactic
requirement). Usually, some of the statements in the switch body are
labeled with case labels or with start labels or with the default
label. The default label can appear only once.
The constant-string in the case label is compared for equality with the controlling
expression. The constant-string in the start label is compared for equality with the
first characters of the controlling expression. In a given switch statement, no two
constant strings in start or case statements can evaluate to the same value.
The switch statement behaviour depends on how the controlling
expression matches with labels. If a case label exactly matches with
the controlling expression, control is transferred to the statement
following that label. If failed, start labels are iterated into the
lexicographical order, and the control is transferred to the statement
following the first label that matches with the beginning of the controlling expression.
If failed, control is transferred to the default statement or, if not present,
an error is thrown.
A switch statement can be nested. In such cases, case or start or
default labels associate with the most deeply nested switch
statements that enclose them.
Control is not impeded by case or start or default labels. To
stop execution at the end of a part of the compound statement, insert a
break statement. This transfers control to the statement after the
switch statement.
2.5.4 The 'foreach' statement
The BNF representation of this statement is: foreach_statement ::= "foreach"index"in" [direction]?
[sorted_declaration]? [cascading_declaration]? list-nodebody_statement direction ::= "reverse" sorted_declaration ::= "sorted" ["no_case"]? ["by_value"]? cascading_declaration ::= "cascading" ["first" | "last"]?
A foreach statement iterates all items of the list owned by node list-node.
The index refers to the current item of the list, and the body statement is executed
on it.
Items are iterated either in the order of entrance, or in alphabetical order if
option sorted is set. The sort operates on keys, except if the option by_value is set.
The order is inverted if option reverse was chosen.
To ignore the case, these options must be followed by no_case.
If not, uppercase letters are considered as smaller than any lowercase letter.
// file "Documentation/ForeachSampleSorted.cws": local list; insert list["silverware"] = "tea spoon"; insert list["Mountain"] = "Everest"; insert list["SilverWare"] = "Tea Spoon"; insert list["Boat"] = "Titanic"; insert list["acrobat"] = "Circus"; traceLine("Sorted list in a classical order:"); foreach i in sorted list { traceLine("\t" + key(i)); } traceLine("Note that uppercases are listed before lowercases." + endl()); traceLine("Sorted list where the case is ignored:"); foreach i in sorted no_case list { traceLine("\t" + key(i)); } traceLine("Reverse sorted list:"); foreach i in reverse sorted list { traceLine("\t" + key(i)); } traceLine("Reverse sorted list where the case is ignored:"); foreach i in reverse sorted no_case list { traceLine("\t" + key(i)); }
Output:
Sorted list in a classical order:
Boat
Mountain
SilverWare
acrobat
silverware
Note that uppercases are listed before lowercases.
Sorted list where the case is ignored:
acrobat
Boat
Mountain
SilverWare
silverware
Reverse sorted list:
silverware
acrobat
SilverWare
Mountain
Boat
Reverse sorted list where the case is ignored:
silverware
SilverWare
Mountain
Boat
acrobat
Control may not be sequential into the body statement. break and return enable
exiting definitely the loop, and continue transfers the control to the head of the
foreach statement for the next iteration.
Option cascading allows propagating foreach on item nodes. The way
it works is illustrated by an example:
foreach i in cascading myObjectModeling.packages ...
At the beginning, i points to myObjectModeling.packages#front and
the body is executed. Before iterating i to the next item, the foreach checks whether
the item node myObjectModeling.packages#front owns attribute packages or not.
If yes, it applies recursively foreach on myObjectModeling.packages#front.packages.
Option cascading avoids writing the following code:
function propagateOnPackages(myPackage : variable) {
foreach i in myPackage {
// my code to apply on this package
if existVariable(myPackages.packages)
propagateOnPackages(myPackages.packages);
}
}
propagateOnPackages(myObjectModeling.packages);
Option cascading offers two behaviours:
first means that the item is cascaded before running the body,
Massif
Alps
Himalaya
Rock Mountains
Silverware
Spoon
Fork
Knife
propagates the foreach on the current item after executing the body.
2.5.5 The 'forfile' statement
The BNF representation of this statement is: forfile_statement ::= "forfile"index"in" [sorted_declaration]? [cascading_declaration]? file-patternbody_statement sorted_declaration ::= "sorted" ["no_case"]? cascading_declaration ::= "cascading" ["first" | "last"]?
A forfile statement iterates the name of all files that verify the filter file-pattern.
The index refers to the current item of the list composed of retained file names, and the body statement is executed
on it. Note that the file pattern may begin with a path, which cannot contain jocker characters ('*' and '?').
Like for the foreach statement, items are iterated either in the order of entrance, or in alphabetical order of keys if
option sorted is set. To ignore the case, the option must be followed by no_case.
If not, uppercase letters are considered as smaller than any lowercase letter.
Control may not be sequential into the body statement. break and return enable
exiting definitely the loop, and continue transfers the control to the head of the
forfile statement for the next iteration.
The option cascading allows propagating forfile on directories recursively.
The way it works is illustrated by an example:
// file "Documentation/ForfileSample.cws": forfile i in cascading "*.html" { if isNegative(findString(i, "manual_")) && isNegative(findString(i, "Bugs")) { traceLine(i); } }
At the beginning, i points to the first HTML file of the current directory and
the body is executed. Before iterating i to the next item, the forfile checks whether
the directory of the current file owns subfolders or not. If yes, it applies recursively
forfile on subfolders.
Option cascading offers two behaviours:
first means that the subfolders are visited before running the body,
last is the default behaviour, as seen in previous examples, and
propagates the forfile on the subfolder after executing the body.
2.5.6 The 'select' statement
The BNF representation of this statement is: select_statement ::= "select"index"in" [sorted_declaration]? node-motifbody_statement sorted_declaration ::= "sorted"first-key [, other-key]* first-key ::= branch other-key ::= branch
A select statement iterates a list of nodes that match a motif expression.
The index refers to the current item of the list composed of retained nodes, and the body statement is executed
on it.
i = 01
i = 02
i = 03
i = 11
i = 12
i = 13
i = 21
i = 22
i = 23
Like for the foreach statement, items are iterated either in the order of entrance, or
according to the sorting result if the option sorted is set.
Control may not be sequential into the body statement. break and return enable
exiting definitely the loop, and continue transfers the control to the head of the
select statement for the next iteration.
2.5.7 The 'try'/'catch' statement
The BNF representation of this statement is: try-catch-statement ::= "try"try-statement"catch" '('error_message_variable')'catch-statement
Error handling is implemented by using the try, catch, and error
keyword. With error handling, your program can communicate unexpected events to a higher
execution context that is better able to recover from such abnormal events.
These errors are handled by code that is outside the normal flow of control.
The compound statement after the try clause is the guarded section of code. An error
is thrown (or raised) when command error(message-text) is called or
when CodeWorker encounters an internal error. The compound statement after the catch
clause is the error handler, and catches (handles) the error thrown. The catch clause
statement indicates the name of the variable that must receive the error message.
2.5.8 The 'exit' statement
The BNF representation of this statement is: exit_statement ::= "exit"integer-expression";"
A exit statement leaves the application and returns an error code, given by the integer-expression.
Example:
exit -1;
2.6 User-defined functions
The BNF representation of a user-defined function to implement is: user-function ::= classical-function-definition | template-function-definition classical-function-definition ::= classical-function-prototypecompound-statement classical-function-prototype ::= "function"function-name'('parameters')' template-function-definition ::= see the next section, template function, for more information parameters ::= parameter [','parameter]* parameter ::= argument [':'parameter-mode]? parameter-mode ::= "value" | "variable" | "reference" | "index"
The scripting language allows the user implementing its own functions. Parameters may be passed
to the body of the function. A value may be returned by the function and, if so, the return
type is necessary a sequence of characters. Of course, functions manage their own stack, and
so, accept recursive calls.
2.6.1 Parameters and return value
Arguments passed by parameter must be chosen among the following modes:
value: if the mode of argument is omitted, this is the default mode ; it
requires a sequence of characters (a value of node, a constant string or the
result of a expression),
variable: a node is passed and it may be changed or inspected in the body.
The scope of a reference assignment is limited to the scope of the function: once
the function is left, the variable receives the value of the referenced node. It
is explained by the fact that the parameter is a new local variable, which refers
to the node passed as argument. So, a reference assignment is applied on the
local variable only.
index: the index of a foreach statement is expected,
reference: a node is passed and it may be changed or expected in the body.
On the contrary of variable mode, a reference assignment is propagated
outside the scope of the function.
If you have omitted to return a value from a function, it returns an empty string ; in that
case, you expects to call this function as a procedure and the result isn't exploited. The
special procedure nop takes a function call as parameter and allows executing the
function and ignoring the result. It isn't compulsory to use nop for calling a function
as a procedure. As in C or C++, you can type the fonction call followed by a semi-colon and
the result is lost.
It exists two possibilities for returning a value:
to populate an internal local variable whose name is the same as the function name,
to use the return statement, followed by the expression to evaluate,
If you wish to execute a particular process in any case before leaving a function and:
it exists more than one controlling sequence to leave,
some errors may be raised,
2.6.2 The 'finally' statement
the statement finally warrants you that the block of instructions that follows the
keyword will be systematically executed before leaving. This declaration may be placed
anywhere into the body of the function. Its syntax conforms to: finally-statement ::= "finally"compound-statementExample:
// file "Documentation/FinallySample.cws": 1 function f(v : value) { 2 traceLine("BEGIN f(v)"); 3 finally { 4 traceLine("END f(v)"); 5 } 6 // the body of the function, with more than 7 // one way to exit the function, for example: 8 if !v return "empty"; 9 if v == "1" return "first"; 10 if v == "2" return "second"; 11 if v == "3" return "third"; 12 return "other"; 13 } 14 15 traceLine("...f(1) has been executed and returned '" + f(1) + "'");
line 3: the finally statement is put anywhere in the body, line 4: this statement will be executed while exiting the function, even if an exception was
raised, Output:
BEGIN f(v)
END f(v)
...f(1) has been executed and returned 'first'
2.6.3 Unusual function declarations
It may arrive that a function prototype must be declared before being implemented, because of
a cross-reference with another function for instance. The scripting language offers the forward
declaration to answer this need. To do that, the prototype of the function is written,
preceded by the declare keyword: forward-declaration ::= "declare"function-prototype';'
If the body of the function must be implemented in another library and into C++ for example,
the prototype of the function is preceded by the external keyword (see section C++ binding): external-declaration ::= "external"function-prototype';'
2.6.4 Template functions
CodeWorker proposes a special category of functions called template functions.
Because of CodeWorker doesn't provide a typed scripting language, template hasn't to
be understood as it is commonly exploited in C++ for instance.
A template function represents a set of functions with the
same prototype, except the dispatching constant. The dispatching constant
is a constant string that extends that name of the function. These functions
instantiate the template function for a particular dispatching
constant. Each instantiated function implements its own body.
The BNF representation of a template function to implement is: template-function-definition ::= instantiated-function-definition | generic-function-definition instantiated-function-definition ::= instantiated-function-prototypecompound-statement instantiated-function-prototype ::= "function"function-name'<'dispatching-constant'>' '('parameters')' dispatching-constant ::= a constant string between double quotes generic-function-definition ::= generic-function-prototype [compound-statement | template-based-body] generic-function-prototype ::= "function"function-name'<'generic-key'>' '('parameters')' generic-key ::= an identifier that matches any dispatching constant with no attached prototype template-based-body ::= "{{"template-based-script"}}" template-based-script ::= a piece of template-based script describing the generic implementation
A call to a template function requires to provide a dispatching expression
to determine the dispatching constant. The dispatching expression will be
evaluated during the execution and CodeWorker will resolve what instantiated
fonction of this template to call: the result of the dispatching expression must
match with the dispatching constant of the instantiated function.
The BNF representation of a call to a template function is: instantiated-function-call ::= function-name'<'dispatching-expression'>' '('parameters')' parameters ::= expression [','expression]*
Note that a dispatching constant may be empty and such an instantiated function
can be called as a classical function. In fact, classical functions are considered as
instantiated functions where the dispatching constant is empty.
template functions bring generic programming in the language:
let imagine that we need function getType(myType : variable), to decline for every
language we could have to generate (C++, Java, ...).
Normally, you'll write the following lines to recover the type depending on the
language for which you are producing the source code:
if doc_language == "C++" {
sType = getCppType(myParameterType);
} else if doc_language == "JAVA" {
sType = getJAVAType(myParameterType);
} else {
error("unrecognized language '" + doc_language + "'");
}
Thanks to the template functions, you may replace the precedent lines by the next one:
sType = getType<doc_language>(myParameterType);
with:
function getType<"JAVA">(myType : variable) {
... // implementation for returning a Java type
}
function getType<"C++">(myType : variable) {
... // implementation for returning a C++ type
}
During the execution, the function getType<T>(myType : variable) resolves
on what instantiated function it has to dispatch: either getType<"JAVA">(myType : variable)
or getType<"C++">(myType : variable), depending on what value is assigned to
variable doc_language.
Trying to call an instantiated function that doesn't exist, raises an error at runtime.
However, one might imagine an implementation by default. For instance:
function getType<T>(myType : variable) {
... // common implementation for any unrecognized language
}
For those that know generic programming with C++ templates, here is a classical example of
using template functions:
function f<1>() { return 1; }
function f<N>() { return $N*f<$N - 1$>()$; }
local f10 = f<10>();
if $f10 != 3628800$ error("10! should be worth 3628800");
traceLine("10! = " + f10);
Output:
10! = 3628800
To provide more flexibility in the implementation of the template function, depending on
the generic key <T>, the body admits a template-based script to implement
the source code of the function.
The specialization of the function for a given template instantiation key is then resolved at runtime.
Example:
The template function f inserts a new attribute in a tree node. The attribute has the name passed
to the generic key for instantiation, and the value of the instantiation key is assigned to the new attribute.
Then, the function calls itself recursively on the instantiation key without the last character.
For instance, the source code of f<"field"> should be:
function f<"field">(x : node) {
insert x.field = "field";
f<"fiel">(x); // cut the last character
}
Code:
//a synonym of f<"">(x : node), terminal condition for recusive calls
function f(x : node) {/*does nothing*/}
function f<T>(x : node) {{
// '{{' announces a template-based script, which
// will generate the correct implementation during the instantiation insert x.@T@ = "@T@"; f<"@T.rsubString(1)@">(x); @
// '}}' announces the end of the template-based script
}}
f<"field">(project);
traceObject(project);
Output:
Tracing variable 'project':
field = "field"
fiel = "fiel"
fie = "fie"
fi = "fi"
f = "f"
End of variable's trace 'project'.
2.6.5 Methods
For more readability, syntactical facilities are offered to call functions on a node as if
this function was a method of the node. For example, it is possible to call function
leftString on the node a like this:
a.leftString(2), instead of the classical functional form:
leftString(a, 2).
The rule is that every function (user-defined included) whose first argument is passed either
by value or by variable (but never by index or by reference) can
offer a method call.
In that case, the method call applies on the first argument, which has to be a node.
The BNF representation of a method call is: method-call ::= variable'.'function-name'('parameters')' parameters ::= expression [','expression]*
where parameters have missed the first argument of the function called function-name.
It exists some exceptions where the method doesn't apply to the first argument:
findElement applies on the second argument,
replaceString applies on the third argument,
The following methods offer a synonym to the function name:
empty is a synonym as a method of the function isEmpty,
length is a synonym for the function lengthString,
size is a synonym for the function getArraySize,
2.6.6 The 'readonly' hook
The BNF representation of this statement is: readonlyHook-statement ::= "readonlyHook" '('filename')'compound-statement
The token filename is the argument name that the user chooses for passing the name
of the file to the body of the hook.
This special function allows implementing a hook that will be called each time a
read-only file will be encountered while generating the output file through the
generate or expand instruction.
Limitations: only one declaration of this hook is authorized, and it can't be declared inside
a parsing or pattern script.
Example:
Common usage: file to generate has to be checked out from a source code control system
(see system command to run executables).
readonlyHook(sFilename) {
if !getProperty("SSProjectFolder") || !getProperty("SSWorkingFolder") || !getProperty("SSExecutablePath") || !getProperty("SSArchiveDir") {
traceLine("WARNING: properties 'SSProjectFolder' and 'SSWorkingFolder' and 'SSExecutablePath' and 'SSArchiveDir' should be passed to the command line for checking out read-only files from Source Safe");
} else {
if startString(sFilename, getProperty("SSWorkingFolder")) {
local sourceSafe;
insert sourceSafe.fileName = sFilename;
generate("SourceSafe.cwt", sourceSafe, getEnv("TMP") + "/SourceSafe.bat");
if sourceSafe.isOk {
putEnv("SSDIR", getProperty("SSArchiveDir"));
traceLine("checking out '" + sFilename + "' from Source Safe archive '" + getProperty("SSArchiveDir") + "'");
local sFailed = system(getEnv("TMP") + "/SourceSafe.bat");
if sFailed {
traceLine("Check out failed: '" + sFailed + "'");
}
}
} else {
traceLine("Unable to check out '" + sFilename + "': working folder starting with '" + getProperty("SSWorkingFolder") + "' expected");
}
}
}
2.6.7 The 'write file' hook
This special function allows implementing a hook that will be called just before writing a
file, after ending a text generation process such as expanding or generating or translating
text.
It is very important to notice that it returns a boolean value. A true value means that
the generated text must be written into the file. A false boolean value means that the
generated text doesn't have to be written into the file.
CodeWorker always interprets not returning a value explicitly into a function as
returning an empty string. If you forget to return a value, the generated text will not be
written into the file!
The BNF representation of this statement is: writefileHook-statement ::= "writefileHook" '('filename','position','creation')'compound-statement
Argument
Type
Description
filename
string
The argument name that the user chooses for
passing the file name to the body of the hook.
position
int
The argument name that the user chooses for
passing a position where a difference occurs between the new
generated version of the file and the precedent one.
If the files don't have the same size, the position is worth
-1.
creation
boolean
The argument name that the user chooses for
passing whether the file is created or updated.
The argument is worth true if the file doesn't exist
yet.
Limitations: only one declaration of this hook is authorized, and it can't be declared inside
a parsing or pattern script.
Example:
This special function is automatically called before that the extended BNF engine
resolves the production rule of a BNF non-terminal. Combined with stepoutHook(),
it is very useful for trace and debug tasks.
This hook can be implemented in parse scripts only.
The BNF representation of this statement is: stepintoHook-statement ::= "stepintoHook" '('sClauseName','localScope')'compound-statement
Argument
Type
Description
sClauseName
string
The name of the non-terminal.
localScope
tree
The scope of parameters used into the production rule.
2.6.9 The 'step out' hook
This special function is automatically called once the extended BNF engine has finished
the resolution of a BNF non-terminal. Combined with stepintoHook(), it is very
useful for trace and debug tasks.
This hook can be implemented in parse scripts only.
The BNF representation of this statement is: stepoutHook-statement ::= "stepoutHook" '('sClauseName','localScope','bSuccess')'compound-statement
Argument
Type
Description
sClauseName
string
The name of the non-terminal.
localScope
tree
The scope of local variables and parameters used into the production rule.
bSuccess
boolean
Whether the resolution of the production rule has succeeded or not.
2.7 Statement's modifiers
A statement's modifier is a directive that stands just before a statement, meaning an instruction
or a compound statement.
This directive operates some actions in the scope of the statement and then restores the
behaviour as being before.
This action may be:
to measure the time that is consumed by the execution of the statement,
to redirect into a variable all messages intended to the console during the
execution of the statement,
to push a new project parse tree,
to change the output file during the execution of the statement, while generating
text,
to redirect the output stream into a variable during the execution of the statement,
while generating text,
to change the output file during the execution of the statement, while generating
text, and to apply an expansion mode on it,
2.7.1 Statement's modifier 'delay'
This keyword stands just before an instruction or a compound statement. It executes the statement and then, it
measures the time it has consumed.
Function getLastDelay (getLastDelay()) gives you the last measured duration.
Example:
local list;
local iIndex = 4;
delay while isPositive(decrement(iIndex)) {
pushItem list = "element " + iIndex;
traceLine("creating node '" + list#back + "'");
}
traceLine("time of execution = " + getLastDelay() + " seconds");
Output:
creating node 'element 3'
creating node 'element 2'
creating node 'element 1'
time of execution = 0.000035174079564180316 seconds
2.7.2 Statement modifier 'quiet'
This keyword stands just before an instruction or a compound statement. It executes the
statement and all messages intended to the console are concatenated into a string, instead of
being displayed. The variable that receives the concatenation of messages is specified after
the quiet keyword.
The BNF representation of the quiet statement modifier looks like: quiet_modifier ::= "quiet"'('variable')'statement
Note that the variable must have been declared before, as a local one or as an attribute of
the parse tree. If this variable doesn't exist while executing the statement, an error is
raised.
2.7.3 Statement modifier 'new project'
This keyword stands just before an instruction or a compound statement. A new project
parse tree is created, which is empty and that replaces temporarily the current one.
The statement is executed and, once the controlling sequence leaves the statement, the
temporary parse tree is removed, and the precedent project comes back as the current
one.
The BNF representation of the new_project statement modifier looks like: new_project_modifier ::= "new_project"statement
This statement modifier is useful to handle a task that doesn't have to interact with the main parse tree.
2.7.4 Statement modifier 'file as standard input'
This keyword stands just before an instruction or a compound statement. A new standard input
is opened for reading data. Generally, the keyboard is the standard input, but here, it will
be the content of a file that is passed to the argument filename. Once the execution
of the statement has completed, the precedent standard input comes back.
The BNF representation of the file_as_standard_input statement's modifier looks like: file_as_standard_input_modifier ::= "file_as_standard_input" '('filename')'statement
This statement modifier is useful to replay a sequence of commands for the debugger or to drive
the standard input from an external module that puts its instructions into a file for a batch
mode or anything else.
2.7.5 Statement modifier 'string as standard input'
This keyword stands just before an instruction or a compound statement. A new standard input
is opened for reading data. Generally, the keyboard is the standard input, but here, it will
be the content of the string that is passed to argument. Once the execution of the statement
has completed, the precedent standard input comes back.
The BNF representation of the string_as_standard_input statement's modifier looks like: string_as_standard_input_modifier ::= "string_as_standard_input" '('expression')'statement
The standard input is the result of evaluating expression.
This statement modifier is useful to drive the standard input of CodeWorker from an external
module, such as a JNI library or an external C++ application ( see chapter external bindings).
2.7.6 Statement modifier 'parsed file'
This keyword stands just before an instruction or a compound statement that belongs to a
parsing/translation script exclusively. A new input file is opened for source scanning,
and replaces temporarily the precedent during the execution of the statement.The statement is
executed and, once the controlling sequence leaves the statement, the input file is closed
properly and the precedent one comes back.
The BNF representation of the parsed_file statement modifier looks like: parsed_file_modifier ::= "parsed_file" '('filename')'statement
The token filename is an expression that is evaluated to give the name of the input
file.
This statement modifier is useful to handle a task that must redirect the text to parse into
another input file. An example could be to emulate the C++ preprocessing on #include
directives.
2.7.7 Statement modifier 'parsed string'
This keyword stands just before an instruction or a compound statement that belongs to a
parsing/translation script exclusively. The result of an expression is taken as the source to scan,
and replaces temporarily the precedent input during the execution of the statement.The statement is
executed and, once the controlling sequence leaves the statement the precedent input comes back.
The BNF representation of the parsed_string statement modifier looks like: parsed_string_modifier ::= "parsed_string" '('expression')'statement
The token fexpression is an expression that is evaluated to give the text to scan.
This statement modifier is useful to handle a task that must temporary parse a string.
2.7.8 Statement modifier 'generated file'
This keyword stands just before an instruction or a compound statement that belongs to a
pattern script exclusively. A new output file is opened for source code generation, preserving
protected areas as usually, and replaces temporarily the current one during the execution of the
statement. The statement is executed and, once the controlling sequence leaves the statement,
the output file is closed properly and the precedent one takes its place.
The BNF representation of the generated_file statement modifier looks like: generated_file_modifier ::= "generated_file" '('filename')'statement
The token filename is an expression that is evaluated to give the name of the output
file.
This statement modifier is useful to handle a task that must redirect the generated text into
another output file. An example could be to split an HTML text to generate into a few files
for implementing a frame set.
2.7.9 Statement modifier 'generated string'
This keyword stands just before an instruction or a compound statement that belongs to a
pattern script exclusively. The output stream is redirected into a variable that
replaces temporarily the current output stream during the execution of the statement.
The statement is executed and, once the controlling sequence leaves the statement,
the variable is populated with the content of the output produced during this scope and the
precedent output stream takes its place.
The BNF representation of the generated_string statement modifier looks like: generated_string_modifier ::= "generated_string" '('variable')'statement
The variable argument gives the name of the variable that will be populated with the
generated text. This variable must already exist, declared on the stack or referring a node of
the current parse tree.
2.7.10 Statement modifier 'appended file'
This keyword stands just before an instruction or a compound statement that belongs to a
pattern script exclusively. A new output file is opened for appending source code generation
at the end of the file and replaces temporarily the current one during the execution of the
statement. The statement is executed and, once the controlling sequence leaves the statement,
the output file is closed properly and the precedent one takes its place.
The BNF representation of the appended_file statement modifier looks like: appended_file_modifier ::= "appended_file" '('filename')'statement
The token filename is an expression that is evaluated to give the name of the output
file to append.
3 Common functions and procedures
All functions and procedures that are described below may be encountered in any kind of
scripts : parsing, source code generation and file expanding, process driving, included script
files.
Category interpreter
Function for running a CodeWorker script
autoexpand
Expands a file on markups, following the directives self-contained in the file.
executeString
Executes a script given in a string.
executeStringQuiet
Interprets a string as a script and returns all traces intended to the console.
expand
Expands a file on markups, following the directives of a template-based script.
generate
Generates a file, following the directives of a template-based script.
generateString
Generates a string, following the directives of a template-based script.
parseAsBNF
Parses a file with a BNF script.
parseFree
Parses a file with an imperative script.
parseFreeQuiet
Parses a file with an imperative script, reroute all console messages and returns them as a string.
parseStringAsBNF
Parses a string with a BNF script.
traceEngine
Displays the state of the interpreter.
translate
Performs a source-to-source translation or a program transformation.
translateString
Performs a source-to-source translation or a program transformation on strings.
Category string
Functions for handling strings
charAt
Returns the characters present at a given position of a string.
completeLeftSpaces
Completes a string with spaces to the left so that it reaches a given size.
completeRightSpaces
Completes a string with spaces to the right so that it reaches a given size.
composeCLikeString
Converts a sequence of characters to a C-like string without double quote delimiters.
composeHTMLLikeString
Converts a sequence of characters to an HTML-like text
composeSQLLikeString
Converts a sequence of characters to a SQL-like string without single quote delimiters.
coreString
Extracts the core of a string, leaving the beginning and the end.
countStringOccurences
How many occurences of a string to another.
cutString
Cuts a string at each separator encountered.
endString
Compares the end of the string.
endl
Returns an end-of-line, depending on the operating system.
equalsIgnoreCase
Compares two strings, ignoring the case.
executeString
Executes a script given in a string.
executeStringQuiet
Interprets a string as a script and returns all traces intended to the console.
findFirstChar
Returns the position of the first character amongst a set, encountered into a string.
findLastString
Returns the position of the last occurence of a string to another.
findNextString
Returns the next occurence of a string to another.
findString
Returns the first occurence of a string to another.
generateString
Generates a string, following the directives of a template-based script.
joinStrings
Joins a list of strings, adding a separator between them.
leftString
Returns the beginning of a string.
lengthString
Returns the length of a string.
midString
Returns a substring starting at a point for a given length.
parseStringAsBNF
Parses a string with a BNF script.
repeatString
Returns the concatenation of a string repeated a few times.
replaceString
Replaces a substring with another.
replaceTabulations
Replaces tabulations with spaces.
rightString
Returns the end of a string.
rsubString
Returns the left part of a string, ignoring last characters.
startString
Checks the beginning of a string.
subString
Returns a substring, ignoring the first characters.
toLowerString
Converts a string to lowercase.
toUpperString
Converts a string to uppercase.
trim
Eliminates heading and trailing whitespaces.
trimLeft
Eliminates the leading whitespaces.
trimRight
Eliminates the trailing whitespaces.
truncateAfterString
Special truncation of a string.
truncateBeforeString
Special truncation of a string.
Category array
Functions handling arrays
findElement
Checks the existence of an entry key in an array.
findFirstSubstringIntoKeys
Returns the first entry key of an array, containing a given string.
findNextSubstringIntoKeys
Returns the next entry key of an array, containing a given string.
getArraySize
Returns the number of items in an array.
invertArray
Inverts the order of items in an array.
isEmpty
Checks whether a node has items or not.
removeAllElements
Removes all items of the array.
removeElement
Removes an item, given its entry key.
removeFirstElement
Removes the first item of the array.
removeLastElement
Removes the last item of the array.
Category node
Functions handling a node
clearVariable
Removes the subtree and assigns an empty value.
equalTrees
Compares two subtrees.
existVariable
Checks the existence of a node.
getVariableAttributes
Extract all attribute names of a tree node.
removeRecursive
Removes a given attribute from the subtree.
removeVariable
Removes a given variable.
slideNodeContent
Moves the subtree elsewhere on a branch.
sortArray
Sort an array, considering the entry keys.
Category index
Functions handling a foreach index
first
Returns true if the index points to the first item.
key
Returns the entry key of the item pointed to by the index.
last
Returns true if the index points to the last item.
Category file
Functions handling files
appendFile
Writes the content of a string to the end of a file
canonizePath
Builds an absolute path, starting to the current directory.
changeFileTime
Changes the access and modification times of a file.
chmod
Changes the permissions of a file.
copyFile
Copies a file.
copyGenerableFile
Copies a file with protected areas or expandable markups, only if the hand-typed code differs between source and destination.
copySmartFile
Copies a file only if the destination differs.
createVirtualFile
Creates a transient file in memory.
createVirtualTemporaryFile
Creates a transient file in memory, CodeWorker choosing its name.
deleteFile
Deletes a file on the disk.
deleteVirtualFile
Deletes a transient file from memory.
existFile
Checks the existence of a file.
existVirtualFile
Checks the existence of a transient file, created in memory.
exploreDirectory
Browses all files of a directory, recursively or not.
fileCreation
Returns the creation date of a file.
fileLastAccess
Returns the last access date of a file.
fileLastModification
Returns the last modification date of a file.
fileLines
Returns the number of lines in a file.
fileMode
Returns the permissions of a file.
fileSize
Returns the size of a file.
getGenerationHeader
Returns the comment to put into the header of generated files.
getShortFilename
Returns the short name of a file
indentFile
Indents a file, depending on the target language.
loadBinaryFile
Loads a binary file and stores each byte in a hexadecimal representation of 2 digits.
loadFile
Returns the content of a file or raises an error if not found.
loadVirtualFile
Returns the content of a transient file or raises an error if not found.
pathFromPackage
Converts a package path to a directory path.
relativePath
Returns the relative path, which allows going from a path to another.
resolveFilePath
Gives the location of a file with no ambiguity.
saveBinaryToFile
Saves binary data to a file.
saveToFile
Saves the content of a string to a file
scanDirectories
Explores a directory, filtering filenames.
scanFiles
Returns a flat list of all filenames matching with a filter.
Category directory
Functions handling directories
changeDirectory
Changes the current directory (chdir() in C).
copySmartDirectory
Copies files of a directory recursively only when destination files differ from source files.
createDirectory
Creates a new directory.
exploreDirectory
Browses all files of a directory, recursively or not.
getCurrentDirectory
Returns the current directory (getcwd() in C).
removeDirectory
Removes a directory from the disk.
scanDirectories
Explores a directory, filtering filenames.
scanFiles
Returns a flat list of all filenames matching with a filter.
Category URL
Functions working on URL transfers (HTTP,...)
decodeURL
Decodes an HTTP URL.
encodeURL
Encodes an URL to HTTP.
getHTTPRequest
Sends an HTTP's GET request.
postHTTPRequest
Sends an HTTP's POST request.
sendHTTPRequest
Sends an HTTP request.
Category datetime
Functions handling date-time
compareDate
Compares two dates.
completeDate
Extends an incomplete date with today characteristics.
fileCreation
Returns the creation date of a file.
fileLastAccess
Returns the last access date of a file.
fileLastModification
Returns the last modification date of a file.
formatDate
Changes the format of a date.
getLastDelay
Returns the time consumed to execute a statement.
getNow
Returns the current date-time.
setNow
Fixes the current date-time.
Category numeric
Functions handling numbers
add
Equivalent admitted writing is $a + b$.
ceil
Returns the smallest integer greater that or equal to a number
decrement
Equivalent admitted writing is set a = $a - 1$;.
div
Equivalent admitted writing is $a / b$.
equal
Equivalent admitted writing is $a == b$.
exp
Returns the exponential of a value.
floor
Returns the largest integer less that or equal to a number
increment
Equivalent admitted writing is set a = $a + 1$;.
inf
Equivalent admitted writing is $a < b$.
isNegative
Equivalent admitted writing is $a < 0$.
isPositive
Equivalent admitted writing is $a > 0$.
log
Returns the Neperian logarithm.
mod
Equivalent admitted writing is $a % b$.
mult
Equivalent admitted writing is $a * b$.
pow
Raises a number to the power of another.
sub
Equivalent admitted writing is $a - b$.
sup
Equivalent admitted writing is $a > b$.
Category standard
Classical functions of any standard library
UUID
Generates an UUID.
error
Raises an error message
inputKey
If any, returns the last key pressed on the standard input.
inputLine
Wait for the standard input to the console.
isIdentifier
Checks whether a string is a C-like identifier or not.
randomInteger
Generates a pseudorandom number.
randomSeed
Changes the seed of the pseudorandom generator.
traceLine
Displays a message to the console, adding a carriage return.
traceObject
Displays the content of a node to the console.
traceStack
Displays the stack to the console.
traceText
Displays a message to the console.
Category conversion
Type conversion
byteToChar
Converts a byte (hexadecimal representation of 2 digits) to a character.
bytesToLong
Converts a 4-bytes sequence to an unsigned long integer in its decimal representation.
bytesToShort
Converts a 2-bytes sequence to an unsigned short integer in its decimal representation.
charToByte
Converts a character to a byte (hexadecimal representation of 2 digits).
charToInt
Converts a character to the integer value of the corresponding ASCII.
hexaToDecimal
Converts an hexadecimal representation to an integer.
hostToNetworkLong
Converts a 4-bytes representation of a long integer to the network bytes order.
hostToNetworkShort
Converts a 2-bytes representation of a short integer to the network bytes order.
longToBytes
Converts an unsigned long integer in decimal base to its 4-bytes representation.
networkLongToHost
Converts a 4-bytes representation of a long integer to the host bytes order.
networkShortToHost
Converts a 2-bytes representation of a short integer to the host bytes order.
octalToDecimal
Converts an octal representation to a decimal integer.
shortToBytes
Converts an unsigned short integer in decimal base to its 2-bytes representation.
Category system
Functions relative to the operating system
computeMD5
Computes the MD5 of a string.
environTable
Equivalent of environ() in C
existEnv
Checks the existence of an environment variable.
getEnv
Returns an environment variable, or raises an error if not exist.
openLogFile
Opens a log file for logging every console trace.
putEnv
Puts a value to an environment variable.
system
Equivalent to the C function system().
Category command
Relative to the command line
compileToCpp
Translates a script to C++.
getIncludePath
Returns the include path passed via the option -I.
getProperty
Returns the value of a property passed via the option -D.
getVersion
Returns the version of the interpreter.
getWorkingPath
Returns the output directory passed via option -path.
setIncludePath
Changes the option -I while running.
setProperty
Adds/changes a property (option -D) while running.
setVersion
Gives the version of scripts currently interpreted by CodeWorker.
setWorkingPath
Does the job of the option -path.
Category generation
Functions relative to generation
addGenerationTagsHandler
Adds your own CodeWorker's tags handler
autoexpand
Expands a file on markups, following the directives self-contained in the file.
expand
Expands a file on markups, following the directives of a template-based script.
extractGenerationHeader
Gives the generation header of a generated file, if any.
generate
Generates a file, following the directives of a template-based script.
generateString
Generates a string, following the directives of a template-based script.
getCommentBegin
Returns the current format of a comment's beginning.
getCommentEnd
Returns the current format of a comment's end.
getGenerationHeader
Returns the comment to put into the header of generated files.
getTextMode
Returns the text mode amongst "DOS", "UNIX" and "BINARY".
getWriteMode
Returns how text is written during a generation (insert/overwrite).
listAllGeneratedFiles
Gives the list of all generated files.
removeGenerationTagsHandler
Removes a custom generation tags handler
selectGenerationTagsHandler
Selects your own CodeWorker's tags handler for processing generation tasks
setCommentBegin
Changes what a beginning of comment looks like, perhaps before expanding a file.
setCommentEnd
Changes what an end of comment looks like, perhaps before expanding a file.
setGenerationHeader
Specifies a comment to put at the beginning of every generated file.
setTextMode
"DOS", "UNIX" or "BINARY"
setWriteMode
Selects how to write text during a generation (insert/overwrite).
translate
Performs a source-to-source translation or a program transformation.
translateString
Performs a source-to-source translation or a program transformation on strings.
Category parsing
Functions relative to scanning/parsing
parseAsBNF
Parses a file with a BNF script.
parseFree
Parses a file with an imperative script.
parseFreeQuiet
Parses a file with an imperative script, reroute all console messages and returns them as a string.
parseStringAsBNF
Parses a string with a BNF script.
translate
Performs a source-to-source translation or a program transformation.
translateString
Performs a source-to-source translation or a program transformation on strings.
Category socket
Socket operations
acceptSocket
Listens for a client connection and accepts it.
closeSocket
Closes a socket descriptor.
createINETClientSocket
Creates a stream socket connected to the specified port and IP address.
createINETServerSocket
Creates a server stream socket bound to a specified port.
receiveBinaryFromSocket
Reads binary data from the socket, knowing the size.
receiveFromSocket
Reads text or binary data from a socket.
receiveTextFromSocket
Reads text from a socket, knowing the size.
sendBinaryToSocket
Writes binary data to a socket.
sendTextToSocket
Writes text to a socket.
Category unknown
Various types of function
not
The boolean negation, equivalent to !a.
produceHTML
saveProject
Saves the parse tree of the project to XML.
saveProjectTypes
Factorizes nodes of the projects to distinguish implicit types for node and saves it to XML.
3.1 acceptSocket
function acceptSocket(serverSocket:int) :int
Parameter
Type
Description
serverSocket
int
a server socket previously created via createINETServerSocket()
Returns the result of arithmetic addition left+right.
Members are converted from strings to numbers, supposed being worth 0 if a parsing error occurs;
then the addition is processed, and the result is converted to a string,
skipping fractional part if all digits after the dot are 0.
Remember that the symbol '+' means the concatenation of text. Using
this operator instead of function add will concatenate digits!
However, it exists an escape mode that allows writing arithmetic expressions
between '$' symbols, as formula under LaTeX. So, $left+right$
is equivalent to add(left, right).
Example:
function addGenerationTagsHandler(key:string, reader:script, writer:script) :bool
Parameter
Type
Description
key
string
designates the handler
reader
script<BNF>
extended-BNF script of the reader
writer
script<pattern>
template-based script of the writer
Adds a new generation tags handler, designated by key.
Returns true if key isn't reserved yet for another generation tags handler.
See also:removeGenerationTagsHandlerremoveGenerationTagsHandler(), selectGenerationTagsHandlerselectGenerationTagsHandler()
the current node that will be accessed with this variable
Expands an existing file whose name is passed to the argument outputFileName,
executing template-based scripts located at each markup. The file contains
its own scripts for expanding code.
Expanding a file consists of generating code into marked out areas only, the rest
of the file remaining the same. The markup is put into a comment, knowing that the
syntax of the comment must conform to the type of the expanded file outputFileName.
So, an HTML file expects <!- - and - ->, a JAVA file is waiting
for // and "\n", ... The markup is announced by
##markup## followed by a string that represents the markup key.
Don't forget to configure correctly the syntax of comment boundaries with procedures
setCommentBegin() (see setCommentBegin()) and setCommentEnd() (see setCommentEnd()).
When the procedure is called, CodeWorker jumps from a markup to another. To
handle a markup, it checks whether text was already generated, put between tags
##begin##"markup-key" and ##end##"markup-key",
added automatically the first time an expansion is required, to demarquate the
portion of code that doesn't belong to the user. Then, it extracts all protected
areas, if any, and it generates code at the position of the markup, adding
begin/end tags seen before.
The interpreter reclaims the tags ##script## just after the markup.
It extracts the embedded text, considered as a template-based script, eventually
put between comments, and the interpreter executes this embedded script.
Note that some data might be put between tags ##data##, accessible
in the template-based script via the function getMarkupValue() (see getMarkupValue()).
This block of custom data comes after the ##script## tag, if present.
Be careful not to confuse this prodedure with generate() that doesn't care
about markups and that overwrites the output file completely, except protected
areas of course.
See also:expandexpand(), generategenerate(), generateStringgenerateString(), translatetranslate(), parseAsBNFparseAsBNF(), parseFreeparseFree(), parseFreeQuietparseFreeQuiet(), parseStringAsBNFparseStringAsBNF(), translateStringtranslateString()
3.6 bytesToLong
function bytesToLong(bytes:string) :ulong
Parameter
Type
Description
bytes
string
a 4-bytes representation of an unsigned long integer (host bytes order)
Converts a 4-bytes representation of an unsigned long integer to its decimal representation.
Bytes are ordered in the host order (memory storage).
If the argument bytes is malformed, the function raises an error.
Example:
a 2-bytes representation of an unsigned short integer (host bytes order)
Converts a 2-bytes representation of an unsigned short integer to its decimal representation.
Bytes are ordered in the host order (memory storage).
If the argument bytes is malformed, the function raises an error.
Example:
Converts a byte to a character. A byte is considered as an hexadecimal number of
2 digits exactly.
If the argument byte doesn't contain an hexadecimal number of 2 digits,
an error is raised. If byte is worth '00', the function returns an empty
string.
Example:
current directory = 'C:/Projects/generator/'
path = 'WebSite/downloads/CodeWorker.zip'
result = 'c:/Projects/generator/WebSite/downloads/CodeWorker.zip'
current directory = 'C:/Projects/generator/Documentation/'
path = '../Scripts/Tutorial/GettingStarted/tiny.html'
result = 'c:/Projects/generator/Scripts/Tutorial/GettingStarted/tiny.html'
current directory = 'C:/Projects/generator/'
path = '.'
result = 'c:/Projects/generator'
The function changes the current directory of CodeWorker to the directory
specified by the path argument. The parameter must refer to an existing
directory.
Example:
traceLine("current directory = '" + getCurrentDirectory() + "'");
local sOldDirectory = getCurrentDirectory();
local sNewDirectory = sOldDirectory + "Documentation";
traceLine("call to changeDirectory('" + sNewDirectory + "')");
changeDirectory(sNewDirectory);
traceLine("new current directory = '" + getCurrentDirectory() + "'");
changeDirectory(sOldDirectory);
Output:
current directory = 'C:/Projects/generator/'
call to changeDirectory('C:/Projects/generator/Documentation')
new current directory = 'C:/Projects/generator/Documentation/'
function changeFileTime(filename:string, accessTime:string, modificationTime:string) :int
Parameter
Type
Description
filename
string
name of the file to set
accessTime
string
date-time of the last access
modificationTime
string
date-time of the last modification
The function changes the access and modification times of the file filename.
The user ID of the process must be the owner of the file, or the process must
have appropriate privileges.
In case of failure, the function returns a negative integer:
-1: unknown error that shouldn't appear,
-2: permission denied,
-3: too many files have been opened,
-4: file not found,
-5: invalid times argument,
Example:
local oldAccessTime = fileLastAccess("readme.txt");
local oldModifTime = fileLastModification("readme.txt");
traceLine("old modification time of 'readme.txt' = '" + oldModifTime + "'");
if $changeFileTime("readme.txt", getNow(), getNow()) < 0$
error("'changeFileTime()' has failed!");
local newModifTime = fileLastModification("readme.txt");
traceLine("new modification time of 'readme.txt' = '" + newModifTime + "'");
// put the same times as before calling the example:
if $changeFileTime("readme.txt", oldAccessTime, oldModifTime) < 0$
error("'changeFileTime()' has failed!");
Output:
old modification time of 'readme.txt' = '14jan2004 19:03:26'
new modification time of 'readme.txt' = '07oct2004 20:42:00'
Returns the character at the specified index. An index ranges from 0 to lengthString(text) - 1. The first character of the sequence is at index 0, the next at index 1, and so on. If the index argument is out of bounds (negative or not less than the length of text), it returns an empty string.
Example:
local sText = "I have but one lamp by which my feet are guided, and that is the lamp of experience. (P. Henry)";
traceLine("charAt('" + sText + "', 2) = '" + charAt(sText, 2) + "' <- index = 2 gives the third character of the string");
Output:
charAt('I have but one lamp by which my feet are guided, and that is the lamp of experience. (P. Henry)', 2) = 'h' <- index = 2 gives the third character of the string
Converts a character to its hexadecimal representation, taking 2 digits, even if
less than 0x10.
If the argument char is empty, the function returns '00'. If it contains
more than one character, an error is raised.
Example:
Returns the conversion of char as an unsigned integer, corresponding to its ASCII form generally. If char doesn't contain just one char, it returns an empty string.
Example:
function chmod(filename:string, mode:string) :bool
Parameter
Type
Description
filename
string
file to which change the permission setting
mode
string
permission setting as a concatenation of 'R' and/or 'W' and/or 'X'
The chmod function changes the permission setting of the file specified by filename.
The permission setting controls read and write and execute access to the file.
The argument mode holds the permission setting of the file as a concatenation of chars amongst the following:
'R' for reading permitted,
'W' for writing permitted,
'X' for executing permitted (ignored on Windows platform),
The function fails when the file given by the argument filename is not found, and an error is thrown when the argument mode contains an unexpected character.
Example:
local bSuccess = chmod("Documentation/CodeWorker.tex", "RW");
if !bSuccess error("file 'Documentation/CodeWorker.tex' not found!");
traceLine("R + W permitted on file 'Documentation/CodeWorker.tex'");
Output:
R + W permitted on file 'Documentation/CodeWorker.tex'
All attributes of the argument node are deleted, its array of nodes is
cleared and its value becomes an empty string. If the node was referring to
another node, the link is cleared.
Please note that the node isn't removed; see removeVariable() for that.
Example:
local myNode = "the value";
insert myNode.a1 = "attribute 1";
insert myNode.a2 = "attribute 2";
insert myNode.array["1"] = "node 1";
insert myNode.array["2"] = "node 2";
traceObject(myNode);
traceLine("-- the variable 'myNode' is cleared:");
clearVariable(myNode);
traceObject(myNode);
Output:
Tracing variable 'myNode':
"the value"
a1 = "attribute 1"
a2 = "attribute 2"
array
array["1", "2"]
End of variable's trace 'myNode'.
-- the variable 'myNode' is cleared:
Tracing variable 'myNode':
End of variable's trace 'myNode'.
the location where to put on the disk the scripts compiled to C++
CodeWorkerDirectory
string
the root path of CodeWorker either in development or distributed state
Compiles the leader script file called scriptFileName and all scripts
that might be reclaimed during the execution. The corresponding C++ files are
stored into projectDirectory with the makefile (a Visual C++'s DSP
file). The path to libraries and the origin of some important include files is
determined thanks to the path to CodeWorker put into CodeWorkerDirectory.
The script file cannot be a pattern script or a parsing script.
If an error occurs, an error message is raised.
Example:
local sScriptFile = "GettingStarted/LeaderScript6.cws";
local sDirectory = getWorkingPath() +
"Scripts/Tutorial/GettingStarted/bin";
removeDirectory(sDirectory);
compileToCpp(sScriptFile, sDirectory, ".");
local theFiles;
if !scanFiles(theFiles, sDirectory, "", true) error("should have worked!");
traceLine("generated files:");
foreach i in sorted theFiles traceLine(" " + i);
function completeDate(date:string, format:string) :string
Parameter
Type
Description
date
string
a date-time representation to complete
format
string
the format that the date argument conforms to
Completes the date passed to the argument date, so as it conforms to the
syntax of a date in CodeWorker meaning: "%d%b%Y %H:%M:%S.%L".
Starting from today date with reset time (00:00:00.0), it replaces date-time
characteristics with those of the parameter date and returns the result
of the substitutions.
See formatDate() to reading the description of a date format.
A format type was added for this function: '%|'.
Once the date has been iterated up to the end, if the format wasn't applied on it completely,
an error occurs, except if '%|' stands at the current position in the format.
Example:
traceLine("Today date with reset time = '" + completeDate(getNow(), "%d%b%Y") + "'");
local dDateAsNumber = formatDate(getNow(), "%t");
traceLine("Today date (Excel-like) = '" + dDateAsNumber + "'");
traceLine("Preceding day = '" + completeDate($dDateAsNumber - 1$, "%t") + "'");
traceLine("15th of the current month = '" + completeDate("15", "%d") + "'");
traceLine("august of this year = '" + completeDate("08", "%m") + "'");
traceLine("15/04 = '" + completeDate("15/04", "%d/%m") + "'");
traceLine("december 31, 2003 = '" + completeDate("december 31, 2003", "%B %d, %Y") + "'");
Output:
Today date with reset time = '07oct2004'
Today date (Excel-like) = '38267.862506'
Preceding day = '06oct2004 20:42:00.518'
15th of the current month = '15oct2004'
august of this year = '07aug2004'
15/04 = '15apr2004'
december 31, 2003 = '31dec2003'
function completeLeftSpaces(text:string, length:int) :string
Parameter
Type
Description
text
string
a sequence of characters
length
int
the length to obtain for text
Completes the string given by argument text with spaces to the left, so
that the resulting string takes up length characters long. If the
argument text contains more than length characters, it returns
text.
Example:
function completeRightSpaces(text:string, length:int) :string
Parameter
Type
Description
text
string
a sequence of characters
length
int
the length to obtain for text
Completes the string given by argument text with spaces to the right, so
that the resulting string takes up length characters long. If the
argument text contains more than length characters, it returns
text.
Example:
a sequence of character to convert to a C-like string
Returns the conversion of the sequence of characters given by argument
text to a C-like string, without double quote delimiters. It means that special characters of text
are replaced by their escape sequence, the rest remaining the same.
It recognizes the following escape sequences:
'\\' as backslash (\), ASCII value 92,
'\' ' as single quotation mark ('), ASCII value 39,
'\"' as double quotation mark ("), ASCII value 34,
function composeHTMLLikeString(text:string) :string
Parameter
Type
Description
text
string
a sequence of character to convert to an HTML-like string
Returns the conversion of the sequence of characters given by argument
tex to an HTML-like string. It means that special characters of text
are replaced by their HTML escape sequence (&...;), the rest remaining the same.
Example:
function composeSQLLikeString(text:string) :string
Parameter
Type
Description
text
string
a sequence of character to convert to a SQL-like string
Returns the conversion of the sequence of characters given by argument
text to a SQL-like string, without single quote delimiters. It means that special characters of text
are replaced by their escape sequence, the rest remaining the same.
It recognizes the following escape sequences:
'\\' as backslash (\), ASCII value 92,
'\' ' as single quotation mark ('), ASCII value 39,
'\"' as double quotation mark ("), ASCII value 34,
'\a' as alert (BEL), ASCII value 7,
'\b' as backspace (BS), ASCII value 8,
'\f' as formfeed (FF), ASCII value 12,
'\n' as newline (LF), ASCII value 10,
'\r' as carriage return (CR), ASCII value 13,
'\t' as horizontal tab (HT), ASCII value 9,
'\v' as vertical tab (VT), ASCII value 11,
The function translates the single quote to an escape sequence "\'", instead of
repeating twice the single quote as in the SQL-standard. It presents the advantage
of being more readable, but if you encounters a drawback in using this translation,
apply replaceString() to change "\'" in "''".
Example:
Computes the MD5 of a string.
This optimized MD5 implementation conforms to RFC 1321.
Source: http://www.cr0.net:8040/code/crypto/md5/
Copyright 2001-2004 Christophe Devine
Example:
local sSentence = "Garfield squashed 5 spiders yesterday";
local sCode = computeMD5(sSentence);
if sCode != "B2D989F0C0501E9A9D4A9F1B4D06E2C5" {
error("bad result from 'computeMD5()'!");
}
traceLine("computeMD5('" + sSentence + "') = " + sCode);
This procedure copies a file sourceFileName to another location
destinationFileName. It raises an error if something wrong has happened
(either the source file doesn't exist or permissions are insufficient for copy).
Example:
deleteFile("Documentation/readme.txt");
copyFile("readme.txt", "Documentation/readme.txt");
traceLine("file 'readme.txt' has been copied successfully!");
This procedure copies a directory sourceDirectory to another location
destinationPath recursively, checking for each file if it doesn't exist yet
or it the content has changed. It avoids copying a file whereas it has no impact,
and then modifying the last changing date of the destination file.
It raises an error if something wrong has happened (the source directory
must exist and permissions must be sufficient to copy if required). Note that
empty directories are ignored.
See also:changeDirectorychangeDirectory(), canonizePathcanonizePath(), exploreDirectoryexploreDirectory(), getCurrentDirectorygetCurrentDirectory(), relativePathrelativePath(), removeDirectoryremoveDirectory(), resolveFilePathresolveFilePath(), scanDirectoriesscanDirectories()
3.31 copySmartFile
function copySmartFile(sourceFileName:string, destinationFileName:string) :bool
Parameter
Type
Description
sourceFileName
string
the name of the file to copy
destinationFileName
string
the name of the copy
This function copies a file sourceFileName to another location
destinationFileName only if either file destinationFileName
doesn't exist yet or the content of file destinationFileName is
different of the content of file sourceFileName. It avoids copying
a file when it has no impact, and then modifying the last changing date of
the destination file. It raises an error if something wrong has happened
(either the file doesn't exist or permissions aren't sufficient to copy when required).
If the function copies the file, and only in that case, it return true.
Example:
deleteFile("Documentation/readme.txt");
traceLine("First call to the 'copySmartFile()': the file is copied");
copySmartFile("readme.txt", "Documentation/readme.txt");
traceLine("Second call to the 'copySmartFile()': nothing is done");
copySmartFile("readme.txt", "Documentation/readme.txt");
Output:
First call to the 'copySmartFile()': the file is copied
Second call to the 'copySmartFile()': nothing is done
function coreString(text:string, pos:int, lastRemoved:int) :string
Parameter
Type
Description
text
string
the string to work on
pos
int
the beginning position, inclusive, starting at 0
lastRemoved
int
the number of characters to ignore at the end of text
Returns a substring of argument text.
The substring begins at the position specified by argument pos and ignores the last characters, which number is specifier by argument lastRemoved.
The first character starts at position 0, the next at position 1, and so on.
Example:
local sSentence = "Do you believe in human being?";
traceLine("coreString('" + sSentence + "', 18, 7) = '" + coreString(sSentence, 18, 7) + "'");
Output:
coreString('Do you believe in human being?', 18, 7) = 'human'
function countStringOccurences(string:string, text:string) :int
Parameter
Type
Description
string
string
sequence of characters where occurrences of substring text are to be counted
text
string
substring to count
Returns the number of times the substring specified by argument text is found into the sequence of characters held by argument string.
Example:
local sSentence = "Do you believe in human being?";
traceLine("countStringOccurences('" + sSentence + "', 'in') = " + countStringOccurences(sSentence, "in"));
Output:
countStringOccurences('Do you believe in human being?', 'in') = 2
This function creates a new directory and returns whether the operation has succeeded or not.
It fails if the complete path already exists, or if it is invalid.
3.35 createINETClientSocket
function createINETClientSocket(remoteAddress:string, port:int) :int
function createVirtualFile(handle:string, content:string) :bool
Parameter
Type
Description
handle
string
the name of the virtual file to create
content
string
the content to put into the virtual file
This function allows creating a virtual file. The handle parameter
corresponds to the name given to the virtual file, which may be any
sequence of characters. The virtual file is populated with the text assigned to
the content argument.
The function always returns true, but it may be changed in the future if
some naming rules will be imposed to the handles for instance. Calling the function
to an existing virtual file causes the content to be updated with the
new one.
CodeWorker manipulates the concept of file that isn't persistent on a physical
disk, but remains stored in memory. These virtual files may be used
everywhere a file is required so as to replace it: copy, parsing or text generation.
When CodeWorker tries to open a file for reading or writing, it starts looking
for a virtual file that has the same name.
Once a virtual file doesn't serve anymore, don't forget to free the
useless memory it takes up by calling the deleteVirtualFile() function
(see deleteVirtualFile()).
Example:
function createVirtualTemporaryFile(content:string) :string
Parameter
Type
Description
content
string
the content to put into the virtual file
This function allows creating a virtual file, for which the name of the
virtual file must be chosen by the routine. The virtual file is populated with
the text assigned to the content argument.
The function returns the name that the routine has composed for this virtual file.
The only difference with the function createVirtualFile() (see
createVirtualFile()) lies in the way to choose of the virtual file name.
After creating the file, it behaves as any other virtual file.
Example:
local sScriptFile = createVirtualTemporaryFile(
"a protected area:" + endl() +
"@setProtectedArea(\"umbrella\");@finished!");
traceLine("Name of the (virtual) script file = '" + sScriptFile + "':");
local sGeneratedFile = createVirtualTemporaryFile("");
generate(sScriptFile, project, sGeneratedFile);
traceLine("generated (virtual) file '" + sGeneratedFile + "':");
traceLine(loadVirtualFile(sGeneratedFile));
deleteVirtualFile(sGeneratedFile);
deleteVirtualFile(sScriptFile);
Output:
Name of the (virtual) script file = '.~#0':
generated (virtual) file '.~#1':
a protected area:
//##protect##"umbrella"
//##protect##"umbrella"
finished!
This procedure looks for slices into the argument text, which are
separated by the substring put into argument separator. These slices
are pushed into an array node as items called list.
If the argument text doesn't contain any occurrence of the argument
separator, the argument list will contain only one item that
is text.
Example:
local sText = "a yellow submarine";
local listOfItems;
traceLine("cutString('" + sText + "', ' ', listOfItems):");
cutString(sText, " ", listOfItems);
traceObject(listOfItems);
Decode an URL from an HTTP request, meaning that the '+' character changes in a space and
all hexadecimal descriptions of bytes (2 digits preceded by '%') are
converted to characters.
Note that conversions are transparent while doing HTTP requests.
Example:
local sURL = "Roger+Rabbit%26%25%24%3D%21%3F";
traceLine("URL before HTTP decoding = '" + sURL + "'");
traceLine("URL after HTTP decoding = '" + decodeURL(sURL) + "'");
Output:
URL before HTTP decoding = 'Roger+Rabbit%26%25%24%3D%21%3F'
URL after HTTP decoding = 'Roger Rabbit&%$=!?'
Deletes the file whose name is given by parameter filename. If the file
cannot be found or if it cannot be deleted, the function returns false.
Note that if the file name is a relative path, it is understood as being relative
to the current directory where the interpreter has been launched. So, the
interpreter doesn't search into include directories passed to the command line
(option -I), to offer a more secure use.
Example:
This function removes from memory the virtual file whose name is given
by the handle parameter.
It returns true if the virtual file was created before and has been
removed successfully.
See also:createVirtualFilecreateVirtualFile(), createVirtualTemporaryFilecreateVirtualTemporaryFile(), existVirtualFileexistVirtualFile(), loadVirtualFileloadVirtualFile()
3.44 div
function div(dividend:double, divisor:double) :double
Parameter
Type
Description
dividend
double
the dividend
divisor
double
the divisor
Returns the result of arithmetic division dividend/divisor.
Members are converted from strings to numbers, supposed being worth 0 if a parsing error occurs;
then the division is processed, and the result is converted to a string,
skipping fractional part if all digits after the dot are 0.
Remember that the symbol '/' doesn't mean anything in the standard syntax of the
language, so there is no way to confuse for expressing a division.
However, it exists an escape mode that allows writing arithmetic expressions
between '$' symbols, as formula under LaTeX. So, $dividend/divisor$
is equivalent to div(dividend, divisor).
Example:
Encode an URL for an HTTP request, meaning that the space character changes in '+' and
all non-alphanumeric characters are encoded in hexadecimal, preceded by '%'.
Note that conversions are transparent while doing HTTP requests.
Example:
local sURL = "Roger Rabbit&%$=!?";
traceLine("URL before HTTP encoding = '" + sURL + "'");
traceLine("URL after HTTP encoding = '" + encodeURL(sURL) + "'");
Output:
URL before HTTP encoding = 'Roger Rabbit&%$=!?'
URL after HTTP encoding = 'Roger%20Rabbit%26%25%24%3D%21%3F'
function endl() :string
Returns an end of line, value depending on the platform on which the
script is executed. It returns "\r\n" under Windows,
and "\n" on any UNIX platform.
Example:
endl() = '
'
length = 2
first ASCII character = 13
3.47 endString
function endString(text:string, end:string) :bool
Parameter
Type
Description
text
string
a sequence of characters to test
end
string
the postfix
"true" if the argument end is a postfix of the character
sequence represented by text; "" otherwise. Note also that
"true" will be returned if end is an empty string or is
equal to argument text.
Example:
will contain the list of all environment variable names
The procedure returns the array of all environment variable in the argument table.
The name of the environment variable is assigned to the value of the item.
Example:
local theTable;
environTable(theTable);
foreach i in theTable traceLine(i);
Output:
ALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\clemaire\Application Data
ClassPath=C:\Win32App\TIBCO\RV6\java\rvjpro.jar;
COMMONHOME=C:\Program Files\Rational\common
CommonProgramFiles=C:\Program Files\Common Files
COMPUTERNAME=HP2666-90RPY
ComSpec=C:\WINNT\system32\cmd.exe
DISPLAY=:0.0
HOME=C:\Documents and Settings\clemaire
HOMEDRIVE=C:
HOMEPATH=\Documents and Settings\clemaire
LOGONSERVER=\\GIEDORIS
NUMBER_OF_PROCESSORS=2
NUTCROOT=C:\PROGRA~1\RATIONAL\RATION~1\NUTCROOT
NUTSUFFIX=1
NUT_SUFFIXED_SEARCHING=1
OS=Windows_NT
Os2LibPath=C:\WINNT\system32\os2\dll;
Path=C:\Perl\bin\;C:\win32app\miktex\miktex\bin;C:\win32app\Tcl\bin;C:\win32app\TIBCO\RV6\BIN;C:\WINNT\system32;C:\WINNT;C:\WINNT\System32\Wbem;C:\Reuters\Login;c:\win32app\j2sdk_nb\j2sdk1.4.2\bin;c:\projects\QUEST\thirdParty\bin;c:\projects\QUEST\build\bin;C:\PROGRA~1\ATT\Graphviz\bin;C:\PROGRA~1\ATT\Graphviz\bin\tools;C:\Program Files\Common Files\Compuware\NMShared
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.tcl
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 15 Model 2 Stepping 9, GenuineIntel
PROCESSOR_LEVEL=15
PROCESSOR_REVISION=0209
ProgramFiles=C:\Program Files
PROMPT=$P$G
QUEST=c:\projects\QUEST
RATL_RTHOME=C:\Program Files\Rational\Rational Test
ROOTDIR=C:\PROGRA~1\RATIONAL\RATION~1\NUTCROOT
RQAHOME=C:\Program Files\Rational\Rational Test\QualityArchitect
SHELL=C:\PROGRA~1\RATIONAL\RATION~1\NUTCROOT\mksnt\sh.exe
SMS_LOCAL_DIR=C:\WINNT
SystemDrive=C:
SystemRoot=C:\WINNT
TEMP=C:\DOCUME~1\clemaire\LOCALS~1\Temp
TERM=nutc
TERMCAP=C:\PROGRA~1\RATIONAL\RATION~1\NUTCROOT\etc\termcap
TERMINFO=C:\PROGRA~1\RATIONAL\RATION~1\NUTCROOT\usr\lib\terminfo
TMP=C:\DOCUME~1\clemaire\LOCALS~1\Temp
TMPDIR=C:\DOCUME~1\clemaire\LOCALS~1\Temp
USERDNSDOMAIN=gaia.lmmodev.bnp.fr
USERDOMAIN=GAIA
USERNAME=clemaire
USERPROFILE=C:\Documents and Settings\clemaire
VS71COMNTOOLS=C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\
windir=C:\WINNT
Compares two numbers and returns true if they are identical.
Sometimes, the operator '==' is suitable to compare numbers, in the
case where their decimal representation are strictly the same. But be careful
that expression "7.0" == "7" is false!
However, it exists an escape mode that allows writing arithmetic comparisons
between '$' symbols, as formula under LaTeX. So, $left==right$
is equivalent to equal(left, right).
Example:
function equalTrees(firstTree:treeref, secondTree:treeref) :bool
Parameter
Type
Description
firstTree
treeref
a parse tree
secondTree
treeref
another parse tree to compare with the first one
Compares two parse trees and returns true if they are identical
(same sub-nodes, same values, same entry keys on arrays of node, repeated
recursively).
Example:
the current node that will be accessed via this variable
command
string
some instructions of the scripting language to execute
This procedure interprets the argument command as instructions to
execute, written in the scripting language.
Example:
local myContext;
executeString(myContext,
"traceLine(\"Beginning of string execution:\");"
"insert this.name = \"execution\";"
"traceLine(\"End of string execution.\");");
traceLine("What we did during the string execution:");
traceObject(myContext);
Output:
Beginning of string execution:
End of string execution.
What we did during the string execution:
Tracing variable 'myContext':
name = "execution"
End of variable's trace 'myContext'.
function executeStringQuiet(this:tree, command:string) :string
Parameter
Type
Description
this
tree
the current node that will be accessed with this variable
command
string
some instructions of the scripting language to execute
This function interprets the argument command as instructions to
execute, written in the scripting language, but doesn't display messages to the
standard output stream. Messages are put into a string that is returned
by the function.
Example:
local myContext;
local sMessages = executeStringQuiet(myContext,
"traceLine(\"Beginning of string execution:\");"
"insert this.name = \"execution\";"
"traceLine(\"End of string execution.\");");
traceLine("What we did during the execution:");
traceObject(myContext);
traceLine("What was intended to the console during the execution:");
traceLine(sMessages);
Output:
What we did during the execution:
Tracing variable 'myContext':
name = "execution"
End of variable's trace 'myContext'.
What was intended to the console during the execution:
Beginning of string execution:
End of string execution.
Checks whether a file exists or not, looking for include directories passed on
the command line.
This function doesn't work to check the existence of a directory; use exploreDirectory() instead.
Example:
local sFilename = "Documentation/CodeWorker.pdf";
traceLine("Checks existence of file '" + sFilename + "' = '" + existFile(sFilename) + "'");
Output:
Checks existence of file 'Documentation/CodeWorker.pdf' = 'true'
file name or block of instructions of the pattern script
this
treeref
the current node that will be accessed with this variable
outputFileName
string
the existing file to expand
Expands an existing file whose name is passed to the argument outputFileName,
by executing the pattern scriptpatternFileName on it.
Expanding a file consists of generating code into marked out areas only, the rest
of the file remaining the same. The markup is put into a comment, knowing that the
syntax of the comment must conform to the type of the expanded file outputFileName.
So, an HTML file expects <!- - and - ->, a JAVA file is waiting
for // and "\n", ... The markup is announced by
##markup## followed by a string that represents the markup key.
Don't forget to configure correctly the syntax of comment boundaries with procedures
setCommentBegin() (see setCommentBegin()) and setCommentEnd() (see setCommentEnd()).
When the procedure is called, CodeWorker jumps from a markup to another. To
handle a markup, it checks whether text was already generated, put between tags
##begin##"markup-key" and ##end##"markup-key",
added automatically the first time an expansion is required, to demarquate the
portion of code that doesn't belong to the user. Then, it extracts all protected
areas, if any, and it generates code at the position of the markup, adding
begin/end tags seen before.
If the interpreter finds the tag ##script## just after the markup,
it extracts the embedded text, considered as a script, eventually put between comments.
Otherwise, the interpreter executes the pattern script.
Note that some data might be put between tags ##data##, accessible
in the template-based script via the function getMarkupValue() (see getMarkupValue()).
This block of custom data comes after the ##script## tag, if present.
The same pattern script is called for all markups, so, to distinguish
them and not to generate always the same text, it controls the current markup key
being processed via the function getMarkupKey() (see getMarkupKey()).
Be careful not to confuse this prodedure with generate() that doesn't care
about markups and that overwrites the output file completely, except protected
areas of course.
See also:autoexpandautoexpand(), generategenerate(), generateStringgenerateString(), translatetranslate(), parseAsBNFparseAsBNF(), parseFreeparseFree(), parseFreeQuietparseFreeQuiet(), parseStringAsBNFparseStringAsBNF(), translateStringtranslateString()
3.61 exploreDirectory
function exploreDirectory(directory:tree, path:string, subfolders:bool) :bool
Parameter
Type
Description
directory
tree
node that will contain the name of all files and folders
path
string
the directory to explore
subfolders
bool
to explore sub directories recursively
Explores the directory whose name is passed to the argument path. The list
of files is put into the node's array directory.files and the
list of directories are put into the node's array directory.directories.
Exploring sub directories is required by the argument subfolders and each
node of the node's array directory.directories repeats the same
process recursively. The key of an array's node is the short name of the file or
the directory and the value of a directory item is the relative path, whereas
the value of a file item is also the short name.
If the directory cannot be found, the variable directory doesn't change
and the function returns false. If the directory doesn't contain any
file, the attribute directory.files isn't created. If the
directory doesn't contain any subfolder, the attribute
directory.directories isn't created.
Example:
local theDirectory;
local sPathToExplore = project.winBinaries; // Windows package of CodeWorker
if !exploreDirectory(theDirectory, sPathToExplore, true) error("unable to find the directory");
traceLine("starting directory = '" + theDirectory + "':");
foreach j in theDirectory.files {
traceLine(" '" + j + "'");
}
foreach i in cascading theDirectory.directories {
traceLine("- directory '" + i + "':");
foreach j in i.directories {
traceLine(" subfolder '" + key(j) + "'");
traceLine(" path '" + j + "'");
}
if key(i) == "GettingStarted" {
traceLine(" ... a lot of files!");
} else {
foreach j in i.files {
traceLine(" '" + j + "'");
}
}
}
function extractGenerationHeader(filename:string, generator:stringref, version:stringref, date:stringref) :string
Parameter
Type
Description
filename
string
generated file to check
generator
stringref
name of the application that has generated the file
version
stringref
version of the generator
date
stringref
date/time of the generation
Looks for a generation header into the file passed to the argument filename.
It returns the comment that was put into the header (see procedure
setGenerationHeader()setGenerationHeader()) during the generation,
after having assigned the output parameters:
generator with the name of the application that have generated the file, "CodeWorker" normally,
version with the version of the generator, the version of CodeWorker normally,
date with the date and time of the generation, conforming to
"%d%b%Y %H:%M:%S"; 12dec2002 10:00:23 for example,
The generation header is inlayed in the comment delimeters and conforms to the
format:
if the comment holds on a single line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##" '"'comment'"'end-comment
if the comment holds on more than one line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##"end-comment begin-comment"##header start##"end-comment begin-commentline1end-comment ... begin-commentlinenend-comment begin-comment"##header end##"end-comment
Example:
setGenerationHeader("Popeye's Village\nGozo and Comino");
local sScriptFile = "GettingStarted/Tiny-JAVA.cwt";
local sFileName = "Documentation/" + project.listOfClasses#back.name + ".java";
generate(sScriptFile, project.listOfClasses#back, sFileName);
local sGenerator;
local sVersion;
local sDateTime;
traceLine("comment of the generation header = '" + extractGenerationHeader(sFileName, sGenerator, sVersion, sDateTime) + "'");
traceLine("generator = '" + sGenerator + "'");
traceLine("version = '" + sVersion + "'");
traceLine("date = '" + sDateTime + "'");
setGenerationHeader("");
Output:
comment of the generation header = 'Popeye's Village
Gozo and Comino'
generator = 'CodeWorker'
version = 'v1.39'
date = '16dec2002 15:31:38'
Returns the date-time of last access to file whose name is passed to the argument filename.
If an error occurs, it returns one code among the following:
"-1": unknown error that shouldn't appear,
"-2": permission denied,
"-3": too many files have been opened,
"-4": file not found,
Example:
local sFileName = "Documentation/CodeWorker.tex";
local sLastAccessTime = fileLastAccess(sFileName);
if startString(sLastAccessTime, "-") error("error code = " + sLastAccessTime + "!");
traceLine("last access to '" + sFileName + "' = " + sLastAccessTime);
Output:
last access to 'Documentation/CodeWorker.tex' = 07oct2004 17:34:21
function fileLastModification(filename:string) :string
Parameter
Type
Description
filename
string
name of the file to ask for its last modification time
Returns the date-time of last modification to file whose name is passed to the argument filename.
If an error occurs, it returns one code among the following:
"-1": unknown error that shouldn't appear,
"-2": permission denied,
"-3": too many files have been opened,
"-4": file not found,
Example:
local sFileName = "Documentation/CodeWorker.tex";
local sLastModificationTime = fileLastModification(sFileName);
if startString(sLastModificationTime, "-") error("error code = " + sLastModificationTime + "!");
traceLine("last modification of '" + sFileName + "' = " + sLastModificationTime);
Output:
last modification of 'Documentation/CodeWorker.tex' = 07oct2004 05:52:00
Returns the number of lines that the file passed to the argument filename
contains.
If the file cannot be found nor opened, the function returns -1.
Example:
local theFiles;
if !scanFiles(theFiles, "Generation", "*.cw?", true) error("impossible to scan the directory");
local iLines = 0;
foreach i in theFiles iLines = $iLines + fileLines(i)$;
traceLine("total of script lines to generate \"CodeWorker\" = " + iLines);
Output:
total of script lines to generate "CodeWorker" = 6501
The chmod function returns the permission setting of the file specified by filename.
The permission setting controls read and write and execute access to the file.
The returned value holds the permission setting of the file as a concatenation of chars amongst the following:
'R' for reading permitted,
'W' for writing permitted,
'X' for executing permitted (ignored on Windows platform),
If an error occurs, the function returns one code among the following:
"-1": unknown error that shouldn't appear,
"-2": permission denied,
"-3": too many files have been opened,
"-4": file not found,
Example:
local sPermission = fileMode("Documentation/CodeWorker.tex");
if startString(sPermission, "-") error("error code = " + sPermission);
traceLine("permission on file 'Documentation/CodeWorker.tex' = '" + sPermission + "'");
Output:
permission on file 'Documentation/CodeWorker.tex' = 'RW'
function findElement(value:string, variable:treeref) :bool
Parameter
Type
Description
value
string
a key as an array entry
variable
treeref
a variable that contains an array of nodes
This function looks for a key, given by argument value, as an entry of
the nodes array passed to argument variable. If the key is found, the
function returns true, and and empty string otherwise.
Example:
function findFirstChar(text:string, someChars:string) :int
Parameter
Type
Description
text
string
the string to explore
someChars
string
a set of individual characters
Returns the location into text of the first character encountered that
belongs to the set of characters passed to argument someChars. The
position starts counting at 0. If no occurrence has been found, the
negative value -1 is returned.
Example:
local sText = "looking for a token: \"...\" f(a,b) {...}";
traceLine("sText = '" + composeCLikeString(sText) + "'");
traceLine("findFirstChar(sText, '\"({') = " + findFirstChar(sText, "\"({"));
Output:
sText = 'looking for a token: \"...\" f(a,b) {...}'
findFirstChar(sText, '"({') = 21
function findFirstSubstringIntoKeys(substring:string, array:treeref) :int
Parameter
Type
Description
substring
string
a sequence of characters to search into keys of a node's array
array
treeref
a variable that contains an array of nodes
This function returns the position of the first item, such as its corresponding
entry key into the list owned byvariable contains the substring passed
to argument substring. The position starts counting at 0.
If no item is found, the negative value -1 is returned.
Example:
function findLastString(text:string, find:string) :int
Parameter
Type
Description
text
string
a sequence of characters to explore
find
string
a substring to find into text
Returns the position of the last occurrence of the substring find into
the sequence of characters passed to argument text. The position starts
counting to 0.
If the substring find doesn't belong to text, the negative value
-1 is returned.
Example:
local sText = "the lamp of experience";
traceLine("sText = '" + sText + "'");
traceLine("findLastString(sText, 'p') = '" + findLastString(sText, "p") + "'");
Output:
sText = 'the lamp of experience'
findLastString(sText, 'p') = '14'
function findNextString(text:string, find:string, position:int) :int
Parameter
Type
Description
text
string
a sequence of characters to explore
find
string
a substring to find into text
position
int
the position in the string (starting at 0) the search must begin
Returns the lowest beginning index of the substring find that matches
the sequence of characters passed to argument text, starting the search at position included. The index starts
counting to 0.
If the substring find doesn't belong to text (starting at position), the negative value
-1 is returned.
Example:
local sText = "the lamp of experience";
traceLine("sText = '" + sText + "'");
traceLine("findNextString(sText, 'p', 8) = '" + findNextString(sText, "p", 8) + "'");
Output:
sText = 'the lamp of experience'
findNextString(sText, 'p', 8) = '14'
function findNextSubstringIntoKeys(substring:string, array:treeref, next:int) :int
Parameter
Type
Description
substring
string
a sequence of characters to search into keys of a node's array
array
treeref
a variable that contains an array of nodes
next
int
the position after which looking for the next item
Returns the position of the next item of list passed to argument variable,
whose entry key contains the substring given by argument substring. The
next item is searched after position passed to argument next. The
position starts counting at 0.
If no item is found, the negative value -1 is returned.
Example:
function findString(text:string, find:string) :int
Parameter
Type
Description
text
string
a sequence of characters to explore
find
string
a substring to find into text
Returns the position of the first occurrence of the substring find into
the sequence of characters passed to argument text. The position starts
counting to 0.
If the substring find doesn't belong to text, the negative value
-1 is returned.
Example:
local sText = "the lamp of experience";
traceLine("sText = '" + sText + "'");
traceLine("findString(sText, 'of') = '" + findString(sText, "of") + "'");
Output:
sText = 'the lamp of experience'
findString(sText, 'of') = '9'
Returns true if the index argument i points to the first item
of the iterated list.
Example:
local myTree;
insert myTree["Everest"] = "mountain";
insert myTree["Tea spoon"] = "silverware";
foreach i in myTree {
if first(i) traceLine("The first item key of the list is '" + key(i) + "'");
}
function formatDate(date:string, format:string) :string
Parameter
Type
Description
date
string
a date-time representation to transform
format
string
the format that will be applied to the date argument
Converts a date to another format, or extracts just a part of the date. The date
is passed to argument date, and the format given by format.
Each field of the format specification is a single character or a format type
signifying a particular format option. A format option
starts with a percent sign. If a percent sign is followed by a character that
has no meaning as a format type, an error is raised. To print a percent-sign
character, use '%%'.
The format type determines how the associated argument, at the current location
to the date, must be interpreted:
'%d' means that a 2-digits day of the month must be written,
'%e' means that a day of the month must be written, such as
1 but not 01
'%j' means that the day of the year must be written,
'%m' means that a 2-digits month must be written,
'%B' means that the complete english name of the month must
be written,
'%b' means that the truncated english name of the month must
be written: only the 3 first characters,
'%Y' means that a 4-digits year must be written,
'%y' means that a 2-digits year must be written,
'%t' means that the number of days since 30dec1899 must be written (WingZ format),
'%w' means that the weekday must be written as an integer
(0-6; 0 is sunday),
'%W' means that the weekday must be written as the complete english name,
'%H' means that a 2-digits hour must be written,
'%I' means that a 2-digits hour (12 max) must be written,
'%p' means that "AM" / "PM" must be written,
'%M' means that a 2-digits minute must be written,
'%S' means that a 2-digits second must be written,
'%L' means that a 3-digits millisecond must be written,
'%D' is equivalent to '%m/%d/%y',
'%r' is equivalent to '%I:%M:%S %p',
'%T' is equivalent to '%H:%M:%S',
An error occurs if a temporal argument doesn't belong to those listed above, or if
the date to format doesn't conform to "%d%b%Y %H:%M:%S.%L".
Example:
traceLine("release of the documentation = '" + getNow() + "'");
traceLine("a new format = '" + formatDate(getNow(), "%B %d, %Y") + "'");
traceLine("the hour only = '" + formatDate(getNow(), "%H") + "'");
Output:
release of the documentation = '07oct2004 20:42:00.500'
a new format = 'october 07, 2004'
the hour only = '20'
the current node that will be accessed via this variable
outputFileName
string
the output file to generate
Generates a file whose name is passed to the argument outputFileName,
by executing the pattern scriptpatternFileName on it.
Up to version 2.18, the pattern script was
necessary passed as a script file name. Since version 2.19,
the function admits to embed the script in the place of the corresponding
argument patternFileName, inlaying the script in brackets:
//generation of an HTML file, which shows the title and the content
//of some financial market news previously extracted generate(
{<html>
<body>
@
foreach i in this.news {
@@composeHTMLLikeString(i.title)@<br>@endl()@@
@<table><tr><td>@endl()@@
@@composeHTMLLikeString(i.body) + endl()@@
@</td></tr></table>@endl()@@
}
@ </body>
</html>
@}, project, "news.html");
It avoids the writing of 2 files, as it was unavoidable before:
generate("news2HTML.cwt", project, "news.html");
such as "news2HTML.cwt", which contains:
<html>
<body>
@
foreach i in this.news {
@@composeHTMLLikeString(i.title)@<br>@endl()@@
@<table><tr><td>@endl()@@
@@composeHTMLLikeString(i.body) + endl()@@
@</td></tr></table>@endl()@@
}
@ </body>
</html>
Generating a file consists of extracting the protected areas from the output
file, before overwriting it with the text generated by the pattern script.
It is possible to put a header of generation at the beginning of the file that
will specify some information such as the name of the generating tool
(CodeWorker normally) and the version of the generator and the date of generation
and a custom field of data. This header of generation (see setGenerationHeader()setGenerationHeader()) isn't taken into account while comparing the new
generated text with the precedent version of the file on disk.
If the output file may contain some protected areas, don't forget to configure
correctly the syntax of comment boundaries with procedures setCommentBegin()
(see setCommentBegin()) and setCommentEnd() (see setCommentEnd()).
Be careful not to use this prodedure instead of expand(). Expansion
saves all text, except into markups, while generation saves protected areas only
and overwrites the rest!
See also:expandexpand(), autoexpandautoexpand(), generateStringgenerateString(), translatetranslate(), parseAsBNFparseAsBNF(), parseFreeparseFree(), parseFreeQuietparseFreeQuiet(), parseStringAsBNFparseStringAsBNF(), translateStringtranslateString()
the current node that will be accessed via this variable
outputString
stringref
the output text to generate
Generates a sequence of characters, which is stored into the argument outputString,
by executing the pattern scriptpatternFileName on it.
Generating a sequence of characters consists of extracting the protected areas from the
outputString string, before overwriting it with the text generated by the pattern script.
It is possible to put a header of generation at the beginning of the file that
will specify some information such as the name of the generating tool
(CodeWorker normally) and the version of the generator and the date of generation
and a custom field of data. This header of generation (see setGenerationHeader()setGenerationHeader()) isn't taken into account while comparing the new
generated text with the precedent version of the file on disk.
If the output string may contain some protected areas, don't forget to configure
correctly the syntax of comment boundaries with procedures setCommentBegin()
(see setCommentBegin()) and setCommentEnd() (see setCommentEnd()).
See also:expandexpand(), autoexpandautoexpand(), generategenerate(), translatetranslate(), parseAsBNFparseAsBNF(), parseFreeparseFree(), parseFreeQuietparseFreeQuiet(), parseStringAsBNFparseStringAsBNF(), translateStringtranslateString()
3.81 getArraySize
function getArraySize(variable:treeref) :int
Parameter
Type
Description
variable
treeref
any node of a tree
Returns the number of items the argument variable contains
into its embedded array, or 0 if the array doesn't exist.
Example:
function getCommentBegin() :string
Returns the value of a beginning of comment, which is exploited by the procedures
taking in charge the source code generation, such as expand or generate.
CodeWorker must know the format of comments recognized by the output file,
to be able to extract or put protected areas, or to detect expansion
markups.
The beginning of comment assigned by default is worth '//'. This is
the symbol of C++ and JAVA comments that are the most frequently files
encountered for generation. Use the procedure setCommentBegin to change
it.
Some languages accept more than one format of comment. It is the case of C++ or
JAVA or non-standard HTML (Microsoft extended HTML with the
non-recommended tag '<COMMENT' that the W3C hasn't admitted).
CodeWorker can't handle more than one beginning of comment format for an
output file, but you'll haven't to suffer about it, because you have the control
on writing the markups into the output file, and so, to conform to a unique
representation of comments.
Be careful that if the beginning and the end of comments haven't been assigned
correctly before generating a file, the protected areas will not be extracted,
and so, lost for ever!
Example:
traceLine("This example is running while processing the documentation, so we are expecting a LaTeX comment: '" + getCommentBegin() + "'");
Output:
This example is running while processing the documentation, so we are expecting a LaTeX comment: '//'
function getCommentEnd() :string
Returns the value of an end of comment, which is exploited by the procedures
taking in charge the source code generation, such as expand or generate.
CodeWorker must know the format of comments recognized by the output file,
to be able to extract or put protected areas, or to detect expansion
markups.
The end of comment assigned by default is worth '\r\n'. This is
the symbol of C++ and JAVA comments that are the most frequently files
encountered for generation. Use the procedure setCommentEnd to change
it.
Some languages accept more than one format of comment. It is the case of C++ or
JAVA or non-standard HTML (Microsoft extended HTML with the
non-recommended tag '/COMMENT>' that the W3C hasn't admitted).
CodeWorker can't handle more than one end of comment format for an
output file, but you'll haven't to suffer about it, because you have the control
on writing the markups into the output file, and so, to conform to a unique
representation of comments.
Be careful that if the beginning and the end of comments haven't been assigned
correctly before generating a file, the protected areas will not be extracted,
and so, lost for ever!
Example:
traceLine("This example is running while processing the documentation, so we are expecting a LaTeX comment: '" + composeCLikeString(getCommentEnd()) + "'");
Output:
This example is running while processing the documentation, so we are expecting a LaTeX comment: '\n'
function getCurrentDirectory() :string
Returns the current directory’s name as a fully qualified path, where separators
are always forward slashes / like in UNIX. Note that the current
directory is closed by a separator.
The function returns an empty string if the path is longer than 1024 characters.
Example:
The function returns the environment table entry containing the variable.
An error message is thrown if variable is not found in the
environment table.
See function existEnv() to check the existence before getting.
Use the putenv function to modify the value of an environment variable.
Example:
function getGenerationHeader() :string
Returns the comment that is added automatically to each file generated with the
procedure generate. Defining a comment for the generation header may be
required by passing the option -genheader on the command line or by
calling the procedure setGenerationHeader().
The generation header is inlayed in the comment delimeters and conforms to the
format:
if the comment holds on a single line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##" '"'comment'"'end-comment
if the comment holds on more than one line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##"end-comment begin-comment"##header start##"end-comment begin-commentline1end-comment ... begin-commentlinenend-comment begin-comment"##header end##"end-comment
Example:
if !getGenerationHeader() traceLine("no generation header required for the moment");
setGenerationHeader("Popeye's Village\nKnights of Malta");
traceLine("new generation header = '" + getGenerationHeader() + "'");
local sFileName = "GettingStarted/Tiny-JAVA.cwt";
traceLine("script to execute:");
local sContent = replaceString("\r", "", loadFile(sFileName));
local lines;
cutString(sContent, "\n", lines);
foreach i in lines if !startString(i, "//")
traceLine("\t" + i);
traceLine("class to generate = '" + project.listOfClasses#front.name + "'");
local sOutputText;
generateString(sFileName, project.listOfClasses#front, sOutputText);
traceLine("generated text:");
traceLine(sOutputText);
setGenerationHeader("");
Output:
no generation header required for the moment
new generation header = 'Popeye's Village
Knights of Malta'
script to execute:
package tiny;
public class @
name@ @
if existVariable(parent) {
@ extends @parent.name@ @
}
@{
// attributes:
@
function getJAVAType(myAttribute : variable) {
local sType = myAttribute.class.name;
if myAttribute.isArray {
set sType = "java.util.ArrayList/*<" + sType + ">*/";
}
return sType;
}
foreach i in listOfAttributes {
@ private @getJAVAType(i)@ _@i.name@ = null;
@
}
@
//constructor:
public @i.name@() {
}
// accessors:
@
foreach i in listOfAttributes {
@ public @getJAVAType(i)@ get@toUpperString(i.name)@() { return _@i.name@; }
public void set@toUpperString(i.name)@(@getJAVAType(i)@ @i.name@) { _@i.name@ = @i.name@; }
@
}
setProtectedArea("Methods");
@}
class to generate = 'Planet'
generated text:
//##generation header##CodeWorker##3.8.2##07oct2004 17:34:23##
//##header start##
//Popeye's Village
//Knights of Malta
//##header end##
package tiny;
public class Planet {
// attributes:
private _diameter = null;
//constructor:
public () {
}
// accessors:
public getDIAMETER() { return _diameter; }
public void setDIAMETER( diameter) { _diameter = diameter; }
//##protect##"Methods"
//##protect##"Methods"
}
function getHTTPRequest(URL:string, HTTPSession:tree, arguments:tree) :string
Parameter
Type
Description
URL
string
URL of the HTTP server
HTTPSession
tree
an object to describe the HTTP session
arguments
tree
list of the arguments to GET; the key contains the name of the argument and the element gives the value
This function sends an HTTP's GET request to the HTTP server pointed to by the parameter
URL with the list of arguments put into the the parameter arguments.
The function returns the document read from the HTTP server.
The function sendHTTPRequest() (see sendHTTPRequest()) describes
the structure of the HTTP session object.
See also:postHTTPRequestpostHTTPRequest(), sendHTTPRequestsendHTTPRequest()
3.88 getIncludePath
function getIncludePath() :string
It returns the include path passed to the command line with one or more times
the setting of option -I, or the latest include path set via the procedure
setIncludePath().
The include path is a concatenation of paths separated by semi-commas ( extbf{';'}).
Example:
traceLine("getIncludePath():");
local list;
cutString(getIncludePath(), ';', list);
foreach i in list traceLine(i);
function getLastDelay() :double
The function returns the last duration that was measured by a statement modifier
delay (see delay). The duration is expressed in seconds, eventually
with a floating point.
If the function is called during the execution while measuring the time consuming
(controlling sequence under a delay statement modifier), it returns the
time elapsed since the beginning of the time-keeping.
Example:
local list;
local iIndex = 4;
delay while isPositive(decrement(iIndex)) {
pushItem list = "element " + iIndex;
traceLine("creating node '" + list#back + "'");
}
traceLine("time of execution = " + getLastDelay() + " seconds");
Output:
creating node 'element 3'
creating node 'element 2'
creating node 'element 1'
time of execution = 0.000034876108924822608 seconds
function getNow() :string
Returns the current date-time, conforming to the format: %d%b%Y %H:%M:%S.%L
For explanations about format types, see function formatDate
at formatDate().
Example:
function getTextMode() :string
Returns the mode of text that has been retained for parsing and source code generation:
"DOS": the default value if the interpreter is running under a Windows platform,
"UNIX": the default value if the interpreter isn't running under a Windows platform,
"BINARY": not exploited yet, but intended to specify later that
the parsing and the source code generation are applied on binary files,
The impact of having samp{"DOS"} instead of any other mode is that special
comments, which announce markup keys and protected areas, will finish by
"\r\n" when the end of comment is a newline '\n'.
Example:
traceLine("This documentation is generated under '" + getTextMode() + "' text mode");
Output:
This documentation is generated under 'DOS' text mode
function getVariableAttributes(variable:treeref, list:tree) :int
Parameter
Type
Description
variable
treeref
the variable to explore
list
tree
will contain the name and type (reference to another node or not) of each attribute
Populates a list with all attribute names of a tree node.
The name of branches just below the node variable are put into list.
The attribute's name is a key in the list and there is no value assigned to the item,
except for attributes that point to another node (a reference). In that case, the
item is worth the complete name of the referenced node.
The function returns the number of attributes found, or a negative value (-1) if the
tree node variable doesn't exist.
Note: use #evaluateVariable() to navigate along a tree node, where the
complete name is determined at runtime.
Example:
local videostores;
insert videostores["Italia"].names["Video Coliseum"].town = "Roma";
local movies;
insert movies["Lock, Stock & Two Smoking Barrels"].director = "Guy Ritchie";
ref movies#front.shop = videostores["Italia"].names["Video Coliseum"];
local attributeNames;
getVariableAttributes(movies#front, attributeNames);
foreach i in attributeNames {
if i traceLine("movies#front." + key(i) + " -> " + i);
else traceLine("movies#front." + key(i) + " = \"" + composeCLikeString(#evaluateVariable("movies#front." + key(i))) + "\"");
}
function getVersion() :string
Returns the version number of the CodeWorker interpreter or, if a
version name has been passed to the command line via the option
-version, the version of old scripts being executed.
Example:
traceLine("The version of the interpreter is '" + getVersion() + "'");
function getWriteMode() :string
Returns how text is written during a generation or during an implicit copy
while translating: "insert" or "overwrite" mode (default mode).
See also:setWriteModesetWriteMode()
3.98 hexaToDecimal
function hexaToDecimal(hexaNumber:string) :int
Parameter
Type
Description
hexaNumber
string
an hexadecimal integer to convert to a decimal number
Converts an hexadecimal integer, passed to the argument hexaNumber, to a
signed decimal integer and returns the result. If hexaNumber doesn't conform to
the syntax of an hexadecimal number (hexaNumber ::= #!ignore ['0'..'9' | #noCase 'A'..'F']+),
the function raises an error.
Example:
a 4-bytes representation of a long integer sorted in the host bytes order
Converts a 4-bytes representation of a long integer to the network bytes order.
CodeWorker stores a byte as a 2-hexadecimal digits; the function raises
an error if the argument bytes is malformed.
Use longToBytes() and bytesToLong() to swap between decimal
and host binary representation of a long integer.
Example:
a 2-bytes representation of a short integer sorted in the host bytes order
Converts a 2-bytes representation of a short integer to the network bytes order.
CodeWorker stores a byte as a 2-hexadecimal digits; the function raises
an error if the argument bytes is malformed.
Use shortToBytes() and bytesToShort() to swap between decimal
and host binary representation of a short integer.
Example:
Indents the file passed to parameter file according to rules that depend
on the file extension:
cpp, cxx, h, hxx: will indent as expected for a C++ format,
java: will indent as expected for a JAVA format,
More format will be recognized in the future.
The function returns true if the file needed to be indented,
meaning that it has changed after processing the indentation.
Example:
We'll indent file 'Documentation/IndentSample.cpp' containing:
int f(int i) {
switch (i) {
case 2:
case 3:
if (i == 2) {
h();
}
g(i - 1);
break;
}
}
File changed after indenting = 'true'
File 'Documentation/IndentSample.cpp' after indentation:
int f(int i) {
switch (i) {
case 2:
case 3:
if (i == 2) {
h();
}
g(i - 1);
break;
}
}
Compares two numbers and returns true if the first member given by
argument left is strictly smaller than the second member passed to
argument right.
Don't use the operator '<' to compare numbers in the classical syntax of
the interpreter: it only checks the lexicographical order. So, '12 < 3' is true.
However, it exists an escape mode that allows writing arithmetic comparisons
between '$' symbols, as formula under LaTeX. So, $left<right$
is equivalent to inf(left, right).
Example:
asks for echoing the standard input on the console
Returns a character extracted from the standard input, the keyboard generally.
If no key was pressed, it returns an empty string.
See statement modifiers file_as_standard_input (file as standard input) and
string_as_standard_input (string as standard input) to change
the source of the standard input.
If the source of the standard input is the keyboard, the argument echo
has no effects. Otherwise, the input text is displayed into the console only if
echo is worth true.
3.105 inputLine
function inputLine(echo:bool) :string
Parameter
Type
Description
echo
bool
asks for echoing the standard input on the console
Returns a line that was extracted from the standard input, the keyboard generally.
See statement modifiers file_as_standard_input (file as standard input) and
string_as_standard_input (string as standard input) to change
the source of the standard input.
If the source of the standard input is the keyboard, the argument echo
has no effects. Otherwise, the input text is displayed into the console only if
echo is worth true.
Example:
traceText("Please enter something> ");
local sKeyboardText = inputLine(true);
traceLine("The user said: '" + sKeyboardText + "'");
Output:
Please enter something> These characters were typed by hand on the keyboard!
The user said: 'These characters were typed by hand on the keyboard!'
3.106 invertArray
procedure invertArray(array:treeref)
Parameter
Type
Description
array
treeref
the array to handle
Inverts the elements of the array passed to the well-named argument array,
such as the first item becomes the last one, and the last item the first one.
Example:
local list;
insert list["twin peaks"] = "twin peaks";
insert list["karakorum"] = "karakorum";
insert list["everest"] = "everest";
insert list["kilimanjaro"] = "kilimanjaro";
traceLine("before inverting the array:");
foreach i in list traceLine("\t" + i);
invertArray(list);
traceLine("after inverting the array:");
foreach i in list traceLine("\t" + i);
Output:
before inverting the array:
twin peaks
karakorum
everest
kilimanjaro
after inverting the array:
kilimanjaro
everest
karakorum
twin peaks
This predicate checks whether the number passed by parameter is strictly
negative or not.
If the argument isn't recognized as a number, the number is supposed to be worth
0, so the function returns false.
Be careful if you choose the expression '< 0' to compare numbers in the
classical syntax of the interpreter: it only checks the lexicographical order.
So, '+0.0 <= 0' is false!
However, it exists an escape mode that allows writing arithmetic comparisons
between '$' symbols, as formula under LaTeX. So, $number<= 0$
is equivalent to isNegative(number).
Example:
This predicate checks whether the number passed by parameter is strictly
positive or not.
If the argument isn't recognized as a number, the number is supposed to be worth
0, so the function returns false.
Be careful if you choose the expression '> 0' to compare numbers in the
classical syntax of the interpreter: it only checks the lexicographical order.
So, '-0.0 >= 0' is false!
However, it exists an escape mode that allows writing arithmetic comparisons
between '$' symbols, as formula under LaTeX. So, $number>= 0$
is equivalent to isPositive(number).
Example:
function joinStrings(list:tree, separator:string) :string
Parameter
Type
Description
list
tree
the list that contains the strings to join
separator
string
the sequence of chars that separates the strings
This function returns the concatenation of all strings put into list,
putting a separator between each of them.
If the list is empty, the function will return an empty string.
Example:
Returns true if the index argument i points to the last item
of the iterated list.
Example:
local myTree;
insert myTree["Everest"] = "mountain";
insert myTree["Tea spoon"] = "silverware";
foreach i in myTree {
if last(i) traceLine("The last item key of the list is '" + key(i) + "'");
}
function leftString(text:string, length:int) :string
Parameter
Type
Description
text
string
a sequence of characters
length
int
a positive number
Returns the first characters that belong to the string passed to the argument
text. The number of characters to take is given by argument
length. If the string contains less than length characters, the
function returns all of them.
Example:
Returns the length of the sequence of characters represented by argument text.
Example:
local sText = "A rabbit ran in the garden"; // size of this string is 26 characters
traceLine("lengthString(\"" + sText + "\") = " + lengthString(sText));
populated with the names of all files generated since the interpreter has launched
Populates the parameter files with the list of all output files generated
since the interpreter has launched.
The array files indexes each node with the name of the generated output file,
and each node owns a branch called scripts.
This branch gives the list of all template-based scripts that have contributed to the
generation of the output file (often one script only, but could be more).
The key index and the value of the nodes in the array scripts are worth the script file names.
The procedure raises an error if the tree parameter files doesn't exist.
Example:
local allOutputFiles;
listAllGeneratedFiles(allOutputFiles);
traceLine("List of all generated files:");
foreach i in allOutputFiles {
// A lot of output files are generated before building
// this document, such as C++ sources of CodeWorker:
// they are ignored
if i.endString(".cpp") || i.endString(".h") continue;
// Other files are displayed:
traceLine(" * '" + i.key() + "'");
traceText(" -> {");
foreach j in i.scripts {
if !j.first() traceText(", ");
traceText('\"' + j + '\"');
}
traceLine('}');
}
Returns the binary content of the file whose name is passed to argument file.
The content concatenates a sequence of hexadecimal digits, so a byte is stored in
2 characters: binary-content ::= [byte]*; byte ::= ['0'..'9' | 'A'..'F' | 'a'..'f']2;
If the file doesn't exist or can't be read with success, an error occurs.
Example:
local sContent = loadBinaryFile("readme.txt");
local sFormatedContent;
while $sContent.length() > 40$ {
if sFormatedContent set sFormatedContent += endl();
set sFormatedContent += leftString(sContent, 80);
set sContent = subString(sContent, 80);
}
if sFormatedContent && sContent set sFormatedContent += endl();
if sContent set sFormatedContent += sContent;
traceLine("content of file 'readme.txt' is:" + endl() + sFormatedContent);
Returns the content of the file whose name is passed to argument file.
If the file doesn't exist or couldn't be read with success, an error occurs.
Example:
traceLine("content of file 'readme.txt' is:" + endl() + loadFile("readme.txt"));
Output:
content of file 'readme.txt' is:
//////////////////////////////////////////////////
// CodeWorker //
// ---------- //
//////////////////////////////////////////////////
* You have downloaded CodeWorker_WINxxx.zip.
Directories and files:
___
|- "GettingStarted.bat" allows running all examples, which are proposed
| into the 'getting started' section of the documentation,
|
|- include\ :
| . "CW4dl.h" is the C++ header for building packages intended to
| extend the functionalities via dynamic libraries,
| . "CGRuntime.h", "CppParsingTree.h", "UtlException.h" allow the
| binding of CodeWorker in a C++ application.
|
|- bin\
| . "CodeWorker.exe" is the interpreter,
| . "CodeWorker.lib" is the library for binding CodeWorker in a
| C++ application,
| . "libcurl.dll" provides the cURL HTTP functionalities
|
|- Scripts\
| |_ Tutorial\
| |___ GettingStarted\ : examples of the 'getting started'
| section are found here,
* You have downloaded the sources from CodeWorker_SRCxxx.zip.
Directories and files:
___
|- "Makefile": run 'make all' to compile CodeWorker under Linux,
|- "CodeWorker.dsw": VC++ workspace to compile CodeWorker under Windows
|- "*.cpp", "*.h": source code
|
|- Documentation/
| . "CodeWorker.tex" is the TeX source of the documentation
| . other files are satellite files
|
|- Generation/ puts together all scripts needed to generate the
| source code, the documentation and the web site of
| CodeWorker
| . "GeneratorDriver.cws" is the leader script for generation tasks,
| . "descriptor.txt" describes all functions and procedures
| available in CodeWorker, with their prototype and an example,
|
|- Scripts/
| |_ Tutorial/
| |___ GettingStarted/ : examples of the 'getting started'
| section are found here,
|
|- WebSite/ the web site of CodeWorker, except downloads and the manual
| |
| |- "index.html": the entry point,
| |- "francais.html": same, but in French
| |
| |- examples/ all examples shown on the site,
| |
| |- repository/ the repository of CodeWorker's scripts
| | | . "CodeWorker_grammar.cwp": the complete grammar of the
| | | scripting language,
| | | . "CWscript2HTML.cwp": syntax highlighting + contextual
| | | help of CW scripts in HTML,
| | | . a lot of other scripts...
* FAQ
o How to generate the whole HTML documentation locally on my computer?
Answer:
- type:
CodeWorker -I Generation -path <parent-directory-of-WebSite>
-script GeneratorDriver.cws -D makeHTMLDoc
- example: if the directory "WebSite/" is in the folder
"CodeWorkerSources/", you have to type:
CodeWorker -I Generation -path CodeWorkerSources
-script GeneratorDriver.cws -D makeHTMLDoc
Returns the content of the virtual file whose name is passed to argument file.
If the virtual file doesn't exist or couldn't be read with success, an error occurs.
See also:createVirtualFilecreateVirtualFile(), createVirtualTemporaryFilecreateVirtualTemporaryFile(), deleteVirtualFiledeleteVirtualFile(), existVirtualFileexistVirtualFile()
3.120 log
function log(x:double) :double
Parameter
Type
Description
x
double
the floating-point whose logarithm is to compute
Returns the logarithm of x.
If x is negative, it throws an error.
If x is 0, it returns infinite.
Example:
function midString(text:string, pos:int, length:int) :string
Parameter
Type
Description
text
string
a sequence of characters
pos
int
a position into argument text
length
int
the number of characters to extract
Returns a substring located into the string text to the position passed
to the argument pos. The position starts counting at 0. The
substring will be extracted for a size given by parameter length, or
less if it has reached the end of the string.
If the argument pos is greater than the length of text, the
function returns an empty string.
Example:
Returns the remainder when the first operand is divided by the second. It
applies the modulus operator.
Members are converted from strings to integers, supposed being worth 0 if a parsing error occurs;
then the modulus is processed, and the result is converted to a string.
Remember that the symbol '%' doesn't mean anything in the standard syntax of the
language, so there is no way to confuse for expressing a modulus operator.
However, it exists an escape mode that allows writing arithmetic expressions
between '$' symbols, as formulae under LaTeX. So, $dividend%divisor$
is equivalent to mod(dividend, divisor).
Example:
Returns the result of arithmetic multiplication left*right.
Members are converted from strings to numbers, supposed being worth 0 if a parsing error occurs;
then the multiplication is processed, and the result is converted to a string,
skipping fractional part if all digits after the dot are 0.
Remember that the symbol '*' doesn't mean anything in the standard syntax of the
language, so there is no way to confuse for expressing a multiplication.
However, it exists an escape mode that allows writing arithmetic expressions
between '$' symbols, as formulae under LaTeX. So, $left*right$
is equivalent to mult(left, right).
Example:
a 4-bytes representation of a long integer sorted in the network bytes order
Converts a 4-bytes representation of a long integer to the host bytes order.
CodeWorker stores a byte as a 2-hexadecimal digits; the function raises
an error if the argument bytes is malformed.
Use longToBytes() and bytesToLong() to swap between decimal
and host binary representation of a long integer.
Example:
a 2-bytes representation of a short integer sorted in the network bytes order
Converts a 2-bytes representation of a short integer to the host bytes order.
CodeWorker stores a byte as a 2-hexadecimal digits; the function raises
an error if the argument bytes is malformed.
Use shortToBytes() and bytesToShort() to swap between decimal
and host binary representation of a short integer.
Example:
This function does the same work as the unary operator '!': it returns
true if the evaluation of the expression is an empty string, and
false otherwise.
Example:
local myVariable;
traceLine("not(existVariable(myVariable)) = '" + not(existVariable(myVariable)) + "'");
Output:
not(existVariable(myVariable)) = ''
3.128 octalToDecimal
function octalToDecimal(octalNumber:string) :int
Parameter
Type
Description
octalNumber
string
an octal integer to convert to a decimal number
Converts an octal integer, passed to the argument octalNumber, to a
signed decimal integer and returns the result. If octalNumber doesn't conform to
the syntax of an octal number (octalNumber ::= #!ignore ['0'..'8']+),
the function raises an error.
Example:
name of the file where log information will be put
Creates (or erases if already exists) a log file, which remains valid upto the end of the execution.
Each trace function (traceLine(), traceText(), traceStack())
will write in the log file.
This function is very convenient for debugging a CGI script, where the standard
output is devoted to the result page.
Note that passing an empty filename stops the log mechanism.
function parseFreeQuiet(designFileName:string, this:tree, inputFileName:string) :string
Parameter
Type
Description
designFileName
string
the name of the parsing script that reads tokens in a procedural way
this
tree
the current node that will be accessed with this variable
inputFileName
string
the file to parse
This function parses the file passed to argument inputFileName, following
the instructions of the procedure-driven parsing script given by parameter
designFileName, but doesn't display messages to the standard output
stream. Messages are put into a string that is returned by the function.
Example:
local sScript = "GettingStarted/SimpleML-token-reading.cws";
local sDesign = "GettingStarted/SolarSystem0.sml";
traceLine("sScript = '" + sScript + "'");
traceLine("sDesign = '" + sDesign + "'");
traceLine("messages of parseFreeQuiet(sScript, project, sDesign):");
traceLine(parseFreeQuiet(sScript, project, sDesign));
Output:
sScript = 'GettingStarted/SimpleML-token-reading.cws'
sDesign = 'GettingStarted/SolarSystem0.sml'
messages of parseFreeQuiet(sScript, project, sDesign):
the file has been read successfully
Converts a package path to a directory path. A package path is a
sequence of identifiers separated by dots. All dots ('.') encountered
are replaced by a path separator ('\\ under
Windows and '/' on UNIX platforms). A path
separator is added at the end.
Example:
function postHTTPRequest(URL:string, HTTPSession:treeref, arguments:treeref) :string
Parameter
Type
Description
URL
string
URL of the HTTP server
HTTPSession
treeref
an object to describe the HTTP session
arguments
treeref
list of the arguments to POST; the key contains the name of the argument and the element gives the value
This function sends an HTTP's POST request to the HTTP server pointed to by the parameter
URL with the list of arguments put into the the parameter arguments.
The function returns the document read from the HTTP server.
The function sendHTTPRequest() (see sendHTTPRequest()) describes
the structure of the HTTP session object.
See also:getHTTPRequestgetHTTPRequest(), sendHTTPRequestsendHTTPRequest()
3.136 pow
function pow(x:double, y:double) :double
Parameter
Type
Description
x
double
the base
y
double
the exponent
Returns value of the argument x raised to the power of the second
argument y. The arguments are converted to numerics, being worth
0 when a conversion fails. The power is then processed, and the
result is converted to a string, skipping fractional part if all digits after
the dot are 0.
Example:
the HTML file that represents the highlighted script
This procedure proposes to highlight a script written for CodeWorker and
to provide the resulting colored script into an HTML file. Only '@'
and the text to put into the ouput stream are highlighted.
Example:
produceHTML("Scripts/Tutorial/GettingStarted/Tiny-JAVA.cwt", getWorkingPath() + "Scripts/Tutorial/GettingStarted/Tiny-JAVAhighlight.html");
traceLine("the script file has been highlighted into 'Tiny-JAVAhighlight.html'");
Output:
the script file has been highlighted into 'Tiny-JAVAhighlight.html'
Known bugs:
The procedure needs to be improved, so as to highlight tokens and keywords of the
language too. It doesn't work yet on BNF-driven scripts intended to a translation.
3.138 putEnv
procedure putEnv(name:string, value:string)
Parameter
Type
Description
name
string
name of the variable environment
value
string
new value to assign to the variable environment
If variable name is already part of the environment, its value is
replaced by value; otherwise, the new variable and its value are added
to the environment. You can remove a variable from the environment by specifying
an empty string.
This procedure affects only the environment that is local to the current
process; you cannot use them to modify the command-level environment.
That is, these functions operate only on data structures accessible to the
run-time library and not on the environment "segment" created for a
process by the operating system. When the current process terminates, the
environment reverts to the level of the calling process (in most cases, the
operating-system level). However, the modified environment can be passed to
any new processes created by the instruction system, and these new
processes get any new items added by putEnv.
Example:
putEnv("JUST_FOR_FUN", "I'd like to finish reading my newspaper");
traceLine("getEnv('JUST_FOR_FUN') = '" + getEnv("JUST_FOR_FUN") + "'");
Output:
getEnv('JUST_FOR_FUN') = 'I'd like to finish reading my newspaper'
function randomInteger() :int
Generates a pseudorandom number.
See also:randomSeedrandomSeed()
3.140 randomSeed
procedure randomSeed(seed:int)
Parameter
Type
Description
seed
int
a new seed for generating pseudorandom integers
Sets the seed for generating a series of pseudorandom integers. To change the seed
to a given starting point, choose any positive value different of 1 as the seed argument.
A value of 1 reinitializes the generator. Any negative value let CodeWorker choose
a random seed for you.
See also:randomIntegerrandomInteger()
3.141 receiveBinaryFromSocket
function receiveBinaryFromSocket(socket:int, length:int) :string
function receiveFromSocket(socket:int, isText:boolref) :string
Parameter
Type
Description
socket
int
a client socket descriptor
isText
boolref
the function will populate this parameter with true if read bytes designate a string and false if they are binary data
This function waits for bytes to read from socket and returns them.
If an error occurs, the function returns an empty string.
The function sets isText to:
true if it has received a text,
false if it has received binary data: the returned string is
then a sequence of bytes (CodeWorker represents a byte with 2 hexadecimal digits),
function relativePath(path:string, reference:string) :string
Parameter
Type
Description
path
string
the path to give as relative to reference
reference
string
a path that serves as the reference to determine the relative path
Returns the relative path that allow going to the well-named path,
considering the reference path as the starting point (like a current
directory). Under the Windows platform, if the two arguments don't
hold on the same drive, the absolute path of the first argument is returned.
Note that the arguments are converted to canonical paths (see canonicalPath()canonicalPath() for more information).
Example:
The function removes the directory specified by path. The directory must
not be the current working directory or the root directory.
The function returns false if the path is invalid or cannot be deleted.
Example:
local sDirectory = getWorkingPath() + "Scripts/Tutorial/GettingStarted/bin";
if !removeDirectory(sDirectory) error("impossible to remove '" + sDirectory + "'");
Removes the first element from the array of nodes called list.
Nothing occurs if list doesn't exist or is empty.
Example:
local myTree = "monkey";
pushItem myTree["Everest"];
pushItem myTree["Tea spoon"];
traceLine("the array 'myTree' has " + myTree.size() + " elements");
traceLine("the first element is removed:");
removeFirstElement(myTree);
traceObject(myTree);
Output:
the array 'myTree' has 2 elements
the first element is removed:
Tracing variable 'myTree':
"monkey"
myTree["Tea spoon"]
End of variable's trace 'myTree'.
function removeGenerationTagsHandler(key:string) :bool
Parameter
Type
Description
key
string
designates the handler to remove
Removes the current generation tags handler amongst those previously registered
thanks to the function addGenerationTagsHandler().
If the current generation tags handler is worth this one, no custom handler is selected.
Returns true if key designates a registered handler.
See also:addGenerationTagsHandleraddGenerationTagsHandler(), selectGenerationTagsHandlerselectGenerationTagsHandler()
3.150 removeLastElement
procedure removeLastElement(list:treeref)
Parameter
Type
Description
list
treeref
an array of nodes
Removes the last element from the array of nodes called list.
Nothing occurs if list doesn't exist or is empty.
Example:
local myTree = "monkey";
pushItem myTree["Everest"];
pushItem myTree["Tea spoon"];
traceLine("the array 'myTree' has " + myTree.size() + " elements");
traceLine("the last element is removed:");
removeLastElement(myTree);
traceObject(myTree);
Output:
the array 'myTree' has 2 elements
the last element is removed:
Tracing variable 'myTree':
"monkey"
myTree["Everest"]
End of variable's trace 'myTree'.
the attribute 'toRemove' has been removed from 'myTree' recursively
3.152 removeVariable
procedure removeVariable(node:treeref)
Parameter
Type
Description
node
treeref
the node to remove from the tree
All attributes of the argument node are deleted, its array of nodes is
cleared and its value becomes an empty string. If the node was referring to
another node, the link is cleared. Once these task are completed, the variable
node is removed from the tree it belongs to (as an attribute or an
element).
Note that trying to remove a local variable throws an error.
Example:
local myTree;
insert myTree.nodeToRemove = "the value";
localref myNode = myTree.nodeToRemove;
insert myNode.a1 = "attribute 1";
insert myNode.a2 = "attribute 2";
insert myNode.array["1"] = "node 1";
insert myNode.array["2"] = "node 2";
traceObject(myNode);
traceLine("-- the variable 'myNode' is removed:");
removeVariable(myNode);
traceObject(myTree);
Output:
Tracing variable 'myTree.nodeToRemove':
"the value"
a1 = "attribute 1"
a2 = "attribute 2"
array
array["1", "2"]
End of variable's trace 'myTree.nodeToRemove'.
-- the variable 'myNode' is removed:
Tracing variable 'myTree':
End of variable's trace 'myTree'.
function replaceTabulations(text:string, tab:int) :string
Parameter
Type
Description
text
string
a sequence of characters where spaces must be inserted instead of tabulations
tab
int
size of a tabulation
Returns the result of replacing all tabulations (character '\t') of
the string passed to argument text by spaces. The maximum of spaces to
insert instead of tabulation is given by the parameter tab.
Notice that spaces to insert are determined according to the position of the
tabulation in the string and the beginning of the line (it means that
'\n' characters are taken into account) and the tabulation size. So,
this function isn't equivalent to replaceString()replaceString().
Example:
local sText = " a little joke";
traceLine("replaceTabulations(sText, 4) = '" + replaceTabulations(sText, 4) + "'");
traceLine("replaceString('\t', " ", sText) = '" + replaceString("\t", " ", sText) + "'");
Output:
replaceTabulations(sText, 4) = ' a little joke'
replaceString(' ', , sText) = ' a little joke'
Searches the file filename in the current directory and, if fails, it
continues searching it in the include directories ('-I' switch on the
command line).
It returns the location of the file in directories, removing any ambiguity.
If the file doesn't exist, the function returns an empty string.
If filename points to a virtual file, the function returns filename. Example:
function rightString(text:string, length:int) :string
Parameter
Type
Description
text
string
a sequence of characters
length
int
a positive number
Returns the last characters that belong to the string passed to the argument
text. The number of characters to take is given by argument
length. If the string contains less than length characters, the
function returns all of them.
Example:
an output file that will contain the XML description of the main parse tree called project
Saves the parse tree of the project as an XML file. Each element of the XML
hierarchy takes the name of the corresponding attribute in the parse tree.
When a value is assigned to an attribute, it is reported into an XML attribute
called __VALUE. When an attribute represents an array of nodes, all
nodes are inlayed in the body of the XML element like it: each node is put
into an XML element called __ARRAY_ENTRY where the XML attribute
__KEY contains the entry key.
Example:
an output file that will contain the XML description of the structure of the main parse tree called project
Factorizes nodes of the parse tree of the project to distinguish an implicit
type for nodes, depending on their locations into the graph. The typed tree is
saved as an XML file.
Example:
this file has been parsed successfully
<project>
<listOfClasses>
<__ARRAY_TYPE name="compulsory" parent="optional[25%]">
<listOfAttributes>
<__ARRAY_TYPE class="compulsory" isArray="optional[66%]" name="compulsory">
</__ARRAY_TYPE>
</listOfAttributes>
</__ARRAY_TYPE>
</listOfClasses>
</project>
Known bugs:
Sometimes, when a type is encountered twice in very different locations of the
parse tree, a mistake on the proportion of presence may occur. It will be
corrected later.
See also:saveProjectsaveProject()
function scanDirectories(directory:tree, path:string, pattern:string) :bool
Parameter
Type
Description
directory
tree
node that will contain the name of filtered files and folders
path
string
the directory from where to start the exploration
pattern
string
the filter to apply on files to keep
Explores the directory whose name is passed to the argument path and
filters all files that validate the pattern. The list of files is put
into the node's array directory.files and the list of
directories are put into the node's array directory.directories.
The argument subfolders requires exploring sub-directories and each
node of the node's array directory.directories repeats the same
process recursively. The key of an array's node is the short name of the file or
the directory and the value of a directory item is the relative path, whereas
the value of a file item is also the short name.
If the directory cannot be found, the variable directory doesn't change
and the function returns false. If the directory doesn't contain any
file, the attribute directory.files isn't created. If the
directory doesn't contain any subfolder, the attribute
directory.directories isn't created.
Example:
local theDirectory;
local sPathToExplore = project.winBinaries; // Windows package of CodeWorker
if !scanDirectories(theDirectory, sPathToExplore, "Leader*.cws") error("unable to find the directory");
traceLine("starting directory = '" + theDirectory + "':");
foreach j in theDirectory.files {
traceLine(" '" + j + "'");
}
foreach i in cascading theDirectory.directories {
traceLine("- directory '" + i + "':");
foreach j in i.directories {
traceLine(" subfolder '" + key(j) + "'");
traceLine(" path '" + j + "'");
}
foreach j in i.files {
traceLine(" '" + j + "'");
}
}
function scanFiles(files:tree, path:string, pattern:string, subfolders:bool) :bool
Parameter
Type
Description
files
tree
node that will contain files that validate the pattern
path
string
the directory where to scan files
pattern
string
the filter to apply on files to keep
subfolders
bool
to scan sub directories recursively
Explores the directory path and filters all files that validate the pattern
given by parameter. Files are put into the node's array called files with
their relative path, which is assigned to the value of the item. The scan is
applied on subfolders if the argument subfolders passes true.
The pattern argument accepts the standard jocker characters ('*' and
'?'). If empty, pattern is considered as being worth "*".
The function returns true if the directory to scan exists.
Example:
local files;
local sDirectory = "Scripts/Tutorial/GettingStarted";
if !scanFiles(files, sDirectory, "Leader*.cws", true) error("impossible to find the directory");
traceLine("filtering recursively all files that conform to 'Leader*.cws'");
foreach i in files {
traceLine(" " + subString(i, lengthString(sDirectory)));
}
Output:
filtering recursively all files that conform to 'Leader*.cws'
/LeaderScript0.cws
/LeaderScript1.cws
/LeaderScript2.cws
/LeaderScript3.cws
/LeaderScript4.cws
/LeaderScript5.cws
/LeaderScript6.cws
function selectGenerationTagsHandler(key:string) :bool
Parameter
Type
Description
key
string
designates the handler to take
Selects the current generation tags handler amongst those previously registered
thanks to the function addGenerationTagsHandler().
If the parameter key is worth false (empty string), the default
generation tags handler is used.
Returns true if key designates a registered handler.
See also:addGenerationTagsHandleraddGenerationTagsHandler(), removeGenerationTagsHandlerremoveGenerationTagsHandler()
3.166 sendBinaryToSocket
function sendBinaryToSocket(socket:int, bytes:string) :bool
function sendHTTPRequest(URL:string, HTTPSession:treeref) :string
Parameter
Type
Description
URL
string
URL of the HTTP server
HTTPSession
treeref
an object to describe the HTTP session
This function sends an HTTP request to the HTTP server pointed to by the argument
URL, and returns the document read from the HTTP server.
If the request fails, an error message is thrown.
The well-named argument HTTPSession specifies some information into devoted attributes:
agent(optional) is the browser name, "CodeWorker" by default,
referer(optional),
proxy(optional):
proxy.host(compulsory),
proxy.port(compulsory),
proxy.userpwd(optional) is worth "user:password",
cookies(optional) is a list of nodes such as:
name(compulsory),
value(optional) is worth "" by default,
path(optional), populated from the HTTP header (see below)
domain(optional), populated from the HTTP header (see below)
expires(optional) for a permanent cookie, populated from the HTTP header (see below)
After processing the request successfully, you'll find information about the returned data.
If the data is a binary format, such as an archive or an image, the field HTTPSession.binary_data
is worth true.
If the data is a textual format, the detail of the received header lines are filled in
the array HTTPSession.header_lines. The array HTTPSession.cookies
is updated with the cookies extracted from the header.
The entry nodes of the array HTTPSession.header_lines are indexed with the name of
the header directive. These entry nodes just contain the list of all header values attached to such a directive.
Example:
These fields are then injected in the array HTTPSession.header_lines.
The header directive Set-Cookie appears twice, but gives rise to only one entry node:
HTTPSession.header_lines["Set-Cookie"]. This entry node is a list containing
two elements. The first one defines all characteristics of SESSIONID and
the second one provides the characteristics of the cookie PREFERENCES.
Here, a piece of code that displays the header lines:
foreach i in theSession.header_lines {
foreach j in i {
traceLine("'" + i.key() + "' = '" + j + "'");
a sequence of characters that represents the beginning of a comment for an output file to handle
Sets the value of a beginning of comment, which is exploited by the procedures
taking in charge the source code generation, such as expand or generate.
CodeWorker must know the format of comments recognized by the output file,
to be able to extract or put protected areas, or to detect expansion
markups.
This procedure should be called before calling the source code generation,
otherwise the new value is ignored by the preprocessing of output files that
looks for protected areas and markups.
The beginning of comment assigned by default is worth '//'. This is
the symbol of C++ and JAVA comments that are the most frequently files
encountered for generation. However, depending on the output file to generate,
you'll change the beginning of comment to:
'/*' to work on a C file,
'- -' to work on a ADA file,
'<!- -' to work on a HTML or XML file,
'%' to work on a LaTeX file,
Note that if the beginning of comments is set to an empty string, protected areas
aren't extracted and the expand mode does nothing.
The function getCommentBegin allows asking for the last assigned value.
Example:
a sequence of characters that represents the end of a comment for an output file to handle
Sets the value of an end of comment, which is exploited by the procedures
taking in charge the source code generation, such as expand or generate.
CodeWorker must know the format of comments recognized by the output file,
to be able to extract or put protected areas, or to detect expansion
markups.
This procedure should be called before calling the source code generation,
otherwise the new value is ignored by the preprocessing of output files that
looks for protected areas and markups.
The end of comment assigned by default is worth '\r\n'. This is
the symbol of C++ and JAVA comments that are the most frequently files
encountered for generation. However, depending on the output file to generate,
you'll change the end of comment to:
'*/' to work on a C file,
'-->' to work on a HTML or XML file,
The function getCommentEnd allows asking for the last assigned value.
Example:
If the text passed to the argument comment isn't empty, a comment is added
automatically to each file generated with the procedure generate. Passing
the option -genheader on the command line may require the functionality.
This generation header is inlayed in the comment delimeters and conforms to the
format:
if the comment holds on a single line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##" '"'comment'"'end-comment
if the comment holds on more than one line: begin-comment"##generation header##CodeWorker##" version-number"##"generation-date"##"end-comment begin-comment"##header start##"end-comment begin-commentline1end-comment ... begin-commentlinenend-comment begin-comment"##header end##"end-comment
Changing the generation header doesn't lead to modify the generated file necessary:
the header is ignored while comparing two files.
Example:
setGenerationHeader("Popeye's Village\nOlive hates spinash");
traceLine("new generation header = '" + getGenerationHeader() + "'");
local sFileName = "GettingStarted/Tiny-JAVA.cwt";
traceLine("script to execute:");
local sContent = replaceString("\r", "", loadFile(sFileName));
local lines;
cutString(sContent, "\n", lines);
foreach i in lines if !startString(i, "//")
traceLine("\t" + i);
traceLine("class to generate = '" + project.listOfClasses#[1].name + "'");
local sOutputText;
generateString(sFileName, project.listOfClasses#[1], sOutputText);
traceLine("generated text:");
traceLine(sOutputText);
setGenerationHeader("");
Output:
new generation header = 'Popeye's Village
Olive hates spinash'
script to execute:
package tiny;
public class @
name@ @
if existVariable(parent) {
@ extends @parent.name@ @
}
@{
// attributes:
@
function getJAVAType(myAttribute : variable) {
local sType = myAttribute.class.name;
if myAttribute.isArray {
set sType = "java.util.ArrayList/*<" + sType + ">*/";
}
return sType;
}
foreach i in listOfAttributes {
@ private @getJAVAType(i)@ _@i.name@ = null;
@
}
@
//constructor:
public @i.name@() {
}
// accessors:
@
foreach i in listOfAttributes {
@ public @getJAVAType(i)@ get@toUpperString(i.name)@() { return _@i.name@; }
public void set@toUpperString(i.name)@(@getJAVAType(i)@ @i.name@) { _@i.name@ = @i.name@; }
@
}
setProtectedArea("Methods");
@}
class to generate = 'Earth'
generated text:
//##generation header##CodeWorker##3.8.2##07oct2004 17:34:23##
//##header start##
//Popeye's Village
//Olive hates spinash
//##header end##
package tiny;
public class Earth extends Planet {
// attributes:
private _countryNames = null;
//constructor:
public () {
}
// accessors:
public getCOUNTRYNAMES() { return _countryNames; }
public void setCOUNTRYNAMES( countryNames) { _countryNames = countryNames; }
//##protect##"Methods"
//##protect##"Methods"
}
It changes the include path passed to the command line with one or more times
the setting of the option -I.
The include path expects a concatenation of paths separated by semi-commas (';').
Example:
local sOldPath = getIncludePath();
setIncludePath("Here/is;better/than;before");
traceLine("one changes the path: '" + getIncludePath() + "'");
setIncludePath(sOldPath);
Output:
one changes the path: 'Here/is/;better/than/;before/'
Fixes the current date-time to the value passed to argument constantDateTime,
conforming to the format: %d%b%Y %H:%M:%S.%L
The procedure doesn't change the system time. now is just frozen for the
scripting language when calling getNow(). One passes an empty date-time to
unfreeze the time.
For explanations about format types, see function formatDate
at formatDate().
Example:
// the time is already frozen for building the documentation
local sOldFrozenTime = getNow();
traceLine("now = " + getNow());
traceLine("one freezes the time to '19jan2003 06:30:00.100'");
setNow("19jan2003 06:30:00.100");
traceLine("now = '" + getNow() + "' is frozen to this value");
setNow(sOldFrozenTime);
Output:
now = 07oct2004 20:42:00.500
one freezes the time to '19jan2003 06:30:00.100'
now = '19jan2003 06:30:00.100' is frozen to this value
It assigns the value held by the argument value to a property whose name
is given by parameter define. It is equivalent of writing
'-D define=value' on the command line.
An error is raised if the define argument is an empty string.
Example:
Sets the mode of text that must be retained for parsing and source code generation. The
argument textMode is worth one of the following values:
"DOS": the default value if the interpreter is running under a Windows platform,
"UNIX": the default value if the interpreter isn't running under a Windows platform,
"BINARY": not exploited yet, but intended to specify later that
the parsing and the source code generation are applied on binary files,
An exception is raised if the argument textMode passes a bad value.
The impact of choosing samp{"DOS"} instead of any other mode is that special
comments, which announce markup keys and protected areas, will finish by
"\r\n" when the end of comment is a newline '\n'.
Example:
local sTextMode = getTextMode();
traceLine("This documentation is generated under '" + sTextMode + "' text mode");
setTextMode("BINARY");
traceLine("Now, it is generated under '" + getTextMode() + "'!");
setTextMode(sTextMode);
Output:
This documentation is generated under 'DOS' text mode
Now, it is generated under 'BINARY'!
Indicates to the CodeWorker interpreter that scripts must be considered
as written in an older version of the scripting language, given by the parameter
version.
It allows CodeWorker to behave as if it was an ancient interpreter and
eventually, to adapt deprecated forms.
Example:
local sVersion = getVersion();
traceLine("The version of scripts is '" + sVersion + "'");
setVersion("1.5.2");
traceLine("Now, the version of scripts is '" + getVersion() + "'");
setVersion(sVersion);
Output:
The version of scripts is '3.8.2'
Now, the version of scripts is '1.5.2'
Selects how to write text during a generation and how to apply an implicit copy
during a translation.
By default, a text is written in overwrite mode (mode = "overwrite"):
if the file cursor doesn't point to the end of the current output file, the new text
overwrites the old one and the remaining, if any, is inserted at the end.
The insert mode (mode = "insert") causes a shift of the old text, so
as to preserve it.
See also:getWriteModegetWriteMode()
3.179 shortToBytes
function shortToBytes(short:ushort) :string
Parameter
Type
Description
short
ushort
an unsigned short integer using the decimal base
Converts an unsigned short integer in decimal base to its 2-bytes representation.
Bytes are ordered in the host order (memory storage).
Example:
Moves the entire content (both attributes and array nodes) of the node passed
to the argument orgNode, so as to put it at the extremity of a new
branch, added to the original node orgNode once its content has been
taken off.
For instance, slideNodeContent(pExpr, left) means
that the content of pExpr slides to pExpr.left.
Example:
local pExpr;
// a given parsing leads to populate an expression:
insert pExpr.operator = "*";
insert pExpr.left = 3.141592;
insert pExpr.right = "X";
traceLine("'pExpr' represents '3.141592 * X'");
// the parsing continues and reveals that the precedent
// expression was the left part of a bigger arithmetic
// expression:
traceLine("After moving, the content of 'pExpr' becomes the left hand:");
slideNodeContent(pExpr, left);
traceLine("' - 'pExpr' contains only the sub-node 'left'");
traceLine("' - 'pExpr.left' represents '3.141592 * X'");
insert pExpr.operator = "+";
insert pExpr.right = "Y";
traceLine("'pExpr' describes now '(3.141592 * X) + Y'");
Output:
'pExpr' represents '3.141592 * X'
After moving, the content of 'pExpr' becomes the left hand:
' - 'pExpr' contains only the sub-node 'left'
' - 'pExpr.left' represents '3.141592 * X'
'pExpr' describes now '(3.141592 * X) + Y'
Sort an array in the lexicographical order of the entry keys.
Example:
local myArray;
insert myArray["Garfield"];
insert myArray["Tea spoon"];
insert myArray["Everest"];
traceLine("Sort the array 'myArray':");
sortArray(myArray);
foreach i in myArray {
traceLine("\t\"" + i.key() + "\"");
}
Output:
Sort the array 'myArray':
"Everest"
"Garfield"
"Tea spoon"
3.182 startString
function startString(text:string, start:string) :bool
Parameter
Type
Description
text
string
a sequence of characters to test
start
string
the prefix
"true" if the argument start is a prefix of the character
sequence represented by text; "" otherwise. Note also that
"true" will be returned if start is an empty string or is
equal to argument text.
Example:
Returns the result of arithmetic subtraction left-right.
Members are converted from strings to numbers, supposed being worth 0 if a parsing error occurs;
then the subtraction is processed, and the result is converted to a string,
skipping fractional part if all digits after the dot are 0.
Remember that the symbol '-' doesn't mean anything in the standard syntax of the
language, so there is no way to confuse for expressing a subtraction.
However, it exists an escape mode that allows writing arithmetic expressions
between '$' symbols, as formula under LaTeX. So, $left-right$
is equivalent to sub(left, right).
Example:
Compares two numbers and returns true if the first member given by
argument left is strictly greater than the second member passed to
argument right.
Don't use the operator '>' to compare numbers in the classical syntax of
the interpreter: it only checks the lexicographical order. So, '3 > 12' is true.
However, it exists an escape mode that allows writing arithmetic comparisons
between '$' symbols, as formula under LaTeX. So, $left>right$
is equivalent to inf(left, right).
Example:
This function passes command to the command interpreter, which executes
the string as an operating-system command. If command is empty, the
function simply checks to see whether the command interpreter exists and returns
an empty string. If an error occurs, it returns the corresponding error message.
Example:
local sScript = getWorkingPath() + "Documentation/System.cws";
local sCommand;
if existFile("CodeWorker.exe") set sCommand = "CodeWorker.exe";
else set sCommand = "Release\\CodeWorker";
set sCommand += " -script " + sScript;
local sOutput = getWorkingPath() + "Documentation/System.out";
traceLine("another \"CodeWorker\" is launched from here,");
local sError = system(sCommand + " > " + sOutput);
if sError error(sError);
traceLine("and executes the following script:");
traceLine(loadFile(sScript));
traceLine("the trace was put into a file:");
traceLine(loadFile(sOutput));
Output:
another "CodeWorker" is launched from here,
and executes the following script:
traceLine("This text comes from an external \"CodeWorker\"");
the trace was put into a file:
CodeWorker v3.8.2 (LGPL), parses and generates source code easily;
Copyright (C) 1996-2004 Cedric Lemaire; see 'http://www.codeworker.org'.
This text comes from an external "CodeWorker"
procedure traceEngine()
Traces some states about the interpreter and the current script.
See also:traceLinetraceLine(), traceObjecttraceObject(), traceStacktraceStack(), traceTexttraceText()
3.190 traceLine
procedure traceLine(line:string)
Parameter
Type
Description
line
string
a string expression to display to the console
Evaluates the expression passed to argument line, and displays the
resulting string to the console. An end of line is added automatically.
In case of quiet execution, there is no output to the console,
but the line will be kept:
for processing if used through the JNI interface, to raise all
messages to the JAVA application that exploit the CodeWorker
library,
for concatenating it into a string that collects all messages and
that returns it after calling function executeStringQuiet
(see executeStringQuiet()),
Example:
traceLine("A text to display, and then");
traceLine("I go to the next line");
Displays all sub-nodes (called attributes) and the node item's array,
if any, belonging to an object passed to argument object.
The value assigned to the object is displayed too, when it isn't an empty
string. If an array of nodes exists, then all entry keys are display, followed
by the value assigned to the item node if not empty.
Example:
procedure traceStack()
Displays the stack of local variables recursively.
In case of quiet execution, there is no output to the console,
but the entire call stack description will be kept:
for processing if used through the JNI interface, raising all
messages to the JAVA application that exploit the CodeWorker
library,
for concatenating it into a string that collects all messages and
that returns it after calling function executeStringQuiet
(see executeStringQuiet()),
Evaluates the expression passed to argument line, and displays the
resulting string to the console. On the contrary of traceLine, there
is no carriage return at the end.
In case of quiet execution, there is no output to the console,
but the text will be kept:
for processing if used through the JNI interface, to raise all
messages to the JAVA application that exploit the CodeWorker
library,
for concatenating it into a string that collects all messages and
that returns it after calling function executeStringQuiet
(see executeStringQuiet()),
Example:
traceText("A text to display, ");
traceText("but I refuse to go to line!");
traceLine("");
file name of the pattern script, which merges both the BNF syntax and the source code generation tags
this
tree
the current node that will be accessed via this variable
inputFileName
string
the input file to parse
outputFileName
string
the output file to generate
Parses an input file whose name is given by the argument inputFileName
and generates a translated file given by the argument outputFileName,
following the instructions of the pattern script called patternFileName.
The pattern script merges the BNF syntax presented section BNF syntax with
the source code generation syntax described section source code generation.
See also:expandexpand(), autoexpandautoexpand(), generategenerate(), generateStringgenerateString(), parseAsBNFparseAsBNF(), parseFreeparseFree(), parseFreeQuietparseFreeQuiet(), parseStringAsBNFparseStringAsBNF(), translateStringtranslateString()
3.195 translateString
function translateString(patternFileName:script, this:tree, inputString:string) :string
Parameter
Type
Description
patternFileName
script<translate>
file name of the pattern script, which merges both the BNF syntax and the source code generation tags
this
tree
the current node that will be accessed via this variable
variable that contains the characters to be trimmed
This function trims heading and trailing whitespace characters from the referenced
argument string and returns the number of characters that were removed.
It removes newline, space, and tab characters.
Example:
local sText = " Spaces for sale! ";
traceLine("trim(sText) = '" + trim(sText) + "'");
variable that contains the characters to be trimmed
This function trims leading whitespace characters from the argument
string and returns the number of characters that were removed. It
removes newline, space, and tab characters.
Example:
local sText = " Spaces for sale! ";
traceLine("trimLeft(sText) = '" + trimLeft(sText) + "'");
variable that contains the characters to be trimmed
This function trims trailing whitespace characters from the referenced argument
string and returns the number of characters that were removed. It
removes newline, space, and tab characters.
Example:
local sText = " Spaces for sale! ";
traceLine("trimRight(sText) = '" + trimRight(sText) + "'");
function truncateAfterString(variable:treeref, text:string) :string
Parameter
Type
Description
variable
treeref
this parameter passes a string to handle and receives its truncation
text
string
a sequence of characters to find into the value held by variable
This function:
searches the sequence of characters passed to the argument text
into the string given by the parameter variable,
assigns the substring standing on the right of text (so,
text is excluded) to the parameter variable,
returns the left part of the substring that hasn't been assigned.
If the sequence of characters text isn't found into the value of
variable, the function returns an empty string, and the variable doesn't
change.
Example:
local sVariable = "my tongue fell out with my teeth";
traceLine("sentence = '" + sVariable + "'");
local sResult = truncateAfterString(sVariable, "out");
traceLine("Now, the variable is worth '" + sVariable + "'");
traceLine("And the result = '" + sResult + "'");
Output:
sentence = 'my tongue fell out with my teeth'
Now, the variable is worth ' with my teeth'
And the result = 'my tongue fell out'
function truncateBeforeString(variable:treeref, text:string) :string
Parameter
Type
Description
variable
treeref
this parameter passes a string to handle and receives its truncation
text
string
a sequence of characters to find into the value held by variable
This function:
searches the sequence of characters passed to the argument text
into the string given by the parameter variable,
assigns the substring standing on the left of text (so,
text is excluded) to the parameter variable,
returns the right part of the substring that hasn't been assigned.
If the sequence of characters text isn't found into the value of
variable, the function returns an empty string, and the variable doesn't
change.
Example:
local sVariable = "my tongue fell out with my teeth";
traceLine("sentence = '" + sVariable + "'");
local sResult = truncateBeforeString(sVariable, "out");
traceLine("Now, the variable is worth '" + sVariable + "'");
traceLine("And the result = '" + sResult + "'");
Output:
sentence = 'my tongue fell out with my teeth'
Now, the variable is worth 'my tongue fell '
And the result = 'out with my teeth'
function UUID() :string
This function generates an UUID and returns it as a string. An UUID is a 128-bit
universally unique id, used by Microsoft and being proposed as an internet standard.
Example:
traceLine("generation of an UUID = '" + UUID() + "'");
traceLine("generation of another one = '" + UUID() + "'");
Output:
generation of an UUID = 'a671209f-9c9a-437a-b252-02fbd781a033'
generation of another one = 'd48256dd-9f58-4063-8868-6df05f86fef8'
4 The extended BNF syntax for parsing
A BNF description of a grammar is more flexible and more synthetic than a procedural
description of parsing. CodeWorker accepts parsing scripts that conform to a BNF.
BNF is the acronym of Backus-Naur Form, and consists
of describing a grammar with production rules. The first production rule that is encountered into the
script and that isn't a special one (beginning with a '#' like the {#empty} clause),
is chosen as the main non-terminal to match with the input stream, when the BNF-driven
script is executed.
A non-terminal (often called a clause in the documentation) breaks down into terminals and other non-terminals.
Defining how to break down a non-terminal is called a production rule.
A clause is valid as soon as the production rule matches its part of the input stream.
The syntax of a clause looks like:
["#overload"]? <clause_specifier><preprocessing>"::="<sequence> ['|'<sequence>]* ';'
where: <preprocessing> ::= "#!ignore" | "#ignore" ['('<ignore-mode>')']? ';' <ignore-mode> ::= "blanks" | "C++" | "JAVA" | "HTML" | "LaTeX";
<sequence> ::= non-terminal | terminal;
<terminal> ::= symbol of the language: a constant character or string
A sequence is a set of terminals and non-terminals that must match the input stream, starting at the current position.
A production rule may propose alternatives: if a sequence doesn't match, the engine tries
the next one (the alternation symbol '|' separates the sequences).
A regular expression asks for reading tokens into the input stream. If tokens are put in sequence, one
behind the other, they are evaluated from the left to the right and all of them must match the
input stream. For example, "class" '{' is a sequence of 2 non-terminals, which requires that
the input stream first matches with "class" and then is followed by '{'.
Putting #overload just before the declaration of a production rule means that the
non-terminal was already defined and that it must be replaced by this new rule when called.
Example:
Now, calling nonterminal executes the second production rule. Use the directive #super
to call the overloaded clause. The precedent overloading might be written:
#overload takes an important place in the reuse of BNF scripts. A parser might be built
as reusing a scanner, where some non-terminals only have to be extended, for populating a parse
tree for instance.
The statement #transformRules provides also a convenient way to reuse a BNF script.
It defines a rule that describes how to transform the header (left member) and the production
rule (right member) of a non-terminal declaration.
Example:
INTEGER ::= #ignore ['0'..'9']*;
INTEGER is the header and #ignore ['0'..'9']* is the production rule.
During the compilation of a BNF parse script, before processing the declaration of a non-terminal,
the compiler checks whether a transforming rule validates the name of the non-terminal. If so,
both the header of the declaration and the production rule are translated, following the
directives of the rule.
The #transformRules statement must be put in the BNF script, before the production
rules to transform.
The syntax the statement #transformRules looks like: transform-rules ::= "#transformRules"filterheader-transformationprod-rule-transformation filter ::= expression header-transformation ::= '{'translation-script''prod-rule-transformation ::= '{'translation-script''}
}
The filter is a boolean expression, applied on the name of the production rule.
The variable x contains the name of the production rule.
header-transformation consists on a translation script, which describes how to
transform the header. If the block remains empty, the header doesn't change.
prod-rule-transformation consists on a translation script, which describes how to
transform the production rule. If the block remains empty, the header doesn't change.
Example:
This example describes how to transform each production rule, whose name ends
with "expr".
or_expr ::= and_expr ["&&" and_expr]*;
becomes or_expr(myExpr : node) ::= and_expr(myExpr.left) ["&&":myExpr.operator and_expr(myExpr.right)]*;
The original production rules are just scanning the input, and the example shows
how to transform them for populating a node of the parse tree.
#transformRules
// The filter accepts production rules that have a name
// ending with "expr" only.
// Note that the variable x holds the name
// of the production rule. x.endString("expr")
A script for transforming the header of the production rule:
{
// By default, copies the input to the output
#implicitCopy
// Writes the declaration of the parameter myExpr
// after the non-terminal and copies the rest.
header ::= #readIdentifier
=> {@(myExpr : node)@}
->#empty;
}
A script for transforming the production rule itself:
{
#implicitCopy
// - Pass the left member of the expression to populate,
// to the first non-terminal,
// - assign the operator to the expression,
// - Pass the right member of the expression to populate,
// to the first non-terminal.
// In any case, the rest of the production rule remains
// invariant.
prodrule ::= [
#readIdentifier
=>{@(myExpr.left)@}
->[
"'" #readChar "'" => {@:myExpr.operator@}
|
#readCString => {@:myExpr.operator@}
]
#readIdentifier
=>{@(myExpr.right)@}
]?
]->#empty;
}
4.1 BNF tokens
Below are described all BNF tokens that CodeWorker recognizes:
a constant string:
Heading
Description
Syntax
A C-like string, written between double quotes and that
admits escape sequences: "C-like_string"
Matching
The token "C-like_string" is valid if
it matches the sequence of characters that starts at the current position of
the input stream. And then, the position moves just after the string.
Procedural way
This BNF token is equivalent to:
readIfEqualTo("C-like_string")
Example
"bottle" means that the sequence of 6 characters
that starts at the current position of the input stream must match with
bottle.
a constant character:
Heading
Description
Syntax
A C-like character, written between single quotes and that
admits escape sequences: 'C-like_char'
Matching
The token "C-like_char" is valid if
it matches the character that stands at the current position of
the input stream. And then, the position moves to the next character.
Procedural way
This BNF token is equivalent to:
readIfEqualTo("C-like_char")
Example
'(' means that the current character of the
input stream must be an opening parenthesis.
an range of characters:
Heading
Description
Syntax
Two C-like characters, written between single quotes,
which admit escape sequences, and separated by ..: 'lower_char_boundary'..'upper_char_boundary'
Matching
The token 'lower_char_boundary'..'upper_char_boundary'
is valid if the character that stands at the current position of the input
stream is comprise between the two boundaries (included). And then, the
position moves to the next character.
Note that 'lower_char_boundary' must be smaller than
'upper_char_boundary' considering the ASCII order.
Procedural way
This BNF token is equivalent to the boolean expression:
'a'..'z' means that the current character of the
input stream must be a lower case letter with no accent.
the complementary of an expression:
Heading
Description
Syntax
An expression preceded by the symbol ^ or ~: ^expression or ~expression
Matching
The token is valid when the expression failed to match
the input stream at the current position. And then, position of the input
stream moves to the next character.
Procedural way
This BNF token is equivalent to the function:
function validateThisSpecialComplementary() {
local iLocation = getInputLocation();
if /*evaluate the expression*/ {
setInputLocation(iLocation);
return false;
} else readChar();
return true;
}
Example
~"bottle" means that the 6 next characters of the
input stream must be different from bottle. If so, the position of
the input stream moves to the next character.
the negation of an expression:
Heading
Description
Syntax
An expression preceded by the symbol '!': !expression
Matching
The token is valid when the expression failed to match
the input stream at the current position. On the contrary of the complementary,
the position of the input stream doesn't move to the next character.
Procedural way
This BNF token is equivalent to the function:
function validateThisSpecialNegation() {
local iLocation = getInputLocation();
if /*evaluate the expression*/ {
setInputLocation(iLocation);
return false;
}
return true;
}
Example
!"bottle" means that the 6 next characters of the
input stream must be different from bottle.
reading a character:
Heading
Description
Syntax
Keyword readChar preceded by the symbol '#': #readChar
Matching
If the end of the input stream hasn't been reached yet,
the token is valid and the position of the input stream points to the next
character.
Procedural way
This BNF token is equivalent to:
readChar()
Example
#readChar means that the position points
to the next character if the end of the input stream hasn't been reached yet.
reading a byte:
Heading
Description
Syntax
Keyword readByte preceded by the symbol '#': #readByte
Matching
If the end of the input stream hasn't been reached yet,
the token is valid and the position of the input stream points to the next
character/byte. It scans a byte, which is converted to a 2-hexadecimal digits.
Procedural way
This BNF token is equivalent to:
readByte()
Example
#readByte:sByte means that the position points
to the next character if the end of the input stream hasn't been reached yet and, if
the byte 0x0F was at the current position, the variable sByte
is worth "0F".
reading a sequence of bytes:
Heading
Description
Syntax
Keyword readBytes preceded by the symbol '#',
and followed by an expression, whose result is a positive integer that
specifies how many bytes have to be scanned: #readBytes(integer-expression)
Matching
If the end of the input stream hasn't been reached yet,
the token is valid and the position of the input stream points after the next integer-expression
bytes. It scans N bytes (N resulting of the evaluation of integer-expression),
which are converted to a sequence of 2-hexadecimal digits.
Procedural way
This BNF token is equivalent to:
readBytes(N)
Example
#readBytes(4):sBytes means that the position points
to the next four bytes if the end of the input stream hasn't been reached yet.
If the sequence of bytes 0x0F0E0D0C was at the current position, the variable sBytes
is worth "0F0E0D0C".
reading a sequence of characters:
Heading
Description
Syntax
Keyword readChars preceded by the symbol '#',
and followed by an expression, whose result is a positive integer that
specifies how many characters have to be scanned: #readChars(integer-expression)
Matching
If the end of the input stream hasn't been reached yet,
the token is valid and the position of the input stream points after the next integer-expression
characters. It scans N characters, N resulting of the evaluation of integer-expression).
Procedural way
This BNF token is equivalent to:
readChars(N)
Example
#readChars(4):sChars means that the position points
to the next four characters if the end of the input stream hasn't been reached yet.
If the sequence of characters "cats and dogs" was at the current position, the variable sChars
is worth "cats".
reading a C-like constant character:
Heading
Description
Syntax
Keyword readCChar preceded by the symbol '#': #readCChar
Matching
The non-terminal BNF symbol #readCChar reads a C-like constant character. In case of assignement
of the scanned value to a variable, only the constant character is returned, without the single quotes.
A C-like character stands between single quotes and admits the escape character
\.
Example
#readCChar:sValue1#readCChar:sValue2
applied to 'A''\n' will move the
position of the input stream after the second trailing single quote. The variable
sValue1 will contain the letter A and sValue2 will contain the newline characters.
reading a C-like string:
Heading
Description
Syntax
Keyword readCString preceded by the symbol '#': #readCString
Matching
The token is valid if the character that stands at the
current position of the input stream is a double quote (") and that
if all following characters up to the trailing double quote are recognized as
C-like characters (meaning also that the escape sequences are as presented for
the function composeCLikeString()composeCLikeString()).
Procedural way
This BNF token is equivalent to:
readString(sString)
Example
#readCString:sValue applied to
"Popeye's village\nOlive hates spinash" will move the
position of the input stream after the trailing double quote. The variable
sValue will contain the string without double quotes and such as
the escape sequences are converted to their ASCII representation.
reading an identifier:
Heading
Description
Syntax
Keyword readIdentifier preceded by the symbol '#': #readIdentifier
Matching
The token is valid if the characters that stand
starting at the current position of the input stream match the following
BNF sequence: #!ignore ['a'..'z'|'A'..'Z'|'_'] ['a'..'z'|'A'..'Z'|'_'|'0'..'9']*//
Procedural way
This BNF token is equivalent to:
readIdentifier()
Example
#readIdentifier applied to
mushroom12,carpet will move the position of the input stream on
the comma after reading mushroom12.
reading insignificant characters:
Heading
Description
Syntax
"#skipIgnore" ['('<ignore-mode>')']?
where <ignore-mode> specifies what is an insignificant character (see #ignore).
Matching
It reads insignificant characters, such as blanks and
comments, as specified via the BNF directive #ignore(...). this BNF
directive is always valid, even if the parsing has already reached the end
of file or if there are no insignificant characters.//
Procedural way
This BNF token is equivalent to:
skipIgnore()
Example
#skipIgnore applied to
/*mushroom12,carpet*/.fish will move the position of the input stream on
the dot before fish (if #ignore(C++) was set before, for instance).
reading up to the first insignificant characters:
Heading
Description
Syntax
"#readUptoIgnore" ['('<ignore-mode>')']?
where <ignore-mode> specifies what is an insignificant character (see #ignore).
Matching
It reads all significant characters, up to encountering a whitespace or
a comment, which style was specified via the BNF directive #ignore(...). this BNF
directive is valid if it reads at least one significant character.//
Example
#readUptoIgnore #readUptoIgnore applied to
mushroom12/*comment*/carpet fish will read "mushroom12" (first directive) and
"carpet" (second call) before moving the position of the input stream on
the whitespace that stands before fish (if #ignore(C++) was set before, for instance).
reading an integer:
Heading
Description
Syntax
Keyword readInteger preceded by the symbol '#': #readInteger
Matching
The token is valid if the characters that stand
starting at the current position of the input stream match the following
BNF sequence: #!ignore ['-']? ['0'..'9']+//
Procedural way
This BNF token is equivalent to:
readInteger(sInteger)
Example
#readInteger applied to
12.34 will move the position of the input stream on
the dot after reading 12.
reading a numeric:
Heading
Description
Syntax
Keyword readNumeric preceded by the symbol '#': #readNumeric
Matching
The token is valid if the characters that stand
starting at the current position of the input stream match the following
BNF sequence: #!ignore [#readInteger ['.' ['0'..'9']*]? | '.' ['0'..'9']*] [['e' | 'E'] ['+' | '-']? ['0'..'9']+]?//
Procedural way
This BNF token is equivalent to:
readNumber(sNumber)
Example
#readNumeric applied to
12.34E+3mushrooms will move the position of the input stream at the
beginning of mushrooms.
matching the result of a classical string expression:
Heading
Description
Syntax
Keyword readText preceded by the symbol
'#', and followed by the classical string expression to match,
between parenthesis: #readText(classical-expression)
Matching
The token is valid if the classical expression matches the
input stream at the current position. If so, the position moves just after
the expression that matched.
Procedural way
This BNF token is equivalent to:
readIfEqualTo(classical-expression)
Example
#readText(sName) where sName = "horse"
means that the next 5 characters encountered to the input stream at the current
position must match "horse", which is the result of the evaluation
of the expression.
Classical string expression is seen in opposite of expressions of tokens,
as being a classical expression of the scripting language (concatenation, call of
functions, reference to variables, ...) and not a combination of tokens.
checking the validity of a classical string expression:
Heading
Description
Syntax
Keyword check preceded by the symbol
'#', and followed by the classical string expression to check,
between parenthesis: #check(classical-expression)
Matching
The token is valid if the classical expression returns
a populated string. The position of the input stream is never impacted by this
clause.
Procedural way
This BNF token is equivalent to:
if classical-expression // call the rest of the sequence
Example
#check(sID == "struct") '{' [attribute]* '}'
means that if sID is equal to "struct", we try to match the
reading of attributes embedded between braces. The checking has no effect on the
current position of the input stream.
Classical string expression is seen in opposite of expressions of tokens,
as being a classical expression of the scripting language (concatenation, call of
functions, reference to variables, ...) and not a combination of tokens.
checking whether the end of the stream has been reached or not:
Heading
Description
Syntax
Keyword empty preceded by the symbol '#': #empty
Matching
The token is valid if the position of the input stream
points to the end.
Procedural way
This BNF token is equivalent to the function:
function validateEndOfFile() {
if readChar() {
goBack();
return false;
}
return true;
}
Example
#empty is valid if the end of the
input stream has been reached.
repeating an expression:
Heading
Description
Syntax/matching
The expression is put between brackets and followed by
symbols that determine the multiplicity:
[expression]: the expression must
match the input stream once (and perhaps more, but it isn't checked);
in fact, it is used to impose the priority of token evaluation (for
adding the '|' operator to ask for a 'OR'),
[expression]?: the expression may
be absent or present once (and perhaps more, but it isn't checked);
this token is always true,
[expression]*: the expression may
be absent or matches as long as possible; this token is always true,
[expression]+: the expression must
match at least once and as long as possible; it is equivalent to the
sequence [expression][expression]*
[expression]iterations:
the expression must be repeated iterations times (and perhaps
more, but it isn't checked),
[expression]begin..end:
the expression must be repeated a number of times included in the range
[begin, end] (and perhaps more than end,
but it isn't checked); boundaries are constant integers,
[expression]begin..*:
the expression must be repeated at least begin times and as
long as possible, begin being a constant integer; note that the star might be replaced by 'n',
[expression]#repeat(begin-expr):
the expression must be repeated begin-expr times exactly; begin-expr
is an arithmetic expression evaluated just before processing the regular
expression, and returning a positive integer,
[expression]#repeat(begin-expr, end-expr):
the expression must be repeated at least begin-expr times but not more
than end-expr times (could be more, but not checked);
begin-expr and end-expr are arithmetic expressions
evaluated just before processing the regular expression;
they both return a positive integer,
Procedural way
This BNF token is equivalent to do a loop, controlling
the iterations according to the required multiplicity, and returning
true or false.
finding the next occurrence of a given BNF expression:
Common syntax:->BNF-expressionMatching: The token is valid if the BNF expression is found
somewhere into the input stream. The, the cursor jumps to the end of the sub sentence scanned by the BNF expression.
The BNF operator '->' admits a syntax extension, for the adjustment of its internal
mechanism.
->A jumps just after the the first matching of A in the sentence.
It processes the equivalent piece of extended-BNF script:
=> local iLocation; ~[A => iLocation = getInputLocation();]*
=> setInputLocation(iLocation);
To intervene on the boundaries of the repeated sequence [A ...]*,
an extension was added to the syntax: ->boundaries A, where
boundaries gives the multiplicity of the bracketed sequence ('?', '+',
'2..*', #repeat(iBegin, iEnd) ...).
Note: the boundaries must be declared just after the arrow.
The text covered by ->A includes the unmatched characters plus the sub sentence
scanned by A, so ->A:v assigns the complete
covered text to the variable v. This is sometimes a drawback: perhaps do you want to take
the unmatched character or the sub sentence scanned by A.
So now, you can specify the variables intended to receive these intermediate values: ->(:varBefore-:varAfter)A:varTotal
Note: the intermediate variables are declared just before the BNF symbol A, after the boundaries if any.
Example:
We will apply [->(:varBefore-:varAfter) #readNumeric]:varTotal
on the sentence "Garfield.laziness 99.99 percent":
varBefore = "Garfield.laziness "
varAfter = "99.99"
varTotal = "Garfield.laziness 99.99"
The last extension brought to the jump operator ->A
is to allow the execution of a BNF sequence at the beginning of the sub sequence
matched by A. This BNF sequence is declared into the parenthesis used
for the intermediate variables, behind these variables, if any: ->(:varBefore-:varAfterB)A
The advantage of infiltrating the BNF sequence B is that the intermediate variables
are populated, and that the cursor doesn't point after the matching of A yet,
but at the beginning of the sub sentence matched by A.
Procedural way: This BNF token is equivalent to the function:
function validateFindToken() {
local bSuccess;
do {
set bSuccess = // expression is expanded here;
} while !bSuccess && readChar();
return bSuccess;
}
Example:->"C" matches the stream "ABCD" and the
cursor in the input stream points to 'D'.
restricting the scan to a sub sentence.
The sequence A|>B is understood as considering
the sub sentence scanned by A, which delimits the portion of text left
visible to B. B starts scanning at the beginning of
the sub sentence covered by A and cannot go beyond.
Once the operator has achieved with success, the cursor points to the end of the
sub sentence covered by A.
You'll find below the different steps processed by the operator:
the BNF literal A is executed,
if success, the cursor comes back to the beginning of the sub sentence covered by A
then, B is executed, knowing that the operator forces the
end of the sub sentence where A had finished,
if success, the cursor points to the end of A, even if
B hadn't scan up to the end of the sentence.
Example:
We want to recognize all colons present on a line. The sub sentence we would like
to scan is a line: ->'\n'. Recognizing colons is like: [->':']*,
which asks for jumping from a colon to another, without considering the end of line. ->'\n' |> [->':']* restricts the colon recognition
to the line.
calling a clause to match its rules:
Heading
Description
Syntax
The name of the clause to match and, if any, the expected
parameters between parenthesis separated by commas: clause-name or
clause-name(P1, ...Pn)
Matching
The token is valid if the clause matches at the current
position of the input stream.
Procedural way
This BNF token is equivalent to the function:
function validateClause() {
return /*matching of rules of the clause*/;
}
Example
INTEGER'.' INTEGER with
INTEGER ::= ['0'..'9']+ means that the clause must match at least
one digit into the input stream, and be followed by a dot and then by another
positive integer.
offering a choice between 2 sequences of BNF tokens:
Heading
Description
Syntax
The 2 expressions are separated by the symbol '|': sequence1|sequence2
Matching
The token is valid if the clause matches one of the two
sequences at the current position of the input stream.
Procedural way
This BNF token is equivalent to:
/*evaluate sequence1*/ || /*evaluate sequence2*/
Example
"class"IDENT'{' | "interface"IDENT';' with
IDENT ::= ['a'..'z' | 'A'..'Z']+ means that the clause must match at least
one digit into the input stream, and be followed by a dot and then by another
positive integer.
4.2 Preprocessing of a clause
If no processing has been specified to a clause, characters will be ignored into the input stream, following the
instruction of the ignore mode (determined by the predefined clause #ignore),
just before running the clause.
Sometimes, it arrives that the ignore mode should change before calling the clause.
Let's imagine that C++ comments and blanks are ignored, except at some places where a line-comment
is expected, holding a description. If the clause that matches the line-comment is called
description, each time a description has to be read, the following sequence must be written:
On our example, each time a description has to be read, calling the clause description
is naturally reduced to:
description:sDescription
4.3 Inserting instructions of the scripting language
Instruction of the scripting language may be inserted into a sequence of tokens, and are considered
as valid, except when the controlling sequence is interrupted by the break statement.
These instructions doesn't apply a matching on the input stream, but they serve generally to
check the consistence of data and to populate the parse tree. They are announced by the
symbol '=>': "=>"instruction';' or "=>"compound-statement where a compound-statement is a block
of instructions between braces.
Example:
The first swapping to the scripting language is just an instruction to trace, which must end
with a semi-colon and that isn't the end of the clause! The second swapping to the script
language implements a little more work that is put between braces.
Be careful about declaration of local variables. If you declare a variable into a compound
statement, it disappears once the controlling sequence leaves the scope. To declare a variable
local to the clause, you can do: ...
=> local myVariable;
...
4.4 Common properties of BNF tokens
The sequence of characters that a BNF token has matched may be assigned to a variable. Then
the variable may follow the token, separated by a colon: token':'variable_nameExample:
IDENT : sName
(where IDENT ::= ['a'..'z' | 'A'..'Z']+) means that
if the clause IDENT is valid, the identifier matching the BNF token is assigned to
sName. Be careful that if the variable doesn't exist, it is pushed into the stack,
on the contrary of a classic CodeWorker script that asks for declaring explicitly a local
variable.
You can also specify to concatenate the text covered by the BNF token, to the ancient value of the variable: B:+v.
Example:
If v is worth "nebula:" and if the sentence starts with "Orion.",
then v becomes "nebula:Orion" after the resolution of: #readIdentifier:+v The sequence of characters that a BNF token has matched may be worth a constant or may belong
to a set of values. Then, the constant or the set of values is following the token, separated
by a colon, as for variables: token':'constant_value [':'variable_name] or token':' '{'values_of_the_set'}'':'variable_name where values_of_the_set ::= constant_value [','constant_value]*Examples:
IDENT : {"class", "interface"
} (where IDENT ::= ['a'..'z' | 'A'..'Z']+)
means that the identifier must be worth "class" or "interface". It isn't
equivalent to ["class" | "interface"], because this new clause matches the
beginning of "classify" or "interfaces" and that's not
what is expected.
#readString : {"tea spoon", "fork"
: sSilverware}
means that the string must be worth "tea spoon" or "fork" and that the
parsed value will be assigned to the variable called sSilverware.
4.5 BNF directives
Some directives are available:
#appendedFile(classical-expression): this directive is valid
only for pattern and translation scripts. The directive is put into a sequence of tokens and will
be applied on the rest of the sequence: the classical-expression is evaluated
and gives the name of an output file, which becomes the new output stream where code
generation will be processed, such as text is appended to the output file.
#break:
this directive is put in a repeat token, ending a sequence of tokens.
It leaves successfully the closer repeat token into which the directive was put.
Example:
["---" #break | attribute]2,*
Here, the engine reads at least one attribute, and leaves with success when it
encounters the string "---". To succeed, the #break interruption cannot occur
before the second iteration of this BNF sequence. Otherwise, the minus boundary isn't reached.
Note that brackets with a multiplicity of 1 (brackets used as parenthesis for changing the
priority of BNF sequence/alternation resolutions) propagate the
#break interruption to the closer repeat token it is inlayed in.
#continue:
this directive is put into a sequence of tokens, and means that the rest of the token's
sequence must match with the input stream. If not, an error is raised, giving the call
stack and the position of the input stream where the mistake has occurred.
Example:
The following sentence is passed to the interpreter for scanning:
a -> b // we have forgotten the semi-comma!
b -> c;
something that looks like state transitions for an automaton.
Now, we write the corresponding BNF-parsing script:
; TRANSITION ::= STATE"->"STATE';'; STATE ::= #readIdentifier;}
You notice that the syntax reclaims a semi-comma to close a transition. So, the
sentence shows a syntax error at the first line. The BNF script will fail applying
the non-terminal TRANSITION, so the scanning will stop before running
the traceLine() procedure. It fails silently: the head of the grammar doesn't
match the sentence and that's all. The interpreter cannot guess that the failure is
due to a semi-comma: the non-terminal TRANSITION doesn't match, that's right,
but perhaps that the caller (the non-terminal TRANSITIONS here) proposes
an alternative?
To constraint the interpreter to raise a syntax error automatically, you have to employ the #continue keyword:
TRANSITION ::= STATE"->" #continue STATE';';
means that once the antecedent of a state transistion is detected, the rest of the
production rule must be valid in any case. If the sequence following #continue
(STATE';' here) fails, a detailed syntax error is thrown.
Considering our sentence, the BNF script will raise a syntax error about the lacking
semi-comma at the first line.
Not using the directive #continue obliges you to write something like:
TRANSITION ::= STATE"->" /*syntax checking made by hand from here*/
[STATE | => error("syntax error: STATE token expected");]
[';' | => error("syntax error: ';' expected");];
#explicitCopy:
this directive is available in a source-to-source translation script and may be put:
inside a sequence of tokens, meaning that the text scanned by the rest
of the sequence won't be copied into the output stream automatically,
outside the clauses, at the same level as their declaration, meaning
that the entire input stream will be parsed without copying the scanned text
into the output stream ; this is the default behavior,
If source code has to be put into the output stream, the developer must specify it explicitly
between '@' symbols in a compound statement announced by =>.
See #implicitCopy to put the scanned text to the output stream automatically.
#generatedFile(classical-expression): this directive is valid
only for pattern and translation scripts. The directive is put into a sequence of tokens and will
be applied on the rest of the sequence: the classical-expression is evaluated
and gives the name of an output file, which becomes the new output stream where code
generation will be processed. It allows changing the output file during the translation,
to split it into a few files for example.
#generatedString(variable): this directive is valid
only for pattern and translation scripts. The directive is put into a sequence of tokens and will
be applied on the rest of the sequence: the output file is redirected into the string variable
passed to the argument variable, which becomes the new output stream where code
generation will be processed.
#ignore:
this directive is put into a sequence of tokens, and means that the rest of the token's
sequence must ignore blanks and comments before evaluating tokens. It exists some
different formats available:
#ignore: it calls a clause implemented by the user, which
is also named #ignore (see section BNF special clauses),
#ignore("constant-string"): it also calls a clause implemented
by the user, but known as attached to a specific identifier. This custom ignore clause
is named #ignore["constant-string"] (see section BNF special clauses),
#ignore(blanks): it ignores blank characters (spaces, new lines,
tabulations, carriage returns, ...), considered as having an ASCII code smaller
than 32 but not null,
#ignore(C++): it ignores blank characters and C comments
('/* ... */') and line comments ('// ...' up to the end of the line),
#ignore(HTML): it ignores blank characters and SGML comments
('<!- - ... - ->'),
#ignore(JAVA): it ignores blank characters and C comments
('/* ... */') and line comments ('// ...' up to the end of the line),
#ignore(LaTeX): it ignores line comments that are start with
the '%' character, but not spaces or empty lines that have a signification,
#!ignore: this directive is put into a sequence of tokens, and
means that the rest of the token's sequence will not ignore blanks or comments between
tokens. It works recursively when evaluating a clause call. It is useful for reading
a number literal for instance, where digits must be glued together.
#implicitCopy:
this directive is available in a source-to-source translation script and may be put:
inside a sequence of tokens, meaning that the text scanned by the rest
of the sequence is copied into the output stream automatically,
outside the clauses, at the same level as their declaration, meaning
that the entire input stream will be parsed and the scanned text will be
copied into the output stream,
#implicitCopy means that the scanned text is copied to the output stream as
long as the pattern matching succeeds. If a rule fails, the scanned text is removed
from the output stream, up to the last valid token. This isn't the default behavior
of a translation mode. See #explicitCopy to switch this mode off.
#insert(variable) sequence-of-BNF-instructions:
the directive creates a new node variable (if it doesn't exist yet) and executes
the sequence of BNF instructions that follow. If the sequence fails and if the node was created
by the directive, the variable is removed.
This directive is very useful while populating the parse tree, when some choices are
proposed: either populate this node or another, or a branch...
To write:
#insert(myNode) tryNewNode(myNode)
is equivalent to:
=> local bCreated = !existVariable(myNode);
=> if bCreated insert myNode;
[
tryNewItem(myNode)
|
=> if bCreated removeVariable(myNode);
#check(false)
]
#moveAhead:
Located into a BNF sequence, it means that after a valid matching of the rest
of the sequence, the cursor has necessary moved.
Example:
#moveAhead [A]? [B]?
If A doesn't match the input file, then B must match so that the scan has read at least one character.
#nextStep:
this directive is put into BNF sequences inlayed in a BNF jump operator (->) or in a
BNF complementary operator (^ or ~). Normally, these operators move the cursor of
the input stream one position further, in case of failure while applying the BNF sequence.
Using #nextStep allows changing the shift of the cursor to more than one character.
It is very useful when you encounter to quoted strings or identifiers. For instance, if you are looking
for constant strings and then number or identifiers, the following code is incorrect:
->[#readCString #readInteger | #readIdentifier]
Why is it incorrect? If the constant string matches but not the integer, the next iteration will put the
cursor just after the quote and will perhaps point to an identifier, embedded into the constant string.
Then, the operator will match during the second iteration.
In fact, if the constant string matches but not the rest of the BNF sequence, we want to
force the next jump just after the constant string:
inside a sequence of tokens, meaning that the rest of the sequence must
match without taking into account the case of the letters,
outside the clauses, at the same level of their declaration, meaning
that the entire input stream will be parsed without taking into account
the case of letters,
#parsedFile(filename): this directive is valid
only for parsing and translation scripts. The directive is put into
a sequence of tokens and will be applied on the rest of the sequence: the input file
is redirected into the file passed to the argument filename, which becomes
the new input stream where scanning and parsing will be processed.
#parsedString(expression): this directive is valid
only for parsing and translation scripts. The directive is put into
a sequence of tokens and will be applied on the rest of the sequence: the input stream
is redirected to the text resulting of the evaluation of the argument expression, which becomes
the new input stream where scanning and parsing will be processed.
#pushItem(variable) sequence-of-BNF-instructions:
the directive pushes a new item into the array of the node variable and executes
the sequence of BNF instructions that follow. If the sequence fails, the last element
of the array variable is removed.
This directive is very useful while populating the parse tree, when some choices are
proposed: either populate this array or another, or a branch...
To write:
#ratchet:
When encountered in a production rule, the BNF engine memorizes what is the current position
in the input stream, and then controls that the scan will never come back before this position.
#super "::"clause_name:
this directive applies to a non-terminal call, which was overloaded via the #overload keyword.
The directive means that the underlying non-terminal must be called
in place of the overloadee clause. If the non-terminal wasn't overloaded, an error
is thrown.
#trace:
this directive traces the resolution steps of the grammar. Hit a key to interrupt the display
of trace information and to pause the controlling sequence.
#try sequence-of-BNF-instructions#catch(variable)sequence-of-BNF-instructions:
The try/catch statement catches all error messages thrown from the
embedded sequence.
The error message is available in the variable passed to the catch statement.
If no error occurs, the flow of control continues on the sequence following the catch.
In case of error raising, the sequence breaks at the catch statement in failure after
populating the variable with the error message. Example:
#try
non_terminal_call
#catch(sError)
=> traceLine("No trouble! There was no error thrown.");
|
=> traceLine("Error! Message = '" + sError + "'");
4.6 Declaring a clause
We have seen that a clause may expect some arguments. Such a kind of clause conforms to the
syntax: clause_specifier ::= clause_name [parameters]? [':'return_type]? '::='clause_body clause_name ::= identifier [template_resolution]?;
template_resolution ::= '<' [identifier | constant_string] '>';
parameters ::= '('parameter [','parameter]* ')' parameter ::= argument_name':'argument_mode argument_mode ::= "value" | ["node" | "variable"] | "reference" return_type ::= "list" | "node" | "value" clause_body ::= rule_expression';'
where the argument mode means:
Mode
Description
value
the parameter is passed by value to the clause, as for user-defined functions
node or variable
the parameter expects a tree node, as for user-defined functions
reference
the parameter expects a reference to a variable, which allows
changing the node pointed to by the variable, as for user-defined functions
While reusing production rules from a scanner to build a parser, for example, the non-terminal
symbols of the parser need to pass a node intended to be fulfilled with parsing information,
or to contain some data about the context.
It exists a special clause the user may have to define, named #ignore. It allows
the implementation of its own production rule for processing empty characters between tokens.
This clause doesn't expect any parameter: #ignore ::= ... /*the production rule of how to skip characters*/;
To activate it in a production rule, type #ignore with no parameter.
In some cases, you might have to define more than one customized #ignore clause.
It is possible too, assigning a key to each new special clause while their implementation: #ignore["the key"] ::= ... /*the production rule of how to skip characters*/;
To activate it in a production rule, type #ignore("the key") with no parameter,
as you could have written #ignore(C++) for activating a predefined ignore mode.
Note that these special clauses must figure at the beginning of the extended-BNF script, before the
first appearance for activation in a production rule.
5 Reading tokens for parsing
The functions and procedures described below are available in a kind of parsing scripts: those
which read tokens in a procedural way, proposing a set of appropriate functions and
procedures. All examples that illustrate how to exploit them are applied on the floowing text to
parse:
// file "Documentation/ParsingSample.txt": identifier: _potatoes41$ numbers: 42 23.45e6 string: "a C-like string that accepts backslash-escape sequences" word: 1\'ecurie_1stable blanks: "blanks are ignored" C++: /*comment*/ // other comment "blanks and C++ comments are ignored" HTML: <!--comment--> "blanks and HTML comments are ignored" LaTeX: % comment "blanks must be skipped explicitly" "only comments were ignored"
There is no syntax extension provided for this mode of parsing, so it is really considered as
procedure-driven, in the opposite of the BNF-driven mode that has been seen
in the precedent section.
function getInputFilename() :string
Returns the path of the input file being parsed.
5.6 getInputLocation
function getInputLocation() :int
Returns the current file position for reading the input stream.
Example:
// we move further into the input file, just after the '$' character
readNextText("$");
traceLine("The character following '$' is put at position " + getInputLocation() + ", starting at 0");
Output:
The character following '$' is put at position 24, starting at 0
Deprecated form:getLocation has disappeared since version 3.7.1See also:setInputLocationsetInputLocation(), goBackgoBack()
5.7 getLastReadChars
function getLastReadChars(length:int) :string
Parameter
Type
Description
length
int
number of characters to read
Returns the last length characters that have been read: it takes up to
the number of characters passed to the argument length, characters that
are preceding the current position of the input file.
Example:
// we move further into the input file
readNextText("$");
traceLine("getLastReadChars(12) = '" + getLastReadChars(12) + "'");
procedure goBack()
Moves back the position of the input stream, pointing to the character just
before. If the current position was pointing to the beginning of the input
stream, the function has no effect.
Example:
traceLine("we move further into the input file, just after '$'");
readNextText("$");
traceLine("and now, we go back to it");
goBack();
if !readIfEqualTo("$") error("'$' expected");
Output:
we move further into the input file, just after '$'
and now, we go back to it
Checks whether the next characters of the input stream match with the string
passed to argument text. If so, the function returns true and
the position of the input stream hasn't moved.
Example:
// we move further into the input file,
// just after 'C++: '
readNextText("C++: ");
local iPosition = getInputLocation();
traceLine("lookAhead('/*') = '" + lookAhead("/*") + "'");
if iPosition != getInputLocation() error("What did I say? The file position shouldn't have moved!");
function peekChar() :string
Returns the character found at the current input stream position, or an empty
string if the end of file has been reached. If succeeded, the position of the
input file doesn't move to the next character.
Example:
setInputLocation(10);
traceLine("at position 10, peekChar() = '" + peekChar() + "'");
if !equal(getInputLocation(), 10) error("the position of the input stream shouldn't have moved!");
traceLine("the position didn't change, peekChar() = '" + peekChar() + "' again");
Output:
at position 10, peekChar() = ':'
the position didn't change, peekChar() = ':' again
function readByte() :string
Returns the byte read at the current file position, or an empty
string if the end of file has been reached. If succeeded, the position of the
input file moves to the next character.
The byte is returned as a 2-hexadecimal digit.
Example:
while !lookAhead(":") traceText("0x" + readByte() + " ");
Returns the sequence of length bytes read at the current file position, or an empty
string if the end of file has been reached. If succeeded, the position of the
input file moves just after.
The sequence of bytes is returned as a concatenation of 2-hexadecimal digits.
Example:
function readCChar() :string
Returns the C-like constant character read at the current file position.
A C-like character stands between single quotes and admits the escape character \r.
If succeeded, the position of the input file moves to the trailing single quote.
5.14 readChar
function readChar() :string
Returns the character read at the current file position, or an empty
string if the end of file has been reached. If succeeded, the position of the
input file moves to the next character.
Example:
function readCharAsInt() :int
Returns the ASCII value of character read at the current file position, or
a negative number -1 if the end of file has been reached. If succeeded,
the position of the input file moves to the next character.
Example:
traceLine("we move to the end of a line,");
readNextText("$");
traceLine("so carriage return or newline is " + readCharAsInt());
Output:
we move to the end of a line,
so carriage return or newline is 13
Returns the sequence of length characters read at the current file position, or an empty
string if the end of file has been reached. If succeeded, the position of the
input file moves just after.
Example:
traceLine("6 first characters = '" + readChars(6) + "'");
function readIdentifier() :string
Returns the identifier token read at the position of the input file,
or an empty string if it doesn't match.
An identifier begins with an alphabetical character (letter without
accent) or an underscore and
may be followed by any of them or by digits.
Example:
traceLine("we jump just before the identifier:");
readNextText("identifier: ");
traceLine("identifier = '" + readIdentifier() + "'");
Output:
we jump just before the identifier:
identifier = '_potatoes41'
Checks whether the next characters of the input stream match with the string
passed to argument text. If so, the function returns true and
the position of the input stream moves just after.
Example:
// we move further into the input file,
// just after 'C++: '
readNextText("C++: ");
local iPosition = getInputLocation();
traceLine("readIfEqualTo('/*') = '" + readIfEqualTo("/*") + "'");
if iPosition == getInputLocation() error("The file position should have moved after '/*'!");
function readIfEqualToIdentifier(identifier:string) :bool
Parameter
Type
Description
identifier
string
an identifier is a string composed of letters and underscores ; digits are admitted too, except at the first place
Checks whether the next characters of the input stream match with the
identifier passed to argument. If so, the function returns
true and the position of the input stream moves just after.
This function warrants that the character just before the beginning of the
identifier into the input stream is neither a letter nor a digit nor
an underscore, to assure that the identifier really starts at the current
position of the input stream.
Example:
function readIfEqualToIgnoreCase(text:string) :bool
Parameter
Type
Description
text
string
a sequence of characters to match
Checks whether the next characters of the input stream match with the string
passed to argument text, ignoring the case. If so, the function returns
true and the position of the input stream moves just after.
Example:
traceLine("readIfEqualToIgnoreCase('IDENTIFIER') = '" + readIfEqualToIgnoreCase("IDENTIFIER") + "'");
if !readIfEqualTo(":") error("':' expected after matching with 'IDENTIFIER'!");
Reads the next line, starting at the current position of the input file, and
puts it into parameter text. Characters '\r' or
'\n are ignored, and the position points to the beginning
of the next line or at the end of file if reached.
If succeeded, the function returns true.
Example:
traceLine("Reads the 2 first lines:");
local sLine;
if !readLine(sLine) error("line 1 expected, instead of end of file");
traceLine("\t" + sLine);
if !readLine(sLine) error("line 2 expected, instead of end of file");
traceLine("\t" + sLine);
Output:
Reads the 2 first lines:
identifier: _potatoes41$
numbers: 42 23.45e6
Looks for the next occurrence of the expression given by argument text,
starting at the current position of the input file.
If succeeded, the position moves just after the expression given by text
and the function returns true.
Example:
traceLine("position of the input stream = " + getInputLocation());
if !readNextText("word:") error("where is 'word:'?");
traceLine("we jump to 'word:', and the new position is " + getInputLocation());
Output:
position of the input stream = 0
we jump to 'word:', and the new position is 119
a variable that will contain the number read into the input stream
Reads a number at the current position of the input stream, and puts it into
the variable number. A number is either an integer or a floating-point
representation as encountered ordinary.
If succeded, the position moves just after the token and the function returns
true.
Example:
traceLine("we jump just before the numbers:");
readNextText("numbers: ");
local dNumber;
if !readNumber(dNumber) error("integer expected!");
traceLine("integer = " + dNumber);
skipBlanks();
if !readNumber(dNumber) error("double expected!");
traceLine("double = " + dNumber);
Output:
we jump just before the numbers:
integer = 42
double = 23450000
a variable that will contain the string literal extracted from the input stream
Reads a string literal surrounded by double quotes, and where escape sequences
are written as presented in function composeCLikeString()
(composeCLikeString()). The token is then put into the variable passed to
argument text, without double quotes and after converting the escape
sequences into their ASCII representation.
If succeeded, the position moves just after the trailing double quote and the
function returns true.
Example:
traceLine("we jump just before the string:");
readNextText("string: ");
local sText;
if !readString(sText) error("constant string expected!");
traceLine("string = '" + sText + "'");
Output:
we jump just before the string:
string = 'a C-like string that accepts backslash-escape sequences'
function readUptoJustOneChar(oneAmongChars:string) :string
Parameter
Type
Description
oneAmongChars
string
a set of characters
Reads the input stream up to encountering a character that belongs to the
parameter oneAmongChars, and returns the sequence of characters read.
The position of the input stream points to the first character that belongs to
the parameter oneAmongChars. Calling readChar() or readIfEqualTo()
just after allows determining what character, amongst those put into oneAmongChars,
was encountered first.
Example:
function readWord() :string
Returns the word token read at the position of the input file,
or an empty string if it doesn't match.
CodeWorker understands a word token as a sequence of alphabetical
characters, including letters with an accent as existing in French or Germany, or
underscores or digits.
Example:
traceLine("we jump just before the word:");
readNextText("word: ");
traceLine("readWord() = '" + readWord() + "'");
This procedure moves the position of the input stream elsewhere. The position
starts at 0.
Example:
traceLine("we jump to identifier 'potatoes' at position 12");
setInputLocation(12);
if !readIfEqualToIdentifier("_potatoes41") error("identifier '_potatoes41' expected");
Output:
we jump to identifier 'potatoes' at position 12
Deprecated form:setLocation has disappeared since version 3.7.1See also:goBackgoBack(), getInputLocationgetInputLocation()
5.28 skipBlanks
function skipBlanks() :bool
Ignores all blank characters. The current file position moves to the first
character that isn't a blank. A blank character is a space, a newline, a
carriage return, a tabulation and, more generally, any ASCII character smaller
that 32.
Example:
readNextText("blanks: ");
traceLine("we skip blank characters");
skipBlanks();
traceText("now, we read the string token: ");
local sText;
if !readString(sText) error("constant string expected");
traceLine("'" + sText + "'");
Output:
we skip blank characters
now, we read the string token: 'blanks are ignored'
function skipEmptyCpp() :bool
Ignores all blank characters and all C++/JAVA-like comments. The current file
position moves to the first character that must be kept.
Example:
readNextText("C++: ");
traceLine("we skip C++ empty tokens");
skipEmptyCpp();
traceText("now, we read the string token: ");
local sText;
if !readString(sText) error("constant string expected");
traceLine("'" + sText + "'");
Output:
we skip C++ empty tokens
now, we read the string token: 'blanks and C++ comments are ignored'
function skipEmptyCppExceptDoxygen() :bool
Ignores all blank characters and all C++/JAVA-like comments, except when the comment conforms to the Doxygen format. The current file
position moves to the first character that must be kept.
Example:
readNextText("C++: ");
traceLine("we skip C++ empty tokens, except Doxygen comments");
skipEmptyCppExceptDoxygen();
traceText("now, we read the string token: ");
local sText;
if !readString(sText) error("constant string expected");
traceLine("'" + sText + "'");
Output:
we skip C++ empty tokens, except Doxygen comments
now, we read the string token: 'blanks and C++ comments are ignored'
function skipEmptyHTML() :bool
Ignores all blank characters and all HTML/XML-like comments. The current file
position moves to the first character that must be kept.
Example:
readNextText("HTML: ");
traceLine("we skip HTML empty tokens");
skipEmptyHTML();
traceText("now, we read the string token: ");
local sText;
if !readString(sText) error("constant string expected");
traceLine("'" + sText + "'");
Output:
we skip HTML empty tokens
now, we read the string token: 'blanks and HTML comments are ignored'
function skipEmptyLaTeX() :bool
Ignores all LaTeX comments. A LaTeX comment is announced by the character
percent and finishes at the end of the line. The current file
position moves to the first character that must be kept.
Example:
readNextText("LaTeX: ");
traceLine("we skip LaTeX comments");
skipEmptyLaTeX();
traceText("now, we read the string token: ");
local sText;
if !readString(sText) error("constant string expected");
traceLine("'" + sText + "'");
Output:
we skip LaTeX comments
now, we read the string token: 'blanks must be skipped explicitly'
6 Syntax and instructions for generating source code
A script that must be processed for source code generation is called a pattern script in the
CodeWorker vocabulary. It exists three ways to generate a file:
the classical generation mode is used to let the script produce the
most part of the output. The output file is rewritten completely. Only some areas
called protected areas in the vocabulary of CodeWorker are preserved in
the file. This philosophy has been adopted by some modeling tools that generate
a skinny skeleton copiously interspersed with areas intended to the developer.
expansion mode is used when the file is mainly written by
hand, but small portions need to be generated. The points where to
insert the code are known as markers in the vocabulary of
CodeWorker . Visual C++ changes source code like it via the Class Wizard.
translation mode is used when both parsing and source code generation are
required to process a file. It arrives when a file must be rewritten in a different
syntax, or when the positions the code must be inserted depend on a strategy that
is determined by the parsing. For example, a script should add a trace at the beginning
of each function body of a JAVA or C++ source code. To do that, parsing will allow
discovering function bodies, and source code generation will write the C++ or JAVA code that
implements the trace.
A pattern script, except in translation mode, begins with a sequence of
characters exactly like they must be written into the output file, up to it encounters special
character '@' or JSP-like tag '<%'. Then it swaps into script mode, and
everything is interpreted as script instructions, up to the special character '@' or
the JSP-like tag '%>' are encountered. The content of the script file is again
understood as a sequence of characters to write into the output file, up to the next special
character. And it continues swapping from a mode to another...
For convenience, the script mode might just be restrained to an expression (often a variable
expression) whose value is written into the output file.
Expanding a file consists of generating code to some determined points of the file. These
points are called markers and are noted ##marker##"name-of-the-marker",
surrounded by comment delimiters.
For example, a valid marker inlayed in a C++ file could be: //##marker##"factory"
and a valid marker inlayed in an HTML file could be: <!- -##marker##"classes"- ->
A pattern script intended to expand code is launched thanks to the procedure
expand that expects three parameters:
the first one is the file name of the script,
the second one is the current context of execution that will be accessed via the
this keyword into the script,
the last one is the name of the file to expand,
Each time CodeWorker will encounter a marker, it will call the pattern script that
will decide how to populate it. The code generated by the pattern script for this
marker is surrounded by tags ##begin##"name-of-the-marker" and
##end##"name-of-the-marker", automatically added by the
interpreter. If some protected areas were put into the generated code, they are preserved the
next time the expansion is required.
Note that CodeWorker doesn't change what is written outside the markers and their begin/end
delimiters.
A script that is intended to work on translation mode expects first a
BNF-like description as presented at section BNF syntax. As for any kind of
BNF-driven script, procedural-driven script may be inlayed in braces
after symbol '=>'. This compound statement may contain a subset of pattern
script, as described in the precedent paragraph, which will take in charge of generating code
into the output file. Note that the flow of execution enters into the compound statement in
script mode.
Such as for parsing, it exists some functions to handle a position into the output stream.
However, the principle is quite different, insofar as the current position of the output
stream cannot be changed and always points to the end.
A position is called a floating location and has an ID. A floating location
is used for overwriting or for inserting text to a point of the stream that has already been
generated. While generating a C++ body for example, it may be interesting to insert the
'#include' preprocessor directive as references to other headers are discovered during
the iteration of the parse tree.
The procedure newFloatingLocation allows attaching a position to an ID, which
represents the name of the location. The function getFloatingLocation returns the
position attached to a given floating location ID.
Inserting text at a position leads to shift all floating locations that are pointing
to, or after, the insertion point. The offset corresponds to the size of the text. So, it is
called a floating location because the position assigned initially to the ID might
change in the future.
You'll find below a list of all functions or procedures that may be used into a
pattern script.
function countOutputCols() :int
Determines the column number in the line where the output stream cursor points to.
See also:countOutputLinescountOutputLines()
6.3 countOutputLines
function countOutputLines() :int
Determines the line number where the output stream cursor points to.
See also:countOutputColscountOutputCols()
function existFloatingLocation(key:string, parent:bool) :bool
Parameter
Type
Description
key
string
name of a floating position into the output stream
parent
bool
while expanding a file, search into precedent markup area if this floating location exists or not
Returns true if the floating location, whose name is passed to argument key, already exists.
If required (parameter parent set to true) and if the current output
file is being expanded, the function searches the floating location in precedent
visited markup areas.
Example:
Roger Rabbit@
newFloatingLocation("Roger Rabbit");
@ doesn't like spinash@
traceLine("Does the floating location 'Roger Rabbit' exists? '" + existFloatingLocation("Roger Rabbit", false) + "'");
Output:
Does the floating location 'Roger Rabbit' exists? 'true'
name of an existing floating position into the output stream
Returns the position into the output stream attached to a floating location
whose name is passed to argument key.
The function raises an error if the floating location doesn't exist or is present
in a precedent markup area of a being-expanded file, but not in the current markup
area.
Example:
Roger Rabbit@
newFloatingLocation("Roger Rabbit");
@ doesn't like spinash@
traceLine("Position just after 'Roger Rabbit' is " + getFloatingLocation("Roger Rabbit"));
function getMarkupKey() :string
This function must be called into a pattern script that runs in expansion mode.
It returns the current markup key where code has to be expanded now.
Example:
@
if getMarkupKey() == "examples" {
@A little example?@
} else {
@We are doing the LaTeX documentation in expansion mode!@
traceLine("current markup of the LaTeX documentation = '" + getMarkupKey() + "'");
}
Output:
current markup of the LaTeX documentation = 'generation script functions'
Generated text:
We are doing the LaTeX documentation in expansion mode!
Deprecated form:getMarkerKey has disappeared since version 2.14See also:getMarkupValuegetMarkupValue()
6.10 getMarkupValue
function getMarkupValue() :string
This function must be called into a pattern script that runs in expansion mode.
It returns the current markup value, if any, put between tags '##data##' after declaring the markup key.
Example - hand-typed code to expand:
... extension of C++/Java: implements a 'switch' on strings
//##markup##"switch(sText)"
//##data##
//Product
//RentalItem
//Customer
//RentalOrder
//Figurine
//Movie
//DVD
//VideoStore
//##data##
... next code
Example - the script:
{
int iHashCode = 0;
std::string sKey = @coreString(getMarkupKey(), 7, 1)@;
for (int i = 0; i < sKey.length(); i++) {
unsigned char c = sKey[i];
iHashCode = (31*iHashCode + (c%31)) % 64000000;
}
switch(iHashCode) {
@
local sData = getMarkupValue();
while sData {
local iIndex = sData.findString('\n');
if $iIndex < 0$ || !sData.startString("//") error("syntax error");
local sKey = sData.midString(2, $iIndex - 2$);
if sKey.endString('\r') set sKey = sKey.rsubString(1);
local iHashCode = 0;
local i = 0;
while $i < sKey.length()$ {
local c = sKey.charAt(i);
iHashCode = $(31*iHashCode + (c.charToInt()%31)) % 64000000$;
increment(i);
}
@ case @iHashCode@: // "@sKey@"
@
setProtectedArea("case \"" + sKey + "\":");
set sData = sData.subString($iIndex + 1$);
}
@ default:
@
setProtectedArea("default:");
@ }
}
@
Generated text:
... extension of C++/Java: implements a 'switch' on strings
//##markup##"switch(sText)"
//##data##
//Product
//RentalItem
//Customer
//RentalOrder
//Figurine
//Movie
//DVD
//VideoStore
//##data##
//##begin##"switch(sText)"
{
int iHashCode = 0;
std::string sKey = sText;
for (int i = 0; i < sKey.length(); i++) {
unsigned char c = sKey[i];
iHashCode = (31*iHashCode + (c%31)) % 64000000;
}
switch(iHashCode) {
case 17133617: // "Product"
//##protect##"case \"Product\":"
//##protect##"case \"Product\":"
case 19256985: // "RentalItem"
//##protect##"case \"RentalItem\":"
//##protect##"case \"RentalItem\":"
case 26793087: // "Customer"
//##protect##"case \"Customer\":"
//##protect##"case \"Customer\":"
case 26446891: // "RentalOrder"
//##protect##"case \"RentalOrder\":"
//##protect##"case \"RentalOrder\":"
case 20050752: // "Figurine"
//##protect##"case \"Figurine\":"
//##protect##"case \"Figurine\":"
case 14413458: // "Movie"
//##protect##"case \"Movie\":"
//##protect##"case \"Movie\":"
case 6516: // "DVD"
//##protect##"case \"DVD\":"
//##protect##"case \"DVD\":"
case 56055430: // "VideoStore"
//##protect##"case \"VideoStore\":"
//##protect##"case \"VideoStore\":"
default:
//##protect##"default:"
//##protect##"default:"
}
}
//##end##"switch(sText)"
... next code
function getProtectedArea(protection:string) :string
Parameter
Type
Description
protection
string
unique ID of a protected area
Returns the content of the protected area whose name is given by parameter
protection. If the protected area wasn't found into the ancient version
of the output stream, and if it hasn't been put yet via setProtectedArea
into the output stream, it returns an empty string.
Example:
@
setProtectedArea("keep this code for me, please!");
if !getProtectedArea("keep this code for me, please!") {
traceLine("you have never populated the protected area!");
}
Output:
you have never populated the protected area!
Generated text:
//##protect##"keep this code for me, please!"
//##protect##"keep this code for me, please!"
it will contain keys of all protected areas that were found into the output stream before the generation
Looks for all protected areas that were found into the output stream before processing
the source code generation and puts their keys into the well-named argument keys.
Note: keys stores the ID of protected areas in the lexicographical order.
The function returns how many protected areas were found into the output stream.
Both key and value of items are worth the protected area key. If the list wasn't
declared as local or global or as belonging to this,
a negative value is returned.
Example - precedent output generated:
My first protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
My second protected area:
//##protect##"don't forget me, please!"
This portion of text is also very important.
//##protect##"don't forget me, please!"
Now, you can erase text, I don't care.
Example - the script:
My first protected area:
@
local listOfKeys;
local iNbKeys = getProtectedAreaKeys(listOfKeys);
traceLine("Number of preserved area at the beginning = " + iNbKeys + ":");
foreach i in listOfKeys traceLine(" '" + i + "'");
setProtectedArea("hand-typed code");
traceLine("after removing and setting protected areas,");
traceLine("the function behaves the same");
removeProtectedArea("don't forget me, please!");
if $getProtectedAreaKeys(listOfKeys) != 2$ error("bad behavior");
@Now, you can erase text, I don't care.
@
Output:
Number of preserved area at the beginning = 2:
'don't forget me, please!'
'hand-typed code'
after removing and setting protected areas,
the function behaves the same
Generated text:
My first protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
Now, you can erase text, I don't care.
Indents the output stream, conforming to a given type of file, to choose amongst:
C++
JAVA
More format will be recognized in the future.
The function returns true if the output stream needed to be indented,
meaning that it has changed after processing the indentation.
Be careful that if you are expanding a file, only the expanded area will be
indented. In that case, it is better to use indentFile once the generation
has completed.
Example:
int f(int i) {
switch (i) {
case 1:
g(i + 1);
break;
case 2:
case 3:
if (i == 2) {
h();
}
g(i - 1);
break;
}
}@
if indentText("C++") traceLine("the output stream wasn't indented correctly");
Output:
the output stream wasn't indented correctly
Generated text:
int f(int i) {
switch (i) {
case 1:
g(i + 1);
break;
case 2:
case 3:
if (i == 2) {
h();
}
g(i - 1);
break;
}
}
Inserts a sequence of characters passed to argument text, at the position
of the output stream given by argument location. The position starts
counting at 0. All floating locations that point to location,
or after, are impacted by the insertion, and shift for an offset that is worth
the size of the text to insert.
If the position isn't valid, negative or bigger than the end of the output
stream, an error is raised.
In expansion mode, the position 0 points to the first character written
for expansion and cannot exceed the last character written for expansion.
Generally, the position is given by the function getFloatingLocation().
Example:
I'll drink a bottle of 'Margaux' year 1994@
newFloatingLocation("You'll be drunk!");
@ before smoking a cigar.@
traceLine("My glass is empty, let's try another bottle, year 1996");
insertText(getFloatingLocation("You'll be drunk!"), " and year 1996");
traceLine("My glass is empty once again, let's try another bottle, year 2000");
insertText(getFloatingLocation("You'll be drunk!"), " and year 2000");
Output:
My glass is empty, let's try another bottle, year 1996
My glass is empty once again, let's try another bottle, year 2000
Generated text:
I'll drink a bottle of 'Margaux' year 1994 and year 1996 and year 2000 before smoking a cigar.
Inserts a sequence of characters passed to argument text, at the position
of the output stream given by argument location, only if the text has
never been encountered previously by a insertTextOnce() or a writeTextOnce()
procedure. The position starts counting at 0. All floating locations
that point to location, or after, are impacted by the insertion, and
shift for an offset that is worth the size of the text to insert.
If the position isn't valid, negative or bigger than the end of the output
stream, an error is raised.
In expansion mode, the position 0 points to the first character written
for expansion and cannot exceed the last character written for expansion.
Generally, the position is given by the function getFloatingLocation().
Example:
@
newFloatingLocation("include files");
@
void f(const std::string& s) {
...
}
@
traceLine("I need an include: <string>!");
insertTextOnce(getFloatingLocation("include files"), "#include <string>" + endl());
@std::vector<std::string> g() {
...
}
@
traceLine("I need two includes: <string> and <vector>!");
insertTextOnce(getFloatingLocation("include files"), "#include <string>" + endl());
insertTextOnce(getFloatingLocation("include files"), "#include <vector>" + endl());
Output:
I need an include: <string>!
I need two includes: <string> and <vector>!
Inserts a sequence of characters passed to argument text, at the position
of the floating location, whose name is given by the argument location, but
only if the text has never been encountered previously by a insertTextOnce()
or a writeTextOnce()procedure.
If the floating location doesn't exist, the function raises an error.
In expansion mode, the floating location might point into a markup area
previously generated. So, on the contrary of insertTextOnce(), the function
permits to insert text out of the current markup area.
See also:insertTextinsertText(), insertTextOnceinsertTextOnce(), insertTextToFloatingLocationinsertTextToFloatingLocation(), overwritePortionoverwritePortion(), writeByteswriteBytes(), writeTextwriteText(), writeTextOncewriteTextOnce()
Inserts a sequence of characters passed to argument text, at the position
of the floating location, whose name is given by the argument location.
If the floating location doesn't exist, the function raises an error.
In expansion mode, the floating location might point into a markup area
previously generated. So, on the contrary of insertText(), the function
permits to insert text out of the current markup area.
See also:insertTextinsertText(), insertTextOnceinsertTextOnce(), insertTextOnceToFloatingLocationinsertTextOnceToFloatingLocation(), overwritePortionoverwritePortion(), writeByteswriteBytes(), writeTextwriteText(), writeTextOncewriteTextOnce()
6.20 newFloatingLocation
function newFloatingLocation(key:string) :bool
Parameter
Type
Description
key
string
name of a floating position into the output stream
Assigns the current position into the output stream to a floating location
whose name is passed to argument key. If the floating location
already exists, its position is updated.
Example:
Roger Rabbit@
newFloatingLocation("Roger Rabbit");
@ doesn't like spinash@
traceLine("Position just after 'Roger Rabbit' is " + getFloatingLocation("Roger Rabbit"));
Writes a sequence of characters passed to argument text, at the position
of the output stream given by argument location. The text overwrites up to
size characters and inserts the rest if any. The position starts
counting at 0.
About the behaviour of the overwriting:
If there are more than size characters in text,
all floating locations that point to location + size,
or after, are impacted by the insertion of the remaining characters of text,
and shift for an offset that is worth the size of the text minus the size of the
portion to overwrite.
overwritePortion(pos, text, 0) is worth insertText(pos, text).
If the portion to overwrite is bigger than the length of text,
all not overwritten characters of the portion are removed.
overwritePortion(pos, "", size) removes size characters from the output stream at position pos.
If the position isn't valid, negative or bigger than the end of the output
stream, an error is raised.
In expansion mode, the position 0 points to the first character written
for expansion and cannot exceed the last character written for expansion.
Generally, the position is given by the function getFloatingLocation().
Example:
I'll drink a bottle of '@
newFloatingLocation("You'll be drunk!");
@Margaux' year 1994 before smoking a cigar.@
traceLine("Finally, I prefer to drink a bottle of Saint-Estephe,");
traceLine("I correct the output:");
local iPosition = getFloatingLocation("You'll be drunk!");
overwritePortion(iPosition, "Saint-Estephe", 7 /*size of 'Margaux'*/);
Output:
Finally, I prefer to drink a bottle of Saint-Estephe,
I correct the output:
Generated text:
I'll drink a bottle of 'Saint-Estephe' year 1994 before smoking a cigar.
name of the protected area ; must be unique into the output stream
content
string
the content to copy into the protected area
This procedure assigns a content to the protected area whose name is passed to
argument protectedAreaName and puts the protected area at the current
position of the output stream.
An error is raised if the protected area had already been put into the output stream.
Example:
code to generate
@
if !getProtectedArea("reserved for the user")
populateProtectedArea("reserved for the user", "I can't stand an empty protected area" + endl());
@I continue the code to generate@
Generated text:
code to generate
//##protect##"reserved for the user"
I can't stand an empty protected area
//##protect##"reserved for the user"
I continue the code to generate
it will contain keys of all protected areas that haven't been placed into the output stream yet
Looks for all protected areas that haven't been placed into the output stream yet
and puts their keys into the well-named argument keys. Both key and value
of items are worth the protected area key.
The list is sorted in the lexicographical order rather than the order of entrance.
The function returns how many protected areas are waiting for being put into the
output stream. If the variable keys wasn't declared (local or
global or as an attribute of this), samp{-1} is returned.
Example - precedent output generated:
My first protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
My second protected area:
//##protect##"don't forget me, please!"
This portion of text is also very important.
//##protect##"don't forget me, please!"
Now, you can erase text, I don't care.
Example - the script:
My first protected area:
@
local listOfKeys;
local iHowMany = remainingProtectedAreas(listOfKeys);
traceLine("It remains " + iHowMany + " keys to set into the output file:");
foreach i in listOfKeys traceLine(" '" + i + "'");
traceLine("writing of protected area 'hand-typed code'");
setProtectedArea("hand-typed code");
@My second protected area:
@
set iHowMany = remainingProtectedAreas(listOfKeys);
traceLine("It remains " + iHowMany + " keys to set into the output file:");
foreach i in listOfKeys traceLine(" '" + i + "'");
traceLine("writing of protected area '" + listOfKeys#front + "'");
setProtectedArea(listOfKeys#front);
@Now, you can erase text, I don't care.
@
set iHowMany = remainingProtectedAreas(listOfKeys);
if $iHowMany != 0$ error("shouldn't remain any protected area!");
traceLine("It doesn't remain any area to set into the output file");
Output:
It remains 2 keys to set into the output file:
'don't forget me, please!'
'hand-typed code'
writing of protected area 'hand-typed code'
It remains 1 keys to set into the output file:
'don't forget me, please!'
writing of protected area 'don't forget me, please!'
It doesn't remain any area to set into the output file
Generated text:
My first protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
My second protected area:
//##protect##"don't forget me, please!"
This portion of text is also very important.
//##protect##"don't forget me, please!"
Now, you can erase text, I don't care.
function removeProtectedArea(protectedAreaName:string) :bool
Parameter
Type
Description
protectedAreaName
string
name of the protected area to remove
Removes the protected area whose name is passed to the argument protectedAreaName
from the list of protected areas that were found into the output file before the
generation and that aren't been put into the output stream yet via the setProtectedArea()
or populateProtectedArea() functions.
Example - precedent output generated:
My first protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
My second protected area:
//##protect##"don't forget me, please!"
This portion of text is also very important.
//##protect##"don't forget me, please!"
Now, you can erase text, I don't care.
Example - the script:
Finally, I have changed my mind and I keep only one protected area:
@
local listOfKeys;
local iHowMany = remainingProtectedAreas(listOfKeys);
traceLine("It remains " + iHowMany + " keys to set into the output file:");
foreach i in listOfKeys traceLine(" '" + i + "'");
traceLine("Protected area 'don't forget me, please!' is removed");
removeProtectedArea("don't forget me, please!");
set iHowMany = remainingProtectedAreas(listOfKeys);
traceLine("It remains " + iHowMany + " keys to set into the output file:");
foreach i in listOfKeys traceLine(" '" + i + "'");
traceLine("writing of protected area 'hand-typed code'");
setProtectedArea("hand-typed code");
Output:
It remains 2 keys to set into the output file:
'don't forget me, please!'
'hand-typed code'
Protected area 'don't forget me, please!' is removed
It remains 1 keys to set into the output file:
'hand-typed code'
writing of protected area 'hand-typed code'
Generated text:
Finally, I have changed my mind and I keep only one protected area:
//##protect##"hand-typed code"
I don't want to loose this portion of text
after another generation!
//##protect##"hand-typed code"
This procedure changes the size of the output stream to newSize. The
only allowed resizing is to reduce the stream (the request is ignored otherwise).
If the current position becomes out of the boundaries, it points to the new end
of the output stream.
Example:
I write 22 characters.@
traceLine("Current position to the output stream = " + getOutputLocation());
setOutputLocation(8);
@15@
resizeOutputStream(15);
This procedure moves the position of the output stream elsewhere. The position
passed to the argument location starts at 0.
If location is worth -1, the cursor moves to the end of the output stream.
Example:
I write 22 characters.@
traceLine("Current position to the output stream = " + getOutputLocation());
setOutputLocation(8);
@one sentence, finally!@
name of the protected area ; must be unique into the output stream
This procedure puts a protected area at the current position of the output stream,
and allows preserving the code of the user between two code generations.
A protected area is bounded by the sequence ##protect##"...", put into a comment.
The syntax of the comment must conform to the type of the target language expected in the
file outputFileName.
So, an HTML file expects <!- - and - ->, a JAVA file is waiting
for // and "\n", ...
Don't forget to configure correctly the syntax of comment boundaries with procedures
setCommentBegin() (see setCommentBegin()) and setCommentEnd() (see setCommentEnd()).
An error is raised if the protected area had already been put into the output stream.
Example:
code to generate
@
setProtectedArea("reserved for the user");
@I continue the code to generate@
Generated text:
code to generate
//##protect##"reserved for the user"
//##protect##"reserved for the user"
I continue the code to generate
sequence of characters to write at the current position of the output stream
Writes a sequence of characters passed to argument text, at the current
position of the output stream. It does the same work as the @ tag, but
it puts a string into the output stream.
This is the common way to write the symbol '@' into the output stream.
Example:
sequence of characters to write at the current position of the output stream
Writes a sequence of characters passed to argument text, at the current
position of the output stream, only if the text has never been encountered
previously by a insertTextOnce() or a writeTextOnce() procedure.
Example:
@
traceLine("Do you know that Roger Rabbit is tired?");
writeTextOnce("Roger Rabbit is tired");
traceLine("Once again, Roger Rabbit is tired!");
writeTextOnce("Roger Rabbit is tired");
traceLine("Once more, Roger Rabbit is tired!");
writeTextOnce("Roger Rabbit is tired");
traceLine("The message hasn't been repeated into the generated text.");
Output:
Do you know that Roger Rabbit is tired?
Once again, Roger Rabbit is tired!
Once more, Roger Rabbit is tired!
The message hasn't been repeated into the generated text.