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 #include #include #include /* 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 QSTRING %type V P HCIG CCIG BT TC NZ PT MFC %type IDENTIFIER %type dev_class dev_id %type COMPARISON %type INTEGER %type REAL %type 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 /* for NULL, fprintf(), ect... */ #include /* for SIGALRM */ #include "mvcl.h" /* for statement ID's */ #include /* 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