// // Copyright (c) Microsoft Corporation 1995 // // parser.c // // This file contains the parsing functions. // // Conventions: // // where Foo is a non-terminal // { Bar } where there are 0 or more occurrences of Bar // "x" where x is the literal character/string // CAPS where CAPS is a token type // // The grammar is as follows: // // ::= { } // ::= proc IDENT { } endproc // ::= IDENT [ = ] // ::= integer | string | boolean // // ::= { } // ::= | | | // | | | // | | | // // // ::= halt // ::= waitfor [ , matchcase ] // [ then IDENT // { , [ , matchcase ] then IDENT } ] // [ until ] // ::= transmit [ , raw ] // ::= delay // ::= set // ::= IDENT = // ::= IDENT : // ::= goto IDENT // ::= while do endwhile // ::= if then endif // // ::= ipaddr | port | // screen // ::= databits | parity | // stopbits // ::= keyboard // // ::= { , } // ::= { or } // ::= { and } // ::= | // ::= <= | != | < | >= | > | == // ::= { (+|-) } // ::= { (*|/) } // ::= - | INT | "(" ")" | IDENT | // STRING | | "TRUE" | "FALSE" | // ! // ::= getip [ ] // // ::= 5 | 6 | 7 | 8 // ::= none | odd | even | mark | space // ::= 1 | 2 // ::= on | off // // // History: // 05-20-95 ScottH Created // #include "proj.h" #include "rcids.h" RES PRIVATE StmtBlock_Parse(HPA hpa, PSCANNER pscanner, PSYMTAB pstProc, SYM symEnd); /*---------------------------------------------------------- Purpose: Parses the next token as an identifier. If the token is an identifier, it is returned in *pptok and the function returns RES_OK. Otherwise, *pptok is NULL and an error is returned. Returns: RES_OK RES_E_IDENTMISSING Cond: The caller must destroy *pptok if RES_OK is returned. */ RES PRIVATE Ident_Parse( PSCANNER pscanner, PTOK * pptok) { RES res; PTOK ptok; *pptok = NULL; res = Scanner_GetToken(pscanner, &ptok); if (RSUCCEEDED(res)) { if (SYM_IDENT == Tok_GetSym(ptok)) { res = RES_OK; *pptok = ptok; } else { res = Scanner_AddError(pscanner, NULL, RES_E_IDENTMISSING); Tok_Delete(ptok); } } return res; } /*---------------------------------------------------------- Purpose: Adds an identifier to the symbol table. If the identifier has already been defined in this scope, this function adds the error to the error list, and returns the error value. Returns: RES_OK RES_E_REDEFINED RES_E_OUTOFMEMORY Cond: -- */ RES PRIVATE Ident_Add( LPCSTR pszIdent, DATATYPE dt, PTOK ptok, PSCANNER pscanner, PSYMTAB pst) { RES res; // Is this identifier unique? if (RES_OK == Symtab_FindEntry(pst, pszIdent, STFF_DEFAULT, NULL, NULL)) { // No; we have a redefinition res = Scanner_AddError(pscanner, ptok, RES_E_REDEFINED); } else { // Yes; add to symbol table PSTE pste; res = STE_Create(&pste, pszIdent, dt); if (RSUCCEEDED(res)) { res = Symtab_InsertEntry(pst, pste); } } return res; } // // Exprs // RES PRIVATE Expr_Parse(PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst); /*---------------------------------------------------------- Purpose: Look ahead to see if the next token indicates an expression of some kind. Returns: TRUE if the next token is a leader to an expression Cond: -- */ BOOL PRIVATE IsExprSneakPeek( PSCANNER pscanner) { BOOL bRet; SYM sym; Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_MINUS: case SYM_NOT: case SYM_INT_LITERAL: case SYM_LPAREN: case SYM_STRING_LITERAL: case SYM_GETIP: case SYM_IDENT: case SYM_TRUE: case SYM_FALSE: bRet = TRUE; break; default: bRet = FALSE; break; } return bRet; } /*---------------------------------------------------------- Purpose: Parses a factor expression. Grammar is: ::= - | INT | "(" ")" | IDENT | STRING | | TRUE | FALSE | ! ::= getip [ ] Returns: RES_OK Cond: -- */ RES PRIVATE FactorExpr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; PTOK ptok; DWORD iLine = Scanner_GetLine(pscanner); DBG_ENTER(FactorExpr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; if (RES_OK == Scanner_CondReadToken(pscanner, SYM_MINUS, NULL)) { // Negation res = FactorExpr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = UnOpExpr_New(ppexpr, UOT_NEG, pexpr, iLine); if (RFAILED(res)) Expr_Delete(pexpr); } } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_NOT, NULL)) { // One's complement res = FactorExpr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = UnOpExpr_New(ppexpr, UOT_NOT, pexpr, iLine); if (RFAILED(res)) Expr_Delete(pexpr); } } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_LPAREN, NULL)) { // "(" res = Expr_Parse(ppexpr, pscanner, pst); if (RSUCCEEDED(res)) { if (RES_OK != Scanner_ReadToken(pscanner, SYM_RPAREN)) { Expr_Delete(*ppexpr); res = Scanner_AddError(pscanner, NULL, RES_E_RPARENMISSING); } } } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_INT_LITERAL, &ptok)) { // Integer literal res = IntExpr_New(ppexpr, TokInt_GetVal(ptok), iLine); Tok_Delete(ptok); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_STRING_LITERAL, &ptok)) { res = StrExpr_New(ppexpr, TokSz_GetSz(ptok), iLine); Tok_Delete(ptok); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_TRUE, &ptok)) { res = BoolExpr_New(ppexpr, TRUE, iLine); Tok_Delete(ptok); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_FALSE, &ptok)) { res = BoolExpr_New(ppexpr, FALSE, iLine); Tok_Delete(ptok); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_IDENT, &ptok)) { res = VarExpr_New(ppexpr, Tok_GetLexeme(ptok), iLine); Tok_Delete(ptok); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_GETIP, NULL)) { // 'getip' // Parse optional nth parameter if (IsExprSneakPeek(pscanner)) { res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = UnOpExpr_New(ppexpr, UOT_GETIP, pexpr, iLine); } } else { // Default to 1st IP address res = IntExpr_New(&pexpr, 1, iLine); if (RSUCCEEDED(res)) res = UnOpExpr_New(ppexpr, UOT_GETIP, pexpr, iLine); } } else res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); DBG_EXIT_RES(FactorExpr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses a term expression. Grammar is: ::= { (*|/) } Returns: RES_OK Cond: -- */ RES PRIVATE TermExpr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr1; DBG_ENTER(TermExpr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; // Parse factor expression res = FactorExpr_Parse(&pexpr1, pscanner, pst); if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); PEXPR pexprTerm = pexpr1; SYM sym; // Parse optional factor operator Scanner_Peek(pscanner, &sym); while (SYM_MULT == sym || SYM_DIV == sym) { PEXPR pexpr2; Scanner_ReadToken(pscanner, sym); res = FactorExpr_Parse(&pexpr2, pscanner, pst); if (RSUCCEEDED(res)) { BINOPTYPE binoptype = sym - SYM_PLUS + BOT_PLUS; res = BinOpExpr_New(&pexprTerm, binoptype, pexpr1, pexpr2, iLine); if (RFAILED(res)) { Expr_Delete(pexpr2); break; } } else break; pexpr1 = pexprTerm; Scanner_Peek(pscanner, &sym); } if (RFAILED(res)) { Expr_Delete(pexpr1); // pexpr1 by design pexprTerm = NULL; // pexprTerm by design } *ppexpr = pexprTerm; } DBG_EXIT_RES(TermExpr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses a sum expression. Grammar is: ::= { (+|-) } Returns: RES_OK Cond: -- */ RES PRIVATE SumExpr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr1; DBG_ENTER(SumExpr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; // Parse term expression res = TermExpr_Parse(&pexpr1, pscanner, pst); if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); PEXPR pexprSum = pexpr1; SYM sym; // Parse optional sum operator Scanner_Peek(pscanner, &sym); while (SYM_PLUS == sym || SYM_MINUS == sym) { PEXPR pexpr2; Scanner_ReadToken(pscanner, sym); res = TermExpr_Parse(&pexpr2, pscanner, pst); if (RSUCCEEDED(res)) { BINOPTYPE binoptype = sym - SYM_PLUS + BOT_PLUS; res = BinOpExpr_New(&pexprSum, binoptype, pexpr1, pexpr2, iLine); if (RFAILED(res)) { Expr_Delete(pexpr2); break; } } else break; pexpr1 = pexprSum; Scanner_Peek(pscanner, &sym); } if (RFAILED(res)) { Expr_Delete(pexpr1); pexprSum = NULL; } *ppexpr = pexprSum; } DBG_EXIT_RES(SumExpr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses a test expression. Grammar is: ::= | ::= <= | != | < | >= | > | == Returns: RES_OK Cond: -- */ RES PRIVATE TestExpr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; DBG_ENTER(TestExpr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; // Parse sum expression res = SumExpr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); PEXPR pexpr2; SYM sym; // Parse optional relational operator Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_LEQ: case SYM_NEQ: case SYM_LT: case SYM_GEQ: case SYM_GT: case SYM_EQ: Scanner_ReadToken(pscanner, sym); res = SumExpr_Parse(&pexpr2, pscanner, pst); if (RSUCCEEDED(res)) { BINOPTYPE binoptype = sym - SYM_LEQ + BOT_LEQ; res = BinOpExpr_New(ppexpr, binoptype, pexpr, pexpr2, iLine); if (RFAILED(res)) Expr_Delete(pexpr2); } break; default: *ppexpr = pexpr; break; } if (RFAILED(res)) Expr_Delete(pexpr); } DBG_EXIT_RES(TestExpr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses a conjunction expression. Grammar is: ::= { and } Returns: RES_OK Cond: -- */ RES PRIVATE ConjExpr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr1; DBG_ENTER(ConjExpr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; // Parse test expression res = TestExpr_Parse(&pexpr1, pscanner, pst); if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); PEXPR pexprConj = pexpr1; SYM sym; Scanner_Peek(pscanner, &sym); while (SYM_AND == sym) { PEXPR pexpr2; Scanner_ReadToken(pscanner, sym); res = TestExpr_Parse(&pexpr2, pscanner, pst); if (RSUCCEEDED(res)) { res = BinOpExpr_New(&pexprConj, BOT_AND, pexpr1, pexpr2, iLine); if (RFAILED(res)) { Expr_Delete(pexpr2); break; } } else break; pexpr1 = pexprConj; Scanner_Peek(pscanner, &sym); } if (RFAILED(res)) { Expr_Delete(pexpr1); pexprConj = NULL; } *ppexpr = pexprConj; } DBG_EXIT_RES(ConjExpr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses an expression. Grammar is: ::= { or } Returns: RES_OK Cond: -- */ RES PRIVATE Expr_Parse( PEXPR * ppexpr, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr1; DBG_ENTER(Expr_Parse); ASSERT(ppexpr); ASSERT(pscanner); *ppexpr = NULL; // Parse conjunction expression res = ConjExpr_Parse(&pexpr1, pscanner, pst); if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); PEXPR pexprDisj = pexpr1; SYM sym; // Parse optional 'or' Scanner_Peek(pscanner, &sym); while (SYM_OR == sym) { PEXPR pexpr2; Scanner_ReadToken(pscanner, sym); res = ConjExpr_Parse(&pexpr2, pscanner, pst); if (RSUCCEEDED(res)) { res = BinOpExpr_New(&pexprDisj, BOT_OR, pexpr1, pexpr2, iLine); if (RFAILED(res)) { Expr_Delete(pexpr2); break; } } else break; pexpr1 = pexprDisj; Scanner_Peek(pscanner, &sym); } if (RFAILED(res)) { Expr_Delete(pexpr1); pexprDisj = NULL; } *ppexpr = pexprDisj; } DBG_EXIT_RES(Expr_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'set port' statement Grammar is: ::= databits | parity | stopbits ::= 5 | 6 | 7 | 8 ::= none | odd | even | mark | space ::= 1 | 2 Returns: RES_OK Cond: -- */ RES PRIVATE PortData_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine = Scanner_GetLine(pscanner); PORTSTATE ps; PTOK ptok; DBG_ENTER(PortData_Parse); ASSERT(ppstmt); ASSERT(pscanner); // Parse 'databits' if (RES_OK == Scanner_CondReadToken(pscanner, SYM_DATABITS, NULL)) { ps.dwFlags = SPF_DATABITS; // Parse 5 | 6 | 7 | 8 res = Scanner_GetToken(pscanner, &ptok); if (RSUCCEEDED(res)) { if (TT_INT == Tok_GetType(ptok)) { DWORD dwVal = TokInt_GetVal(ptok); if (InRange(dwVal, 5, 8)) { // Create object ps.nDatabits = LOBYTE(LOWORD(dwVal)); res = SetPortStmt_New(ppstmt, &ps, iLine); } else res = Scanner_AddError(pscanner, ptok, RES_E_INVALIDRANGE); } else res = Scanner_AddError(pscanner, ptok, RES_E_SYNTAXERROR); Tok_Delete(ptok); } else res = Scanner_AddError(pscanner, ptok, RES_E_INTMISSING); } // Parse 'parity' else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_PARITY, NULL)) { SYM sym; ps.dwFlags = SPF_PARITY; res = RES_OK; // assume success Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_NONE: ps.nParity = NOPARITY; break; case SYM_ODD: ps.nParity = ODDPARITY; break; case SYM_EVEN: ps.nParity = EVENPARITY; break; case SYM_MARK: ps.nParity = MARKPARITY; break; case SYM_SPACE: ps.nParity = SPACEPARITY; break; default: res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); break; } if (RES_OK == res) { res = SetPortStmt_New(ppstmt, &ps, iLine); // eat token Scanner_GetToken(pscanner, &ptok); Tok_Delete(ptok); } } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_STOPBITS, NULL)) { PTOK ptok; ps.dwFlags = SPF_STOPBITS; // Parse 1 | 2 res = Scanner_GetToken(pscanner, &ptok); if (RSUCCEEDED(res)) { if (TT_INT == Tok_GetType(ptok)) { DWORD dwVal = TokInt_GetVal(ptok); if (InRange(dwVal, 1, 2)) { // Create object ps.nStopbits = LOBYTE(LOWORD(dwVal)); res = SetPortStmt_New(ppstmt, &ps, iLine); } else res = Scanner_AddError(pscanner, ptok, RES_E_INVALIDRANGE); } else res = Scanner_AddError(pscanner, ptok, RES_E_SYNTAXERROR); Tok_Delete(ptok); } else res = Scanner_AddError(pscanner, ptok, RES_E_INTMISSING); } else res = Scanner_AddError(pscanner, NULL, RES_E_INVALIDPORTPARAM); DBG_EXIT_RES(PortData_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'set screen' statement Grammar is: ::= keyboard ::= on | off Returns: RES_OK Cond: -- */ RES PRIVATE ScreenSet_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine = Scanner_GetLine(pscanner); SCREENSET ss; PTOK ptok; DBG_ENTER(ScreenSet_Parse); ASSERT(ppstmt); ASSERT(pscanner); // Parse 'keyboard' if (RES_OK == Scanner_CondReadToken(pscanner, SYM_KEYBRD, NULL)) { SYM sym; ss.dwFlags = SPF_KEYBRD; res = RES_OK; // assume success Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_ON: ss.fKBOn = TRUE; break; case SYM_OFF: ss.fKBOn = FALSE; break; default: res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); break; } if (RES_OK == res) { res = SetScreenStmt_New(ppstmt, &ss, iLine); // eat token Scanner_GetToken(pscanner, &ptok); Tok_Delete(ptok); } } else res = Scanner_AddError(pscanner, NULL, RES_E_INVALIDSCRNPARAM); DBG_EXIT_RES(ScreenSet_Parse, res); return res; } // // Stmt // /*---------------------------------------------------------- Purpose: Parse the case-portion of the 'waitfor' statement. This portion is: [ , matchcase ] [ then IDENT ] and: [ , matchcase ] then IDENT Set bThenOptional to TRUE to parse the first case, FALSE to parse the second case. Returns: RES_OK RES_FALSE (if bThenOptional and "[ then IDENT ]" was not parsed) Cond: -- */ RES PUBLIC WaitforStmt_ParseCase( HSA hsa, PSCANNER pscanner, PSYMTAB pst, BOOL bThenOptional) { RES res; PEXPR pexpr; // Parse string expression res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { BOOL bParseThen; DWORD dwFlags = WCF_DEFAULT; // Parse optional ", matchcase" if (RES_OK == Scanner_CondReadToken(pscanner, SYM_COMMA, NULL)) { if (RES_OK == Scanner_ReadToken(pscanner, SYM_MATCHCASE)) SetFlag(dwFlags, WCF_MATCHCASE); else res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } // Is 'then IDENT' optional? if (bThenOptional) { // Yes; determine whether to parse it SYM sym; Scanner_Peek(pscanner, &sym); bParseThen = (SYM_THEN == sym); if (!bParseThen) res = RES_FALSE; } else { // No; we don't have a choice, parse it bParseThen = TRUE; } if (bParseThen) { res = Scanner_ReadToken(pscanner, SYM_THEN); if (RSUCCEEDED(res)) { PTOK ptok; // Parse identifier res = Ident_Parse(pscanner, &ptok); if (RSUCCEEDED(res)) { // (Wait until typechecking phase to check for // existence of identifier) LPSTR pszIdent = Tok_GetLexeme(ptok); // Add this case to the list res = Waitcase_Add(hsa, pexpr, pszIdent, dwFlags); Tok_Delete(ptok); } } else res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } else { // Add this case to the list res = Waitcase_Add(hsa, pexpr, NULL, dwFlags); } } return res; } /*---------------------------------------------------------- Purpose: Parses the 'waitfor' statement Grammar is: ::= waitfor [ , matchcase ] [ then IDENT { , [ , matchcase ] then IDENT } ] [ until ] Returns: RES_OK Cond: -- */ RES PRIVATE WaitforStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine; HSA hsa; DBG_ENTER(WaitforStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); *ppstmt = NULL; // Parse 'waitfor' res = Scanner_ReadToken(pscanner, SYM_WAITFOR); ASSERT(RES_OK == res); res = Waitcase_Create(&hsa); if (RSUCCEEDED(res)) { // Parse [ , matchcase ] [ then IDENT { , [ , matchcase ] then IDENT } ] // (Note we explicitly check for RES_OK only) res = WaitforStmt_ParseCase(hsa, pscanner, pst, TRUE); if (RES_OK == res) { // Parse { , then IDENT } while (RES_OK == Scanner_CondReadToken(pscanner, SYM_COMMA, NULL)) { res = WaitforStmt_ParseCase(hsa, pscanner, pst, FALSE); if (RFAILED(res)) break; } } if (RSUCCEEDED(res)) { PEXPR pexprUntil = NULL; // Parse optional 'until ' if (RES_OK == Scanner_CondReadToken(pscanner, SYM_UNTIL, NULL)) { res = Expr_Parse(&pexprUntil, pscanner, pst); } if (RSUCCEEDED(res)) { // Create object res = WaitforStmt_New(ppstmt, hsa, pexprUntil, iLine); } } if (RFAILED(res)) Waitcase_Destroy(hsa); } DBG_EXIT_RES(WaitforStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'transmit' statement Grammar is: ::= transmit [ , raw ] Returns: RES_OK Cond: -- */ RES PRIVATE TransmitStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; DWORD iLine; DWORD dwFlags = TSF_DEFAULT; DBG_ENTER(TransmitStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); *ppstmt = NULL; // Parse 'transmit' res = Scanner_ReadToken(pscanner, SYM_TRANSMIT); ASSERT(RES_OK == res); // Parse string expression res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { // Parse optional ", raw" parameter if (RES_OK == Scanner_CondReadToken(pscanner, SYM_COMMA, NULL)) { if (RSUCCEEDED(Scanner_ReadToken(pscanner, SYM_RAW))) SetFlag(dwFlags, TSF_RAW); else res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } if (RSUCCEEDED(res)) { // Create object res = TransmitStmt_New(ppstmt, pexpr, dwFlags, iLine); } } else res = Scanner_AddError(pscanner, NULL, RES_E_STRINGMISSING); DBG_EXIT_RES(TransmitStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'delay' statement Grammar is: ::= delay Returns: RES_OK Cond: -- */ RES PRIVATE DelayStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; DWORD iLine = Scanner_GetLine(pscanner); DBG_ENTER(DelayStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); *ppstmt = NULL; // Parse 'delay' res = Scanner_ReadToken(pscanner, SYM_DELAY); ASSERT(RES_OK == res); // Parse expression res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = DelayStmt_New(ppstmt, pexpr, iLine); } DBG_EXIT_RES(DelayStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'while' statement Grammar is: ::= while do endwhile Returns: RES_OK Cond: -- */ RES PRIVATE WhileStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; DWORD iLine; HPA hpa; DBG_ENTER(WhileStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); *ppstmt = NULL; if (PACreate(&hpa, 8)) { // Parse 'while' res = Scanner_ReadToken(pscanner, SYM_WHILE); ASSERT(RES_OK == res); // Parse res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { // Parse 'do' res = Scanner_ReadToken(pscanner, SYM_DO); if (RSUCCEEDED(res)) { char szTop[MAX_BUF_KEYWORD]; char szEnd[MAX_BUF_KEYWORD]; // Generate unique label names res = Symtab_NewLabel(pst, szTop); if (RSUCCEEDED(res)) { res = Symtab_NewLabel(pst, szEnd); if (RSUCCEEDED(res)) { // Parse statement block res = StmtBlock_Parse(hpa, pscanner, pst, SYM_ENDWHILE); if (RSUCCEEDED(res)) { PSTMT pstmtT; // Add a goto statement to loop to the top again res = GotoStmt_New(&pstmtT, szTop, Scanner_GetLine(pscanner)); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pstmtT)) res = RES_E_OUTOFMEMORY; else { // Create object res = WhileStmt_New(ppstmt, pexpr, hpa, szTop, szEnd, iLine); } } } } } } else res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } } else res = RES_E_OUTOFMEMORY; DBG_EXIT_RES(WhileStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'if' statement Grammar is: ::= if then endif Returns: RES_OK Cond: -- */ RES PRIVATE IfStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; PEXPR pexpr; DWORD iLine; HPA hpa; DBG_ENTER(IfStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); *ppstmt = NULL; if (PACreate(&hpa, 8)) { // Parse 'if' res = Scanner_ReadToken(pscanner, SYM_IF); ASSERT(RES_OK == res); // Parse res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { // Parse 'then' res = Scanner_ReadToken(pscanner, SYM_THEN); if (RFAILED(res)) res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } } else res = RES_E_OUTOFMEMORY; if (RSUCCEEDED(res)) { char szElse[MAX_BUF_KEYWORD]; char szEnd[MAX_BUF_KEYWORD]; // Generate unique label names res = Symtab_NewLabel(pst, szElse); if (RSUCCEEDED(res)) { res = Symtab_NewLabel(pst, szEnd); if (RSUCCEEDED(res)) { // Parse statement block for the 'then' block res = StmtBlock_Parse(hpa, pscanner, pst, SYM_ENDIF); if (RSUCCEEDED(res)) { // Create object res = IfStmt_New(ppstmt, pexpr, hpa, szElse, szEnd, iLine); } } } } DBG_EXIT_RES(IfStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'halt' statement Grammar is: ::= halt Returns: RES_OK Cond: -- */ RES PRIVATE HaltStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine = Scanner_GetLine(pscanner); DBG_ENTER(HaltStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); *ppstmt = NULL; // Parse 'halt' res = Scanner_ReadToken(pscanner, SYM_HALT); ASSERT(RES_OK == res); // Create object res = HaltStmt_New(ppstmt, iLine); DBG_EXIT_RES(HaltStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the assignment statement Grammar is: ::= IDENT = Returns: RES_OK Cond: -- */ RES PRIVATE AssignStmt_Parse( PSTMT * ppstmt, PTOK ptok, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine; PEXPR pexpr; DBG_ENTER(AssignStmt_Parse); ASSERT(ppstmt); ASSERT(ptok); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); // (We already have the IDENT in the ptok passed in. We // also already parsed the '='. Skip parsing these.) // Parse res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { // (Wait until typechecking phase to check for existence // of identifier) LPSTR pszIdent = Tok_GetLexeme(ptok); // Create object res = AssignStmt_New(ppstmt, pszIdent, pexpr, iLine); } DBG_EXIT_RES(AssignStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the label statement Grammar is: ::= IDENT : Returns: RES_OK Cond: -- */ RES PRIVATE LabelStmt_Parse( PSTMT * ppstmt, PTOK ptok, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine; LPSTR pszIdent; DBG_ENTER(LabelStmt_Parse); ASSERT(ppstmt); ASSERT(ptok); ASSERT(pscanner); ASSERT(pst); iLine = Scanner_GetLine(pscanner); pszIdent = Tok_GetLexeme(ptok); res = Ident_Add(pszIdent, DATA_LABEL, ptok, pscanner, pst); if (RSUCCEEDED(res)) { // Create label object res = LabelStmt_New(ppstmt, pszIdent, iLine); } DBG_EXIT_RES(LabelStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'goto' statement Grammar is: ::= goto IDENT Returns: RES_OK Cond: -- */ RES PRIVATE GotoStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DWORD iLine = Scanner_GetLine(pscanner); PTOK ptok; DBG_ENTER(GotoStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); // Parse 'goto' res = Scanner_ReadToken(pscanner, SYM_GOTO); ASSERT(RES_OK == res); // Parse identifier res = Ident_Parse(pscanner, &ptok); if (RSUCCEEDED(res)) { // (Wait until typechecking phase to check for existence // of identifier) LPSTR pszIdent = Tok_GetLexeme(ptok); // Create object res = GotoStmt_New(ppstmt, pszIdent, iLine); Tok_Delete(ptok); } DBG_EXIT_RES(GotoStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses the 'set' statement Grammar is: ::= set ::= ipaddr | port | screen Returns: RES_OK Cond: -- */ RES PRIVATE SetStmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; DBG_ENTER(SetStmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); *ppstmt = NULL; // Parse 'set' res = Scanner_ReadToken(pscanner, SYM_SET); ASSERT(RES_OK == res); // Parse set parameter if (RES_OK == Scanner_CondReadToken(pscanner, SYM_IPADDR, NULL)) { // Parse PEXPR pexpr; DWORD iLine = Scanner_GetLine(pscanner); res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = SetIPStmt_New(ppstmt, pexpr, iLine); } } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_PORT, NULL)) { res = PortData_Parse(ppstmt, pscanner, pst); } else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_SCREEN, NULL)) { res = ScreenSet_Parse(ppstmt, pscanner, pst); } else { res = Scanner_AddError(pscanner, NULL, RES_E_INVALIDSETPARAM); } DBG_EXIT_RES(SetStmt_Parse, res); return res; } /*---------------------------------------------------------- Purpose: Parses a statement. Grammar is: ::= | | | | | | | | | Returns: RES_OK Cond: -- */ RES PRIVATE Stmt_Parse( PSTMT * ppstmt, PSCANNER pscanner, PSYMTAB pst) { RES res; SYM sym; PTOK ptok; DBG_ENTER(Stmt_Parse); ASSERT(ppstmt); ASSERT(pscanner); ASSERT(pst); *ppstmt = NULL; Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_WHILE: res = WhileStmt_Parse(ppstmt, pscanner, pst); break; case SYM_IF: res = IfStmt_Parse(ppstmt, pscanner, pst); break; case SYM_WAITFOR: res = WaitforStmt_Parse(ppstmt, pscanner, pst); break; case SYM_TRANSMIT: res = TransmitStmt_Parse(ppstmt, pscanner, pst); break; case SYM_DELAY: res = DelayStmt_Parse(ppstmt, pscanner, pst); break; case SYM_HALT: res = HaltStmt_Parse(ppstmt, pscanner, pst); break; case SYM_SET: res = SetStmt_Parse(ppstmt, pscanner, pst); break; case SYM_IDENT: // This can be a label or an assignment res = Scanner_GetToken(pscanner, &ptok); ASSERT(RES_OK == res); if (RSUCCEEDED(res)) { // Is this a label? if (RES_OK == Scanner_CondReadToken(pscanner, SYM_COLON, NULL)) { // Yes res = LabelStmt_Parse(ppstmt, ptok, pscanner, pst); } // Is this an assignment? else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_ASSIGN, NULL)) { // Yes res = AssignStmt_Parse(ppstmt, ptok, pscanner, pst); } else { res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); } Tok_Delete(ptok); } break; case SYM_GOTO: res = GotoStmt_Parse(ppstmt, pscanner, pst); break; case SYM_EOF: res = Scanner_AddError(pscanner, NULL, RES_E_EOFUNEXPECTED); break; default: res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); break; } DBG_EXIT_RES(Stmt_Parse, res); return res; } // // ProcDecl // /*---------------------------------------------------------- Purpose: Parse the variable declarations for the proc decl. Grammar is: ::= IDENT [ = ] ::= integer | string | boolean Returns: RES_OK Cond: -- */ RES PRIVATE ProcDecl_ParseVarDecl( HPA hpa, PSCANNER pscanner, PSYMTAB pst) { RES res = RES_OK; PTOK ptok; DATATYPE dt; // Parse the variable decl block while (RES_OK == res) { SYM sym; Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_BOOLEAN: case SYM_STRING: case SYM_INTEGER: if (RES_OK == Scanner_CondReadToken(pscanner, SYM_INTEGER, NULL)) dt = DATA_INT; else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_STRING, NULL)) dt = DATA_STRING; else if (RES_OK == Scanner_CondReadToken(pscanner, SYM_BOOLEAN, NULL)) dt = DATA_BOOL; else ASSERT(0); res = Ident_Parse(pscanner, &ptok); if (RSUCCEEDED(res)) { LPSTR pszIdent = Tok_GetLexeme(ptok); res = Ident_Add(pszIdent, dt, ptok, pscanner, pst); // Parse optional '= ' if (RES_OK == Scanner_CondReadToken(pscanner, SYM_ASSIGN, NULL)) { PEXPR pexpr; PSTMT pstmt; DWORD iLine = Scanner_GetLine(pscanner); res = Expr_Parse(&pexpr, pscanner, pst); if (RSUCCEEDED(res)) { res = AssignStmt_New(&pstmt, pszIdent, pexpr, iLine); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pstmt)) res = RES_E_OUTOFMEMORY; } } } Tok_Delete(ptok); } break; default: // Continue on with further parsing res = RES_FALSE; break; } } return res; } /*---------------------------------------------------------- Purpose: Parse a statement block Returns: RES_OK Cond: -- */ RES PRIVATE StmtBlock_Parse( HPA hpa, PSCANNER pscanner, PSYMTAB pstProc, SYM symEnd) { RES res = RES_OK; // Parse the statement block while (RES_OK == res) { SYM sym; PSTMT pstmt; Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_EOF: res = Scanner_AddError(pscanner, NULL, RES_E_EOFUNEXPECTED); break; default: // Is this the end of the block? if (symEnd == sym) { // Yes Scanner_ReadToken(pscanner, symEnd); res = RES_FALSE; } else { // No res = Stmt_Parse(&pstmt, pscanner, pstProc); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pstmt)) res = RES_E_OUTOFMEMORY; } } break; } } return res; } /*---------------------------------------------------------- Purpose: Work function that parses the proc declaration. Returns: RES_OK Cond: -- */ RES PRIVATE ProcDecl_PrivParse( PPROCDECL * ppprocdecl, PSCANNER pscanner, HPA hpa, PSYMTAB pstProc, PSYMTAB pst) { RES res; PTOK ptok; DWORD iLine = Scanner_GetLine(pscanner); // Parse 'proc' res = Scanner_ReadToken(pscanner, SYM_PROC); ASSERT(RES_OK == res); // Parse the proc name res = Scanner_GetToken(pscanner, &ptok); if (RSUCCEEDED(res)) { if (SYM_IDENT == Tok_GetSym(ptok)) { LPCSTR pszIdent = Tok_GetLexeme(ptok); // Add the identifier to the symbol table res = Ident_Add(pszIdent, DATA_PROC, ptok, pscanner, pst); // Parse the variable declaration block if (RSUCCEEDED(res)) res = ProcDecl_ParseVarDecl(hpa, pscanner, pstProc); // Parse the statement block if (RSUCCEEDED(res)) res = StmtBlock_Parse(hpa, pscanner, pstProc, SYM_ENDPROC); if (RSUCCEEDED(res)) { // Create object PDECL pdecl; res = ProcDecl_New(&pdecl, pszIdent, hpa, pstProc, iLine); *ppprocdecl = (PPROCDECL)pdecl; } } else res = Scanner_AddError(pscanner, ptok, RES_E_IDENTMISSING); Tok_Delete(ptok); } else res = Scanner_AddError(pscanner, NULL, RES_E_IDENTMISSING); return res; } /*---------------------------------------------------------- Purpose: Parses the proc declaration Grammar is: ::= proc IDENT { } endproc ::= {}* Returns: RES_OK Cond: -- */ RES PRIVATE ProcDecl_Parse( PPROCDECL * ppprocdecl, PSCANNER pscanner, PSYMTAB pst) { RES res; HPA hpa; PSYMTAB pstProc; DBG_ENTER(ProcDecl_Parse); ASSERT(ppprocdecl); ASSERT(pscanner); *ppprocdecl = NULL; if (PACreate(&hpa, 8)) { res = Symtab_Create(&pstProc, pst); if (RSUCCEEDED(res)) { PSTMT pstmtT; DWORD iLine = Scanner_GetLine(pscanner); // Add the prolog res = EnterStmt_New(&pstmtT, pstProc, iLine); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pstmtT)) { res = RES_E_OUTOFMEMORY; Stmt_Delete(pstmtT); } else { res = ProcDecl_PrivParse(ppprocdecl, pscanner, hpa, pstProc, pst); if (RSUCCEEDED(res)) { // Add the epilog res = LeaveStmt_New(&pstmtT, Scanner_GetLine(pscanner)); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pstmtT)) { res = RES_E_OUTOFMEMORY; Stmt_Delete(pstmtT); } } } } } // Did something fail up above? if (RFAILED(res)) { // Yes; cleanup Symtab_Destroy(pstProc); } } // Clean up if (RFAILED(res)) PADestroyEx(hpa, Stmt_DeletePAPtr, 0); } else res = RES_E_OUTOFMEMORY; DBG_EXIT_RES(ProcDecl_Parse, res); return res; } // // ModuleDecl // /*---------------------------------------------------------- Purpose: Parses the script at the module level. Grammar is: ::= {}* Returns: RES_OK RES_E_OUTOFMEMORY Cond: -- */ RES PUBLIC ModuleDecl_Parse( PMODULEDECL * ppmoduledecl, PSCANNER pscanner, PSYMTAB pstSystem) // May be NULL { RES res = RES_OK; HPA hpa; DBG_ENTER(ModuleDecl_Parse); ASSERT(ppmoduledecl); ASSERT(pscanner); TRACE_MSG(TF_GENERAL, "Parsing..."); *ppmoduledecl = NULL; if (PACreate(&hpa, 8)) { PSYMTAB pst; PDECL pdecl = NULL; res = Symtab_Create(&pst, pstSystem); if (RSUCCEEDED(res)) { // Parse the module block while (RES_OK == res) { SYM sym; Scanner_Peek(pscanner, &sym); switch (sym) { case SYM_EOF: res = RES_FALSE; // Time to stop break; case SYM_PROC: { PPROCDECL pprocdecl; res = ProcDecl_Parse(&pprocdecl, pscanner, pst); if (RSUCCEEDED(res)) { if (!PAInsertPtr(hpa, PA_APPEND, pprocdecl)) res = RES_E_OUTOFMEMORY; } } break; default: res = Scanner_AddError(pscanner, NULL, RES_E_SYNTAXERROR); break; } } if (RSUCCEEDED(res)) { DWORD iLine = Scanner_GetLine(pscanner); res = ModuleDecl_New(&pdecl, hpa, pst, iLine); #ifdef DEBUG if (RSUCCEEDED(res)) Ast_Dump((PAST)pdecl); #endif } // Clean up after parsing if (RSUCCEEDED(res)) PADestroy(hpa); // keep pointer elements allocated for pdecl else { // Something failed PADestroyEx(hpa, Decl_DeletePAPtr, 0); Symtab_Destroy(pst); // Parse errors were added to the scanner's list // of errors already. However, errors such as // out of memory still need to be added. if (FACILITY_PARSE != RFACILITY(res)) Scanner_AddError(pscanner, NULL, res); } // Now typecheck the script if (pdecl) { res = ModuleDecl_Typecheck((PMODULEDECL)pdecl, Scanner_GetStxerrHandle(pscanner)); if (RFAILED(res)) { Decl_Delete(pdecl); pdecl = NULL; } } } *ppmoduledecl = (PMODULEDECL)pdecl; } else res = RES_E_OUTOFMEMORY; DBG_EXIT_RES(ModuleDecl_Parse, res); return res; }