/* FILE: parser.c * PURPOSE: * Functions that implement a Predictive Parser with * Panic-Mode error recovery. * The grammar implemented is Program Grammar #2. * (CST 8152 - 98W - Assignment 6) * Grammar productions are shown in the ALGORITHM for each function. * Students should fill in a high-level description of each algorithm. * HISTORY: * Ian D. Allen idallen@freenet.carleton.ca */ //FIXME-- add system includes here //FIXME-- add your local includes here /* Static storage local to this parser: * - the look ahead token returned by the scanner * - a counter of statements parsed by the parser * - the name of the input stream (for error messages) * - the file descriptor of the output stream * - the name of the output stream */ static Token token; static long stmtcounter = 0; static char *pinfname = "UNKNOWN-IN"; static FILE *poutfd = NULL; static char *poutfname = "UNKNOWN-OUT"; /* Here are some macros that make the parser code nicer to read. * We cast the line number into a long int so that it has a known * width when passed to varargs functions (such as printf) on * different architectures. */ #define LOOKAHEAD token.type #define LEXEME token.lexeme #define LINENO (long int)token.lineno #define GET_LOOKAHEAD {token = scanner();} //FIXME-- Your prototypes for your static functions go here. /* FUNCTION: item_value * PURPOSE: * Return a printable version of whatever is in an item. * Must be able to return at least two different static strings, * so it can be safely used twice in an argument list. * ALGORITHM: * If item is an integer, * switch static buffers * safely print it into static buffer * return a pointer to the static buffer. * Otherwise just return the lexeme string. * * NOTE * This re-uses the same two static buffers to hold numbers, so * be careful not to call it in a situation where you need three. */ #define NUMBUFS 2 /* number of rotating static buffers */ #define NUMBUFMAX 80 /* larger than largest formatted number */ static char * item_value( Item *itemp /* IN: pointer to item */ ){ static char numbuffer[NUMBUFS][NUMBUFMAX]; static bufcount; char *ret = "--OOPS--"; switch(itemp->type){ case TT_UINT: /* keep rotating among static buffers */ bufcount = (bufcount+1) % NUMBUFS; sprintf(ret = numbuffer[bufcount], "%ld", itemp->u.uint); break; default: ret = (itemp->lexeme) ? itemp->lexeme : "[NULL POINTER]"; } return ret; } /* FUNCTION: copy_push_item * PURPOSE: * Evaluate a token by copying a Token structure to an Item and strdup() * any string(s), then push the resulting Item on the stack. * ALGORITHM: * Copy the Token structure to an Item structure. * Use mystrdup() to get private copies of any dynamic memory. * (Parser must use item_free() to free the memory later.) * Push the Item on the value stack. */ static void copy_push_item( Token *tokenp /* IN: pointer to token to copy */ ){ Item item = *tokenp; /* bulk copy the token to an item */ if( item.lexeme != NULL ) item.lexeme = mystrdup(item.lexeme); push(&item); } /* FUNCTION: item_free * PURPOSE: * Free any dynamic memory in use by this Item. * (Frees the memory obtained by copy_push_item().) * ALGORITHM: * Free any dynamic memory or strings used by this Item. * Set those freed pointers to NULL. */ static void item_free( Item *ip /* IN: pointer to Item whose memory needs freeing */ ){ if( ip->lexeme != NULL ) mem_free(ip->lexeme); ip->lexeme = NULL; /* ensure string is not re-used */ } /* FUNCTION: execute * PURPOSE: * Execute a semantic value stack operation. * ALGORITHM: * Select each case based on the operation being performed. * The number of operands popped off the stack depends on the operator. * Most cases can apply the second operand to the first and * store the result back in the first operand. * * The operands popped from the stack must either be freed or * pushed back so something else can free them later. * Anything not pushed back on the stack is freed. * * We usually pass the line number of the second operand back as the * line number of the result of the overall operation. */ static Boolean execute( TokenType optype, /* IN: the type of the token (operator) */ long int oplineno /* IN: the line number of the token */ ){ Item op1; /* first operand */ Item op2; /* second operand */ /* CHECK2: A macro to pop two operands and make sure they are integers. * The line number is the average of the line number of the two operands. */ #define CHECK2(a,b) {\ b = pop();\ a = pop();\ if( a.type != TT_UINT || b.type != TT_UINT ) {\ eprintf("File %s Line %ld: Non-numeric operand(s): "\ "%s '%s' and/or %s '%s'",\ pinfname,\ (long)(a.lineno+b.lineno)/2,\ token_name(a.type),\ item_value(&a),\ token_name(b.type),\ item_value(&b));\ item_free(&a);\ item_free(&b);\ return FALSE;\ }\ } /* PUSHFREE: A macro to push one operand and free the other. * The line number is the line number of the second operand. */ #define PUSHFREE(a,b) {\ a.lineno = b.lineno;\ push(&a);\ item_free(&b);\ } switch( optype ){ case ???: /* '*' -- multiply */ CHECK2(op1,op2); //FIXME-- insert the operand math here PUSHFREE(op1,op2); break; case ???: /* '/' -- divide */ CHECK2(op1,op2); //FIXME-- insert the operand math here PUSHFREE(op1,op2); break; case ???: /* '+' -- add */ CHECK2(op1,op2); //FIXME-- insert the operand math here PUSHFREE(op1,op2); break; case ???: /* '-' -- subtract */ CHECK2(op1,op2); //FIXME-- insert the operand math here PUSHFREE(op1,op2); break; case TT_UNARY: /* '-' -- unary minus (not a real token) */ //FIXME-- insert code for the whole unary minus {pop,negate,push} here break; case TT_EQUAL: /* '=' -- assignment */ /* No symbol table! * Assume we have a pushed identifier and pushed expression. */ op2 = pop(); op1 = pop(); eprintf("File %s Line %ld: Would put %s '%s' into %s '%s'\n", pinfname, oplineno, token_name(op2.type), item_value(&op2), token_name(op1.type), item_value(&op1)); item_free(&op1); item_free(&op2); break; default: /* detect programming errors in parser */ eprintf("File %s Line %ld: Unknown operator %d %s", pinfname, (long)oplineno, optype, token_name(optype)); abort(); /*NOTREACHED*/ } return TRUE; } /* FUNCTION: * PURPOSE: * A matching function (similar to match() in text figure 2.17). * Returns TRUE on success; FALSE on error. * ALGORITHM: * Check that the lookahead token type matches the TokenType argument. * If it doesn't, print an error message that includes the file name, * the line number of the token, the type of token that was expected, * the type of token that was found, and the lexeme that was found; * then return FALSE, indicating failure. * Load the next lookahead token and return TRUE if it matched. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize a FACTOR. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * factor := TT_STR | TT_ID | TT_UINT | '(' expr ')' | '-' factor * * Since we don't have a symbol table, push zero when we find * an identifier. * * Error messages contain the file name, the line number of the token, * the type(s) of token(s) that was/were expected, and the * type of token that was found; then, return FALSE, indicating failure. * Load the next lookahead token any time it is matched. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize a TERM. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * term := factor ( ('*'|'/') factor ) * * * No errors can be detected here. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize an EXPR. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * expr := term ( ('+'|'-') term ) * * * No errors can be detected here. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize an ASST. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * asst := TT_ID '=' expr * */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize a PRINT command. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * print := TT_PRINT expr ( ',' expr ) * * */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize a STMT. * Returns TRUE on success; FALSE on error. * ALGORITHM: * * stmt := print ';' | asst ';' | ';' * * This function will need to check the FIRST sets of its * productions, to know which production to expand. * * Additional actions to perform: * Save the line number and lexeme that starts the statement. * When the end of the STMT is found, print a message to the * parser output stream indicating the line number of the token * starting the statement, the number of the statement, and * the lexeme that started the statement. * * Write the code so that even if a parsing error occurs, the memory * used to save the lexeme starting the statement is still freed * before this function returns. Do this without duplicating code. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * A panic-mode function that skips to after the next semicolon. * It also frees anything left on the semantic value stack. * ALGORITHM: * Keep skipping and printing input tokens until we hit EOF * or a semicolon. Print the file name, line number, type, and lexeme * of the token to which we skipped. * Free anything left on the semantic value stack, printing any * items freed. * Advance to the token after the semicolon. */ //FIXME-- your code goes here /* FUNCTION: * PURPOSE: * Recognize a PROGRAM. * Returns the number of errors in the program. * ALGORITHM: * * program := stmt * TT_EOF * * This is the top-level, root parsing function. * Panic-mode error recovery is done here when an error is detected. * Error handling: * Every time a panic-mode error recovery is done, count it. * Return the count of errors when this function returns. */ //FIXME-- your code goes here /* FUNCTION: parser() * PURPOSE: * Parse an open input stream. * ALGORITHM: * Initialize the scanner. * Set the statement counter to zero. * Pre-load the first look-ahead token. * Call the root parsing function and receive the count of errors. * Terminate the scanner. * Print some statistics and return. */ void parser( FILE *infd, /* IN: open input file descriptor to read */ char *infname, /* IN: name of open input stream */ FILE *outfd, /* IN: open output file descriptor to write */ char *outfname /* IN: name of open output stream */ ){ int errcounter; /* receive count of errors in program */ /* Save static copies of initialization data for use by Parser. * We don't have to save a copy of the infd since only the * single scanner_init() call in this function uses it. */ poutfd = outfd; pinfname = infname; poutfname = outfname; fprintf(poutfd, "PARSER: Now parsing '%s' to '%s'\n", pinfname, poutfname); //FIXME-- your code goes here fprintf(poutfd, "PARSER: Parsed %ld statements from '%s' to '%s'.\n", stmtcounter, pinfname, poutfname); if( errcounter > 0 ) fprintf(poutfd, "PARSER: File %s had %d errors.\n", pinfname, errcounter); if( ! empty() ) fprintf(poutfd, "STACK: Stack not empty.\n"); /* Undo what was done during the Parser initialization. */ poutfd = NULL; pinfname = NULL; poutfname = NULL; }