CORRELATION PLOTS ARITHMETIC SYSTEM SPECIFICATION Gregory R. White First Published July 9th, 1993 Last Revised September 9th, 1993 ( Review Changes and malloc VECTs in push) Found in doc$spec:crr_arith_sysspec.txt Assoc. doc doc$spec:crr_arith_funcspec.txt INTRODUCTION This document describes the proposed additions to the Correlation Plots (CP) software necessary to provide an 'arithmetic capability'. For a description of the user interface of the extensions, see the Correlation Plots Arithmetic Functional Specification (DOC$SPEC:CRR_ARITH_FUNCSPEC.TXT). This system specification concentrates on the design of the new software subsystem specifically for Correlation Plots ad-hoc expression processing. The changes required to existing CP software are only outlined, since those amendments will be primarily oriented towards checking whether a given Sampled Variable is in fact defined by an expression and taking some special action - though that special action will usually be a simple one. OVERVIEW The overall principle to be used to process the expressions entered for a Sampled Variable (SV), will be to implement a stack machine program interpreter. Whenever an expression is assigned to a Sampled Variable, the expression will be immediately parsed as an infix arithmetic expression. The parser will construct a 'program' from virtual-machine instructions that we write in C. Those are simply compiled into the CRRUTIL shareable image, and available as external symbols at runtime. When executed (usually as part of the data acquisition cycle), the program will compute the value of the array for the target Sampled Variable. The operators and functions take arrays of length MAX_SAMPLES as their operands. NAMING The software subsystem of Correlation Plots which shall perform the arithmetic functions shall be called CMATH. It shall be written in the C programming language. In general, the prefix for CMATH global symbols (external functions and global variables) names will be "cmth_". Some of the global symbols in CMATH will be those implementing the program language of arithmetic expressions - such as the CMATH built-in functions (like those for computing transcendental functions) have a further letter prefix. The instructions of a CMATH program will be prefixed with "cmthc_". The built-in functions will be prefixed "cmthb_" All the new source modules for implementing CMATH will be named using the "CMTH_" prefix. All modules will be placed in the CRRUTIL shareable image, and include files in REF_C_INC: In this document, when a symbol is first described, its source location (either module or psect) will prepend the symbol name. For instance CMTH_SYMBOL/cmth_SymList_ps. MAJOR DATA STRUCTURES AND VARIABLE DEFINITIONS CHANGES TO EXISTING VARIABLE DEFINITIONS: (APDESC.TXT) AP_DESC/ap_descriptor The definition of the structure describing a Correlation Plots Sampled Variable APDESC.TXT/ap_descriptor will be unchanged. CP software will be amended in order to interpret some of its members differently under the following circumstances; 1. If ap_descriptor.prim == EXPR, the the SV has been assigned from an expression. 2. Given 1 is true: ap_descriptor.expr (ap_descriptor.d(2)) is the address of a 0 terminated string of characters, which is the ascii representation of the expression (as input from a DIALOG prompt). 3. Given 1 is true: ap_descriptor.prog (ap_descriptor.d(3)) is the address of a program, terminated by a STOP instruction, that when executed will assign the AP_DESC/SV_DATA element (an array) to the vector value that would result from the expression in 2. The character string in 2 includes any variable name that was given on the LHS of an '=', and the '=' itself - that is, it is the whole sequence that was entered at the DIALOG prompt for the SV. It is this string which is parsed by cmth_parse(), and it is the string given in plots and table labels. The program in 3 is a sequence of (< NPROG defined below) instructions, where each instruction is either the address of a function (one of those defined in CMATH FUNCTIONS below) or the address of a symbol. NEW TYPE DEFINITIONS: (CMTH.H): A VECT_TA is an array of floats (REAL*4). This is the atomic arithmetic data type in CMATH. The length of the array is sufficient to hold all the data acquired for a Sampled Variable, or the result of a vector operation. typedef float VECT_TA[MAX_SAMPLES] Array of data. INST_TPF is the type of CMATH program instructions. A INST_TPF is a type definition for a pointer to a function returning int. typedef int (*INST_TPF)() Machine instruction definition. The symbol table, that is, the table of all unique symbols that have been seen in a parse of an expression is defined by SYMBOL_TS. In CMATH, all language tokens (as defined by a %token directive in the yacc language definition) are represented as a symbol. Therefore, a symbol instance must be of one of the 4 token types; which it is, is specified by the "type" member, whose value must be one of those defined in CMTH_TAB.H: i NUMBER - an array of floating point numbers, pointed to by val_pa. The name_c member will be blank. svid is not populated. ii. CONST - an array of floating point numbers, all the same value, and their value cannot change. For predefined numbers like PI. The name_c member is the constant name, eg "PI". svid is not populated. iii. VAR, UNASGN or UNDEF - describes a Sampled Variable. The name_c member is the variable name, eg "B3". If the variable has been given a name that name is recorded in the givenName_c member, eg "luminosity". svid is the number of the Sampled Variable (ie 1..160), described by this symbol. iv. BLTIN - a pointer to a function. name_c is the name of the function, eg "mthc_sin"; svid is not populated; inst_pf is a pointer to a CMATH built-in function, eg mthc_sin. typedef struct symbol { struct symbol *last_ps; VAX linked-list tags: for CMATH struct symbol *next_ps; symbol table. char* name_c; eg AA3 if VAR, sin if BLTIN, NULL if NUMBER, PI if CONST. char* givenName_c; eg PYIELD if UNDEF or VAR int type; One of VAR BLTIN NUMBER CONST UNDEF int svid; Iff VAR or NUMBER, then SV id 1..160. union { float val; NUMBER or CONST INST_TPF inst_pf; If BLTIN } _u; } SYMBOL_TS; DATUM_TU describes an element on the program stack. All stack items may either be a symbol or an immediate value. typedef union datum { VECT_TA val_a; An immediate value. SYMBOL_TS* sym_ps; A symbol. } DATUM_TU; PROGRAM CONSTRUCTION (CMTH.Y -> CMTH_TAB.C) Currently, the 2d array CORR_GLOBALS/SAMP_VAR holds all sampled variable names in the structure ap_descriptor described above. It is populated by CRR_INPUT_VALUE/crr_input_value(), which is called by crr_input_but() when an SV button is pushed. crr_input_but makes a dialog with the user to get the name of a plotable entity, which will be assigned to the SV. The new members in ap_descriptor, for describing SV's whose value will be derived by expressions, will be populated by a new function CMTH.Y/cmth_parse, which will be called by crr_input_value if the input line it got contains an '=' anywhere in it. Before calling cmth_parse, crr_input_value will assign 'EXPR' to the ap_descriptor's prim field, and the address of the char string holding the expression to its expr field. vmsstat_t cmth_parse(int svid, struct ap_descriptor* sv_s) Function: Parses an expression for the svid given, and constructs a program to compute the expression. Arguments: svid is the number of an SV button. sv_s is a an instance of the standard structure used throughout Correlation Plots (APDESC.TXT/ ap_descriptor), which describes the name of a step or sampled variable. The expr member of sv_s is a \0 terminated string of characters, being the arithmetic expression input at the prompt for the value of an SV button. Method: cmth_parse first resets the code construction pointers - such as that pointing to the next location that an instruction can be written in a program. Then it sets up the signalling for error handling. The parser must trap and recover from syntax errors in expressions. When a yacc based parser cannot scan an expression, it forces a rule with the "error" symbol in it to be true. In the CMATH language, that rule will ultimately result in a longjmp to the "cmth_Begin" signal label. The cmth_Begin will be setjmped immediately before the call to cmth_yyparse() in cmth_parse. Since cmth_parse is part of a shareable image, where other modules are free to set up setjmps, it must clear the setjmp before exiting if the setjmp was never used (in which case it is cleared by VMS). The setjmp will be cleared by issuing a longjmp. Having set up the signal handling, cmth_parse will call cmth_yyparse() to parse the given expression. If the parse was successful cmth_parse will copy the constructed program to SAMP_VAR(AP_PROG, svid). Global Variable(s) int SvId; /* Sampled Variable Number of */ /* button pushed. */ Static Variable(s) to CMTH_TAB.C for code construction static char *expr_p; /* The expression to scan. */ PDL of cmth_parse() initparse( svid, sv_s ); /* Reset code construction ptrs. */ /* ** Set sig label to longjmp to in event of error. If a category ** ERROR occurs during the parse, the error handling routine ** cmth_parseErr will issue a longjmp(iss, cmth_begin), the call ** stack will be unwrapped to here and the setjmp will return iss. If ** the longjmp is never issued by cmth_parseErr, it must be cleared by ** this routine. */ iss = setjmp( cmth_Begin ); /* When 1st called returns 0. */ if ( iss == 0 || $VMS_STATUS_SUCCESS(iss) ) { cmth_yyparse(); /* Parse the input expression */ } /* setjmp initialization / */ /* longjmp with error status */ if ( iss == 0 ) longjmp( cmth_Begin, OP_ELONGJMPCLEAR ); /* To clear the longjmp context. */ else if (iss == OP_ELONGJMPCLEAR ) iss = SS$_SUCCESS; /* To clear iss after a longjmp on */ /* OP_ELONGJMPCLEAR after an error */ /* free parse. */ /* ** If no errors occurred during the parse and program construction, ** copy the program to the sampled variable. If the flag requiring ** immediate evaluation is true run the program now. */ if ( iss == SS$_SUCCESS ) { cmth_copyProg( &sv_s.prog_p ); if ( cmth_EvalOnEnter ) mth_execute( sv_s.prog_p ); } return iss; void initparse( int svid, struct ap_descriptor* sv_s ) Function and Method: Initializes pointers for CMATH instruction coding and sets static variables from those passed in. PDL of initparse SvId = svid; /* Set global var for sv number. */ expr_p = sv_s.expr; /* Set static var pointing to */ /* expression to scan so that static*/ /* lexical analyzer called by */ /* cmth_yyparse can scan it. */ cmth_InitCode(); /* Set next avail address for code */ /* to address of 1st elem of prog. */ LANGUAGE DEFINITION (CMTH.Y -> CMTH_TAB.C) The CMATH language will be constructed by Bison (like yacc) from its Backus-Naur Form. The BNF of CMATH, the semantic actions of the production rules (coding CMATH instructions), plus the yacc stack symbol definitions are given below. This should be a complete yacc language definition. It will all be sourced in CMTH.Y Yacc will construct a module MTH_TAB.C containing 1 external function, cmth_yyparse(), to perform a parse, plus an include file CMTH_TAB.H defining the token name values. Before the parse is started, two variables static to CMTH_TAB will be set: int SvId - the number of the button for which the program is being constructed. static char *expr_c - the expression to scan. YACC STACK %union { SYMBOL_TS* sym_ps; Symbol table ptr. INST_TPF inst_pf; Machine Instruction. } TOKEN LIST Tokens are listed in yacc language definitions lowest in precedence first. Those with no associativity are preceeded by a %token; those with left associativity - a %left; those with right associativity - a %right. %token NUMBER CONST VAR BLTIN UNDEF %right '=' %left '+' '-' %left '*' '/' '%' %left UNARYMINUS - to distinguish from subtraction %right '^' - exponentiation PRODUCTION RULES A list, that is, a valid input sequence for an SV to be defined as an expression, is either an explicit assignment - in which a symbol to represent the SV is named, or an implicit assignment - in which the symbol name is taken to be that of the button (eg. b5). A list may be an explicit assignment; in which case the assignment to the SV's data has already been done. It only remains to clear up the stack and stop. list : asgn { cmth_codeInst(cmthc_pop); cmth_codeInst(STOP); } Alternatively, a list may be an implicit assignment. In this case the SV to assign to is implied by the button pushed. Never-the-less, a symbol must be created incase of use by successive SV expressions. Once the symbol has been created, we code a function to push it onto the stack and then assign the value of the previous stack entry to it. Finally clean up the stack and stop. | '=' expr { Get the name of the pushed button with cmth_svidToName(). Lookup a symbol for that name, if none exists, create it as UNASGN. Code functions to push that symbol onto the stack and assign its value from the previous stack value. cmth_codeInst(cmthc_varpush); cmth_codeInst((INST_TPF)symbol for button); cmth_codeInst(cmthc_assign); cmth_codeInst(STOP); } An expression may be an unrecognized symbol sequence - in which case a yacc generated parser will always fire the rule that has the "error" reserved symbol in it. yyerrok() is a yacc function that permits it to get back into a sensible parsing state. | error { yyerrok(); } An assignment is an SV variable identifier (either the SV's spreadsheet id or its assigned name, or a default name) followed by an '=' followed by an expression. The SV identifier must be that for the current button (the one we're making a program for). Check that the expression assignment being made is not to a step variable, which is not permitted. Code instructions to create a DATUM_TU on the stack and assign its value (as a symbol) from the value of the previous datum on the stack which at time of execution will be the result of $3. If the symbol on the LHS was undefined, we can now fill in the SV for which it's being defined and its default name (eg A3). asgn : VAR '=' expr { if ( $1->type != UNDEF && $1->svid != id for button )) issue "Illegal definition of a variable" if ( $1->type == UNDEF ) $1->svid = id for button. $1->name_c = cmth_SvidToName( id for button ) $1->type = UNASGN if ( $1->svid == -1 or $1->svid == 0 ) cmth_deleteSym($1) issue "Cannot assign expression to step var" cmth_codeInst(cmthc_varpush); cmth_codeInst((INST_TPF)$1); cmth_codeInst(cmthc_assign); } An expression's value may be an array of NUMBER. Code an instruction to take the next item in the program, which will be a symbol for that number array, and place it on the stack. expr : NUMBER | CONST { cmth_codeInst(cmthc_constpush); cmth_codeInst((INST_TPF)$1); } An expression's value may be that of a VAR. At this stage the symbol must not be UNDEFined and the symbol must not describe an SV which requires a forward reference (or be the same as) the SV for which this expression is being parsed. This is because when the program is run there must be a value to dereference in the VAR. In this case, the datum placed on the stack should be the dereferenced value of the VAR. Code instructions to create a symbol stack datum from the VAR, and then dereference it, leaving the result on the stack as a value datum. | VAR { if ( $1->type == UNDEF ) issue "Undefined Variable" cmth_deleteSym($1) if ( &svidName_as[elem for $1->svid] >= &svidName_as[elem for svid of current button] ) issue "Forward or Self Reference" cmth_codeInst(cmthc_varpush); cmth_codeInst((INST_TPF)$1); cmth_codeInst(cmthc_eval); } An expression's value may be the result of a BLTIN function on an expression. Code an instruction to interpret the next item in the program as the address of a function, and to call that function with the contents of the stack head, and to place the result on the stack head. | BLTIN '(' expr ')' { cmth_codeInst(cmthc_bltin); cmth_codeInst((INST_TPF)$1->_u.inst_pf); } An expression may be an expression within parentheses. The value of this expression is only the value of the expression within the parentheses, so take no action on the stack. | '(' expr ')' An expression's value may be the result of vector addition on two expressions. Code an instruction to interpret the last 2 items on the stack as vectors, and add their corresponding elements, leaving the result at the head of the stack. | expr '+' expr { cmth_codeInst(cmthc_add); } As with expression addition, for subtraction. | expr '-' expr { cmth_codeInst(cmthc_sub); } As with expression addition, for division. | expr '/' expr { cmth_codeInst(cmthc_div); } As with expression addition, for multiplication. | expr '*' expr { cmth_codeInst(cmthc_mult); } As with expression addition, for modular division. | expr '%' expr { cmth_codeInst(cmthc_modu); } An expression's value may be the vector-wise result of one expression's value raised to the power of the corresponding elements from another expression. Code an instruction that pops the previous 2 elements from the stack and raises the elements in the stack item before last, to those in the last. | expr '^' expr { cmth_codeInst(cmthc_power); } An expression maybe an expression preceded by a unary minus operator. The unary minus token is distinguished from the binary by the UNARYMINUS directive in yacc. The result of the target expression in that case is that all elements of the target are negated. Code an instruction to take the last datum of the stack (as an immediate value) and negate all its elements. | '-' expr %prec UNARYMINUS { cmth_codeInst(cmthc_negate); } LEXICAL ANALYSIS (CMTH.Y -> CMTH_TAB.C) All terminal symbols in CMATH, apart from the math operator symbols like '+', will be instantiated as members of the symbol table CMTH_SYMBOL/symList_ps. For each token recognized, the lexical analyzer must set the yacc stack (CMTH_TAB/yylval) symbol ptr member (sym_ps) to the address of the symbol table element that describes the symbol it recognized. That is, the lexical analyzer function must, for each token it 'sees' search the symbol table list; if the token is not in the symbol table, then it must create a new element in the table, set yylval.sym_ps to the address of the symbol table element that describes the token, and return with a token id (like NUMBER). The CMATH lexical analyzer will be named cmth_yylex() and will be static to CMTH_TAB.C (ie be defined static in CMTH.Y). static int cmth_yylex( void ) Function: Performs lexical analysis for CMATH language. Arguments: None. Method: Ignore spaces and tabs. If the following character sequences are recognized: 1. A floating point number. Call cmth_installSym with null name_c, NUMBER type and value being the float type equivalent of the recognized characters. Set yylval.sym_ps to address of symbol table element describing the number and return NUMBER. 2. A Sampled Variable id number Identified by a '#' followed by a 1-3 numeric sequence possibly including leading '-'. Convert the id number to a default sampled variable button name, eg "#19" to "C2" with call to cmth_SvidToName(). Calls cmth_LookupSym with default SV name. If no matching symbol is found, call cmth_InstallSym with name_c being the default SV name, type being UNASGN and val being the svid. Set yylval.sym_ps to the address of the symbol describing the SV; return VAR. 3. An identifier. (Any alphabetic character followed by any sequence of 1 or more alphanumeric characters, including special chars). Once recognized, the sequence will be forced to upper-case. An identifier is the sequence of characters naming a constant (like "GAMMA"), a builtin function (like "log") or a sampled variable name (eg "AA1") pseudo-name (eg "PYEILD"). Call cmth_lookupSym to see if this symbol has been installed before. If it, was, the identifier must be one of the built-in functions or a constant (since these are permanently installed in the symbol table) or a variable that has been previously installed. If not, the identifier must be that of a variable name not previously installed. If the identifier matches one of the default names of the SVs, then it can be installed as an unassigned variable - call cmth_installSym with type = UNASGN, name_c = character sequence (eg "A2") and val_p = svid of the SV. If the identifier fails to match any default name install it as an undefined variable, with given name = character sequence eg "PYEILD"), type = UNDEF, value = 0. Set yylval.sym_ps to address of the symbol table element describing the symbol and return VAR if a variable (either VAR, UNASGN or UNDEF), CONST or BLTIN. 4. Any other single character (if not 1. 2. or 3. then must be non-alphabetic and non-digit), will be returned to the parser, since it may be an arithmetic operand (eg '+'). No symbol is created for these. SYMBOL TABLE MANAGEMENT (CMTH_SYMBOL.C) The symbol table, and its management functions, will be sourced in CMTH_SYMBOL.C The symbol table will be static to this module, and all manipulation of it will be through functions defined here. static SYMBOL_TS* symList_ps The symbol table. External Functions SYMBOL_TS* cmth_LookupSym( const char* name_c ) Function: This function is used to verify whether a symbol has been encountered already. Arguments: name_c is the string of recognized characters if called from the lexical analyzer, or the name of a (non-number) symbol if called from elsewhere. Method: cmth_lookupSym searches the list of symbols symList_ps for an element for which the symbol's name_c or givenName_c is equal to arg name_c. If it finds one, it returns the address of that element, otherwise is returns NULL. NUMBER symbols are never looked up because they are created whether an existing symbol describes them or not. This is because NUMBER symbols are deleted when the symbol describing an SV that is defined by an expression using that NUMBER is deleted. SYMBOL_TS* cmth_InstallSym( const char* name_c, const int type, const void *val_p ) Function: This function is used to add symbol definitions to the symbol table. It will be invoked in the initial setup to populate the symbol list with the predefined symbols for CONST's and BLTIN's. It will be invoked when parsing an input expression to define VAR and NUMBER symbols. Arguments: The arguments give the name, type-of and value-of, a symbol to be created on the end of the symbol table list. Symbols must be of one of the types listed in the %token declaration of the language definition in CMTH.Y. The interdependencies between symbols' types and their other attributes is given in the description of a SYMBOL_TS above. Method: Unless the symbol being installed is a NUMBER cmth_lookupSym should be called before invoking this function to make sure the symbol has not been previously defined. cmth_installSym reserves memory for a SYMBOL_TS. It then populates the appropriate members of this aggregate as given in the arguments. If the type is CONST or NUMBER, then the value is a floating-pt number - assign it to the val member of the symbolt. The svid member is set to 0 (null value) for a CONST. However, for a NUMBER the svid is set to the id of the button for which this parse is being made (Svid). This is used in garbage collection to identify which symbols can be deleted when an SV that was defined by an expression is set to some other value. If the type is BLTIN, the name_c member will be the name of a CMATH builtin function, as defined by CMTH_MATH/Builtins_as. The symbol's value in this case is the address of a CMATH function given in CMTH_MATH/Builtins_as, and is assigned to the symbol's _u.inst_pf member. The svid member is left unset. If the type is UNASGN, the symbol describes a Sampled Variable and the id of that sampled variable is known (but data has not been evaluated); the name_c arg describes the variables default name (eg "A3") and the val arg is the Sampled Variable's number (eg 20 for "A3"). Assign the name_c and svid members of the created symbols from these two args. If the type is UNDEF the specific sampled variable to which this symbol is being created is not yet known. Assign the created symbol's svid member to 0. Assign the givenName_c member to the name given in the argument. The name_c member will be set when the SV for which this symbol is intended is known for sure (when it's used on the LHS of an assignment). Insert the newly created symbol at the tail of the symbol list. Note that for VAX list management, if this is the first element in the list, the last_ps and next_ps members must point to this first element. Return address of created symbol. vmsstat_t cmth_DeleteSym( SYMBOL_TS* sym_ps ) Function: Deletes a symbol from the symbol list. Method: Relinks the symbol list to exclude the given symbol and frees all memory associated with the symbol (including that pointed to by its members). CMATH LANGUAGE INSTRUCTIONS (CMTH_CODE.C) The CMATH instructions are those functions coded by the CMATH parser to compute a given expression. One program so coded, is assigned to a Sampled Variable - SAMP_VAR(AP_PROG, svid). These instructions do not include the CMATH built-in functions, since those are implemented as special cases of symbols. The CMATH program variable and program counter, as well as the following CMATH instructions will be sourced in CMTH_CODE.C. static INST_TPF prog_ap[NPROG] /* A program - instructions & symbols*/ static INST_TPF *prog_p /* Next writable address. */ The program is executed using a linear stack, where each element is a value (a vector) or a symbol. The stack is managed by CMATH instructions cmthc_pop() and cmthc_push() defined in this module. static DATUM_TU stack_au[NSTACK]; /* The stack. */ static DATUM_TU *stack_p /* Next free item on stack. */ static INST_TPF *pc_p /* Program counter - for execution. */ CODING A CMATH PROGRAM When a production rule is matched the cmth_codeInst() codes an instruction at the next available address in the program. void cmth_codeInst( INST_TPF f) Function and Method: PDL of cmth_codeInst if ( prog_p >= &prog_ap[NPROG] ) cmth_parseErr( 'Program Overflow' ); else *prog_p++ = f; CMATH INSTRUCTIONS: DATUM_TU cmthc_pop() Function and Method: Checks that the stack pointer is greater than the first stack address. If not, issue a 'Stack Underflow' message. Otherwise, decrement the stack pointer and return the DATUM_TU at that address. cmthc_push( DATUM_TU item_u ) Function and Method: Checks that the stack pointer is less than the last stack address. If it isn't, issue a 'Stack Overflow' message. Otherwise, place the given datum on the stack at the address of the stack pointer, and increment the stack. cmthc_constpush() Function: Creates a stack datum, and populate it as an array value from the next program instruction. Method: Define a datum as a DATUM_TU. Assign the elements of the val_a member of the datum from the val member of the union in the symbol pointed to by the current (pc) item in the program. Increment the program counter. ie. For all MAX_SAMPLES elements of DATUM_TU's val_a DATUM_TU.val_a[i] = ((SYMBOL_TS*)*pc_p++)->_u.val Push the populated datum onto the stack. cmthc_varpush() Function: Place the next instruction in the program on the stack as a symbol. Method: Create a stack datum. Assign its symbol pointer from the contents of the program counter - interpreted as a symbol. Increment the program counter. Push the datum onto the stack. cmthc_eval() Function: Dereference a variable on the stack. Method: Define a datum as a DATUM_TU. Pop the stack returning a datum. Check that the datum, interpreted as a symbol, is not UNDEFined; if it is a variable that has not yet been parsed, issue an 'Undefined Variable' message. Otherwise: Dereference the symbol, that is, assign the datum's val_a from the data acquired for the Correlation Plots variable specified by the datum's sym_ps pointer. Then the datum will have an immediate value rather than being a variable. ie For all MAX_SAMPLES elements of DATU_TU's val_a DATUM_TU.val_a[i] = sv_data_a[DATUM_TU.sym_ps->svid][i] Push the dereferenced DATUM_TU onto the stack. cmthc_assign() Function: Assign the value of the previous item on the stack from the value of the item before. The previous value must not be a NUMBER CONST or BLTIN (since these are not of the variable type) and must not be VAR (since that would constitute a reassignment) or UNDEF (since you must know which sv to assign to). Method: Pop last datum (d1) and one before (d2). The previous datum is the lvalue, the one before is the rvalue. The lvalue datum should be interpreted as a symbol (using its sym_ps member), the rvalue should be interpreted as an immediate value (using its val_a member). Check that the lvalue is UNASGN), otherwise issue 'Illegal Assignment' message. Then make the assignment to the sv_data_a array corresponding to the lvalue. The sv_data array to make the assignment to is given in the svid member of the lvalue (as a symbol). ie: memcpy( &sv_data_a(lvalue->sym_ps->svid), rvalue->val_a ) Assert that the lvalue is now a VAR. Push the lvalue onto the stack. cmthc_bltin() Function: Perform the operation of a CMATH built-in function on all the elements of the data array of a symbol. Method: Pop the stack. Interpret the current value of the program counter as a pointer to the address of a function. Dereference that function, with the poped datum's val_a as its argument. ie: (* (void(*)()) (*pc_p)) (DATUM_TU.val_a) The CMATH built-in functions return void - they modify the values of their input array's elements. Update the program counter and push the datum back onto the stack. NON-INSTRUCTIONS: The following functions are not CMATH instructions but are in this module so that they can manipulate or access variable static to this module. void cmth_copyProg( void** prog_p ) Function: Once a program is successfully constructed, it must be copied to the sampled variable for which it was written. The argument is the address where the pointer to the program should be written. Method: Malloc a program instruction vector and assign samp_var_a[svid][AP_PROG] from the pointer to it. Copy the constructed program to that vector. void cmth_initcode() Is described in the section INITIALIZATION ROUTINES below. void cmth_execute( INST_TPF* prog_pa ) Is described in the section PROGRAM EVALUATION below. CMATH OPERATOR FUNCTIONS (CMTH_CODE.C) CMATH operators are implemented as cmath instructions (rather than built-ins). In general the format is to pop the required operands of the stack, preform the function required on each element of the data array(s), leaving the result in one of the datum items poped, and push that datum back onto the stack. The prototype of all the operator functions is the same; void f(). The function for addition is defined here; cmthc_add() Function: Adds one datum's data to corresponding elements of another. Method: Pop rval, then lval of stack. For all elements, 0..SAMP_INDEX-1, add rhs val_a element to lhs val_a element, leaving result in lhs. Push lhs. The function for power is defined using the cmthb_pow built-in: cmthc_pow() Function: raise the lhs to the rhs. Method: Pop the lval of the stack, then the rval. Call the CMATH built-in cmthb_pow directly, giving arguments as the val_a args of the lhs and rhs. Leave the result in the lhs. CMATH BUILT-IN FUNCTIONS (CMTH_BLTIN.C) The CMATH built-in functions, except cmthb_pow(), all accept a single argument, being the address of an array of floats, which is the data operand of the built-in. The built-in function will perform its function on the elements in this array - so there is no need for a return argument. cmthb_pow will be called by the cmthc_pow instruction, which is coded by the '^' operand. The CMATH built-in functions will be implemented by calling the VAX C run-time library functions for each element of the input array. Error spotting (like range error) will be performed by checking the value of the VAX errno after the run-time function has evaluated each element. A separate function will perform that error check, and the actual evaluation will be performed by passing the run-time library function to the error checking function as an argument - this ensures that if errno really is bad, it's definitely because the argument to the C rtl function was in error. Note that, when the parser sees a symbol that it recognizes as a function name, like "sqrt", it codes an instruction that calls the built-in function by its actual name - thats why the names of the built-in functions in the prototypes below don't need to match the names of the functions as typed in the users input expression. For instance, the CMATH built-in function sqrt, is actually implemented by the CMATH built-in cmthb_sqrt(). One CMATH built-in is given for example: void cmthb_sqrt( VECT_TA x_pa ) { for ( i = 0 ; i < SAMP_INDEX ; i++ ) x_pa[i] = errcheck( (float) sqrt(x_pa[i]), "sqrt" ); } The static function errcheck() checks the value of errno before returning what it was passed as its first argument, which will be the result of the C rtl function on one element of the data. static float errcheck( float x, const char* func_name_c ) Function: Checks errno immediately after a function operation. Method: If errno equals EDOM there has been a domain error in the C rtl function call - issue 'Domain Error' msg. If errno equals ERANGE there has been a range error resulting from the computation - issue 'Range Error'. Return x argument. PROGRAM EVALUATION (CMTH.C) The programs constructed by the parser, for any SV defined by an expression, are assigned to SAMP_VAR(AP_PROG, svid) by the parser function cmth_parse(). These programs are executed, to evaluate a Sampled Variable, in any one of these situations: [1. At the end of the data acquisition cycle. In this case, CRR_DATA_ACQ will call CMTH.C/cmth_eval_all().] - Linda had some objection to this! 2. When the new panel button EVAL_ALL is pushed. This button will be added to the applications panel. In this case again, cmth_eval_all will be called. 3. When the new panel button EVAL_VAR is pushed. This button will be added to the applications panel. In this case an SV button should be pushed immediately afterward. With the SV number of the SV button pushed, the function CMTH.C/cmth_eval will be called. 4. Immediately after an expression has been successfully entered, if the global Boolean variable cmth_EvalOnEnter is TRUE. The new panel button EVAL_ON toggles cmth_EvalOnEnter and is on the applications panel. vmsstat_t cmth_Eval_all() Function and method: Loops through all 160 Sampled Variables in SAMP_VAR, in the top-down left-to-right order that they are labeled; and for each, if it was defined by an expression (SAMP_VAR(AP_PRIM == EXPR)) it calls cmth_Execute() with the address of the cmth program to execute. void cmth_eval( int svid ) Function: Executes the program for an expression defined SV. Method: Check that the given SV id is defined by an expression. If so, call cmth_execute() with the address of the program to execute for the given sv. Check the return code from cmth_execute() to check that a run-time error had not occurred. If it has, issue it with CMTH_ERR/cmth_msg(). Supply the expression that caused the error (SAMP_VAR(AP_EXPR, svid)) in the supplementary text to cmth_msg(). CMTH_CODE/vmsstat_t cmth_execute( INST_TPF* prog ) Function: Executes a CMATH program. Method: Initialize the program counter to the starting address of the program given and stack pointer to beginning of stack. Dereference the program counter (gets a pointer to an instruction), increment the program counter so that it points to the argument for the instruction, and dereference the pointer again to execute the instruction. Keep going until the program counter points to the STOP instruction. GARBAGE COLLECTION (CMTH_MSC.C) The Symbol Table The symbol table contains constants, built-in functions, variables and numbers: Constants and built-in functions should never be cleared out of the table since their value is constant. Variables of type VAR should not be cleared from the symbol table. This is because even if a sampled variable for which a symbol has been allocated is redefined, there may be another sampled variable which uses that symbol. In the case that the VAR has a givenName_c that should be set to NULL though. Numbers should be cleared from the symbol table. They are cleared when an expression containing that number is cleared. Therefore, when a NUMBER is recognized by the lexical analyzer in an expression, it creates it regardless of whether it was previously in the table and it assigns the svid member of the symbol to the svid of the sampled variable for whom's expression the NUMBER was created. This is so that when the sampled variable is redefined, the NUMBER symbol can be deleted. Programs and Expressions. The program and expression elements of a sampled variable definition (samp_var_a[*][AP_PROG and AP_EXPR] allocated for a sampled variable are freed when a sampled variable defined by expression is redefined. All of the above "frees" should be done when a sampled variable is redefined after having been defined as an expression. The freeing function shall be called at the same time that the existing correlation plots marks all of the data for the SV as invalid. That is, in CRR_INPUT_VALUE, the call to CRR_PUT_VAR ( VAR_INDEX,,, BAD_DATA,) shall be followed by: IF ( SAMP_VAR( AP_PRIM, VAR_INDEX ) .EQ. EXPR_SV ) THEN CALL CMTH_FREESV( VAR_INDEX ) vmsstat_t cmth_freeSV( int svid ) Function: Resets the symbol (VAR) for a sampled variable in the symbol table, and, if the sampled variable was defined by an expression, deletes any NUMBER symbols that were created for that expression. Method: Loops through the symbol table from end to beginning, until an item is found for which the type == BLTIN. During this loop, delete all NUMBER symbols whose svid member is that in the argument. Also, during this loop find any symbol that was created for this svid (check that no more than one VAR symbol was created for it - if so there was an internal error in creating symbols management, issue a "Multiple VAR symbol created error"). If a VAR symbol was created for the svid, and that symbol had a given name, free the memory for the given name and reset the given name to NULL. If the symbol was defined by an expression (samp_var_a[svid][AP_PRIM] == EXPR), free the memory for the expression (SAMP_VAR[svid][AP_EXPR]) and the program for the expression (samp_var_a[svid][AP_PROG]). INITIALIZATION ROUTINES (CMTH_INIT.C) DEFINITIONS TO INITIALIZE Some initialization must be done to load the symbol table with permanent symbols (CONSTands and BLTINs). Both constants and built-ins will be listed in two static arrays of structures, one for constants and one for built-ins. static struct { char* name_c; /* Name of constant. */ float val; /* Floating pt value of const. */ } Const_as[] = { "PI",3.1415....., "E",2.71... "DEG", 57. ..., /* Deg per radian */ ... 0,0 }; static struct { char* name_c; /* Name as typed by user. */ void (*func)(); /* Function doing op on vector. */ } Builtin_as[] = { "abs", cmthb_fabs, "sqrt", cmthb_sqrt, "sin", cmthb_sin, "cos", cmthb_cos, "log", cmthb_log "atan", cmthb_atan, ... 0,0 }; INITIALIZATION PROCEDURE At startup of crrutil, CRRINIT.FOR will call CMTH_INIT/cmth_init() to populate the symbol table Cmth_symList_ps with these symbols. vmsstat_t cmth_init() Function: Populates symbol table with permanent symbols (constants and builtins). Method: Loops through elements of arrays of predefined symbols, until element has 0 value. For each, calls cmth_installSym with arguments suitable for instantiating the symbol. Install constants first, then built-ins; this order is important because the routines that loop through all user defined symbols terminate when they see a symbol for a built-in. (CMTH_CODE.C) void cmth_initcode() Function: Prepares counters etc for program writing. Called by cmth_parse() immediately prior to parsing and program construction. Method: Resets the ptr that indexes the program array, to point to the first element of the program. PLOT LABELLING The function that labels plots will have to be extended to understand that Sampled Variables may know be defined by an expression as well as a device name. The primary difference is that the text of the plot should now be found by dereferencing the SAMP_VAR(AP_EXPR, svid). Currently, the same routine, AP_DEV_TO_STR constructs plot labels and button text. That routine should only be changed so as to trap the fact that the sv is defined by an expression, it should dereference the SAMP_VAR(AP_EXPR, svid) to get the text of the expression - and then construct text of < 40 characters. That deals with what goes on buttons. For plot labels themselves, the routine that constructs the alias for a sample variable (AP_GET_ALIAS) will return the whole expression, and then CRR_CUR_PLOT will use only that expression in labels for sampled variables defined by expression. ERROR HANDLING (CMTH_ERR.C) PARSE ERRORS Errors occurring during the parse of an expression require that a longjmp be issued. The setjmp, longjmp sequence is necessary because the function built by yacc to perform a parse, in this case cmth_yyparse(), is only able to call a function in the event of an error, it is not able to simply return with a bad status. Therefore, if an error occurs during a parse there is no other way of restarting the call stack from a known state. The function that a yacc built parser calls must be named [prefix]yyerror, where the prefix may be given, and be defined in CMTH.Y. CMTH_ERR/int cmth_yyerror( char* suppl_c ) Function: Provides the function that is called by a yacc built parser when it encounters a syntax error. Handles that error. Method: Call cmth_parseErr() with the syntax error function code, CRR_CMTHPARSE; and the supplementary text argument NULL. The parse errors shall be issued, and the longjump issued from a function in CMTH_ERR.C. CMTH_ERR/vmsstat_t cmth_parseErr(vmsstat_t errCode, char* suppl_c ) Function: This function is for handling errors that occurred during the parse only - it should not be used by routines called outside the call stack scope of cmth_parse(). Issue the error and perform a longjmp. Method: Issue the error and supplementary text by calling cmth_msg(). Issue a longjmp to Cmth_begin. GENERAL ERRORS All errors are actually issued with cmth_msg(). CMTH_ERR/void cmth_msg( vmsstat_t errCode, char* suppl_c ) Function and Method: Translates the errCode and cowrites it assuming no arguments; and if the supplementary text argument is non-NULL it cowrites that as well. FLOATING POINT EXCEPTION AND OTHER RUN-TIME ERRORS At the time that a CMATH program is run, the lib$sig_to_ret condition handler will be established in CMTH/cmth_Execute(). If then a run-time error occurs, most probably caused by a floating point exception, the execution will terminate and a bad status will be returned to CMTH/cmth_eval(). cmth_eval() will test that return status and if bad, call cmth_msg() to issue the returned error code. MISCELLANEOUS FUNCTIONS (CMTH_MSC.C) The following array of structures will relate SV numbers to their default labels: struct svidName { int id; char* name_c; } noshare SvidName_as[] { -1,"STV1",0,"STV2",16,"A1",18,"B1",20,"C1",15,"A2",17,"B2",19,"C2", ...160,"G32",0,NULL }; The order in which the button names and values appear in the array enforces the order in which sampled variables defined by expressions must reference other sampled variables in order to avoid forward or self reference. Hence, the ordering is depth-first, left-to-right. Note that order of the svids is not ascending; that's because SV's numbers are not ordered top-to-bottom left-to-right in the original correlation plots definition. BOOLEAN cmth_svFwdOfsv(const int objSvid, const int baseSvid) Function and Method: Determines whether the object svid in arg 1 would be a forward reference if it were used in an expression defined for the SV given in baseSvid. Treating the svidName_as as a sorted array then, this would be achieved by comparing the addresses of the svid members of the elements of svidName_as whose svids are given in the arguments. vmsstat_t cmth_svidToName(const int svid, char** name_c) Function: Takes a Sampled Variable id number and transposes it as a CP spreadsheet name. For instance, if 17 were the given value of svid, "B2" would be returned in name_c. Method: looks up the number in the table svidName_as. vmsstat_t cmth_nameToSvid( const char* name_c, int* svid_p) Function: Takes a Sampled Variable spreadsheet name and transposes it into an SV id number. Method: looks up the number in the table svidName_as. vmsstat_t cmth_dispExpr( int svid ) Function and Method: Displays the expression for a sampled variable in the message window. void* cmth_pcalloc( size_t nmemb, size_t size ) Function and Method: This is the memory allocation function for allocations done during the parsing process (not execution). These are handled specially because if the allocation is unsuccessful a longjmp must be issued so that the yacc built parser can reacquire control of the call stack. Perform a calloc and then perform a check to make sure memory was allocated (ptr != NULL). If it failed, call cmth_parseErr with an ERROR severity so that the parser can recover via the longjmp issued by cmth_parseErr. void* cmth_pcalloc( size_t nmemb, size_t size ) Function and Method: This is the memory allocation function for allocations done during the execution of CMATH programs. These are handled specially because if the allocation is unsuccessful a longjmp must be issued so that the SCP interface routine cmth_eval() can reacquire control of the call stack. Perform a calloc and then perform a check to make sure memory was allocated (ptr != NULL). If it failed, call cmth_MathErr with an ERROR severity so that the parser can recover via the longjmp issued by cmth_MathErr. BUTTONS As defined here there is a need for only 4 additional buttons to the correlation plots interface. These 4 will all appear on the Applications Panel. These 4 buttons, being specific to expression handling, will be handled by a new routine CMTH_BUT/cmth_but(). cmth_but() will declare the variable cmth_fn via a LIST_VAR8 to be equivalent to the SCP logical CMTHFUNC if ACNTRL(1) == ACNTRL_INIT, otherwise it will switch on the values of CMTHFUNC to perform the following functions: 1. "EVAL ALL" - Calls cmth_eval_all(). CMTHFUNC = 'EVAL ALL' 2. "EVAL VAR" - Results in the flag CMATH.H/cmth_EvalSvid being set to true. Then when a sampled variable button is pressed, CRR_INPUT_BUT will call cmth_eval( SV_NO ) to evaluate the button, rather than CRR_INPUT_VALUE. If the flag is already true it is set false. CMTHFUNC = 'EVAL VAR' 3. "DISP EXPR"- Results in the flag CMATH/cmth_DisplayExpr being set to true. Then when a sampled variable button is pressed, CRR_INPUT_BUT will call cmth_DispExpr( SV_NO ) to display the entire expression for an SV. If the flag is already true it is set false. CMTHFUNC = 'DISPEXPR' 4. "EVAL ON [ENTER|CMND]" - Toogles the global Boolean variable cmth_EvalOnEnter. When this variable is TRUE an SV's program is executed as soon as it is compiled. CMTHFUNC = 'EVAL ON ' CONSTANTS AND IMPLEMENTATION PARAMETERS (CMATH.H) NEW CP CONSTANTS AND PARAMETERS. NPROG = 1000 : So no program may include more than 2000 instructions. The ratio of instructions to arithmetic operations (on whole vectors) is about 3:1. NSTACK = 10 : Stack items. This means that there can be no more than 9 right-to-left associative operations in a row, and no more than 9 levels of parentheses. ADDITIONS TO EXISTING CP PARAMETER CLASSES. These will be defined in APDESC.TXT: AP_EXPR = 2 : Index into ap_descriptor.d[] which selects ap_descriptor.expr AP_PROG = 3 : Index into ap_descriptor.d[] which selects ap_descriptor.prog SOFTWARE PRODUCTION AND IMPLEMENTATION PLAN There follows a list of additions to the CMATH facility that maybe implemented in the second release (version 2). 1. Assignment of a variable name to a device name. It is proposed that in the first release, a user-assigned variable name can only be assigned to expressions, not to device-names. So the first tier of expressions (those referencing devices) will still use the default labels (eg A2) to identify devices. A candidate upgrade for version 2 will be allow SVs assigned from device names to be given user defined labels. ALTERNATIVE SPECIFICATIONS This section outlines alternative designs for some aspects of the preceding specification: AN ALTERNATIVE FOR STORING THE VALUE OF CONSTANTS. Constants will be held on the SLC db in new primary CNST, micro VX34. That primary will have the following definition: < :CNST: 166,0; :LABL: 1,1,1S4; ! Textual name of the constant, eg "Plank's Constant". :VALU: 2,1,1R4; ! Real value rounded (for VAX) to 6 decimal places. :DUNI: 3,1,1S4; ! Display units., eg "u-Joules/sec" :SYMB: 4,1,1S4; ! Optional: Constant's symbolic name if printable, eg "H". :FRML: 5,1,1S4; ! Optional: Free text for formula or derivation - ! used for help, may be null, eg "(sqrt(5)+1)/2" In the first instance, the following constants will be created: pi, phi, e, deg, c, h, plus those in slctxt:physical_const.h and machine_const.h. Then vmsstat_t cmth_init() is defined so: Function: Populates symbol table with permanent symbols (constants and builtins). Method: dbget all constant's SYMB and VALU secondaries (for name and value), and install each of them with cmth_installSym. Then install built-ins also with cmth_installSym from the array of predefined built-in symbols. It's important to install constants first, then built-ins since routines that loop through all user defined symbols terminate when they see a symbol for a built-in.