SSG for PC User MANUAL
SSG for Microsoft® Windows™
SSG for Microsoft Windows
Dynamic Programming
Programmable Text Generator
Program Generation Software
Natural Language Input
SYMBOLIC STREAM GENERATOR
301-270-5615
SSG is published by MDRSESCO LLC, a Maryland Corporation
“MDRSESCO, where the software
matters.”
Last Updated: 10 March 2012
Overview
We want you
to understand how to use SSG.
In today’s
model-driven, architecturally-patterned software, different programs exhibit
common structure. Common structure means
that you can vary specifics while keeping the general structure constant. Without assistance, you must write complete,
conformant programs that include both repetitive boilerplate and variations. Enter the Symbolic Stream Generator
(SSG). With SSG, you put the common
structure in concrete, once, and program the variations. With SSG, you generate scads of related
conformant programs. For example,
suppose you are creating a web application with Adobe Flex and the Model-View-Controller
(MVC) architectural pattern. The charts
and graphs that you want, the positions of charts and graphs on the screen, and
their responses to user gestures are all variables. The way you hook them up inside the MVC
pattern is fixed. The way that you
describe charts and graphs is fixed.
What is a Stream?
Stream is SSG parlance for a symbolic
output file, such as a file with the filename extension .TXT. SSG creates zero or more symbolic output
files. These files are fodder for
computer language compilers such as Open Source Eclipse is for ActionScript, a
Java variant. SSG is not limited to any
particular kind of symbolic output, although it is typically used to generate
programming language source code.
Streams are,
by default, Unicode. This gives you the
convenience of producing streams that contain characters that exist outside the
Latin alphabet. SSG can produce ordinary
ASCII streams, too, by special request.
Certain inputs to Windows operating system functions (e.g. command-line
script files) may not be Unicode.
SSG accepts what?
SSG accepts
two input files and produces zero or more output files. One input file is the program. It is called the skeleton. The other input
file is data. It is called the SGS file
or SGSes for short.
You can
write a skeleton yourself or you can use a skeleton that has been created for
you. A locked skeleton is an encoded
skeleton that facilitates distribution and prevents corruption and end user
changes. For administrative reasons,
skeletons that are distributed for general use are usually locked.
Tell Me about Stream Generation
Statements
Stream
Generation Statements are much like natural language sentences or sentence
fragments. They contain words, some of
which are significant, and some of which are present as an aid to
readability. A statement can be very
lengthy. The first word of a statement
classifies the statement. Every
statement that begins with the same word falls into the same class. Members of the class are numbered in sequence,
in the order in which they appear, beginning with 1. Statements of a class can be referenced by
the skeleton by number.
A specific
example follows. Suppose a computer
system comes equipped with a mass storage device of a certain type. It might be nice to identify the model number
of the device, give the device a name, and specify to which control units the
device connects. (In what follows, we
explore implementation of an SGS of the NODE class. Use of the symbol NODE is not special. It
could be some other alphanumeric symbol of your choosing.) You could design a
NODE SGS to appear as follows:
NODE 56A
D8470 CUA,CUB
Here, NODE
is the class of the SGS. There can be many
NODE statements. The symbol 56A names a
specific device. The symbol D8470
associates 56A with a kind of mass storage device. The symbols CUA and CUB identify the control
units to which the device is connected.
Notice that control units CUA and CUB are in a comma-separated list of
symbols. A comma separated list
constitutes a field, as does a single word without commas.
To improve
readability, you can insert noise words.
A casual reader would be better able to understand the SGS without
resort to research. For example,
NODE 56A IS
D8470 AND CONNECTS TO CUA,CUB
Notice that
IS, AND, CONNECTS, and TO are noise words.
They are present only to improve readability.
You could
have two NODE statements that together configure two mass storage devices. For example,
NODE 56A IS D8470 AND
CONNECTS TO CUA,CUB
NODE 56B IS D8470 AND
CONNECTS TO CUA,CUB
Syntax Rule for SGSes
The rule is that
any character that is a capital letter,
a lowercase
letter, a digit, a space, a comma, an underscore, or a dollar
sign is permitted to occur anywhere in an SGS.
Other characters *MUST* be enclosed within a
pair of double-quotes. You can put a BREF inside an
SGS that is dynamically created, because the BREF is replaced at
runtime by whatever the BREF resolves to.
(A BREF is a bracketed reference and a BREF is delimited by square
brackets.) A statically created BREF is
a BREF that originates in the SGS file, field 2 of the shell call line.
N.B. When you
use a BREF in an *CREATE SGS directive, the BREF is replaced with its resolved
value. When that resolved value contains
any of the disallowed characters (i.e. a character that is not a capital
letter, a lowercase letter, a digit, a space, a comma, an
underscore, or a dollar sign), then the *CREATE SGS
directive is syntactically invalid and will be rejected. You avoid trouble by putting BREFs in pairs
of double-quotes in every *CREATE SGS directive.
Comments
You can make a
skeleton or SGS file line a comment by prefixing the line with a period or with
an asterisk followed by a period. A
blank line is a comment. Both the period
and asterisk-period must be followed by a space or end-of-line.
*.
This is an sgs comment
HELLO
WORLD
NODE
CPU0 IS CPU80
. NODE IOAU0 IS
IOAU80 AND CONN_ECTS TO _CPU80
.
NODE CU0 IS D5450 AND CONNECTS TO IOAU80
*. NODE D0 IS D8480
AND CONNECTS TO CU0
NODE
CPU0 IS CPU80
NODE
IOAU0 IS IOAU80 AND CONN_ECTS TO _CPU80
NODE
CU0 IS D5450 AND CONNECTS TO IOAU80
NODE
D0 IS D8480 AND CONNECTS TO CU0
How Do I Reference An SGS?
NODE 56A IS D8470 AND CONNECTS TO CUA,CUB
NODE 56B IS D8470 AND CONNECTS TO CUA,CUB
Now comes the hard
part. How do you reference the words in
an SGS? First, you must identify which
statement you want to reference. As
stated above, statements are numbered beginning with 1 according to class. With reference to this example, statements of
class NODE are numbered in the order in which they are written. The symbol 56B is referenced as [NODE,2,1,1]
because 56B is located in the first subfield of the first field of the second
SGS of class NODE. In general, a word is
referenced as [<SGS-label>,<index>,<field-number>,<subfield-number>]. NODE is the class. NODE is the <SGS-label>. Fields are what follow an SGS-label. A field is a group of one or more
subfields. A subfield is a word. Subfields within a field are separated by
commas. A word is an alphanumeric
symbol. A word can be an arbitrary set
of characters bounded by apostrophes.
Within bounding apostrophes, an apostrophe is coded as two contiguous
apostrophes. The symbol CUB on the first
NODE SGS is referenced as [NODE,1,7,2].
It is the second subfield of the seventh field of the first SGS of class
NODE.
Further notations are
useful. It is often helpful to know how
many subfields are coded in a field. In
this example, [NODE,1,7] evaluates to 2, which is the number of subfields in
the seventh field of the first SGS of class NODE. It is often helpful to know how many fields
are coded in an SGS. In the example,
[NODE,2] evaluates to 7, which is the number of fields of the second SGS of
class NODE. Finally, it is often helpful
to know how many SGSses of a given class exist.
[NODE] evaluates to 2 in this example.
For clarity, let us put
forward the reference notation of every word in the following SGS:
NODE 56A IS D8470 AND
CONNECTS TO CUA,CUB
[NODE,1,1,1] is 56A
[NODE,1,2,1] is IS
[NODE,1,3,1] is D8470
[NODE,1,4,1] is AND
[NODE,1,5,1] is CONNECTS
[NODE,1,6,1] is TO
[NODE,1,7,1] is CUA
[NODE,1,7,2] is CUB
Furthermore,
[NODE,1] is 7
[NODE,1,1] is 1
[NODE,1,2] is 1
[NODE,1,3] is 1
[NODE,1,4] is 1
[NODE,1,5] is 1
[NODE,1,6] is 1
[NODE,1,7] is 2
Several optional
modifications to a subfield reference are available. You can obtain the number of characters in a
subfield with the LENGTH$ option. You
can obtain a subset of characters within a subfield with the SUBSTR$ option. You can obtain an uppercase rewriting of a
subfield with the UCASE$ option. You can
obtain a lowercase rewriting of a subfield with the LCASE$ option.
Options are named in the
fifth position of the reference form. They
are referred to as Special Editing Codes.
When the functions implied by the codes take arguments, they appear starting
in the sixth position and progress through the remaining positions. For example, [NODE,2,3,4,SUBSTR$,2,5]
returns characters 2, 3, 4, 5 and 6 from the 2nd NODE SGS, 3rd
field, 4th subfield.
Let’s Get Technical about SGSes
Formally, a Stream
Generation Statement comprises, in order left to right, zero or more spaces, an
SGS-label, one or more spaces, and one or more fields separated by spaces. An SGS-label must begin with a letter and may
contain letters and digits. An SGS-label
is case insensitive. That means NODE and node and Node are semantically
identical. A field is one or more
subfields. Adjacent subfields are
separated by a single comma. The final
subfield of a field is followed by a space, not a comma. The end of a line is semantically identical
to one or more spaces. A subfield is a
series of digits or alphabetic characters, in uppercase or lowercase. When special characters outside this set are
required, for example, when a space character is required, a subfield is a
series of characters bounded by a pair of double-quote characters. The subfield “HELLO, WORLD” is a single
subfield that contains the embedded special characters comma and space. The bounding double-quotes disappear when the
subfield is resolved. In the event that
you are required to embed a double-quote character in a subfield, simply type
two double-quote characters in succession.
The subfield “””” resolves to a single double-quote character. A subfield of no content (i.e. only spaces) resolves
to a zero-length string, i.e. a string that contains zero characters. In NODE ABLE,BAKER,,DELTA,
the 3rd subfield of the 1st field is a zero-length
subfield. An SGS can contain a comment
that is preceded by <space>.<space>, where <space> is one or
more space characters. The beginning and
the end of a line are equivalent to <space> in this context. Everything to the right of
<space>.<space> is commentary.
Notice that <space>.<space> inside double-quotes does not
signal the start of commentary.
Each SGS class that you
define can be conceptualized as an array.
The array usually is rectangular, but that isn’t necessary. Just as in a relational database, an SGS
class has rows and columns. You
reference data in the array by row and column. You can search columns and rows using special
skeleton directives.
Subfields of an SGS appear
literally (as they are copied to output streams) or quoted. A quoted subfield loses its quotes when it is
copied to an output stream. A subfield
that contains the double-quote character (“) must be quoted and, inside the
bounding quotes each instance of the double-quote must be doubled. A subfield that contains other than letters
and digits generally should be quoted.
For example, a subfield that contains a space must be quoted to avoid
the space acting inappropriately to terminate the subfield.
You can use the DQ$
modifier of a bracketed reference to retain the bounding double-quotes.
For example,
SGS: TAG APPLE
SKEL: [TAG,1,1,1]
produces
APPLE
whereas
SGS: TAG “John said “”Boo!”””
SKEL: [TAG,1,1,1]
produces
John said “Boo!”
SGS: TAG “Hello”
SKEL: [TAG,1,1,1,DQ$]
produces
“Hello”
About Skeletons
A skeleton is a series of
directives. Directives are organized
into a single main program and a series of optional subroutines. Since a skeleton is an executable program,
it is able to iterate directives and skip directives conditionally. The skeleton is first compiled to an internal
form for efficient interpretation. Any
errors in the skeleton are detected during this pass. The interpretation pass does not occur when
errors are detected in the compilation pass.
SGSes are similarly compiled, with errors preventing the interpretation
pass. During skeleton compilation, the
main program and subroutines can be packaged into a unitary locked form that
can be distributed without fear that the skeleton may be modified by end users
or otherwise corrupted. It is possible
to bring in source code from various files to simplify subroutine
maintenance.
A skeleton comprises a
combination of directive lines and non-directive lines. Directive lines instruct the interpreter to
perform some function such as looping and testing. Non-directive lines contain literal data and
references to variables and subfields in SGSes.
(Every directive line starts with an asterisk. There is no asterisk as the first character
of a non-directive line.) The literal data is sent to an output stream as
is. The reference data is resolved and
embedded with the literal data.
Suppose we have the
following non-directive skeleton line:
case ‘[CS01,I,2,5]’:
As has been explained
already, the notation [CS01,I,2,5] is a reference to
all the SGSes of class CS01. The
specific subfield referenced is on the Ith
such SGS, the 2nd field, 5th subfield. The subfield is copied from the SGS and
placed, as shown, in a line that is sent to the output stream. Assume [CS01,I,2,5]
resolves to Q. Then, the line that is
sent to the output stream will be
case ‘Q’:
There are more complex
variations of non-directive statements, particularly those that contain
multiple references, but you get the idea.
Reference values are substituted for all the references. The result is sent as a single line to the
output stream. Where resolution is not
possible, the skeleton stops executing and an error message is displayed.
SSG supports variables
that take string values. You can
reference a variable in a non-directive statement and have its resolved value
substituted. For example,
*SET V14 TO “TRANSISTOR”
I COOKED MY [*V14]
produces the output line
I COOKED MY TRANSISTOR
Notice the asterisk that
precedes V14. Without it, the reference
would be to [V14], and that would resolve to the number of SGSes that are of
the V14 class. Clearly, this is not what
you want. So, the asterisk signifies
that V14 is a previously defined variable.
You can *SET a variable to
a decimal integer.
*SET Z TO 15
[*Z]
In this case the output line
is
15
If you want leading zeroes,
specify the field width by appending the integer variable name with a colon and
the number of characters in the rendered field.
For example,
*INCREMENT Z FROM 8 TO 12
[*Z:2]
*LOOP
Produces the following
output:
08
09
10
11
12
Variables are implicitly
declared at the point where a value is assigned to them. Run-time reference to an undeclared variable
is not permitted.
The reference form, such
as [CS01,I,2,5] is formally referred to as a bracketed
reference. You have seen how it
works. It is possible to nest bracketed
references. For example, suppose
[E,1,1,1] resolves to CS01. Then
[CS01,I,2,5] can be rewritten as [[E,1,1,1],I,2,5]. Further, suppose [G,1,1,1] resolves to
5. Then [CS01,I,2,5] can be rewritten as
[[E,1,1,1],I,2,[G,1,1,1]]. Even though
what [G,1,1,1] resolves to is a string, it is coerced to an integer. Coercion of string values to integer, when
necessary and possible, is a common convenience. For example, the skeleton
*CREATE SGS: CS01 A,B,C,D,E F,G,H,I,J K,L,M,N,O
P,Q,R,S,T,U V,W,X,Y,Z
*CREATE SGS: CS01 A,B,C,D,E F,G,H,I,J K,L,M,N,O
P,Q,R,S,T,U V,W,X,Y,Z
*CREATE SGS: G 5
*CREATE SGS: E "CS01"
*SET I to 2
[CS01,I,2,5]
[[E,1,1,1],I,2,5]
[[E,1,1,1],I,2,[G,1,1,1]]
produces the following
output:
J
J
J
Further,
*INCREMENT Z TO 2
*INCREMENT Y TO 3
*EDIT ON
*CREATE SGS: E [*Z] &
[*Y] &
*EDIT OFF
Z=[E,[E],1,1] Y=[E,[E],2,1]
*LOOP
*LOOP
produces
Z=1 Y=1
Z=1 Y=2
Z=1 Y=3
Z=2 Y=1
Z=2 Y=2
Z=2 Y=3
Finally,
*CREATE SGS: E 5
*CREATE SGS: F 3
*SET V TO [E,1,1,1]
*SET W TO [F,1,1,1]
*SET X TO [E,1,1,1] + [F,1,1,1]
*SET Y TO V + W
[*X]
[*Y]
produces
8
8
The rules for naming
variables are identical to the rules for labeling SGSes. Variable names must begin with a letter and,
otherwise, may contain digits and letters.
Variable names are case-insensitive.
How to Reproduce SGSes
An example in which SSG
produces a workable facsimile of each SGS of a certain type.
*CREATE SGS: NODE APPLE,BANANA
CHRYSANTHEMUM,DAHLIA,EVERGREEN FLOWER,GINGER HAY
*CREATE SGS: NODE 1,2,3,4 10,11,12,13,14,15 21,22 XX
31,32,33,34,35,36 41,42
*INCREMENT A TO [NODE]
*EDIT ON
NODE &
*INCREMENT B TO [NODE,A]
*INCREMENT C TO [NODE,A,B]
[NODE,A,B,C],&
*LOOP
*SET COLED$ TO COLED$ - 1
&
*LOOP
*EDIT OFF
*LOOP
produces
NODE APPLE,BANANA CHRYSANTHEMUM,DAHLIA,EVERGREEN FLOWER,GINGER
HAY
NODE
1,2,3,4 10,11,12,13,14,15 21,22 XX 31,32,33,34,35,36 41,42
Expressions
Throughout a skeleton it
is often possible to reference an integer, a variable, certain kinds of bracketed
references, and expressions composed of integers, variables, and bracketed
references. The SSG expression evaluator
allows parentheses for sub-expression grouping, and various operators. Expressions may contain embedded spaces to
enhance readability. As a trade off,
noise words help the SSG parser orient itself with respect to the location and
extent of an expression. That means that
you should avoid using noise words to name variables. For example, the skeleton
*SET A TO 12
*SET B TO 2
*SET C TO 3
*SET D TO ( A * B ) / C
[*D] is the answer
produces the following
output stream:
8 is the answer
Simple expressions are
permitted inside bracketed references.
They may not contain bracketed references. In those cases where an expression is not
permitted, you can use the *SET directive to create a variable that is assigned
the value of an expression, and use the variable inside a bracketed reference.
*CREATE SGS: CS01 A,B,C,D,E F,G,H,I,J K,L,M,N,O
P,Q,R,S,T,U V,W,X,Y,Z
*CREATE SGS: CS01 A,B,C,D,E F,G,H,I,J K,L,M,N,O
P,Q,R,S,T,U V,W,X,Y,Z
*CREATE SGS: G 5
*CREATE SGS: E "CS01"
*SET I to 2
[CS01,I,2,5]
[[E,1,1,1],I,2,5]
[[E,1,1,1],I,2,[G,1,1,1]]
*SET A TO 1
[[E,1,1,1],I,A+A,[G,1,1,1]]
produces
J
J
J
J
Further,
*CREATE SGS: E A
*SET B TO 2
*CREATE SGS: A B,C,E
[[A,1,1,3],1,1,1]
[[A,1,1,B+1],1,1,1]
produces
A
A
Subroutines
Subroutines allow you to
isolate logic that is repeatedly used throughout a skeleton. You can vary the logic at run-time by varying
the arguments to the subroutine.
Subroutines are not required, but may be useful. The following complete skeleton contains one
subroutine and a main program.
*SUBROUTINE SAMPLE
[#1] IS A [#2].
*ENDSUB
*.
*.
*CALL SAMPLE "JOHN" "BRICKLAYER"
The output follows:
JOHN IS A BRICKLAYER.
It is customary for all
subroutines to appear before the main program.
The main program is the source code that is not bounded by *SUBROUTINE
and *ENDSUB directives. Notice that
string arguments can be passed to subroutines.
They need not be literals. They
can be bracketed references. Arguments
are referenced within a subroutine by number.
Arguments are numbered in the order in which they appear in the *CALL
directive.
The name of the *CALLed
subroutine is permitted to vary. You can
specify, literally, the name of the subroutine.
You can specify a bracketed reference.
You can specify the name of a variable that contains the name of a
subroutine. (The first symbol in the *CALL directive is either a bracketed
reference or a variable name or literally the name that appears in a
*SUBROUTINE directive. In the last and
most common case, where the symbol appears in a *SUBROUTINE directive, the
subroutine that is associated with the *SUBROUTINE directive is called,
regardless of the existence (or non-existence) of an identically named variable
and its contents.)
*SUBROUTINE A
THE VALUE OF THE PARAMETER IS [#1].
*ENDSUB
*CALL A “FIFTEEN”
*CREATE SGS: PROCESS A
*INCREMENT X FROM 1 TO [PROCESS]
*INCREMENT Y FROM 1 TO [PROCESS,X]
*CALL [PROCESS,X,Y,1]
"TWENTY"
*LOOP
*LOOP
*SET
B TO “A”
*CALL
B “THIRTY”
produces
THE
VALUE OF THE PARAMETER IS FIFTEEN.
THE
VALUE OF THE PARAMETER IS TWENTY.
THE
VALUE OF THE PARAMETER IS THIRTY.
The *CALL directive allows
you to pass arguments to a subroutine.
They are identified positionally as they are enumerated from left to
right and are referenced in the subroutine starting with [#1] and increasing as
they go to the right [#2], [#3], and so forth.
The special reference [#0] returns the symbolic name of the calling
subroutine. When the main program is
calling, the name returned is MAIN.
Inside the main program the name returned is NULL. To avoid confusion, you should not name your
subroutines MAIN or NULL.
Looping
Repetition is one of the
things that computers do best. When you
want to process a list, it is handy to be able to iterate through the list, one
item at a time, with each item (more or less) appearing for work at its
appointed time. A skeleton introduces
the *INCREMENT and *LOOP pair of directives.
These bounding directives enclose those statements that define the work
that to be accomplished inside the loop at run-time. An induction variable is assigned an initial
value, a step count, and a limit count in the *INCREMENT directive. The induction variable is available
throughout the loop for use in various ways.
For example, the induction variable can be coerced to string and it may
appear in the output stream. And the
induction variable may be used inside bracketed references to resolve a
subfield among the SGSes. For example,
the skeleton
*CREATE SGS: E ABLE
*CREATE SGS: E BAKER
*CREATE SGS: E CHARLIE
*CREATE SGS: E DELTA
*.
*.
*INCREMENT Z TO [E]
[*Z]. [E,Z,1,1]
*LOOP
produces
1. ABLE
2. BAKER
3. CHARLIE
4. DELTA
Notice that the induction
variable Z is initially assigned the number 1.
The limit count is set to the number of SGSes that have the label
E. The step count is, by default, 1. Thus, the induction variable Z assumes the
values, in order: 1,2,3,4,5. Upon reaching 5, the loop is done. Control then transfers to the statement that
follows the *LOOP directive. In this
case, that is the end of the main program of the skeleton. And so, the skeleton terminates.
If you want to explicitly
set the initial value and step count, you can do so, as shown in the following
example.
*CREATE SGS: E ABLE
*CREATE SGS: E BAKER
*CREATE SGS: E CHARLIE
*CREATE SGS: E DELTA
*.
*.
*INCREMENT Z FROM 1 TO [E] BY 1
[*Z]. [E,Z,1,1]
*LOOP
Loops can be nested, as
shown in the following example.
*CREATE SGS: EX A,B C D,
E, F
*CREATE SGS: EX G H I J K LARA, MARY,NANCY
*CREATE SGS: EX
O
*CREATE SGS: EX P,Q R,SAM,THOMAS,ULYSSES V,W X, Y, Z
1, 2 3, 4, 5 6,7
*INCREMENT I TO [EX]
*EDIT ON
*INCREMENT J TO [EX,I]
*INCREMENT K TO [EX,I,J]
[EX,I,J,K],&
*LOOP
*SET COLED$ TO -1 + COLED$
&
*LOOP
*EDIT OFF
*LOOP
which produces
A,B C D,E,F
G H I J K LARA,MARY,NANCY
O
P,Q R,SAM,THOMAS,ULYSSES V,W X,Y,Z 1,2 3,4,5 6,7
This skeleton defines four
EX class Stream Generation Statements programmatically. It then iterates through the statements using
the I induction variable. During that
iteration, a further iteration occurs.
The J induction variable iterates through the fields of the Ith SGS of the EX class. During that iteration, a further iteration
occurs. The K induction variable
iterates through the subfields of the Jth
field of the Ith SGS of the EX class.
Preparation of lines
destined for the output stream can be spread among several skeleton statements
using the *EDIT directive. The *EDIT ON
directive tells SSG to delay sending the next line to the output stream until
it encounters a *EDIT OFF directive.
Individual segments of the output line are constructed by non-directive
statements, each of which is intended to terminate with an ampersand. The ampersand allows you to specify how many
space characters you want to insert.
This is handy when you want to back up and erase a character, as is done
in this example, where a trailing comma is replaced with a space. The COLED$ SSG is a system-defined variable
that is used during dynamic editing (i.e. between *EDIT ON and *EDIT OFF). The value in COLED$ is the number of column
that will receive the next emitted character.
Columns are numbered beginning with zero.
When you want to exit a
loop before the induction variable has reached its limit, use the *BREAK
statement. For example,
*INCREMENT Z TO 5
[*Z] IS THE INDUCTION VARIABLE
*IF Z = 3
*BREAK
*ENDIF
*LOOP
END OF LOOP
produces
1 IS THE INDUCTION VARIABLE
2 IS THE INDUCTION VARIABLE
3 IS THE INDUCTION VARIABLE
END OF LOOP
And, here is a further
example.
*INCREMENT Z TO 5
*INCREMENT Y TO 5
*INCREMENT X TO 5
[*Z][*Y][*X]
*IF X = 2
*BREAK
*ENDIF
*LOOP
*IF Y = 3
*BREAK
*ENDIF
*LOOP
*IF Z = 4
*BREAK
*ENDIF
*LOOP
produces
111
112
121
122
131
132
211
212
221
222
231
232
311
312
321
322
331
332
411
412
421
422
431
432
Skipping
Sometimes you want to do
something, but only under certain conditions.
That’s where the *IF directive is useful. When the expression that you design for your
*IF directive evaluates to a non-zero value, the statements that immediately
follows the *IF are executed. Otherwise,
they aren’t. Statements are bounded by
the pair *IF and *ENDIF. Everything
between *IF and *ENDIF is skipped unless the *IF expression evaluates
non-zero. You can put a *ELSE directive
between *IF and *ENDIF to make a two-legged branch and join. Everything between *IF and *ELSE is executed
when the *IF expression is non-zero, otherwise everything between *ELSE and
*ENDIF is executed. The *IF construct can
be nested. The *IFNOT directive is
identical to the *IF directive, with one glaring exception. The sense of the expression is reversed. (To transform *IFNOT to *IF, put the *IFNOT
expression in parentheses and add =0 after the closing parenthesis. Then change *IFNOT to *IF.) The skeleton
*INCREMENT i TO 6 FROM -2
The value of I is [*I].
*IF I>0
I is greater than 0
*IF I>1
I is greater than 1
*IF I>2
I is greater than 2
*IF I>3
I is greater than 3
*IF I>4
I is greater than 4
*IF I>5
I is greater than 5
*IF I>6
I is greater than 6
*IF I>7
I is greater than 7
*ELSE
I is less than or equal to 7
*ENDIF
*ELSE
I is less than or equal to 6
*ENDIF
*ELSE
I is less than or equal to 5
*ENDIF
*ELSE
I is less than or equal to 4
*ENDIF
*ELSE
I is less than or equal to 3
*ENDIF
*ELSE
I is less than or equal to 2
*ENDIF
*ELSE
I is less than or equal to 1
*ENDIF
*ELSE
I is less than or equal to 0
*ENDIF
*LOOP
produces
The value of I is -2.
I is less than or equal to 0
The value of I is -1.
I is less than or equal to 0
The value of I is 0.
I is less than or equal to 0
The value of I is 1.
I is greater than 0
I is less than or equal to 1
The value of I is 2.
I is greater than 0
I is greater than 1
I is less than or equal to 2
The value of I is 3.
I is greater than 0
I is greater than 1
I is greater than 2
I is less than or equal to 3
The value of I is 4.
I is greater than 0
I is greater than 1
I is greater than 2
I is greater than 3
I is less than or equal to 4
The value of I is 5.
I is greater than 0
I is greater than 1
I is greater than 2
I is greater than 3
I is greater than 4
I is less than or equal to 5
The value of I is 6.
I is greater than 0
I is greater than 1
I is greater than 2
I is greater than 3
I is greater than 4
I is greater than 5
I is less than or equal to 6
Searching
Often, you want find the
row that contains a particular value.
This can be used to implement an associative memory. You search for a key and return the value
that is associated with the key. SSG
provides columns searches and row searches.
These can be implemented using the looping and skipping constructs
previously mentioned. However, the
resulting code is needlessly complex.
Consider the following skeleton:
*CREATE SGS: E BOVINE COW
*CREATE SGS: E URSINE BEAR
*CREATE SGS: E CANINE DOG
*CREATE SGS: E FELINE LION
*IF COLUMN SEARCH FROM [E,1,1,1] FOR
"URSINE"
FOUND [E,STMT$,FLD$,SFLD$]
WHICH IS A [E,STMT$,2,1]
*ELSE
DID NOT FIND ANYTHING
*ENDIF
which produces
FOUND URSINE WHICH IS A
BEAR
The column beginning with
the 1st SGS of class E at subfield 1 and field 1 is searched. There is a match at the 2nd row on
URSINE. The statements between *IF and
*ELSE are executed. The non-directive
statement bounded by *IF and *ELSE resolves the matching subfield using SSG
system-internal variables STMT$, FLD$, and SFLD$, which are set by *IF COLUMN
SEARCH, the directive when and only when a match occurs.
Use of *IFNOT with COLUMN
SEARCH appears to be straightforward, but it is not. In this case, the search is for a needle in a
haystack. Many similar symbols are mixed
with a few outliers. The purpose of
*IFNOT COLUMN SEARCH is to find an outlier, as in the following skeleton.
*CREATE SGS: E APPLE
*CREATE SGS: E APPLE
*CREATE SGS: E APPLE
*CREATE SGS: E URSINE BEAR
*CREATE SGS: E APPLE
*CREATE SGS: E APPLE
*IFNOT COLUMN SEARCH FROM [E,1,1,1] FOR
"APPLE"
FOUND [E,STMT$,FLD$,SFLD$]
WHICH IS A [E,STMT$,2,1]
*ELSE
DID NOT FIND ANYTHING
*ENDIF
which produces
FOUND URSINE WHICH IS A
BEAR
Row searches are similar
to column searches. Every subfield
(beginning where specified) in a specified SGS is searched from left to right
for a key. When there is a match, the
statements between *IF ROW SEARCH and *ENDIF (or *IF ROW SEARCH and *ELSE) are
executed, and the variables STMT$, FLD$, and SFLD$ are appropriately set.
*CREATE SGS: E APRIL,24 MAY,13, JUNE,26, JULY,4
AUGUST,19 SEPTEMBER,30 OCTOBER,5
*IF ROW SEARCH FROM [E,1,1,1] FOR "13"
THE MONTH OF [E,STMT$,FLD$,1]
AND DAY-OF-MONTH [E,STMT$,FLD$,SFLD$]
*ENDIF
*IF ROW SEARCH FROM [E,1,1,1] FOR "OCTOBER"
THE MONTH OF [E,STMT$,FLD$,SFLD$]
AND DAY-OF-MONTH [E,STMT$,FLD$,2]
*ENDIF
which produces
THE MONTH OF MAY AND
DAY-OF-MONTH 13
THE MONTH OF OCTOBER AND
DAY-OF-MONTH 5
The system-defined variable
CASEINSENSITIVE$ governs whether searching is or is not done with regard to
upper- and lower-case in the key and target values. When you *SET CASEINSENSITIVE$ TO 1,
comparisons regard Table and table to be identical. When you *CLEAR CASEINSENSITIVE$, Table and
table are not the same.
And Construct Array
You can use *IF COLUMN
SEARCH and *IF ROW SEARCH to construct a new SGS class that represents a set of
matches (non-matches in the case of *IFNOT).
The clause AND CONSTRUCT ARRAY <SGS-label> is appended to the
syntax that has already been covered.
The following discussion
references COLUMN SEARCH, but applies equally to ROW SEARCH.
The optional AND CONSTRUCT
ARRAY <SGS-label> clause of *IF COLUMN SEARCH and *IFNOT COLUMN SEARCH operates
as follows. The directive removes all
SGSses that have the label <SGS-label>, and then inserts one SGS for each
match (non-match for *IFNOT) over the entire set of scanned SGSes. For each match (non-match for *IFNOT), an
inserted SGS is given the label <SGS-label> and three single subfield
fields that correspond to the values STMT$, FLD$, and SFLD$. Thus, you have the
location of each relevant SGS and you can work with them easily. The COLUMN SEARCH with the AND CONSTRUCT
ARRAY option is intended to prepare the environment for subsequent processing. Such processing can be based upon certain
knowledge as to the existence and location of relevant SGSes. The statements between the *IF COLUMN SEARCH
(or *IFNOT COLUMN SEARCH) and the matching *ENDIF are assured that the ARRAY
<SGS-label> contains at least one row.
The STMT$, FLD$, and SFLD$ variables assume no defined values following
the execution of a COLUMN SEARCH with the AND CONSTRUCT ARRAY option. <SGS-label> may be a symbol, a symbol
enclosed in double-quotes, or a bracketed reference.
In short, the created
SGSes collectively reflect all of the matches, for *IF, non-matches for *IFNOT. The format is <SGS-label> <STMT$>
<FLD$> <SFLD$>. Searching
commences at the place indicated and halts with the final SGS.
*CREATE SGS: F A
*CREATE SGS: F B
*CREATE SGS: F C
*CREATE SGS: F B
*IF COLUMN SEARCH FROM [F,1,1,1] FOR "B"
AND CONSTRUCT ARRAY CZ
*ENDIF
*INCREMENT Z TO [CZ]
CZ [CZ,Z,1,1] [CZ,Z,2,1]
[CZ,Z,3,1]
*LOOP
produces
CZ 2 1 1
CZ 4 1 1
Further,
*CREATE SGS: F A
*CREATE SGS: F B
*CREATE SGS: F C
*CREATE SGS: F B
*IFNOT COLUMN SEARCH FROM [F,1,1,1] FOR "B"
AND CONSTRUCT ARRAY CZ
*ENDIF
*INCREMENT Z TO [CZ]
CZ [CZ,Z,1,1] [CZ,Z,2,1]
[CZ,Z,3,1]
*LOOP
produces
CZ 1 1 1
CZ 3 1 1
An example using Unicode:
*CREATE
SGS: CT "怎麼現在褐色奶牛呢?" APE
*IF
COLUMN SEARCH FROM [CT,1,2,1] FOR "APE" AND CONSTRUCT ARRAY SUX
*INCREMENT
Z TO [SUX]
*SET
A TO [SUX,Z,1,1]
*SET
B TO [SUX,Z,2,1]
*SET
C TO [SUX,Z,3,1]
[CT,A,B,C,DQ$]
[CT,A,1,1,DQ$]
*LOOP
*ENDIF
produces
"APE"
"怎麼現在褐色奶牛呢?"
Finally,
*CREATE SGS: F A,B,C,D,E,B H,I,B,B,B,J,K B,B
L,M,N,O,P,Q R,B,S,T,B W,X,Y,Z B
*IF ROW SEARCH FROM [F,1,1,1] FOR "B" AND
CONSTRUCT ARRAY CZ
*ENDIF
*INCREMENT Z TO [CZ]
CZ [CZ,Z,1,1] [CZ,Z,2,1]
[CZ,Z,3,1]
*LOOP
produces
CZ 1 1 2
CZ 1 1 6
CZ 1 2 3
CZ 1 2 4
CZ 1 2 5
CZ 1 3 1
CZ 1 3 2
CZ 1 5 2
CZ 1 5 5
CZ 1 7 1
How to Check for File or Directory Existence
There are three variants. One checks for file existence. One checks for directory existence. One checks for file or directory existence.
Respectively, the syntax
of each case is:
*IF FILEEXISTS “C:\Users\Ugen\SKEL.TXT”
*IF DIRECTORYEXISTS “C:\Windows”
*IF EXISTS “C:\Users\Ugen\SKEL.TXT”
The literal strings can be
replaced with bracketed references. The
*IFNOT alternative is also available.
Expressions and Operators
Expressions in SSG are
like expressions in algebra. Operands
alternate with operators. When you use
parentheses to group subordinate expressions, you override the order that is
implied by operator precedence.
Operators imply operations. For
example, the + operator implies that two operands will be added together and
the addition produces a temporary value.
Absent the guidance of parentheses, an operation that has higher
precedence is performed before an operation that has lower precedence. In
the following table, you will find each of the operators that SSG supports
together with its precedence value.
+ |
4 |
- |
4 |
* |
5 |
/ |
5 |
% |
5 |
< |
3 |
= |
3 |
> |
3 |
>= |
3 |
<= |
3 |
<> |
3 |
| |
1 |
& |
1 |
<> |
3 |
‘<> |
3 |
‘= |
3 |
( |
-1 |
) |
-1 |
There follows a sample
skeleton.
*SET GREEN TO 3 + 5 * 7
*SET RED TO ( 3 + 5 ) * 7
*SET BLUE TO 3 + ( 5 * 7 )
GREEN IS [*GREEN]
RED IS [*RED]
BLUE IS [*BLUE]
And it produces…
GREEN IS 38
RED IS 56
BLUE IS 38
In the expression 3+5*7,
you could take 3, add 5 to get 8, and then multiply 7 to get 56. But that would violate the My Dear Aunt Sally
rule (a Peano axiom?) that dictates Multiplication and Division occur before
Addition and Subtraction. Properly, the
expression is evaluated as follows: take
3, take 5, multiply by 7 to get 35, and then add 3 to get 38. The operator precedence mechanism ensures My
Dear Aunt Sally is happy. If you choose
to override the default rule, you can apply parentheses. There is no practical limit to nesting depth
of parentheses or to expression complexity.
Expressions support unary plus and minus.
The operators +, -, *, and
/ are the expected operators for addition, subtraction, multiplication, and
division. The operators + and – can be
used to indicate the sign of an operand.
For example, the expression -1+5 evaluates to 4. The operator % divides the left operand by
the right operand and returns the remainder.
The operators <, =, >, >=, <=, =, <>, ‘<>, and
‘= compare the left operand by the right operand and return 1 or 0 depending on
whether the indicated comparison is true or false, respectively. The & and | operators temporarily convert
the left and right operands to 0 or 1 depending on whether the operands are
zero or non-zero, respectively. Then, in
the case of the & operator, when the operands are both logically 1, the
result is 1, otherwise 0. In the case of
the | operator, when the operands are both logically zero, the result is 0,
otherwise 1.
The ‘<> and ‘=
operators exist because of a special case.
Since variables are stored as strings and may be coerced to integers, it
is possible to have several different string representations of the same
integer. For example, 54 and 0054, when
coerced to integer are identical. It is
possible that comparison of the string representations is wanted. The aforementioned operators ensure that a
string comparison is done. Thus,
“54”’=”0054” evaluates to 0, whereas “54”=”0054” evaluates to 1. These are the only string comparison
operators. Integer coercion must succeed
for all the other comparison operators.
Note that integer coercion is done wherever it is possible.
Dynamic Editing
As previously stated, you
can begin an editing session with the *EDIT ON statement and use various
directives to construct a single line for transmission to the output stream
with several lines of skeleton code.
Termination of editing and transmission occurs with the *EDIT OFF
statement. The following example
demonstrates the power of this technique.
*REMOVE SGS: LIST,1,[LIST]
*INCREMENT Z FROM 1 TO 80 BY 7
*CREATE SGS: LIST [*Z]
*LOOP
*EDIT ON
*INCREMENT Z TO [LIST]
*SET COLED$ TO [LIST,Z,1,1]
X&
*LOOP
*EDIT OFF
*EDIT ON
*INCREMENT Z TO [LIST]
[LIST,Z,1,1] &
*LOOP
*EDIT OFF
produces
X X X X X X X X X X X X
1
8 15 22 29 36 43 50 57 64 71 78
Important Note.
During
text composition you can insert an ampersand by referencing a subfield in an
SGS that evaluates to a single ampersand.
For example, [AMPER$,1,1,1] is pre-defined to resolve to &. If your bracketed reference evaluates to some
string, of length greater than one that contains an ampersand, text composition
halts at the ampersand.
Dynamic Editing of
Programmatically Created SGSes
The
following example produces exactly the same output as the previous
example. The difference is that it shows
how you can create an SGS programmatically using the *EDIT mechanism.
*REMOVE SGS: table,1,[table]
*INCREMENT Z FROM 1 TO 80 BY 7
*CREATE SGS: table [*Z]
*LOOP
*REMOVE SGS: LIST,1,[LIST]
*EDIT ON
*CREATE SGS: LIST &
*INCREMENT Z TO [table]
[table,z,1,1],
*LOOP
*SET COLED$ TO -1 + COLED$
&
*EDIT OFF
*EDIT ON
*INCREMENT Z TO [LIST,1,1]
*SET COLED$ TO [LIST,1,1,Z]
X&
*LOOP
*EDIT OFF
*EDIT ON
*INCREMENT Z TO [LIST,1,1]
[LIST,1,1,Z]
&
*LOOP
*EDIT OFF
The
*TERM directive allows you to change the ampersand terminator character to
something else. This is necessary when
ampersands must be carried through to the output stream or the SGS that you are
editing.
How to Change the
Nasty Ampersand
The
*TERM directive takes one argument. It
is either the single character that you want to use as the dynamic editing
terminator or the word POP. Examples:
*TERM ~
The
statement, above, pushes the current terminator character onto the stack and
changes the current terminator character to tilde.
POP
is case-insensitive. When you change the
terminator, the terminator that was previously in effect is pushed onto a
stack. You can push as many as 80
characters onto the stack. As you finish
your work with one terminator, you can revert to the previous terminator with
POP. Initially, the stack contains one
entry. That entry is the ampersand
(&) character. You can try to pop
the initial ampersand off the stack, but it stays put.
The
*TERM directive’s design allows you to temporarily change the terminator to
something more convenient and then revert it back to its previous state. Example:
*SET ALAN TO 80
*INCREMENT Z TO ALAN
*MSG [*Z] " - A"
*TERM A
*LOOP
*INCREMENT Z TO 1000
*MSG "POP " [*Z]
*TERM POP
*LOOP
*INCREMENT Z TO ALAN
*MSG [*Z] " - B"
*TERM A
*LOOP
*INCREMENT Z TO 1000
*MSG "POP " [*Z]
*TERM POP
*LOOP
*EDIT ON
THIS &
BROWN &
HOUSE &
SERVES ALE.&
*EDIT OFF
*TERM +
*EDIT ON
THIS +
BROWN +
HOUSE +
SERVES ALE & BEER.+
*EDIT OFF
*TERM POP
*EDIT ON
THIS &
BROWN &
HOUSE &
SERVES ALE.&
*EDIT OFF
produces
THIS BROWN HOUSE
SERVES ALE.
THIS BROWN HOUSE
SERVES ALE & BEER.
THIS BROWN HOUSE
SERVES ALE.
The Breakpoint
At
the commencement of skeleton execution, the output stream is sent to the file
that is specified on the shell command.
It is the shell command that gets SSG going. When that file is NUL, the output stream is
sent to stdout, so the output
appears on your monitor. With breakpointing, you close the current output stream and
begin a new one, redirecting output elsewhere.
When
you omit the filename, output is redirected to stdout.
*SET N TO 4
*BRKPT
"C:\Users\Ugen\" "BPFILE" [*N] ".txt"
THIS OUTPUT IS SENT TO FILE
NUMBER [*N] IN Unicode
*SET N TO N+1
*BRKPT "*"
"C:\Users\Ugen\" "BPFILE" [*N] ".txt"
THIS OUTPUT IS SENT TO FILE
NUMBER [*N] IN
ASCII
*BRKPT
THIS OUTPUT IS SENT TO stdout
produces
Nothing
in filename specified on the shell command.
THIS OUTPUT IS SENT TO FILE
NUMBER 4 IN Unicode
in
C:\Users\Ugen\BPFILE4.txt
THIS OUTPUT IS SENT TO FILE
NUMBER 5 IN
ASCII
In
C:\Users\Ugen\BPFILE5.txt
Operating SSG
SSG uses a radical, new
yet unfamiliar user interface known as the command shell. See your System Administrator to learn how to
gain access to this environment. Once
you have arrived at the prompt, you start SSG with one of the following
commands:
SSG <name-of-skeleton-file>
<name-of-SGS-file> <name-of-output-file>
SSG <name-of-skeleton-file>
<name-of-SGS-file> <name-of-output-file> -Q
SSG <name-of-skeleton-file>
<name-of-SGS-file> <name-of-output-file> -L
SSG <name-of-skeleton-file> <name-of-SGS-file>
<name-of-output-file> -R<name-of-locked-skeleton>
The file that contains
the main program and possibly some or all of its related subroutines is in the
file specified as <name-of-skeleton-file>. The file that contains all of the SGSes that
are not created by the skeleton is specified as <name-of-SGS-file>. If you want to save the ordinary output of
the skeleton, you specify the file to receive the output as
<name-of-output-file>. You can, if
you want, specify NUL for both the <name-of-SGS-file> and
<name-of-output-file>. A NUL input
file is like a file with nothing in it.
A NUL output file is a file that accepts data and discards it. The –Q suppresses most output to the command
shell. The –L option produces a trace
file and the most verbose output to the command shell. The absence of any option produces an
intermediate amount of output. The –R
option transforms the complete skeleton to locked form and writes the locked
skeleton to the file named <name-of-locked-skeleton>. A locked skeleton can be read by a subsequent
execution of SSG. You specify the locked
skeleton filename as <name-of-skeleton-file>.
Ordinary Text File Becomes an SGS File
You can prefix
<name-of-SGS-file> with an asterisk to order SSG to create a virtual SGS
file from simple text. The
transformation of text to SGS involves quoting around words that are separated
by spaces and prepending the SGS label TAG to every line. This saves a step when all you have is a list
of things in a text file and you want to process the list. For example, first the input file, then the
transformation of the input file.
A
A,B
A B,C
A B C
SSG 3.1.22-x64-BETA
(Sativa+) 2009/08/03 18:01:19 [120]
Compiled on Aug 3 2009 at
17:08:45
Copyright 2003 MDRSESCO
LLC. All rights reserved.
Use subject to
license. Licensed to Systar 2008.
Install Date:
Client Name:
$$$ Start to compile the
data...
000001 TAG "A"
000002 TAG "A","B"
000003 TAG "A B","C"
000004 TAG "A B C"
$$$ Start to compile the
skeleton...
$$$ Start to execute the
skeleton...
END SSG. 0.0 seconds.
0 skeleton lines. 0 lines
output.
Tracing
When
you are debugging a skeleton, you will find that it is helpful to have a
detailed trace. On the shell command
line, when you specify the –L option, the maximum amount of information is
presented to stdout and to a trace
file that is located at %temp%\tracedump.txt.
You can dynamically control tracing by altering the system-defined
variable LOPTION$. LOPTION$ is initially
zero unless the –L option is present on the SSG shell command. You can *SET LOPTION$ TO 1 to start tracing
and *CLEAR LOPTION$ to stop tracing.
When you start tracing, the trace file is truncated and a new tracing
session is begun. Tracing of a locked
skeleton is not permitted for administrative reasons. Tracing is expensive and should not be
enabled in production skeletons.
Pragmatics
The *PRAGMA directive tells SSG to alter the way the
skeleton is processed. The directive
*PRAGMA
INCLUDE “filename.txt”
brings lines of text from the indicated file into the
skeleton at the point of the *PRAGMA INCLUDE for compilation. This directive is ignored by a locked
skeleton because the included lines are contained within the locked skeleton. They do not have to be retrieved from a
separate file. The purpose of *PRAGMA
INCLUDE is to act as an aid to maintenance.
Projects that require many subroutines are conveniently maintained with
each subroutine in its own file.
How to Programmatically
Add Data
The
collection of SGSes is dynamic. It is
established initially from the file named in the 2nd parameter of
the SSG shell command line. SSG creates
several SGSes of its own beyond those in the file. Once skeleton execution commences, the
skeleton is able to insert and delete SGSes at will.
To
add an SGS, use the *CREATE SGS directive.
In the basic form, you simply write the SGS following the colon, as in
*CREATE SGS: NODE 56A IS D8470 AND CONNECTS TO CUA, CUB
In
the more advanced form, you can build up an SGS using a series of directives
that may conditionally be executed, as in
*CLEAR A
*EDIT ON
*CREATE SGS: NODE &
*IF A>2
56A &
*ELSE
56B &
*ENDIF
IS D8470 AND CONNECTS TO CUA,
CUB&
*EDIT OFF
*INCREMENT I TO [NODE]
*EDIT ON
*INCREMENT J TO [NODE,I]
*INCREMENT K TO [NODE,I,J]
[NODE,I,J,K],&
*LOOP
*SET COLED$ TO -1 + COLED$
&
*LOOP
*EDIT OFF
*LOOP
How to Programmatically Delete Data
You
can remove one or more SGSes by giving the label of the SGS, the sequence
number of the 1st SGS that you want to remove, and the count of
SGSes that are to be removed. For
example, to remove the 2nd and 3rd SGS with label NODE,
you can code
*REMOVE SGS: NODE,2,2
To
remove all SGS of a given class, you can specify 1 as the starting sequence number
and [<label>] as the count, where <label> is the name of the
class. You should avoid attempting to
remove SGSes that are in the same class as the class of SGSes that you are
looping through. Instead, you should
create a temporary SGS class that records the indices of the SGSes in the
original class that you want to delete and iterate through the temporary class.
How to Declare and Initialize a Variable
Variables are declared when they are
initialized. You use *SET and *CLEAR to
initialize a variable. *CLEAR
<var> is equivalent to *SET <var> TO 0. For example,
*CREATE
SGS: E V
*SET
V
[*V]
*CLEAR
V
[*V]
*SET
V TO 1
[*V]
*SET
V TO ((36/9)+(28*64))
[*V]
*SET
[E,1,1,1] TO 24
[*V]
produces
1
0
1
1796
24
The *INCREMENT directive initializes its induction
variable to 1 or to the value that you provide in the directive’s FROM clause.
System-Defined
Variables
Several SSG variables are created when SSG is
initialized. The following discusses
their purpose and usage.
LOPTION$
This variable is initially set to 1 when the shell
command line specifies the –L option and to zero otherwise. It can be changed dynamically as the skeleton
is executed. It turns on and off the
tracing function that is described above.
EDBUFSZ$
This variable allows the skeleton to adjust the size
of the dynamic editing buffer. It is
initially set to zero, which indicates that the skeleton has not yet specified
the size of the dynamic editing buffer in octets. The buffer is generously sized to begin with,
so you may have no need to use this variable.
CASEINSENSITIVE$
This variable specifies that *IF COLUMN SEARCH and *IF/*IFNOT
ROW SEARCH (and the ROW SEARCH analogs) are to match in spite of case
differences between the search key and the target subfield in the SGSes. Initially this variable is set to zero. That means searching initially IS case
sensitive. You can set it to 1 and then
you will be able to perform not-case-sensitive searches.
COLED$
This variable is used during dynamic editing of
non-directive skeleton directives and of SGSes.
It is set to zero by the *EDIT ON directive and advances as your
skeleton inserts characters into a buffered line. You can change to default insert point
anywhere within the bounds of the dynamic editing buffer that is referred to in
the item, above, concerning EDBUFSZ$. It
is possible to position the cursor over a previously inserted character and
change the character.
STMT$
This variable is set by the *IF/*IFNOT ROW SEARCH (and
the ROW SEARCH analogs). It is the
number of the SGS of the class named on the *IF/*IFNOT directive at which a
match was found to occur.
FLD$
This variable is set by the *IF/*IFNOT ROW SEARCH (and
the ROW SEARCH analogs). It is the
number of the field of the SGS of the class named on the *IF/*IFNOT directive
at which a match was found to occur.
SFLD$
This variable is set by the *IF/*IFNOT ROW SEARCH (and
the ROW SEARCH analogs). It is the
number of the subfield of the field of the SGS of the class named on the
*IF/*IFNOT directive at which a match was found to occur.
System-Defined SGSes
The following SGSes are created by SSG during initialization
and updated, as necessary, during skeleton execution.
DATE$
Example: [DATE$,1,1,1]
is 20080710
TIME$
Example: [TIME$,1,1,1]
is 06:06:31
LBRACK$
Example:
[LBRACK$,1,1,1] is [
RBRACK$
Example:
[RBRACK$,1,1,1] is ]
AMPER$
Example:
[AMPER$,1,1,1] is &
Environment
Variables
You can reference environment variables from inside
your skeleton. The bracketed reference
is of the form [%<name-of-environment-variable>]. For example, the skeleton
[%temp]
produces
C:\Users\Ugen\AppData\Local\Temp
on my system.
On your system, the TEMP environment variable is likely to be different.
In those instances where the environment variable to
which you refer does not exist, the BREF resolves to
<environment-vbl-not-found>
How to
Launch a Program or Shell Script
You can launch a program or a shell script from inside
SSG. You can create the script with
judicious use of the *BRKPT directive.
Remember to create an ASCII file, since, as of July, 2008, Windows does
not accept a Unicode file as a shell script.
The *LAUNCH directive instructs the shell to “open” the file. A file ending in .BAT or .CMD is a shell
script. A file ending in .EXE is a
program. For example,
*BRKPT
"*" "SCRIPT.CMD"
ECHO
HELLO, WORLD
PAUSE
*BRKPT
*LAUNCH
"SCRIPT.CMD"
And, for example,
*LAUNCH
"MSPAINT.EXE"
When you want the launched program to present in a
minimized window, you prefix the name of the launch file with an asterisk. For example,
*LAUNCH
"*MSPAINT.EXE"
How to Concatenate
Strings
Sticking strings together is a convenience. It is known as concatenation and you do it
this way:
*CREATE
SGS: E HELLO
*CREATE
SGS: F GOODBYE
*SET
G TO "COMEBACK"
*CONCAT
varnam "1" [E,1,1,1] [F,1,1,1] [*G]
[*VARNAM]
produces
1HELLOGOODBYECOMEBACK
The skeleton directive
*CONCAT
varnam "1" [E,1,1,1] [F,1,1,1] [*G]
can be written
*CONCAT
varnam "1" [E,1,1,1] [F,1,1,1] G
How to
Create a Directory or File
You can create a file with the *CREATE FILE: directive. Successful completion of *CREATE FILE
indicates that a zero-length file by the name given has been created or
pre-existed and has been truncated.
Example:
*CREATE
FILE: “E:\BUTTER\MARGARINE\TRANSFATS\” [E,1,1,1] “.TXT”
You can create a directory with the *CREATE DIRECTORY:
directive. Successful completion of
*CREATE DIRECTORY indicates that a directory by the name given has been created
or pre-existed. Example:
*CREATE
DIRECTORY: “C:\Windows”
How to Send
a Message to the User and Error Terminate SSG
You can send an error message to the user with the
following directive. It is similar to
the *CONCAT directive in that the various parameters are concatenated so that
they appear together in the message.
*CREATE
SGS: NODE WAYWRONG
*SET
Z
*ERRORMSG
"Your parameter on the " [*z] "st NODE
SGS (" [NODE,1,1,1] ") is improper."
produces
***
ERROR ***: Your parameter on the 1st
NODE SGS (WAYWRONG) is improper.
How to Send
a Message to the User and NOT
Error Terminate SSG
The *MSG directive is identical to the *ERRORMSG
directive, except that SSG does not terminate.
It continues.
*MSG
"Roses are red," " violets are blue, " "I am sweet,
" " and so are you."
produces
***
MESSAGE ***: Roses are red, violets are
blue, I am sweet, and
so are you.
Getting Out
You cause SSG to terminate normally with the following
directive:
*QUIT
How to
Insert a Delay
Occasionally you may want SSG to while away the
hours. For example, you can create a
file and feed it to another application.
SSG will be instructed to wait a while before creating the next input to
the application. You use the *SLEEP
directive. The argument is either a
literal integer or the name of a variable that contains an integer. Said integer is the minimum number of
milliseconds that you want SSG to delay at the point of the *SLEEP before
resuming. For example,
*SLEEP
1000
This directive causes SSG to delay at the point of the
*SLEEP directive for 1000 milliseconds.
*SET
HAL TO 1500
*SLEEP
HAL
These directives cause SSG to delay at the point of
the *SLEEP directive for 1500 milliseconds.
How to
Question the User
You use the *ASK directive to solicit input from the
user. The answer appears in an SGS. Example,
*CREATE SGS: A1 CNEP
*CREATE SGS: A2 "Would you?"
*CREATE SGS: A3 "n"
*CREATE SGS: A4 "y"
*SET V TO [A3,1,1,1]
*REMOVE SGS: CNEP,1,[CNEP]
*ASK [A2,1,1,1] [A1,1,1,1] [*V] [A4,1,1,1]
([CNEP,1,1,1])
*REMOVE SGS: CNEP,1,[CNEP]
*ASK "Would you?" CNEP "n"
"y"
([CNEP,1,1,1])
If you wish, you can provide a list of acceptable
answers that follows the <SGS-label> as in
*ASK
“Do you want fries with that? Y/N”
SGSLAB “Y” “N” “”
When you provide no list, any answer is acceptable.
Otherwise, until the user provides an answer from the
list, the question is asked repeatedly. *ASK
compares what the user types against each of the choices, if any. Such comparisons are case insensitive. The empty string matches when the user types
in nothing, but he or she presses the ENTER key. In the case of no reply, the string “<No
Reply>” is put in the SGS. The
parameters can all be bracketed references.
When you want to accept any non-negative integer that
fits in 32 bits without overflowing, you can use “*INTEGER” as the first and
only alternative acceptable answer.
SSG automatically creates an SGS class ANSWER$LOG that
records all questions asked and answered.
For example,
*ASK
"How many days until you marry?" ANSWER40 "*INTEGER"
[ANSWER$LOG]
*INCREMENT
Z TO [ANSWER$LOG]
[ANSWER$LOG,Z,1,1] --- [ANSWER$LOG,Z,2,1]
*LOOP
How to Sort
Your SGSes
You use the *SORT directive to name the SGS that you
want sorted. The first subfield of the
first field is the sort key. For
example,
*SUBROUTINE
X
[ALPHA]
is the count
*INCREMENT
Z TO [ALPHA]
--- [ALPHA,Z,1,1] ---
*LOOP
--------
*ENDSUB
*CREATE
SGS: ALPHA Z
*CREATE
SGS: ALPHA Y
*CREATE
SGS: ALPHA X
*CALL
X
*SORT
ALPHA
*CALL
X
produces
3
is the count
--- Z ---
--- Y ---
--- X ---
--------
3
is the count
--- X ---
--- Y ---
--- Z ---
--------
How to Get Special
Information about a Subfield
As previously stated, the fifth element of a bracketed reference (BREF), when specified, allows you to request special information about a subfield. For example, the subfield [TAG,1,1,1] is a certain number of characters in length. You get the length by coding [TAG,1,1,1,LENGTH$].
LENGTH$ - the character length of a subfield
SUBSTR$ - a contiguous subset of characters from the subfield, specified as SUBSTR$,<start>,<length>
FIND$ - returns the index of the location of a string within a string OR -1 when there is no find
UCASE$ - provides the contents of the subfield with all lower-case characters changed to upper-case
LCASE$ - provides the contents of the subfield with all upper-case characters changed to lower-case
DAYNUMBER$ - converts a date in YYYYMMDD format to a day number (see below)
DNDATE$ - converts a day number to a date in YYYYMMDD format
RANDOM$ - creates a random number
DQ$ - every double-quote character is doubled (i.e. “ becomes “”) and the subfield is enclosed in double-quotes
String Search
Suppose you have a subfield that contains the string “AA:BBB” and you want to separate AA from BBB. You can locate the colon with FIND$ and extract twice using SUBSTR$, as follows.
*CREATE SGS: E "AA:BBB"
*SET INDEX TO [E,1,1,1,FIND$,":"]
*IF INDEX > -1
*SET L TO [E,1,1,1,LENGTH$]
*SET S1 TO [E,1,1,1,SUBSTR$,0,INDEX]
*SET S2 TO [E,1,1,1,SUBSTR$,INDEX+1,L-INDEX-1]
[*S1]
[*S2]
L=[*L]
INDEX=[*INDEX]
*ENDIF
*GENER sgs
*INCREMENT Z TO [SGS$]
---- [SGS$,z,1,1] ----
*LOOP
produces
AA
BBB
L=6
INDEX=2
---- E ----
---- RBRACK$ ----
---- LBRACK$ ----
---- TIME$ ----
----
DATE$ ----
Further,
*CREATE SGS: NODE CPU80A
*SET I TO [NODE,1,1,1,FIND$,"U8"]
[*I]
*SET NNN TO "U8"
*SET I TO [NODE,1,1,1,FIND$,NNN]
[*I]
produces
2
2
What is a Day Number?
A day number is an integer. The date January 1, 1964 is assigned the number 1. The day number of the next day is 2. The day after that, 3. And so forth. Up to December 31, 2099. A day number is freely convertible to a date and back to a day number. If you have a date and you want the date 90 days hence, you convert the date to a day number, add 90, and convert the sum back to a date.
*CREATE SGS: B 19670314
*SET K TO [B,1,1,1,DAYNUMBER$]
[*K]
*SET K to K + 90
[*K]
*CREATE SGS: V [*K]
*SET L TO [V,1,1,1,DNDATE$]
[*L]
produces
1169
1259
19670612
Further,
*CREATE SGS: B 19670314
*SET K TO [B,1,1,1,DAYNUMBER$]
+ 90
*CREATE SGS: V [*K]
[V,1,1,1,DNDATE$]
produces
19670612
How to Generate Random Numbers
The ability to generate
random numbers is handy in a test data generator. The RANDOM$ special editing code resolves a
bracketed reference to a random number.
The undesignated BREF (a BREF lacking a special editing code) specifies
the first positive, nonzero integer that will NOT appear in the random numbers
returned. In this way, you specify the
range from 0-N-1 the numbers that you will accept, where N is the integer to
which the undesignated BREF resolves.
The designated BREF returns a different random number each time it is
resolved. The largest N is 1073676289. Notice that, in what follows, none of the
random numbers is greater than or equal to 21115.
*CREATE SGS: TX 21115
[TX,1,1,1]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
[TX,1,1,1,RANDOM$]
produces something like
21115
18181
12516
1767
1605
7133
17250
6073
6649
5143
19928
Double Quotes in Those Subfields That BREFs Reference
Because of strict syntax
checking, it is not possible to put anything in an SGS subfield other than a
limited set of characters, unless you surround the subfield with double
quotes. When such a subfield is
dereferenced by way of a BREF, the surrounding double quotes come off and
doubled double quotes inside become single double quotes. In various circumstances, it is inappropriate
to reformat the subfield upon dereferencing.
So, the DQ$ special editing code changes the subfield reference to the
safer quoted form, which accommodates special characters and integrated double
quotes.
Example:
*CREATE SGS: TAG “A””B”
*CREATE SGS: NODE [TAG,1,1,1,DQ$]
[NODE,1,1,1]
produces
A”B
A special case is made for
the % flagged BREF so that you can use the DQ$ special editing code. In this way you can obtain a quoted reference
to an environment variable that contains characters that are not ordinarily
permitted inside a subfield. The BREF [%COMPNAME,,,,DQ$] is explicitly permitted.
How to Obtain a List of SGS Labels
Users are instructed how
to format SGSes. When they get the label
wrong, it is possible to overlook the fact and to fail to detect that the user
made a spelling error. Therefore, SSG
provides the skeleton author with the ability to obtain a list of all SGS
labels, so that the skeleton is able to detect any labels that are meaningless.
To obtain a list of SGSes,
you first execute the following directive:
*GENER SGS
This directive instructs
SSG to create a series of SGSes with the label SGS$. Subfield 1 of field 1 of each such SGS
contains the label of an SGS. The SGS$
SGS is not included in the series. Any
pre-existing SGS$ SGSes are deleted before a new series is created.
*CREATE SGS: E "AAAA:BBB"
*SET INDEX TO [E,1,1,1,FIND$,":"]
*IF INDEX > -1
*SET L TO [E,1,1,1,LENGTH$]
*SET A TO INDEX+1
*SET B TO L-INDEX-1
*SET S1 TO [E,1,1,1,SUBSTR$,0,INDEX]
*SET S2 TO [E,1,1,1,SUBSTR$,A,B]
[*S1]
[*S2]
A=[*A]
B=[*B]
L=[*L]
INDEX=[*INDEX]
*ENDIF
*GENER sgs
*INCREMENT Z TO [SGS$]
---- [SGS$,z,1,1] ----
*LOOP
produces
AAAA
BBB
A=5
B=3
L=8
INDEX=4
---- E ----
---- RBRACK$ ----
---- LBRACK$ ----
---- TIME$ ----
---- DATE$ ----