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

usermark@mdrsesco.biz

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$ ----