154 lines
5.3 KiB
C
154 lines
5.3 KiB
C
/*** 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 */
|
||
}
|