/*** PARSER.C -- parsing routines ********************************************* * * Copyright (c) 1988-1989, Microsoft Corporation. All rights reserved. * * Purpose: * This module contains the NMAKE grammar parser. It parses input and uses the * getToken() routine to get the next token. * * Revision History: * 05-Apr-1989 SB made all funcs NEAR; Reqd to make all function calls NEAR * 17-Aug-1988 RB Clean up. * *******************************************************************************/ #include "nmake.h" #include "nmmsg.h" #include "proto.h" #include "globals.h" #include "grammar.h" #pragma hdrstop #include "table.h" /* ---------------------------------------------------------------------------- * macros that deal w/ the productions stack and the actions function table */ #define topStack() (stack[top]) #define popStack() (stack[top--]) #define pushStack(A) (stack[++top] = A) #define doAction(A) (*(actions[A & LOW_NIBBLE]))() /* ---------------------------------------------------------------------------- * parse() table-driven parser for makefile grammar * * arguments: init global boolean value -- TRUE if tools.ini is the * file being parsed * * actions: initializes the stack (by pushing the empty-stack symbol * and the start symbol) * keeps track of current line (because the lexer may have * read '\n' as a delimiter, and will thus be one line * ahead of the parser) * while the stack is not empty * if the top symbol on the stack is an action * do the action, popping the stack * if the symbol on top of the stack now is a token * if it's not the token we're expecting * syntax error, halt * else * pop token off the stack * if the top symbol on the stack is an action * do the action, popping the stack * reset curent line to lexer's current line * get another token (use the lookahead token * if it exists, and if it had caused the * lexer's line count to be incremented, * decrement our local count because we're * still parsing the preceding line) * else the symbol on top of the stack is a production * find next production to do in production table * (based on current input token and current * production on stack) * if the table entry is an error condition * print appropriate error message, halt * pop current production off stack * if the "next production" can be one of two * things, decide which one to use by peeking * at the next input token and looking in the * "useAlternate" decision table (using the last * production and next input token as indexes) * if the appropriate table entry is YES, * use the next larger production from the one * we found in the production table * push each symbol in the production on the stack * loop * * modifies: stack production stack, static to this module * top index of current symbol at top of stack * * Use extreme care in modifying this code or any of the tables associated * with it. The methods used to build the tables are described in detail * in grammar.h and table.h. This parser is based on the predictive parser * described on pages 186-191 of Aho & Ullman "Principles of Compiler Design." * I have modified it to use an extra symbol of lookahead to deal w/ an * ambiguous grammar and have added code to perform appropriate actions as * it parses the productions. */ void NEAR parse() { UCHAR stackTop, token, nextToken = 0; register unsigned n, i; firstToken = TRUE; /* global var */ pushStack(ACCEPT); /* init stack */ pushStack(START); currentLine = line; token = getToken(MAXBUF,START); /* get first token*/ while ((stackTop = topStack()) != ACCEPT) { if (ON(stackTop,ACTION_MASK)) doAction(popStack()); else if (ON(stackTop,TOKEN_MASK)) { if (stackTop != token) makeError(currentLine,SYNTAX+FATAL_ERR,buf); else { popStack(); #ifdef DEBUG_ALL printf ("DEBUG: parse 1: %d\n", line); #endif if (ON(topStack(),ACTION_MASK)) doAction(popStack()); #ifdef DEBUG_ALL printf ("DEBUG: parse 2: %d\n", line); #endif currentLine = line; if (nextToken) { /* if we already */ if (*buf == '\n') --currentLine; /* have a token, */ token = nextToken; /* use it . . . */ nextToken = 0; } else token = getToken(MAXBUF,topStack()); } } else { n = table[stackTop][token & LOW_NIBBLE]; #ifdef DEBUG_ALL printf ("DEBUG: parse 3: %x %d %x %x\n", n, stackTop, token & LOW_NIBBLE, token); #endif if (ON(n,ERROR_MASK)) { #ifdef DEBUG_ALL printf ("DEBUG: parse 4: %d %s\n", line, buf); #endif makeError(currentLine,n+FATAL_ERR,buf); } popStack(); if (ON(n,AMBIG_MASK)) { /* 2 possible prod*/ n &= LOW_NIBBLE; /* only use 4 bits*/ if (!nextToken) /* peek to decide */ nextToken = getToken(MAXBUF,stackTop); n += (useAlternate[stackTop][nextToken & LOW_NIBBLE]); } for (i = productions[n][0]; i; --i) /* put production */ pushStack(productions[n][i]); /* on stack */ } /* 1st elt in prod*/ } /* is its length */ popStack(); /* pop the ACCEPT off the stack */ }