RFC 166

Data Reconfiguration Service: An implementation specification

๐Ÿ“… published May 1971, from ietf

RFC 166: DATA RECONFIGURATION SERVICE -- AN IMPLEMENTATION SPECIFICATION ๐ŸŽ€

Okay bestie, the header first ๐Ÿ’– This is RFC 166 from the Network Working Group, dated 25 May 1971 (NIC 6780). The author list is a whole squad: Bob Anderson (Rand), Vint Cerf (UCLA), Eric Harslem and John Haefner (both Rand), Jim Madden (U. of Illinois), Bob Metcalfe (MIT), Arie Shoshani (SDC), Jim White (UCSB), and David Wood (Mitre) ๐ŸŒธ The header doesn't spell out a category or status, and it doesn't list anything this RFC obsoletes or updates, so there's nothing else to report there โœจ

Contents ๐Ÿงš

Network Working Group                                       Bob Anderson

<!-- placeholder -->

I. Introduction โœจ

Purpose of this RFC

So the whole point of this RFC is to specify the Data Reconfiguration Service (DRS.) ๐Ÿ’…

The DRS experiment is a software mechanism that reformats Network data streams. It can be adapted to lots of different Network application programs. We're hoping the experiment leads to a future standard service that's built on the principles in this RFC ๐Ÿซถ

Motivation

Application programs need specific data I/O formats, and those formats are different from program to program. The vibe here is that the Network should adapt to each program's requirements, instead of changing every program to comply with a standard. This doesn't rule out standards that describe the formats of regular message contents. It just treats a standard as a desirable mode of operation, but not a necessary one ๐Ÿ‘‘

There's also a format mismatch problem when users want to hook up lots of different kinds of consoles to a single service program. It's desirable to have the Network adapt to individual console configurations, rather than needing a unique software package for each console transformation ๐Ÿ’œ

One approach is for sites with serious computing power to offer a data reconfiguration service. This document is a specification of exactly that ๐Ÿฆ‹

The plan is that an applications programmer defines forms that describe data reconfigurations. The service stores the forms by name. Later on, a user (maybe not a programmer at all) uses the service to do a particular transformation of a Network data stream, just by calling the form by name โญ

We tried to make a notation tailored to some specifically needed instances of data reformatting, while keeping the notation and its implementation inside a utility range. The bottom edge of that range is a notation expressive enough to make the experimental service useful. The top edge is a notation that stops short of being a general purpose programming language ๐Ÿ“


II. Overview of the Data Reconfiguration Service ๐Ÿฆ‹

Elements of the Data Reconfiguration SERVICE

An implementation of the Data Reconfiguration Service (DRS) includes a few main pieces ๐ŸŒˆ There are modules for connection protocols, a handler for some of the requests you can make of the service, and a compiler and/or interpreter (called the Form Machine) that acts on those requests. There's also a file storage module for saving and retrieving definitions of data reconfigurations (forms).

This section describes connection protocols and requests. The next section covers the Form Machine language in some detail. File storage isn't described in this document, because it's transparent to how you use the service, and its implementation is different at each DRS host ๐Ÿ™ˆ

Conceptual Network Connections

There are three conceptual Network connections to the DRS. See Fig. 1 ๐Ÿ’…

  1. The control connection (CC) is between an originating user and the DRS. Forms specifying data reconfigurations are defined over this connection. The user indicates (once) which forms to apply to data passing over the two connections described below.
  2. The user connection (UC) is between a user process and the DRS.
  3. The server connection (SC) is between the DRS and the serving process.

Since the goal is to adapt the Network to user and server processes, there's a minimum of requirements imposed on the UC and SC ๐Ÿซถ

      +------------+              +------+          +---------+
      | ORIGINATING|     CC       | DRS  |    SC    | SERVER  |
      | USER       |--------------|      |----------| PROCESS |
      +------------+     ^        +------+     ^    +---------+
                         |           /         |
                         |        UC/ <-----\  |
                         |         /         \ |
                         |   +-----------+    \|
         TELNET ---------+   | USER      |     +-- Simplex or Duplex
         Protocol            | PROCESS   |         Connections
         Connection          +-----------+


                Figure 1.  DRS Network Connections

Part 2 is coming, bestie, and it gets even more fun from here ๐Ÿฆ‹

CONNECTION PROTOCOLS AND MESSAGE FORMATS ๐Ÿ’…

okay bestie, so over a control connection it's literally a direct convo between the originating user and the DRS ๐Ÿ’– this is where the user defines forms or assigns predefined forms to connections for reformatting โœจ

the user connects to the DRS through the standard initial connection protocol (ICP) ๐Ÿฆ‹ and they do NOT go through a logger ๐Ÿ™ˆ instead they call a particular socket that the DRS always listens on ๐ŸŽ€ (experimental socket numbers will be published later ๐Ÿ‘€). then the DRS switches the user over to another socket pair ๐ŸŒธ

messages sent over a control connection use the types and formats specified for TELNET ๐Ÿ’œ (the data type code should specify ASCII, which is the default). so a user at a terminal should be able to connect to a DRS through their local TELNET, like in Fig. 2 ๐Ÿซถ

                            +---------+   CC  +---------+
                  +---------| TELNET  |-------|   DRS   |
                  |         +---------+       +---------+
      +-----------------------+
      |         USER          |
      | (TERMINAL OR PROGRAM) |
      +-----------------------+

Figure 2. A TELNET Connection to DRS

when a user connects to DRS, they supply a six-character user ID (UID) as a qualifier so the uniqueness of their form names is guaranteed ๐Ÿ” and they'll start out with these commands:

  1. DEFFORM (form)
  2. ENDFORM (form)

these two commands define a form, and the text of the form is entered in order between them ๐Ÿ“ the form gets stored in the DRS local file system.

  1. PURGE (form): purges the named form (qualified by the current UID) from the DRS file system ๐Ÿ—‘๏ธ
  2. LISTNAMES (UID): returns the unqualified names of all forms assigned to UID ๐Ÿ“‹
  3. LISTFORM (form): returns the source text of a named form ๐Ÿ”
  4. DUPLEXCONNECT (user site, user receive socket, user method, server site, server receive socket, server method, user-to-server form name, server-to-user form name): makes a duplex connection between two processes using the receive sockets and the sockets one greater. method is defined below ๐Ÿ‘ฏ the forms define the transformations on these connections โœจ
  5. SIMPLEXCONNECT (user site, user socket, user method, server site, server socket, server method, form): makes a simplex connection between the two sockets as specified by method ๐Ÿซง
  6. ABORT (site, receive socket): terminates the reconfiguration of data by closing both the UC and SC specified in part in the command ๐Ÿ›‘

either one, both, or neither of the two parties specified in 6 or 7 may be at the same host as the party issuing the request ๐Ÿ  sites and sockets specify user and server for the connection, and method indicates the way in which the connection is established ๐Ÿ’…

these rules apply to the commands, so pay attention bestie ๐Ÿ“œ

  1. Commands may be abbreviated to the minimum number of characters to identify them uniquely.
  2. All commands should be at the start of a line.
  3. Parameters are enclosed in parentheses and separated by commas.
  4. Imbedded blanks are ignored.
  5. The parameters are:
             form name        1-6 characters
             UID              1-6 characters
             Site             1-2 characters specifying
                                  the hexadecimal host number
             Socket           1-8 characters specifying the
                                  hexadecimal socket number
             Method           A single character
  1. Method has the following values:
  • C: the site/socket is already connected to the DRS as a dummy control connection (should not be the real control connection) ๐ŸŽญ
  • I: connect via the standard ICP (does not apply to SIMPLEXCONNECT) ๐Ÿฆ‹
  • D: connect directly via STR, RTS โšก

the DRS will make at least the following minimal responses to the user ๐Ÿ’Œ

  1. A positive or negative acknowledgement after each line (CR/LF) โœ…
  2. If a form fails or terminates, the response looks like this:
             TERMINATE, ASCII Host # as hex, ASCII Socket # as hex,
                         ASCII Return Code as decimal

which identifies at least one end of the connection ๐ŸŽฏ


EXAMPLE CONNECTION CONFIGURATIONS ๐Ÿงš

okay so there are basically two modes of DRS operation ๐Ÿ“

  1. the user wants to set up DRS UC/SC connection(s) between the programs.
  2. the user wants to set up the same connection(s) with their terminal sitting at the end of the UC or the SC. this one is giving "interact from my terminal" energy, and it fits when the user wants to chat with the serving process (for example, a logger) ๐Ÿ’ฌ

in the first case (Fig. 1, where the originating user is either a terminal or a program) the user issues the appropriate CONNECT command ๐Ÿš€ the UC/SC can be simplex or duplex ๐Ÿ”

the second case has two possible configurations, shown in Figs. 3 and 4 ๐Ÿ‘€

   +-------+    +--------+   CC    +-----+        +----+
   |       |----|        |---------|     |   SC   |    |
   | USER  |    | TELNET |   UC    | DRS |--------| SP |
   |       |----|        |---------|     |        |    |
   +-------+    +--------+         +-----+        +----+

Figure 3. Use of Dummy Control Connection

                +---------+
   +------+    /| USER    |   CC   +-----+
   |      |---/ | SIDE    |--------|     |   SC   +----+
   | USER |     +---------+   UC   | DRS |--------| SP |
   |      |---\ | SERVING |--------|     |        +----+
   +------+    \| SIDE    |        +-----+
                +---------+

Figure 4. Use of Server TELNET

in Fig. 3 the user tells their TELNET to make two duplex connections to DRS ๐Ÿงฉ one is for control information (the CC) and the other is a dummy. when they issue the CONNECT, they reference the dummy duplex connection (UC) using the "already connected" option ๐Ÿซถ

in Fig. 4 the user has their TELNET (user side) call the DRS ๐Ÿ“ž when they issue the CONNECT, the DRS calls the TELNET (server side), which accepts the call on behalf of the console. this distinction is known only to the user since to the DRS, the configuration in Fig. 4 looks identical to the one in Fig. 1 ๐Ÿคซ two points should be noted:

  1. TELNET protocol is needed only to define forms and direct connections. It is not required for the using and serving processes.
  2. The using and serving processes need only a minimum of modification for Network use, i.e., an NCP interface.

III. THE FORM MACHINE ๐ŸŒˆ

INPUT/OUTPUT STREAMS AND FORMS

this section describes the syntax and semantics of forms that specify the data reconfigurations ๐Ÿง  the Form Machine gets an input stream, reformats it according to a form describing the reconfiguration, and emits the reformatted data as an output stream โœจ

while reading, it'll help to picture a form being applied to the data stream, like in Fig. 5 ๐ŸŽ€ an input stream pointer marks the position of data (in the input stream) that a part of the form is analyzing at any given time. likewise, an output stream pointer locates data being emitted in the output stream ๐Ÿ’–

       /\/\                                                  /\/\
  ^    |  |                     FORM                         |  |   ^
  |    |  |                -----------------                 |  |   |
  |    |  |            +-  -----------------  -+             |  |   |
  |    |  |            |   CURRENT PART OF     |             |  |   |
INPUT  |  |<= CURRENT <    -----------------    > CURRENT => |  | OUTPUT
STREAM |  |   POINTER  |   FORM BEING APPLIED  |  POINTER    |  | STREAM
       |  |            +-  -----------------  -+             |  |
       |  |                -----------------                 |  |
       |  |                -----------------                 |  |
       |  |                -----------------                 |  |
       \/\/                                                  \/\/

Figure 5. Application of Form to Data Streams

FORM MACHINE BNF SYNTAX

this is the grammar for forms, and it is very much a "read it slowly" moment ๐Ÿ”ฅ

   form           ::=  rule | rule form

   rule           ;;=  label  inputstream  outputstream ;

   label          ::=  INTEGER | <null>

   inputstream    ::=  terms | <null>

   terms          ::=  term | terms , term

   outputstream   ::=  : terms | <null>

   term           ::=  identifier | identifier  descriptor |
                       descriptor | comparator

   identifier     ::=  an alpha character followed by 0 to 3
                       alphanumerics

   descriptor     ::=  (replicationexpression , datatype ,
                       valueexpression , lengthexpression  control)

   comparator     ::=  (value  connective  value  control)  |
                       (identifier  *<=*  control)

   replicationexpression  ::=  # | arithmeticexpression | <null>

   datatype       ::=  B | O | X | E | A

   valueexpression  ::=  value | <null>

   lengthexpression  ::=      arithmeticexpression | <null>

   connective     ::=  .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.

   value          ::=  literal | arithmeticexpression

   arithmeticexpression  ::=  primary | primary operator
                              arithmeticexpression

   primary        ::=  identifier | L(identifier) | V(identifier) |
                       INTEGER

   operator       ::=  + | - | * | /

   literal        ::=  literaltype "string"
   literaltype    ::=  B | O | X | E | A

   string         ::=  from 0 to 256 characters

   control        ::=  :  options | <null>

   options        ::=  S(where) | F(where) | U(where) |
                       S(where) , F(where) |
                       F(where) , S(where)

   where          ::=  arithmeticexpression | R(arithmeticexpression)

ALTERNATE SPECIFICATION OF FORM MACHINE SYNTAX ๐Ÿ’…

okay bestie, this part is the alternate way of writing the form machine syntax ๐Ÿงš it's drawn out with the little numbers stacked above and below each piece, and "infinity" means there's no upper limit โœจ here's the whole thing:

                                   infinity
form                    ::=  {rule}
                                   1
                                      1         1          1
rule                    ::=  {INTEGER}   {terms}   {:terms} ;
                                      0         0          0
                                         infinity
terms                   ::=  term {,term}
                                         0
                                                      1
term                    ::=  identifier | {identifier}   descriptor
                                                      0
                             | comparator
                                                    1
descriptor              ::=  ({arithmeticexpression}  , datatype ,
                                                    0
                                    1                     1          1
                             {value} ,  {lengthexpression}  {:options}
                                    0                     0          0
                                                                 1
comparator              ::=  (value  connective  value {:options} ) |
                                                                 0
                                                              1
                             (identifier .<=. value {:options} )
                                                              0
connective              ::=  .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.

lengthexpression        ::=  # | arithmeticexpression

datatype                ::=  B | O | X | E | A

value                   ::=  literal | arithmeticexpression

                                                         infinity
arithmeticexpression    ::=  primary  {operator  primary}
                                                         0
operator                ::= + | - | * | /

primary                 ::=  identifier | L(identifier) |
                             V(identifier) | INTEGER
                                                      256
literal                 ::=  literaltype  "{CHARACTER}   "
                                                      0
literaltype             ::=  B | O | X | A | E
                                                 1
options                 ::=  S(where) {,F(where)}  |
                                                 0
                                                 1
                             F(where) {,S(where)}  | U(where)
                                                 0
where                   ::=  arithmeticexpression |
                             R(arithmeticexpression)
                                                     3
identifier              ::=  ALPHABETIC  {ALPHAMERIC}
                                                     0

FORMS ๐ŸŒธ

okay so a form is an ordered set of rules ๐Ÿ“œ it looks like this:

form ::=  rule | rule form

the current rule gets applied to the current spot in the input stream ๐Ÿ‘€ if that rule's input part doesn't correctly describe what's sitting there, another rule becomes current and gets applied to the same spot. which rule comes next is either spelled out by the current term in the current rule, or it's just the next sequential rule by default. the whole flow of control is explained more under TERM AND RULE SEQUENCING, so keep that one bookmarked ๐Ÿ”–

if the input part of a rule does correctly describe the current input stream, then some data may be emitted at the current spot in the output stream, according to the rule ๐ŸŽ€ the input and output stream pointers then move forward over the stuff that got described and emitted, and the next rule is applied to the new current spot in the input stream. the glow-up continues โœจ

the form stops when an explicit return, R(arithmeticexpression), shows up in a rule. at that point the user and server connections get closed, and the return code, (arithmeticexpression), gets sent back to the originating user. that's the big finale ๐ŸŽฌ

RULES ๐Ÿงš

a rule is a replacement, a comparison, and/or an assignment operation. here's the shape:

rule ::= label  inputstream  outputstream

a label is the name of a rule. it exists so the rule can be referenced somewhere else in the form, which lets you explicitly transfer control to it ๐Ÿ’œ labels look like this:

label ::=  INTEGER | <null>

the optional integer labels fall in the range 0 >= INTEGER >= 9999 ๐Ÿ”ข and the rules don't have to be labeled in ascending numerical order, so you do you, no cap ๐Ÿ’…


TERMS ๐Ÿ“

the inputstream (what gets matched in the input) and the outputstream (what gets emitted in the output) are both made of zero or more terms. here's how they're written:

inputstream   ::=  terms | <null>
outputstream  ::=  :terms | <null>
terms         ::=  term | terms , term

there are four term formats, and they look like this ๐ŸŒˆ

term ::=  identifier | identifier  descriptor |
          descriptor | comparator

Term Format 1 ๐Ÿฆ‹

the first term format is just an identifier:

identifier

the identifier is a symbolic reference to a term that was already identified (that's term format 2) earlier in the form ๐Ÿซถ it takes on the same attributes as that term, which are value, length, and type. term format 1 is normally the one you use to emit data in the output stream.

identifiers are formed by an alpha character followed by 0 to 3 alphanumeric characters, so keep 'em short and sweet ๐ŸŒธ

Term Format 2 ๐Ÿ’–

term format 2 looks like this:

identifier descriptor

it's generally used as an input stream term, but it can be used as an output stream term too โœจ

a descriptor is defined like so:

descriptor ::= (replicationexpression, datatype,
               valueexpression, lengthexpression
               control)

the identifier is the symbolic name of the term, in the usual programming-language sense ๐Ÿ‘‘ it takes on the type, length, value, and replication attributes of the term, and it can be referenced elsewhere in the form. iconic behavior ๐Ÿ™Œ

the replication expression, if you specify one, makes the unit value of the term get generated however many times the replication expression's value says. the unit value (the quantity being replicated) comes from the data type, value expression, and length expression attributes. the data type defines what kind of data is being specified, the value expression gives a nominal value that the other attributes augment, and the length expression sets the unit length of the term. (fun fact: this was modeled after the IBM SRL Form C28-6514, and it's a similar take on the defined constant pseudo instruction ๐Ÿ“š)

the replication expression is defined like this:

replicationexpression ::= # | arithmeticexpression | <null>
arithmeticexpression ::= primary | primary operator
                         arithmeticexpression
operator ::= + | - | * | /
primary ::= identifier | L(identifier) | V(identifier) |
            INTEGER

it's basically a repeat function that gets applied to the combined data type, value, and length expressions. it tells you how many times the nominal value gets repeated ๐Ÿ”

the terminal symbol # means an arbitrary replication factor ๐ŸŽฒ it must be explicitly terminated by a match or a non-match to the input stream, and that termination can come from the same term or the following one. don't leave it hanging, bestie ๐Ÿ’€

a null replication expression has the value of one. arithmetic expressions get evaluated from left to right, with no precedence, so no fancy PEMDAS drama here ๐Ÿ™ˆ

the L(identifier) is a length operator. it generates a 32-bit binary integer that matches the length of the named term ๐Ÿ“ the V(identifier) is a value operator, and it generates a 32-bit binary integer that matches the value of the named term. (see Restrictions and Interpretations of Term Functions for more on that ๐Ÿง) the value operator is meant to turn character strings into their numerical correspondents, which is so cute ๐Ÿฅน

the data type is defined like so:

datatype ::= B | O | X | E | A

the data type says what kind of data the term represents ๐Ÿฆ‹ (it's expected that more data types, like floating point and user-defined types, will get added as needed, so stay tuned ๐Ÿ‘€)

here's the data type table, in a code block so it stays neat:

Data Type         Meaning              Unit Length

    B             Bit string              1 bit
    O             Bit string              3 bits
    X             Bit string              4 bits
    E             EBCDIC character        8 bits
    A             Network ASCII character 8 bits

the value expression is defined like this:

valueexpression ::= value | <null>
value ::= literal | arithmeticexpression
literal ::= literaltype "string"
literaltype ::= B | O | X | E | A

it's the nominal value of a term, written in the format the data type says, and it gets repeated according to the replication expression ๐Ÿ’œ

a null value expression in the input stream defaults to whatever data is already present in the input stream ๐Ÿซง the data must still comply with the datatype attribute, though. a null value expression in the output stream generates padding according to Restrictions and Interpretations of Term Functions, so check that section too ๐ŸŒท

the length expression is defined like this:

lengthexpression ::= arithmeticexpression | <null>

it states the length of the field that holds the value expression ๐Ÿ“

if the length expression is less than or equal to zero, the term still succeeds, but the appropriate stream pointer does not get advanced. positive lengths, on the other hand, do advance the appropriate stream pointer, as long as the term otherwise succeeds ๐Ÿšฆ

control is defined under TERM AND RULE SEQUENCING, so yep, same bookmark ๐Ÿ”–

Term Format 3 ๐ŸŽ€

term format 3 looks like this:

descriptor

it's identical to term format 2, just without the identifier ๐Ÿงšโ€โ™€๏ธ it's generally used in the output stream. it also shows up in the input stream, for when input data needs to be passed over but doesn't need to be kept for emission or for later reference. think of it as the "skip it, bestie" move ๐Ÿ’…

Term Format 4 ๐ŸŒˆ

term format 4 is the comparator, and it's used for assignment and comparison โœจ here's how it's written:

comparator    ::= (value connective value control) |
                  (identifier *<=* value  control)
value         ::= literal | arithmeticexpression
literal       ::= literaltype "string"
literaltype   ::= B | O | X | E | A
string        ::= from 0 to 256 characters
connective    ::= .LE. | .LT. | .GE. | .GT. | .EQ. | .NE.

the assignment operator *<=* assigns the value to the identifier ๐Ÿซถ the connectives mean what they usually mean. one thing to watch out for: the values being compared must have the same type and length attributes, or an error condition pops up and the form fails ๐Ÿ’€ no mixing and matching, periodt ๐Ÿ™…โ€โ™€๏ธ

The Application of a Term

Okay so here's how a term actually gets applied, step by step ๐Ÿ’…

  1. First, the data type, value expression, and length expression team up to make one unit value. Let's call it x ๐Ÿงฉ
  2. Next, the replication expression decides how many times x gets repeated. All those xs get stuck together into y, which has length L ๐Ÿ”—
  3. If it's an input stream term, then y of length L gets tested against the input value starting at the current input pointer position ๐Ÿ‘€
  4. If the input value satisfies the constraints of y over length L, then that input value of length L becomes the value of the term โœจ

In an output stream term, it's basically the same procedure, except the source of input is the value of the term(s) named in the value expression, and the data gets emitted in the output stream ๐ŸŽ€

There's also a little twist. The procedure gets modified to include a one term look-ahead whenever replicated values are of indefinite length, because of the arbitrary symbol #. That's a lot of main character energy ๐Ÿฆ‹

Restrictions and Interpretations of Term Functions

  1. Terms with indefinite lengths (because their values get repeated according to the # symbol) must be separated by some type-specific data, like a literal. โœ‚๏ธ (A literal isn't specifically required, though. An arbitrary number of ASCII characters could be terminated by a non-ASCII character ๐Ÿ™ˆ)
  2. Truncation and padding goes like this ๐Ÿ“
    • a) Character to character (A <-> E) conversion is left-justified and truncated or padded on the right with blanks.
    • b) Character to numeric and numeric to numeric conversions are right-justified and truncated or padded on the left with zeros.
    • c) Numeric to character conversions are right-justified and left-padded with blanks.
  3. The following get ignored in a form definition over the control connection ๐Ÿ™…โ€โ™€๏ธ
    • a) TELNET control characters.
    • b) Blanks, except within quotes.
    • c) /* string */ is treated as comments, except within quotes.
  4. The following defaults prevail where the term part is omitted ๐ŸŒธ
    • a) The replication expression defaults to one.
    • b) # in an output stream term defaults to one.
    • c) The value expression of an input stream term defaults to the value found in the input stream, but the input stream must conform to the data type and length expression. The value expression of an output stream term defaults to padding only.
    • e) The length expression defaults to the size of the quantity determined by the data type and value expression.
    • f) Control defaults to the next sequential term if a term is successfully applied. Else, control defaults to the next sequential rule. If where evaluates to an undefined label, the form fails ๐Ÿ’€
  5. Arithmetic expressions are evaluated left-to-right with no precedence. No VIP treatment for multiplication, bestie ๐Ÿงฎ
  6. The following limits prevail ๐Ÿšง
    • a) Binary lengths are <= 32 bits
    • b) Character strings are <= 256 8-bit characters
    • c) Identifier names are <= 4 characters
    • d) Maximum number of identifiers is <= 256
    • e) Label integers are >= 0 and <= 9999
  7. Value and length operators produce 32-bit binary integers. The value operator is currently intended for converting A or E type decimal character strings to their binary correspondents ๐Ÿ”ข For example, the value of E'12' would be 0......01100. The value of E'AB' would cause the form to fail ๐Ÿ˜ญ

TERM AND RULE SEQUENCING

Sequencing may be explicitly controlled by including control in a term. The grammar goes like this ๐ŸŽ€

control ::=  :options | <null>
options ::=  S(where) | F(where) | U(where)
             S(where) , F(where) |
             F(where) , S(where)

where   ::=  arithmeticexpression | R(arithmeticexpression)

S, F, and U denote success, fail, and unconditional transfers, respectively. Where evaluates to a rule label, so transfer can be effected from within a rule (at the end of a term) to the beginning of another rule ๐Ÿฆ‹ R means terminate the form and return the evaluated expression to the initiator over the control connection (if still open) ๐Ÿ“ฎ

If terms aren't explicitly sequenced, these defaults kick in ๐Ÿ’…

  1. When a term fails, go to the next sequential rule.
  2. When a term succeeds, go to the next sequential term within the rule.
  3. At the end of a rule, go to the next sequential rule.

Look at this example to see how transfer of control and movement of the input pointer are connected ๐Ÿ‘€

1   XYZ(,B,,8:S(2),F(3)) : XYZ ;
2   . . . . . . .
3   . . . . . . .

The value of XYZ will never be emitted in the output stream, since control is transferred out of the rule upon either success or failure ๐Ÿ™ˆ If the term succeeds, the 8 bits of input get assigned as the value of XYZ, and rule 2 is then applied to the same input stream data. That's because the complete left hand side of rule 1 wasn't successfully applied, so the input stream pointer isn't advanced ๐Ÿ”


IV. EXAMPLES โœจ

REMARKS

The following examples (forms and also single rules) are simple representative uses of the Form Machine ๐Ÿงš They're written in a term-per-line format only to help with the explanation. Typically, a single rule might be written as a single line, so don't panic when you see it spread out ๐Ÿ’œ

FIELD INSERTION

To insert a field, separate the input into two terms so the inserted field fits between them. For example, to do line numbering for a 121 character/line printer with a leading carriage control character, use this form ๐Ÿ–จ๏ธ

(NUMB*<=*1);       /*initialize line number counter to one*/
1 CC(,E,,1:F(R(99))),  /*pick up control character and save
                       as CC*/
                       /*return a code of 99 upon exhaustion*/
LINE(,E,,121 : F(R(98)))  /*save text as LINE*/
:CC,               /*emit control character*/
(,E,NUMB,2),       /*emit counter in first two columns*/
(,E,E".",1),       /*emit period after line number*/
(,E,LINE,117),     /*emit text, truncated in 117 byte field*/
(NUMB*<=*NUMB+1:U(1));   /*increment line counter and go to
                           rule one*/;;

DELETION

Data to be deleted should be isolated as separate terms on the left, so they can be omitted (by not emitting them) on the right ๐Ÿ—‘๏ธ

(,B,,8),           /*isolate 8 bits to ignore*/
SAVE(,A,,10)       /*extract 10 ASCII characters from
                     input stream*/
:(,E,SAVE,);       /*emit the characters in SAVE as EBCDIC
                     characters whose length defaults to the
                     length of SAVE, i.e., 10, and advance to
                     the next rule*/

In the above example, if either input stream term fails, the next sequential rule gets applied ๐Ÿ™ƒ

VARIABLE LENGTH RECORDS

Some devices, terminals and programs generate variable length records ๐Ÿ“ผ The following rule picks up variable length EBCDIC records and translates them to ASCII ๐ŸŒˆ

CHAR(#,E,,1),      /*pick up all (an arbitrary number of)
                     EBCDIC characters in the input stream*/
(,X,X"FF",2)       /*followed by a hexadecimal literal,
                     FF (terminal signal)*/
:(,A,CHAR,),       /*emit them as ASCII*/
(,X,X"25",2);      /*emit an ASCII carriage return*/

STRING LENGTH COMPUTATION

It's often necessary to prefix a length field to an arbitrarily long character string ๐Ÿง The following rule prefixes an EBCDIC string with a one-byte length field.

Q(#,E,,1),         /*pick up all EBCDIC characters*/
TS(,X,X"FF",2)     /*followed by a hexadecimal literal, FF*/
:(,B,L(Q)+2,8),    /*emit the length of the characters
                     plus the length of the literal plus
                     the length of the count field itself,
                     in an 8-bit field*/
Q,                 /*emit the characters*/
TS,                /*emit the terminal*/

TRANSPOSITION

It's often desirable to reorder fields, like in the following example ๐Ÿ”€

Q(,E,,20), R(,E,,10) , S(,E,,15), T(,E,,5) : R, T, S, Q ;

The terms are emitted in a different order โœจ

CHARACTER PACKING AND UNPACKING

In systems such as HASP, repeated sequences of characters get packed into a count followed by the character, for more efficient storage and transmission ๐Ÿ“ฆ The first form packs multiple characters, and the second unpacks them ๐ŸŽ

/*form to pack EBCDIC streams*/
/*returns 99 if OK, input exhausted*/
/*returns 98 if illegal EBCDIC*/
/*look for terminal signal FF which is not a legal EBCDIC*/
/*duplication count must be 0-254*/
1 (,X,X"FF",2 : S(R(99))) ;
/*pick up an EBCDIC char/*
CHAR(,E,,1) ;
/*get identical EBCDIC chars/*
LEN(#,E,CHAR,1)
/*emit the count and the char/*
: (,B,L(LEN)+1,8), CHAR, (:U(1));
/*end of form*/;;

/*form to unpack EBCDIC streams*/
/*look for terminal*/
1 (,X,X"FF",2 : S(R(99))) ;
/*emit character the number of times indicated*/
/*by the count, in a field the length indicated*/
/*by the counter contents*/
CNT(,B,,8), CHAR(,E,,1) : (CNT,E,CHAR,1:U(1));
/*failure of form*/
(:U(R(98))) ;;

[ This RFC was put into machine readable form for entry ] [ into the online RFC archives by Simone Demmel 03/98 ]

๐Ÿ“ฆ where this page comes from