Category : Miscellaneous Language Source Code
Archive   : NETS_1.ZIP
Filename : PARSER.C

 
Output of file : PARSER.C contained in archive : NETS_1.ZIP
/*=============================*/
/* NETS */
/* */
/* a product of the AI Section */
/* NASA, Johnson Space Center */
/* */
/* principal author: */
/* Paul Baffes */
/* */
/* contributing authors: */
/* Bryan Dulock */
/* Chris Ortiz */
/*=============================*/


/*
----------------------------------------------------------------------
Code For Construction Of Net Structures (Prefix = PS_)
----------------------------------------------------------------------
This code is divided into 4 major sections:

(1) include files
(2) externed functions
(3) global variables
(4) subroutines

Each section is further explained below.
----------------------------------------------------------------------
*/


/*
----------------------------------------------------------------------
INCLUDE FILES
----------------------------------------------------------------------
*/
#include "common.h"
#include "netio.h"


/*
----------------------------------------------------------------------
MACRO DEFINITIONS
----------------------------------------------------------------------
Below are eight different macros used for range checking while the
network spec file is being parsed. Each commands in the file takes
a numeric argument, and these macros can check ranges for any of the
eight possible cases given an upper bound (u) and a lower bound (l)
on the number "n".
----------------------------------------------------------------------
*/
#define GTLT(n,l,u) (((n) > (l) && (n) < (u)) ? 1 : 0)
#define GELT(n,l,u) (((n) >= (l) && (n) < (u)) ? 1 : 0)
#define GTLE(n,l,u) (((n) > (l) && (n) <= (u)) ? 1 : 0)
#define GELE(n,l,u) (((n) >= (l) && (n) <= (u)) ? 1 : 0)

#define LTGT(n,l,u) (((n) < (l) || (n) > (u)) ? 1 : 0)
#define LEGT(n,l,u) (((n) <= (l) || (n) > (u)) ? 1 : 0)
#define LTGE(n,l,u) (((n) < (l) || (n) >= (u)) ? 1 : 0)
#define LEGE(n,l,u) (((n) <= (l) || (n) >= (u)) ? 1 : 0)


/*
----------------------------------------------------------------------
EXTERNED FUNCTIONS AND GLOBALS
----------------------------------------------------------------------
Below are the functions defined in other files which are used by the
code here. They are organized by section.
----------------------------------------------------------------------
*/
extern Sint C_float_to_Sint();
extern char *sys_alloc();
extern FILE *PA_open_binary();
extern int PA_put_to_workfile();
extern void PA_flush();
extern void IO_print();

extern char IO_str[MAX_LINE_SIZE];


/*
----------------------------------------------------------------------
GLOBAL VARIABLES
----------------------------------------------------------------------
First, some markers which are used by several routines and are just
declared here a globals for convenience. A parsing variable is next,
called "use_last_command" which is needed for the parsing of layer
specifications. It determines whether or not to reuse the last
command read in during the parsing. Then come two globals which are
used during the creation of a network to indicate whether or not a
global learning rate and/or momentum has been used. If so, then these
two globals will contain the floating point equivalent of the given
values.
----------------------------------------------------------------------
*/
static int line_count = 1; /* used by parse_iopairs, PS_get_token */
static char last_char; /* holds the last character read in */
static char last_str[MAX_LINE_SIZE]; /* holds last command read in */
static int use_last_command = FALSE; /* used for layer parse (see */
/* PS_get_command). */
float global_learn_base; /* used during parse to set default */
float global_momentum; /* global learn/momentum values */


/*
======================================================================
ROUTINES IN PARSER.C
======================================================================
The routines in this file are grouped below by function. Each routine
is prefixed by the string "PS_" indicating that it is defined in the
"parser.c" file. The types returned by the routines are also shown
here so that cross checking is more easily done between these functions
and the other files which intern them.


Type Returned Routine
------------- -------
void PS_reset_for_layer_parse
int PS_global_spec_check
Layer_spec * PS_get_layer
void PS_get_learn_values
void PS_get_nodes
void PS_get_node_dimensions
void PS_get_target
void PS_get_pattern
int PS_get_command
void PS_print_command
int PS_range_check
int PS_int_check
int PS_parse_iopairs
int PS_check_items
int PS_skip_tokens
int PS_get_token
int PS_get_float_from_file
======================================================================
*/


void PS_reset_for_layer_parse()
/*
----------------------------------------------------------------------
The only thing done here, as of now, is to set the global variable
'use_last_command' to FALSE. This must be done from the OUTSIDE via
this routine because the global is static to this file.
----------------------------------------------------------------------
*/
BEGIN
use_last_command = FALSE;

END /* PS_reset_for_layer_parse */


int PS_global_spec_check(ptr_file)
FILE *ptr_file;
/*
----------------------------------------------------------------------
This routine checks for the existence of two optional specs located
at the very beginning of a network specification file. They are the
GLOBAL-LEARN-RATE and GLOBAL-MOMENTUM respectively. Either or both
of these may be specified by the user. If they are present, they
define default values for the learn_base and momemtum elements of
each layer. These values may be overridden later by using the
LEARN-RATE, SCALE-FACTOR, or MOMENTUM commands for each layer which
needs overriding.

Returns ERROR if EOF encountered while trying to check for these
first two optional commands.
----------------------------------------------------------------------
*/
BEGIN
float num;
int temp, PS_get_command(), PS_range_check();
void PS_print_command();

/*--------------------------------------*/
/* first, set the global values to -1.0 */
/* symbolizing UNKNOWN values */
/*--------------------------------------*/
global_learn_base = UNDEFINED;
global_momentum = UNDEFINED;

/*-----------------------------------*/
/* loop on PS_get_command, until you */
/* hit EOF or a LAYER command. */
/*-----------------------------------*/
temp = PS_get_command(ptr_file, &num);
while (temp != LAYER) BEGIN
if (temp == EOF) BEGIN
sprintf(IO_str, "\n*** ERROR: no specifications found in file");
IO_print(0);
return(ERROR);
ENDIF
else if (temp == GLOBAL_LEARN_RATE) BEGIN
if (PS_range_check( GTLE(num, 0, LEARN_MAX) ) == OK)
global_learn_base = num;
ENDELSE
else if (temp == GLOBAL_MOMENTUM) BEGIN
if (PS_range_check( GELE(num, 0, MOMENTUM_MAX) ) == OK)
global_momentum = num;
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found before LAYER specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE

/*-----------------------------------*/
/* set USE_LAST_COMMAND flag to TRUE */
/*-----------------------------------*/
use_last_command = TRUE;
return(OK);

END /* PS_global_spec_check */


Layer_spec *PS_get_layer(ptr_file)
FILE *ptr_file;
/*
----------------------------------------------------------------------
This routine reads lines from the file pointed to by 'ptr_file' and
converts them into the 'Layer_spec' format described in the file
'netio.h'. Now, to understand how this is done, you must know the
the specifics of how the file is set up. In a sense, the logic of
this routine describes that setup. In conjunction with the def-
inition of 'Layer_spec', that setup is described here.
First, an example of what ONE layer specification might look like in
a file:

LAYER : 25 -- comment for line 1
NODES : 5 -- comment for line 2
-- line 2 comment continued
TARGET : 2
TARGET : 4
TARGET : 6

notice that the first two lines have comments (those comments would
not necessarily appear in the file; they are here as an example
only). There are several important features I would point out in
this example. First, note that a Layer specification consists of
three types of commands: ONE "layer", ONE "nodes" and some (optional)
number of "target" commands. The layer command specifies the ID
number which will be assigned to the layer. The nodes command
indicates the number of nodes contained in the layer. The target
commands (of which there may be zero!) denote the OTHER layers to
which this layer has OUTGOING connections. For example, the layer
above, layer 25, has 3 outgoing connections to layers 2, 4, and 6
respectively. This also means that these three layers have incoming
weights from layer 25 (see Layer structure definition in layer.h);
but NOTE THAT THOSE LAYERS WOULD NOT NEED TARGET SPECIFICATIONS. Only
the layer acting as a SOURCE needs to have the target specification.
Putting the specification in both places is redundant and more
difficult on the user.
A couple of notes about how this routine reads the input file. First
note that each of the commands above looks like "command : number"
THE SPACES HERE ARE SIGNIFICANT. This routine does a pattern match
rather than a sophisticated scanning, so if you leave the spaces out
you get an error. Also, note that the comments have "--" in front
of them. This is not necessary, but it is a good idea. The reason
lies in the fact that EACH LINE MUST HAVE AT MOST ONE COMMAND and
THAT COMMAND MUST NOT BE PRECEDED BY ANY OTHER TEXT. Thus, if you
had a comment that ran over one line, and which also happened to
include something that looked like a command syntax, it could be
interpreted as a command. To avoid this, always preceed any
comments with the double dash.

To summarize the input file syntax:

each line must have AT MOST one command
commands look like "command : number"; SPACES ARE SIGNIFICANT
comments should be preceeded by a double dash: ie "--"
layer connections are specified from SOURCE LAYER ONLY
LAYER and NODES commands are essential for a layer, TARGET is optional
----------------------------------------------------------------------
4-7-88 I've added a new twist to the layer specification to allow for
PATTERNED CONNECTION schemes between layers, in addition to the fully
connected scheme already available. Here's the deal. Imagine that you
have two layers, one of which is larger than the other. Let's call them
layer B (Big) and layer L (Little). You might be in a position where
you did NOT want all of the nodes of layer B to be connected to all
of the nodes in layer L. One reason might be that there would simply
be two many connections. A more plausible reason might be that you
only want LOCAL ASSOCIATIONS between layer B and layer S. That is, you
might want groups of nodes in layer B which are "close together" to
map to a single node in layer L. This is often the case with nets which
are attempting to work with visual input. Often, one tries to associate
small areas of the input layer (usually a screen image) and map them to
separate nodes of a hidden layer. In this way, one simulates "piecing
together" the bits of visual information on the screen by trying to
build larger and larger primitive shapes (line, curve, angle, etc) out
of small regions of the image. Anyway, the point is, you might want to
have some pattern of connections between layer B and L, and I have added
syntax to allow for that.
OK, first things first. Assume two layers B and L where nodes(B) > nodes(L)
(B has more nodes). Assume further that the dimensions of B are UxV (in the
X and Y dimensions respectively) and those of L are MxN. Now, one can
define "Pattern areas" of layer B which are to be mapped as a group
onto SINGLE nodes of L. Let this "pattern area" be of dimension PxQ
(also in terms of X,Y). Finally, one might imagine that these pattern
areas overlap with eachother (why not?) and this overlap may occur in
either, or both, the X and Y dimensions. Let this overlap be defined
as RxS (X overlap = R, Y overlap = S). Here's and example:

Layer B: _______
|0|1|2|3| UxV = 4x2
|4|5|6|7| 12 nodes, numbered 0-C
|8|A|B|C|
-------

Pattern area: _____
|_|_|_| PxQ = 3x2
| | | | Overlap 2 in the X direction, 1 in the Y
----- so RxS = 2x1

with the above specifications, lets walk through how layer L would have
to look. The first pattern area would start at node 0 of B and go 3 in
X direction, and 2 in the Y direction. This would involve nodes 0, 1, 2
and 4, 5, 6 of layer B. This area would map to the first node of layer L.
The second pattern area STARTS AT NODE 1 OF LAYER B!!! This is because the
user specified an overlap of 2 in the X direction. Thus the second pattern
area covers nodes 1, 2, 3 and 5, 6, 7 of layer B. The third pattern area
is now found by moving DOWN (in the Y direction) and since the user
specified a Y overlap of 1, the third pattern area STARTS AT NODE 4 of B.
This area covers nodes 4,5,6 and 8,A,B of layer B. Finally, the last
pattern area again overlaps 2 in the X direction and covers nodes 5,6,7
and A,B,C. Layer L then has only 4 nodes as follows:

Layer L: ___
|0|1| Mxn = 2x2
|2|3| 4 nodes, numbered 0-3
---

In all, 24 weights are needed. That is, 4 pattern areas, each with a
3x2 area mapped to 1 node = 3 * 2 * 4 or 6 * 4 = 24. The following is
a list of weights and the nodes which each connects:

Weight number Node in B Node in L
------------- --------- ---------
0 0 0
1 1 0
2 2 0
3 4 0
4 5 0
5 6 0

6 1 1
7 2 1
8 3 1
9 5 1
10 6 1
11 7 1

12 4 2
13 5 2
14 6 2
15 8 2
16 9 2
17 A 2

18 5 3
19 6 3
20 7 3
21 A 3
22 B 3
23 C 3

SYNTAX: The new syntax has all the same rules as the old plus the
following:
(1) X and Y dimensions are specified separately
(2) dimensions not specified for the layer will default
(3) if No dimension is specified for NODES, then the layer is
assumed to be X = NODES, Y = 1.
(4) if only one dimension is missing, it is assumed = 1.
(5) pattern areas are defined along with the TARGET command
(6) only the overlap specs are optional for a pattern specification.
These X and Y dimensions MUST be specified. If no overlaps are
specified, they will default to 0.
(7) if TARGET specs do not include dimensions, a CONNECT ALL scheme
is assumed.

EXAMPLE: To generate the net above (say there were only two
layers) we would do:

LAYER : 0 -- only one command per line
NODES : 12 -- spacing only significant around ":'s"
X-DIMENSION : 4 -- optional dimensions
Y-DIMENSION : 3
TARGET : 1
PATTERN-X-DIMENSION : 3 -- optional. If not present, then
PATTERN-Y-DIMENSION : 2 -- "connect all" is assumed.
X-OVERLAP : 2
Y-OVERLAP : 1

LAYER : 1
NODES : 4
X-DIMENSION : 2 -- a good idea to specify both layer's
Y-DIMENSION : 2 -- dimensions since system will verify.

Note that the dimensions of BOTH layers will, in general, have to be
specified as the system will check to see that the dimensions are
correct for the given pattern area dimension/overlap specification.

The full BNF for the layer spec syntax is ("{}" indicate optional,
and commands are in all caps):

layer-spec :== LAYER node-spec {target-spec}
node-spec :== NODES {X-DIMENSION} {Y-DIMENSION}
target-spec :== TARGET {pattern-spec} {target-spec}
pattern-spec :== PATTERN-X-DIMENSION PATTERN-Y-DIMENSION {X-OVERLAP}
{Y-OVERLAP}
----------------------------------------------------------------------
5-3-89 Im adding another piece of syntax to the net specification file
which will be optional. I want the user to be able to specify the
learning rate, scaling factor, and momentum in the file so that
delivery networks can be easily created. This will also allow for
automatic generation of network specification files from a simple
list of IO pairs. The new syntax lookes like:

node-spec :== NODES {dimensions} {l-factors}
dimensions :== X-DIMENSION {dimensions} | Y-DIMENSION {dimensions}
l-factors :== LEARN-RATE {l-factors} | SCALE-FACTOR {l-factors}
| MOMENTUM {l-factors}

i.e., any or all of the three learning constants may be entered
after the optional x,y dimension info. Also, I have changed the
syntax to allow for global specification of the learning rate and
momentum. These parameters are considered optional, before the
specification of the layers in the network, as the BNF below shows
(see PS_global_spec_check and B_process_config):

network :== {globals} layer-spec layer-spec {hiddens}
globals :== GLOBAL-LEARN-RATE {globals}
| GLOBAL-MOMENTUM {globals}
hiddens :== layer-spec {hiddens}
----------------------------------------------------------------------
*/
BEGIN
Layer_spec *result;
float num;
int temp, PS_get_command(), PS_range_check(), PS_int_check();
void PS_get_nodes(), PS_get_target(), PS_print_command(),
PS_get_learn_values();

/*----------------------------*/
/* start out as a good layer */
/*----------------------------*/
result = (Layer_spec *) sys_alloc((unsigned)sizeof(Layer_spec));
result->status = OK;

/*------------------------------*/
/* read until good layer or EOF */
/*------------------------------*/
temp = PS_get_command(ptr_file, &num);
while (TRUE) BEGIN
if (temp == EOF) BEGIN
result->status = EOF;
break;
ENDIF
if ( (temp == LAYER)
&& (PS_int_check(num) == OK)
&& (PS_range_check(GELE(num, 0, MAX_LAYERS)) == OK) )
break;
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found outside of LAYER specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
temp = PS_get_command(ptr_file, &num);
ENDWHILE

/*---------------------------------------------*/
/* once you have a layer, read its nodes specs */
/*---------------------------------------------*/
if (result->status == OK) BEGIN
result->ID = (int)num;

/*-------------------------------------------*/
/* look for any learning rate specifications */
/*-------------------------------------------*/
PS_get_learn_values(ptr_file, result);

PS_get_nodes(ptr_file, result);
ENDIF

/*----------------------------*/
/* then read the target specs */
/*----------------------------*/
if (result->status == OK)
PS_get_target(ptr_file, result);

/*-------------------------------------*/
/* if delivery code is being run, then */
/* get the learning values from the */
/* arrays defined in the delivery file */
/*-------------------------------------*/
#if DELIVERY
result->learn_base = 0;
result->learn_scale = 0;
result->momentum = 0;
#endif

/*-------------------------------------------------*/
/* if any errors occur, print message, then return */
/*-------------------------------------------------*/
if (result->status == ERROR) BEGIN
sprintf(IO_str, "\n\n*** ERROR: Problems in Net Specification file");
IO_print(0);
ENDIF
return(result);

END /* PS_get_layer */


void PS_get_learn_values(ptr_file, ptr_layer_spec)
FILE *ptr_file;
Layer_spec *ptr_layer_spec;
/*
----------------------------------------------------------------------
This routine checks for the optional learning rate, scale, and momentum
specifications for a given layer. It is called only from the
"PS_get_nodes" routine. All three of these values will default to 0
unless specified in the file. A zero value for either a learn_base or
the momentum is considered as an "unknown" value when the layer is
allocated. At a later point, the user is prompted for learning rates
and momentum values if none exist in the file.
NOTE: since these three commands are optional, this routine has no way
of knowing when to stop checking except by detecting when its gone
too far. That is, you only know that there are no X and Y dimensions
specified when you (1) read LAYER, (2) read TARGET, or (3) hit EOF.
Thus, you will end up reading some command which may be needed in
another checking routine. As a result, this routine sets the
"use_last_command" flag to TRUE before it exits.
----------------------------------------------------------------------
*/
BEGIN
int temp, PS_get_command(), PS_range_check();
float num;
void PS_print_command();

/*------------------------------------*/
/* first set the default learning and */
/* momentum values. These will be */
/* used unless overwritten below. */
/*------------------------------------*/
ptr_layer_spec->learn_base = global_learn_base;
ptr_layer_spec->momentum = global_momentum;

/*----------------------------------------------------------*/
/* the scale factor is undefined ONLY if no global learning */
/* rate exists. Else, 0 = no scaling unless overwritten */
/*----------------------------------------------------------*/
if (global_learn_base == UNDEFINED)
ptr_layer_spec->learn_scale = UNDEFINED;
else ptr_layer_spec->learn_scale = 0;


/*--------------------------------------------*/
/* read in commands until NODES command found */
/*--------------------------------------------*/
temp = PS_get_command(ptr_file, &num);
while (temp != NODES) BEGIN
if (temp == EOF) BEGIN
ptr_layer_spec->status = EOF;
break;
ENDIF
if (temp == SCALE_FACTOR) BEGIN
if (PS_range_check(GELE(num, 0, SCALE_MAX)) == OK)
ptr_layer_spec->learn_scale = num;
ENDIF
else if (temp == LEARN_RATE) BEGIN
if (PS_range_check(GTLE(num, 0, LEARN_MAX)) == OK)
ptr_layer_spec->learn_base = num;
ENDELSE
else if (temp == MOMENTUM) BEGIN
if (PS_range_check(GELE(num, 0, MOMENTUM_MAX)) == OK)
ptr_layer_spec->momentum = num;
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found in the middle of a LAYER specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE
use_last_command = TRUE;

END /* PS_get_learn_values */


void PS_get_nodes(ptr_file, ptr_layer_spec)
FILE *ptr_file;
Layer_spec *ptr_layer_spec;
/*
----------------------------------------------------------------------
The NODES section of a layer specification is a non-optional delimeter
of how many nodes should be allocated to the layer. It does have two
optional arguments, X-DIMENSION and Y-DIMENSION, which can be used to
determine the geometry of the layer. Furthermore, one can make the
assumption that this routine is ONLY called by the PS_get_layer routine
above. Thus the last command read by the parser is the LAYER command
and we can continue reading PAST that command.
This routine operates as follows. First, a loop is entered which continues
until a NODES command is reached or the EOF is found. If a successful
NODES command is found, then its value is recorded and the routine
'PS_get_node_dimensions' is called to check for the optional X and
Y dimension specifications.
----------------------------------------------------------------------
*/
BEGIN
void PS_get_node_dimensions(), PS_print_command();
float num;
int temp, PS_get_command(), PS_int_check(), PS_range_check();

temp = PS_get_command(ptr_file, &num);
while (TRUE) BEGIN
if (temp == EOF) BEGIN
sprintf(IO_str, "\n*** ERROR: could not find NODES specification for Layer %d",
ptr_layer_spec->ID);
IO_print(0);
ptr_layer_spec->status = ERROR;
break;
ENDIF
if ( (temp == NODES)
&& (PS_int_check(num) == OK)
&& (PS_range_check(GTLE(num, 0, MAX_NODES)) == OK) )
break;
else if (temp == LAYER) BEGIN
sprintf(IO_str, "\n*** Warning: No NODES specification provided for LAYER %d",
ptr_layer_spec->ID);
IO_print(0);
sprintf(IO_str, "\nLayer %d ignored", ptr_layer_spec->ID);
IO_print(0);
ptr_layer_spec->ID = (int)num;
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found before NODES specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE

if (ptr_layer_spec->status == OK) BEGIN
ptr_layer_spec->num_nodes = num;
PS_get_node_dimensions(ptr_file, ptr_layer_spec);
ENDIF

END /* PS_get_nodes */


void PS_get_node_dimensions(ptr_file, ptr_layer_spec)
FILE *ptr_file;
Layer_spec *ptr_layer_spec;
/*
----------------------------------------------------------------------
This routine checks for the optional X and Y dimension specifications
for a given layer's NODES specification. It is called only from the
"PS_get_nodes" routine. The X and Y dimension specs are optional and
will default as follows. If neither is specified, then the X dimension
is set to the NODES value and the Y dimension is set to 1. If only
one dimension is set, then the other dimension is set to 1. In any
event, the product of the X and Y dimensions is found and compared
to the NODES value. If they are not equal and error is sighted.
NOTE: since these two commands are optional, this routine has no way
of knowing when to stop checking except by detecting when its gone
too far. That is, you only know that there are no X and Y dimensions
specified when you (1) read LAYER, (2) read TARGET, (3) hit EOF or
(4) read a LEARN-RATE, SCALE-FACTOR, or MOMENTUM command. Thus, you
will end up reading some command which may be needed in another
checking routine. As a result, this routine sets the "use_last_command"
flag to TRUE before it exits.
----------------------------------------------------------------------
*/
BEGIN
int temp, PS_get_command(), PS_int_check(), PS_range_check();
float num;
void PS_print_command();

ptr_layer_spec->X_dim = -1; /* initialize dimensions to -1 */
ptr_layer_spec->Y_dim = -1;

/*---------------------------------------------------------------------*/
/* read in commands until LAYER or TARGET, checking for X,Y dimensions */
/*---------------------------------------------------------------------*/
temp = PS_get_command(ptr_file, &num);
while ((temp != LAYER)
&& (temp != TARGET)) BEGIN
if (temp == EOF) BEGIN
ptr_layer_spec->status = EOF;
break;
ENDIF
if (temp == X_DIMENSION) BEGIN
if ( (PS_int_check(num) == OK)
&&(PS_range_check(GTLE(num, 0, MAX_NODES)) == OK) )
ptr_layer_spec->X_dim = num;
ENDIF
else if (temp == Y_DIMENSION) BEGIN
if ( (PS_int_check(num) == OK)
&&(PS_range_check(GTLE(num, 0, MAX_NODES)) == OK) )
ptr_layer_spec->Y_dim = num;
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found in the middle of a NODES specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE

/*----------------------------------------------------*/
/* Set default values of X_dim and Y_dim if necessary */
/*----------------------------------------------------*/
if ((ptr_layer_spec->X_dim == -1) && (ptr_layer_spec->Y_dim == -1)) BEGIN
ptr_layer_spec->X_dim = ptr_layer_spec->num_nodes;
ptr_layer_spec->Y_dim = 1;
ENDIF
else BEGIN
if (ptr_layer_spec->X_dim == -1) ptr_layer_spec->X_dim = 1;
if (ptr_layer_spec->Y_dim == -1) ptr_layer_spec->Y_dim = 1;
ENDELSE

if ((ptr_layer_spec->X_dim * ptr_layer_spec->Y_dim)
!= ptr_layer_spec->num_nodes) BEGIN
sprintf(IO_str, "\n*** ERROR: X,Y dimensions inconsistent with the number of nodes in layer %d",
ptr_layer_spec->ID);
IO_print(0);
ptr_layer_spec->status = ERROR;
ENDIF
use_last_command = TRUE;

END /* PS_get_node_dimensions */


void PS_get_target(ptr_file, ptr_layer_spec)
FILE *ptr_file;
Layer_spec *ptr_layer_spec;
/*
----------------------------------------------------------------------
This routine must search for any number of target specs (ie, one or
more).

layer-spec :== LAYER node-spec {target-spec}
node-spec :== NODES {X-DIMENSION} {Y-DIMENSION}
target-spec :== TARGET {pattern-spec} {target-spec}
pattern-spec :== PATTERN-X-DIMENSION PATTERN-Y-DIMENSION {X-OVERLAP}
{Y-OVERLAP}
----------------------------------------------------------------------
*/
BEGIN
void PS_get_pattern(), PS_print_command();
float num;
int temp, target_count, PS_get_command(), PS_int_check(),
PS_range_check();

target_count = 0;
temp = PS_get_command(ptr_file, &num);
while ((temp != LAYER) && (ptr_layer_spec->status == 1)) BEGIN
if (temp == EOF) BEGIN
ptr_layer_spec->status = EOF;
break;
ENDIF
else if (temp == TARGET) BEGIN
if ( (PS_int_check(num) == OK)
&& (PS_range_check(GELE(num, 0, MAX_LAYERS)) == OK) ) BEGIN
ptr_layer_spec->targets[target_count][0] = (int16)num;
ptr_layer_spec->targets[target_count][1] = (int16)0;
ptr_layer_spec->targets[target_count][2] = (int16)0;
ptr_layer_spec->targets[target_count][3] = (int16)0;
ptr_layer_spec->targets[target_count][4] = (int16)0;
PS_get_pattern(ptr_file, ptr_layer_spec, target_count);
target_count++;
ENDIF
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found in the middle of a TARGET specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE
ptr_layer_spec->num_targets = target_count;
use_last_command = TRUE;

END /* PS_get_target */


void PS_get_pattern(ptr_file, ptr_layer_spec, target_index)
FILE *ptr_file;
Layer_spec *ptr_layer_spec;
int target_index;
/*
----------------------------------------------------------------------
The entire pattern spec is optional. However, if present it looks
like:

pattern-spec :== PATTERN-X-DIMENSION PATTERN-Y-DIMENSION
{X-OVERLAP} {Y-OVERLAP}

Reading this specification is achieved in a three step process. First,
the PATTERN-X-DIMENSION command is looked for, if a LAYER or TARGET
command doesn't appear first. Once it is found, step two is to search
for the PATTERN-Y-DIMENSION. If it is not present, that is an error
(you can't have a half-specified pattern). The third step consists
of a while loop to search for the optional OVERLAP dimensions which
will default to 0 if they are not present. Finally, if a pattern is
specified, then the end of its specification will occur on a TARGET
or LAYER command, thus the "use_last_command" flag is set to TRUE.
----------------------------------------------------------------------
*/
BEGIN
int temp, PS_get_command(), PS_int_check(), PS_range_check();
float num;
void PS_print_command();

/*---------------------------------------------------------*/
/* Step 1: find the PATTERN-X-DIMENSION command if present */
/*---------------------------------------------------------*/
temp = PS_get_command(ptr_file, &num);
if (temp == EOF)
ptr_layer_spec->status = EOF;
else if ((temp == LAYER) || (temp == TARGET))
use_last_command = TRUE;
else if (temp != PATTERN_X_DIM) BEGIN
sprintf(IO_str, "\n*** ERROR: found spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command in the middle of a PATTERN specification.");
IO_print(0);
sprintf(IO_str, "\nExpected a PATTERN-X-DIMENSION command.");
IO_print(0);
ptr_layer_spec->status = ERROR;
ENDELSE

/*----------------------------------------------------------------------*/
/* Step 2: if PATTERN-X-DIMENSION command found, search for Y dimension */
/*----------------------------------------------------------------------*/
else BEGIN
if ( (PS_int_check(num) == OK)
&& PS_range_check(GTLE(num, 0, MAX_NODES) == OK) )
ptr_layer_spec->targets[target_index][1] = (int16)num; /* pattern_x_dim */
temp = PS_get_command(ptr_file, &num);
if (temp != PATTERN_Y_DIM) BEGIN /* error if no y dim */
sprintf(IO_str, "\n*** ERROR: Incomplete pattern dimension specification.");
IO_print(0);
sprintf(IO_str, "\nExpected a PATTERN-Y-DIMENSION command.");
IO_print(0);
ptr_layer_spec->status = ERROR;
ENDIF
else BEGIN
if ( (PS_int_check(num) == OK)
&& PS_range_check(GTLE(num, 0, MAX_NODES) == OK) )
ptr_layer_spec->targets[target_index][2] = (int16)num;
temp = PS_get_command(ptr_file, &num);

/*-------------------------------------------------------------*/
/* Step 3: once both dimensions are found, search for OVERLAPs */
/*-------------------------------------------------------------*/
while ((temp != LAYER) && (temp != TARGET)) BEGIN
if (temp == EOF) BEGIN
ptr_layer_spec->status = EOF;
break;
ENDIF
else if (temp == X_OVERLAP) BEGIN
if ( (PS_int_check(num) == OK)
&& PS_range_check(GTLE(num, 0, MAX_NODES) == OK) )
ptr_layer_spec->targets[target_index][3] = (int16)num;
ENDELSE
else if (temp == Y_OVERLAP) BEGIN
if ( (PS_int_check(num) == OK)
&& PS_range_check(GTLE(num, 0, MAX_NODES) == OK) )
ptr_layer_spec->targets[target_index][4] = (int16)num;
ENDELSE
else BEGIN
sprintf(IO_str, "\n*** WARNING: spurious ");
IO_print(0);
PS_print_command(temp);
sprintf(IO_str, " command found in the middle of a pattern OVERLAP specification.");
IO_print(0);
sprintf(IO_str, "\nCommand ignored.");
IO_print(0);
ENDELSE
temp = PS_get_command(ptr_file, &num);
ENDWHILE
use_last_command = TRUE;
ENDELSE
ENDELSE

END /* PS_get_pattern */


int PS_get_command(ptr_file, ptr_data)
FILE *ptr_file;
float *ptr_data;
/*
----------------------------------------------------------------------
Given a pointer to an input file, this guy will read through the
file line by line, until a valid command is reached. Since each
line of the input is to have at most 1 command, only the first
command in a line will be read; any others will be ignored. The
set of valid commands is defined by the BNF for parsing a layer
specification (see netio.h and comments under 'PS_get_layer').
A return code is given which depends upon the command found. These
return codes are defined as constants in the netio.h file. Since
this routine has no idea of syntax or semantics, it cannot tell
whether or not the last command it read was actually used, or
simply served as a delimiter for the BNF. Consequently, this
routine must be told whether or not to reuse the last command
which was found. This information is kept in a global variable
(for convenience) called "use_last_command" and is set or reset
by the 'PS_get_layer' and 'PS_reset_for_layer_parse' routines.
Aside from the return value specified above, any data which accompanies
the command is returned via the 'ptr_data' parameter which is passed
in from the calling routine.
----------------------------------------------------------------------
9-7-89 Note that I have made the "last_str" variable global. I did this
to enable better error messages. By having access to the last command
read by the system, I can print out a message which will be more
meaningful to the user. A global variable seems the best way to make
the last command available to many routines.
----------------------------------------------------------------------
*/
BEGIN
static char temp_str[MAX_LINE_SIZE];

if (use_last_command == FALSE)
if (fgets(last_str, MAX_LINE_SIZE, ptr_file) == NULL)
return(EOF);
use_last_command = FALSE; /* reset flag so reuse only ONCE */

while (TRUE) BEGIN
if (sscanf(last_str, "%s : %f", temp_str, ptr_data) == 2) BEGIN
if (strcmp(temp_str, "TARGET") == 0)
return(TARGET);
if (strcmp(temp_str, "LAYER") == 0)
return(LAYER);
if (strcmp(temp_str, "NODES") == 0)
return(NODES);
if (strcmp(temp_str, "X-DIMENSION") == 0)
return(X_DIMENSION);
if (strcmp(temp_str, "Y-DIMENSION") == 0)
return(Y_DIMENSION);
if (strcmp(temp_str, "LEARN-RATE") == 0)
return(LEARN_RATE);
if (strcmp(temp_str, "GLOBAL-LEARN-RATE") == 0)
return(GLOBAL_LEARN_RATE);
if (strcmp(temp_str, "SCALE-FACTOR") == 0)
return(SCALE_FACTOR);
if (strcmp(temp_str, "GLOBAL-MOMENTUM") == 0)
return(GLOBAL_MOMENTUM);
if (strcmp(temp_str, "MOMENTUM") == 0)
return(MOMENTUM);
if (strcmp(temp_str, "PATTERN-X-DIMENSION") == 0)
return(PATTERN_X_DIM);
if (strcmp(temp_str, "PATTERN-Y-DIMENSION") == 0)

return(PATTERN_Y_DIM);
if (strcmp(temp_str, "X-OVERLAP") == 0)
return(X_OVERLAP);
if (strcmp(temp_str, "Y-OVERLAP") == 0)
return(Y_OVERLAP);
ENDIF

if (fgets(last_str, MAX_LINE_SIZE, ptr_file) == NULL)
return(EOF);
ENDWHILE

END /* PS_get_command */


int PS_range_check(comparison)
int comparison;
/*
----------------------------------------------------------------------
Checks the results given by comparison and determines whether or not
to print out a message. If comparison == 1, then the range check was
OK, otherwise, the command had a number out of range.
NOTE: this routine uses the "last_str" global variable which holds
the last command read in to NETS. This saves me the trouble of passing
the bloody thing around.
----------------------------------------------------------------------
*/
BEGIN
if (comparison)
return(OK);
sprintf(IO_str, "\n *** ERROR: the command:\n %s has a value which is out of range",
last_str);
IO_print(0);
sprintf(IO_str, "\n Command ignored.");
IO_print(0);
return(ERROR);

END /* PS_range_check */


int PS_int_check(num)
float num;
/*
----------------------------------------------------------------------
Some of the commands in NETS need integer arguments. This routine is
called to check that the number associated with a command was indeed
an integer. If not, a message is printed and ERROR is returned.
NOTE: as with the PS_range_check routine, this guy make use of the
"last_str" global variable which is updated by PS_get_command to print
the last command to the user.
----------------------------------------------------------------------
*/
BEGIN
if (num == ((float) ((int) num)) )
return(OK);

sprintf(IO_str, "\n *** ERROR: the command:\n %s should have an INTEGER value",
last_str);
IO_print(0);
sprintf(IO_str, "\n Command ignored.");
IO_print(0);
return(ERROR);

END /* PS_int_check */


void PS_print_command(command)
int command;
/*
----------------------------------------------------------------------
Just a common routine for all of the layer parsing routines which
can be used to print out parts of error messages.
----------------------------------------------------------------------
*/
BEGIN
switch (command) BEGIN
case LAYER : BEGIN
sprintf(IO_str, "LAYER");
IO_print(0);
break;
ENDCASE
case NODES : BEGIN
sprintf(IO_str, "NODES");
IO_print(0);
break;
ENDCASE
case X_DIMENSION : BEGIN
sprintf(IO_str, "X-DIMENSION");
IO_print(0);
break;
ENDCASE
case Y_DIMENSION : BEGIN
sprintf(IO_str, "Y-DIMENSION");
IO_print(0);
break;
ENDCASE
case LEARN_RATE : BEGIN
sprintf(IO_str, "LEARN-RATE");
IO_print(0);
break;
ENDCASE
case SCALE_FACTOR : BEGIN
sprintf(IO_str, "SCALE-FACTOR");
IO_print(0);
break;
ENDCASE
case MOMENTUM : BEGIN
sprintf(IO_str, "MOMENTUM");
IO_print(0);
break;
ENDCASE
case TARGET : BEGIN
sprintf(IO_str, "TARGET");
IO_print(0);
break;
ENDCASE
case PATTERN_X_DIM : BEGIN
sprintf(IO_str, "PATTERN-X-DIMENSION");
IO_print(0);
break;
ENDCASE
case PATTERN_Y_DIM : BEGIN
sprintf(IO_str, "PATTERN-Y-DIMENSION");
IO_print(0);
break;
ENDCASE
case X_OVERLAP : BEGIN
sprintf(IO_str, "X-OVERLAP");
IO_print(0);
break;
ENDCASE
case Y_OVERLAP : BEGIN
sprintf(IO_str, "Y-OVERLAP");
IO_print(0);
break;
ENDCASE
default : break;
ENDSWITCH

END /* PS_print_command */


int PS_parse_iopairs(file_name, sum)
char file_name[];
int sum;
/*
----------------------------------------------------------------------
This guy is called from "PA_setup_iopairs" to do most of the work in
setting up the io pairs for teaching the system. The basic idea
is to read through the file (represented by "filename" above) which
specifies the io-pairs and sift through any comments, creating a
streamlined version of the io-pairs (in Sint format) which will be
used for the actual training. Now, you might be wondering why we
don't just read in the io-pairs and store them in memory since that
would be a much faster approach. The problem is that the number of
io-pairs can get enormous, which would put too great a burden on
the RAM we were attempting to use. A more practical approach is to
convert the io-pairs into the exact format we need (Sints) and then
store them into a temporary file in a convenient format. Then,
when we do our training, we can mass-read a large number of the io-
pairs into a buffer, and train from the buffer. The result is a
compromise between the ideal case of storing all of the io-pairs in
memory, and the worst case of having to read all the io-pairs from
disk one byte at a time.

Returns the number of io pairs successfully read in and translated.
ERROR is returned if there are any errors during processing.

Note that this routine depends upon "the_buffer", "line_count", and
"last_char", all of which are variables global to netio.c
----------------------------------------------------------------------
*/
BEGIN
int PS_check_items(), PS_skip_tokens(), PS_get_token();
FILE *fp_input, *fp_output;
int list_count, item_count, STATUS;
char next_token[MAX_WORD_SIZE];
float fnum;

fp_input = fopen(file_name, "rt");
fp_output = PA_open_binary("workfile.net", WRITE_MODE);

STATUS = OK;
list_count = 0;
while (PS_skip_tokens(fp_input, "(" ) == OK) BEGIN /* more lists */
list_count++; /* incr # of lists */
item_count = 0; /* reset num items */
while (PS_get_token(fp_input, next_token) == OK) BEGIN
if (strcmp(next_token, ")" ) == 0) /* items++ til ")" */
break;
if (sscanf(next_token, "%f", &fnum) == OK) BEGIN
if (PA_put_to_workfile(fp_output, C_float_to_Sint(fnum)) == ERROR)
return(ERROR);
item_count++;
ENDIF
ENDWHILE
STATUS = PS_check_items(item_count, sum, list_count);
ENDWHILE

fclose(fp_input);
PA_flush(fp_output);
fclose(fp_output);
if ((STATUS == ERROR) || (list_count == 0))
return(ERROR);
else return(list_count);

END /* PS_parse_iopairs */


int PS_check_items(num_items, max_items, list_count)
int num_items, max_items, list_count;
/*
----------------------------------------------------------------------
A routine to check the number of items counted in an io pair list to
see if that number is equal to the desired length of the list. If
the 'num_items' is either greater than or less than 'max_items'
(which is the desired length of the io pair) then ERROR is returned
to indicate that an error was found. Also, to help the user in
finding the error, the current line is also printed to indicate
where the current io pair list ended. Note that there is a trick to
printing out the current line. The routine 'PS_get_token' counts the
number of newlines, but it does not indicate whether the list was
ended by a newline or by a comment. If the list was ended by a
comment, then 'PS_get_token' will be waiting on the SAME line as the
end of the list for the next token. On the other hand, if the io
pair list was ended with a ")" followed by a newline, then the
'PS_get_token' routine will be waiting on the NEXT line. Thus the
first two lines of this code check that ending condition, and set
the 'end_list' variable accordingly.

Note that the 'list_count' variable is passed in here for printing
purposes only.
----------------------------------------------------------------------
*/
BEGIN
int end_list;

end_list = line_count; /* see where list ended */
if (last_char == NEWLINE) end_list--; /* decr if ended w/ nl */
if (num_items > max_items) BEGIN
sprintf(IO_str, "\n*** list number %d is too long ***\n", list_count);
IO_print(0);
sprintf(IO_str, " list ends on line %d\n", end_list);
IO_print(0);
return(ERROR);
ENDIF
else if (num_items < max_items) BEGIN
sprintf(IO_str, "\n*** list number %d is too short ***\n", list_count);
IO_print(0);
sprintf(IO_str, " list ends on line %d\n", end_list);
IO_print(0);
return(ERROR);
ENDELSE
return(OK);

END /* PS_check_items */


int PS_skip_tokens(fp, token)
FILE *fp;
char *token;
/*
----------------------------------------------------------------------
skips through the tokens in a file until it reaches the value given
by the input 'token'. Returns ERROR if unsuccessful, otherwise it
returns a OK.
----------------------------------------------------------------------
*/
BEGIN
char temp[MAX_WORD_SIZE];
int PS_get_token();

while (PS_get_token(fp, temp) == OK) BEGIN /* while tokens left in file */
if (strcmp(temp, token) == 0)
return(OK);
ENDWHILE
return(ERROR);

END /* PS_skip_tokens */


int PS_get_token(fp, token)
FILE *fp;
char *token;
/*
----------------------------------------------------------------------
Puts a token in the input parameter 'token' returns ERROR if EOF,
else returns OK. Note that a static integer is used to keep track of
whether or not the last character read in should be reused.
----------------------------------------------------------------------
*/
BEGIN
static int use_last_char = FALSE;
int i;

i = 0;
while (TRUE) BEGIN
/*------------------------------------*/
/* get the next character; if newline */
/* then increment the line count */
/*------------------------------------*/
if (use_last_char == TRUE)
use_last_char = FALSE;
else if ((last_char = getc(fp)) == EOF)
return(ERROR);
if (last_char == NEWLINE) line_count++;

/*-------------------------------------*/
/* if token partially full, then check */
/* for end and return; else add char */
/*-------------------------------------*/
if (i > 0) BEGIN /* inside a word */
if ( (last_char == SPACE)
|| (last_char == TAB)
|| (last_char == NEWLINE)
|| (last_char == CLOSE_PAREN)
|| (last_char == OPEN_PAREN)
|| (i == MAX_WORD_SIZE - 1) ) BEGIN
token[i] = ENDSTRING;
/*--------------------------------------------*/
/* any single character token checked here */
/* and above; flag set here for reuse of char */
/*--------------------------------------------*/
if ((last_char == CLOSE_PAREN) || (last_char == OPEN_PAREN))
use_last_char = TRUE;
return(OK);
ENDIF
else
token[i++] = last_char;
ENDIF

/*------------------------------------*/
/* if at beginning of token, add char */
/* if not whitespace; return if paren */
/*------------------------------------*/
else BEGIN
if ( (last_char != SPACE)
&& (last_char != TAB)
&& (last_char != NEWLINE) )
token[i++] = last_char;
/*-------------------------------*/
/* single character tokens found */
/* are returned automatically */
/*-------------------------------*/
if ( (last_char == OPEN_PAREN)
|| (last_char == CLOSE_PAREN)) BEGIN
token[i] = ENDSTRING;
return(OK);
ENDIF
ENDELSE

ENDWHILE

END /* PS_get_token */


int PS_get_float_from_file(fp, ptr_float)
FILE *fp;
float *ptr_float;
/*
----------------------------------------------------------------------
This routine is used for retrieving floating point numbers from some
input file (specified by the file pointer "fp"). It is used only
during a "query_from_file" request made by the user. In such a
request, inputs to the network are read from a file and mapped onto
the input layer. This routine will read through any file, picking
out floating point numbers. The routine 'N_query_net' makes
sure that the right number of foats are read and mapped onto the
input layer.

Returns OK if a floating point number is successfully loaded into the
'ptr_float' variable, otherwise it returns ERROR. It makes use of the
the 'PS_get_token' routine to do all of the dirty work is getting the
input from the file. Note that he skips over all inputs that are
not floating point.
----------------------------------------------------------------------
*/
BEGIN
static char in_string[MAX_LINE_SIZE];
int PS_get_token();

while (TRUE) BEGIN
if (PS_get_token(fp, in_string) == ERROR)
return(ERROR);
if (sscanf(in_string, "%f", ptr_float) == 1)
return(OK);
ENDWHILE

END /* PS_get_float_from_file */


  3 Responses to “Category : Miscellaneous Language Source Code
Archive   : NETS_1.ZIP
Filename : PARSER.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/