Flexible System Control and Special-Purpose Programming Languages
by Russell W. Mello



Example 1: 
  ...

# Open valve 1. Then wait for the pressure to go above 10 milli torr.
open V1;

# wait a bit to allow the pressure to change.
wait 10.0;

# Check the pressure at PT 3
pt3_val = read PT3;

if pt3_val > .010 then
  print "The pressure at PT3 is above 10 millitorr";
else
  print "The pressure at PT3 is below 10 millitorr";

  # Wait some more.
  wait 5.0;

  # Check the pressure at PT 3 again. If it fails again then give up.
  pt3_val = read PT3;

  if pt3_val > .010 then
    exit;
  endif;

endif;
 ...



Example 2: 
 ...

# Open valve 1. Then wait for the pressure to go above 10 milli torr.
# The waitfor statement will poll PT3 until the comparison becomes true or
# the timeout value has been exceeded.
open V1;
waitfor PT3 > .010;

pt3_val = read PT3;

if pt3_val > .010 then
  print "The pressure at PT3 is above 10 millitorr";
else
  print "The pressure at PT3 is below 10 millitorr";
 ...



Listing One
%{
/*                         MICRION SOFTWARE DEPT.
 *          Copyright (c) 1994 by Micrion Corp. All rights reserved.
 * FILE NAME    : mvcl.y
 * AUTHOR       : R. Mello
 * DATE WRITTEN : 12/12/94
 * VERSION      : 1.00
 * USAGE        : grammar processor for the mvcl programming language
 *  This file contains a yacc grammar for the micrion ( M ) vacuum
 * ( V ) command ( C ) language ( L ).
 */
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <mvcl_int.h>

/* local globals */
static MvclProgramList *prog = (MvclProgramList *)NULL;
static MvclProgramNode *Node;
static char            *tmp;
static int             ret;
static MvclPrintItem   *plist = (MvclPrintItem *)NULL;
%}

%union
  {
   double   real;
   int      integer;
   char     *string;
  }
%token  PROC END
%token  IF THEN ELSE ENDIF
%token  FOR TO DOWNTO ENDFOR
%token  OPEN CLOSE READ SET STATUS WAIT TIMEOUT EXTEND RETRACT
%token  WAITFOR PUMPON PUMPOFF EXIT PERROR PRINT PRETURN
%token  V P HCIG CCIG BT TC NZ PT MFC
%token  NAME SEMICOLON COMPARISON ASSIGNMENT QSTRING IDENTIFIER
%token  SHELL_START STREAM_START STRING_START  INTARG REALARG
%token  INTEGER REAL
%token  UNKNOWN
%token  HELP 

%type <string>  QSTRING
%type <integer> V P HCIG CCIG BT TC NZ PT MFC
%type <string>  IDENTIFIER
%type <integer> dev_class dev_id
%type <integer> COMPARISON
%type <integer> INTEGER
%type <real>    REAL
%type <string>  expr
 
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS

%%
 /* Three grammars can be parsed here. One that will accept 
  * interactive commands. Interpreting each command as it is entered.
  * One that will accept a stream input. Parse the stream into a
  * list of program nodes. Then pass the resultant list through the
  * interpreter. Finally a grammar that will accept input from a string.
  */
combined:       SHELL_START  mvcl_shell
    |       STREAM_START mvcl_stream
        |               STRING_START mvcl_string
    ;
 /* ============== The stream grammar starts here ============== */
mvcl_stream:        proc_statement mvcl_statements end_program
    ;
proc_statement:     PROC QSTRING SEMICOLON
    ;
mvcl_statements:    mvcl_stream_statements SEMICOLON
    |       mvcl_stream_statements SEMICOLON
                        mvcl_statements
    ;
mvcl_stream_statements: status_statement 
        |       read_statement
        |               mvcl_statement
        |               cond_statements
        |               loop_statements
        |       return_statement
        ;
status_statement:   IDENTIFIER ASSIGNMENT STATUS dev_class
                         dev_id
                        {
                         if(BuildMvclStatusNode( prog, $1, $4, $5))
               YYABORT;
            }
    ;
read_statement:     IDENTIFIER ASSIGNMENT READ dev_class dev_id
            {
                         if(BuildMvclReadNode( prog, $1, $4, $5))
               YYABORT;
            }
    ;
cond_statements:       if_statement
        |              if_else_statement
        ;
if_statement:          IF if_test mvcl_statements endif_statement
        ;
if_else_statement:     IF if_test mvcl_statements else_statement
                       mvcl_statements endif_statement
        ;
else_statement:        ELSE
                       {
                        if( BuildMvclElseNode( prog ) )
                          YYABORT;
                       }
        ;
endif_statement:       ENDIF
                       {
                        if( BuildMvclEndifNode( prog ) )
                          YYABORT;
               }
        ;
if_test:               expr COMPARISON expr
                       {
                        if( BuildMvclIfNode( prog, $1, $2, $3 ) )
              YYABORT;
                       }
        ;
loop_statements:       for_statement
        ;
for_statement:         FOR for_args mvcl_statements endfor_statement
        ;
for_args:              IDENTIFIER ASSIGNMENT expr TO expr
                       {
                        if( BuildMvclForNode( prog, $1, $3,
                            MVCL_FOR_TO, $5 ) )
                          YYABORT;
               }
        |              IDENTIFIER ASSIGNMENT expr DOWNTO expr
                       {
                        if( BuildMvclForNode(prog, $1, $3, 
                            MVCL_FOR_DOWNTO,$5))
                          YYABORT;
               }
        ;
endfor_statement:      ENDFOR
                       {
                        if( BuildMvclEndForNode( prog ) )
                          YYABORT;
               }
        ;
expr:                  IDENTIFIER
                       { $$ = $1;
                       }
        |              REAL
                       { $$ = MvclCreateRealSymbol( prog, $1 );
                       }
        |              INTEGER
                       { $$ = MvclCreateIntSymbol( prog, $1 );
                        }
        |              QSTRING
                       { MvclParserError( "Incompatible Types\n" );
                         YYABORT;
                       }
        ;
return_statement:      PRETURN INTEGER
                      {
                        BuildMvclReturnNode( prog, $2 );
                       }
end_program:           END SEMICOLON
    ;
 /* ============= The string grammar starts here ============== */
mvcl_string:           mvcl_string_statements
        ;
mvcl_string_statements: /* empty */
        |               mvcl_shell_commands
        ;
 /* ============= The shell grammar starts here ============== */

mvcl_shell:     mvcl_shell_statements end_shell
   ;
mvcl_shell_statements:  mvcl_shell_commands 
        |       mvcl_shell_commands mvcl_shell_statements
    ;
mvcl_shell_commands:    status_command
        |               read_command
        |               help_command
        |               mvcl_statement
        ;
status_command:         STATUS dev_class dev_id
                        { tmp = MvclGenerateVariable();
                          BuildMvclStatusNode( prog, tmp, $2, $3 );
                        }
    ;
read_command:       READ dev_class dev_id
                        { tmp = MvclGenerateVariable();
              BuildMvclReadNode( prog, tmp, $2, $3 );
            }
    ;
help_command:           HELP
                        { MvclShellHelp();  }
end_shell:      END
               { exit( 0 ); }
    ;
 /* ================ Common grammar elements ================= */

mvcl_statement:     open_statement
    |       close_statement
    |       timeout_statement
    |       set_statement
    |       pumpon_statement
    |       pumpoff_statement
    |       extend_statement
    |       retract_statement
    |       wait_statement
    |       waitfor_statement
        |               print_statement
        |               error
                        { if( MvclGetParserMode() == MVCL_STREAM )
                            YYABORT;
            }
    ;
open_statement:     OPEN V dev_id
            { if( BuildMvclOpenNode( prog, $3 ) )
                            YYABORT;
            }
    ;
close_statement:    CLOSE V dev_id
            { if( BuildMvclCloseNode( prog, $3 ) )
                YYABORT;
                        }
    ;
timeout_statement:  TIMEOUT REAL
            { if( BuildMvclTimeoutNode( prog, $2 ) )
                YYABORT;
            }
    ;
set_statement:      SET dev_class dev_id REAL
            { if( BuildMvclSetNode( prog, $2, $3, $4 ))
                YYABORT;
            }
    ;
pumpon_statement:   PUMPON P dev_id
            { if( BuildMvclPumpOnNode( prog, $3 ) )
                YYABORT;
            }
    ;
pumpoff_statement:  PUMPOFF P dev_id
            { if( BuildMvclPumpOffNode( prog, $3 ) )
                YYABORT;
            }
    ;
extend_statement:   EXTEND NZ dev_id
            { if( BuildMvclExtendNode( prog, $3 ) )
                YYABORT;
            }
    ;
retract_statement:  RETRACT NZ dev_id
            { if( BuildMvclRetractNode( prog, $3 ) )
                YYABORT;
            }
    ;
wait_statement:     WAIT REAL
            { if( BuildMvclWaitNode( prog, $2 ) )
                YYABORT;
            }
    ;
waitfor_statement:  WAITFOR dev_class dev_id COMPARISON REAL
            {
                         if( BuildMvclWaitForNode(prog,$2,$3,$4,$5))
                YYABORT;
            }
   ;
print_statement:        PRINT print_list
                        { ret = BuildMvclPrintNode( prog, plist );
                          /* init for another list. */
                          plist = (MvclPrintItem *)NULL;
                          if( ret )
                            YYABORT;
                        }
        ;
print_list:             print_item
        |               print_item ',' print_list
        ;

print_item:             QSTRING
                        { MvclAddToPrintList( &plist, $1,
                          MVCL_PRINT_STR );
                        }
        |               IDENTIFIER
                        { MvclAddToPrintList( &plist, $1,
                          MVCL_PRINT_IDENT );
                        }
        |               REAL
                        { tmp = MvclCreateRealSymbol( prog, $1 );
                         MvclAddToPrintList( &plist, tmp,
                         MVCL_PRINT_REAL );
                        }
        |               INTEGER
                        { tmp = MvclCreateIntSymbol( prog, $1 );
                         MvclAddToPrintList( &plist, tmp,
                         MVCL_PRINT_INT );
                        }
        ;
dev_class:      V        { $$ = MVCL_VALVE_CLASS;   }
        |       P        { $$ = MVCL_PUMP_CLASS;    }
        |       HCIG     { $$ = MVCL_HCIG_CLASS;    }
        |       CCIG     { $$ = MVCL_CCIG_CLASS;    }
        |       BT       { $$ = MVCL_BT_CLASS;      }
        |       TC       { $$ = MVCL_TC_CLASS;      }
        |       NZ       { $$ = MVCL_NZ_CLASS;      }
        |       PT       { $$ = MVCL_PT_CLASS;      }
        |       MFC      { $$ = MVCL_MFC_CLASS;     }
        ;
dev_id:         /* empty */ { $$ = 0;           }
        |       INTEGER  { $$ = $1;         }
       ;
%%
/***************************************************************/
/* finis */


Listing Two
/*                         MICRION SOFTWARE DEPT.
 *          Copyright (c) 1995 by Micrion Corp. All rights reserved.
 * FILE NAME    : mvcl_int.c
 * AUTHOR       : R. Mello
 * DATE WRITTEN : 3/18/95
 * VERSION      : 1.00
 * USAGE        : module of the mcvl parser
 * This source file contains the mvcl interpreter.
 */
#include <stdio.h>               /* for NULL, fprintf(), ect...   */
#include <signal.h>              /* for SIGALRM                   */
#include "mvcl.h"                /* for statement ID's            */
#include <mvcl_int.h>            /* for the Mvcl data structures  */

/* uncomment the following line for debugging. */
/* #define MVCL_DEBUG */

/* function prototypes. */
int  MvclInterpreter( MvclProgramList *, int * );

/* Interpreter function dispatch table. */
static  PFI  InterpreterTable[]=
  {
   InterpretOpenStatement,
   InterpretCloseStatement,
   InterpretStatusStatement,
   InterpretSetStatement,
   InterpretReadStatement,
   InterpretTimeoutStatement,
   InterpretPumpOnStatement,
   InterpretPumpOffStatement,
   InterpretExtendStatement,
   InterpretRetractStatement,
   InterpretWaitForStatement,
   InterpretWaitStatement,
   InterpretIfStatement,
   InterpretElseStatement,
   InterpretEndifStatement,
   InterpretForStatement,
   InterpretEndForStatement,
   InterpretNullStatement,
   InterpretPrintStatement,
   InterpretReturnStatement
  };
/******************************************************************/
int  MvclInterpreter(
   MvclProgramList   *program,   /* ptr to the program node list  */
   int               *script_return )
  {
   /* MvclInterpreter() -- This function will interpret a translated
    *     source file. The interpreter will simply traverse the list
    *     of program nodes executing the mvcl program.
    *     RETURNS: 0 for success
    *              1 for failure
    */
   int       result;
   MvclProgramNode *current;

   /* Make sure we have a valid program before we continue... */
   if( program != (MvclProgramList *)NULL )
     {
      #ifdef MVCL_DEBUG
      printf( "Beginning Interpretation...\n" );
      #endif

      /* Let's get started... */
      current = program->ip;
      /* Enter the interpreter loop. */
      while( current != (MvclProgramNode *)NULL )
        {
         #ifdef MVCL_DEBUG
         printf( "Executing A '%s' Node At Address %p \n",
         MvclStatementTypeToName( current->StatementType ),
                                  current );
         #endif

         /* based on statement type, dispatch the appropriate function. */
         if( current->StatementType != END )
           {
            result = (*InterpreterTable[current->StatementType])
                     ( program, current );
            /* We're done if we see a return statement. */
            if( current->StatementType == MVCL_RETURN )
              break;
           }
         else
           {
            result = 0;
            break;
           }
         /* test the return value, exit if an error occured. */
         if( result != 0 )
           break;
         /* The next node is based on the instruction pointer. */
         current = program->ip;
        }
     }
   /* reset the program's instruction pointer to the start of the program. */
   program->ip = program->head;

   /* Set the return value. */
   *script_return = program->return_value;

   return( result );
  }
/******************************************************************/
/* finis */
9


