// // Copyright (c) Microsoft Corporation 1995 // // typechk.c // // This file contains the typechecking functions. // // The typechecking rules are: // // waitfor Takes a string expression // transmit Takes a string expression // delay Takes an integer expression // while Evaluates a boolean expression // set ipaddr Takes a string expression // getip Takes an integer expression // // // // History: // 06-15-95 ScottH Created // #include "proj.h" #include "rcids.h" RES PRIVATE Stmt_Typecheck(PSTMT this, PSYMTAB pst, HSA hsaStxerr); /*---------------------------------------------------------- Purpose: Typechecks whether an identifier is a valid type. Returns: RES_OK RES_E_REQUIRELABEL RES_E_UNDEFINED Cond: -- */ RES PRIVATE Ident_Typecheck( LPCSTR pszIdent, DATATYPE dt, PDATATYPE pdt, // May be NULL DWORD iLine, PSYMTAB pst, HSA hsaStxerr) { RES res = RES_OK; PSTE pste; if (RES_OK == Symtab_FindEntry(pst, pszIdent, STFF_DEFAULT, &pste, NULL)) { if (pdt) { *pdt = STE_GetDataType(pste); res = RES_OK; } else if (dt == STE_GetDataType(pste)) { res = RES_OK; } else { switch (dt) { case DATA_LABEL: res = RES_E_REQUIRELABEL; break; case DATA_STRING: res = RES_E_REQUIRESTRING; break; case DATA_INT: res = RES_E_REQUIREINT; break; case DATA_BOOL: res = RES_E_REQUIREBOOL; break; default: ASSERT(0); break; } Stxerr_Add(hsaStxerr, pszIdent, iLine, res); } } else { res = Stxerr_Add(hsaStxerr, pszIdent, iLine, RES_E_UNDEFINED); } return res; } // // Exprs // RES PRIVATE Expr_Typecheck(PEXPR this, PSYMTAB pst, HSA hsaStxerr); /*---------------------------------------------------------- Purpose: Return a string given a binoptype. Returns: Pointer to string Cond: -- */ LPCSTR PRIVATE SzFromBot( BINOPTYPE bot) { #pragma data_seg(DATASEG_READONLY) static const LPCSTR s_mpbotsz[] = { "'or' operand", "'and' operand", "'<=' operand", "'<' operand", "'>=' operand", "'>' operand", "'!=' operand", "'==' operand", "'+' operand", "'-' operand", "'*' operand", "'/' operand", }; #pragma data_seg() if (ARRAY_ELEMENTS(s_mpbotsz) <= bot) { ASSERT(0); return ""; } return s_mpbotsz[bot]; } /*---------------------------------------------------------- Purpose: Return a string given a unoptype. Returns: Pointer to string Cond: -- */ LPCSTR PRIVATE SzFromUot( UNOPTYPE uot) { #pragma data_seg(DATASEG_READONLY) static const LPCSTR s_mpuotsz[] = { "unary '-' operand", "'!' operand", "'getip' parameter", }; #pragma data_seg() if (ARRAY_ELEMENTS(s_mpuotsz) <= uot) { ASSERT(0); return ""; } return s_mpuotsz[uot]; } /*---------------------------------------------------------- Purpose: Typechecks a variable reference expression. Returns: RES_OK or some error result Cond: -- */ RES PRIVATE VarExpr_Typecheck( PEXPR this, PSYMTAB pst, HSA hsaStxerr) { RES res; LPSTR pszIdent; PSTE pste; ASSERT(this); ASSERT(hsaStxerr); pszIdent = VarExpr_GetIdent(this); if (RES_OK == Symtab_FindEntry(pst, pszIdent, STFF_DEFAULT, &pste, NULL)) { DATATYPE dt = STE_GetDataType(pste); ASSERT(DATA_BOOL == dt || DATA_INT == dt || DATA_STRING == dt); Expr_SetDataType(this, dt); res = RES_OK; } else { res = Stxerr_Add(hsaStxerr, pszIdent, Ast_GetLine(this), RES_E_UNDEFINED); } return res; } /*---------------------------------------------------------- Purpose: Typechecks a binary operator expression. Returns: RES_OK or some error result Cond: -- */ RES PRIVATE BinOpExpr_Typecheck( PEXPR this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr1; PEXPR pexpr2; ASSERT(this); ASSERT(hsaStxerr); pexpr1 = BinOpExpr_GetExpr1(this); res = Expr_Typecheck(pexpr1, pst, hsaStxerr); if (RSUCCEEDED(res)) { pexpr2 = BinOpExpr_GetExpr2(this); res = Expr_Typecheck(pexpr2, pst, hsaStxerr); if (RSUCCEEDED(res)) { BINOPTYPE bot = BinOpExpr_GetType(this); // Types must match if (Expr_GetDataType(pexpr1) != Expr_GetDataType(pexpr2)) { res = RES_E_TYPEMISMATCH; } else { // Just choose one of the datatypes, since they // should be the same. DATATYPE dt = Expr_GetDataType(pexpr1); switch (bot) { case BOT_OR: case BOT_AND: Expr_SetDataType(this, DATA_BOOL); if (DATA_BOOL != dt) res = RES_E_REQUIREBOOL; break; case BOT_PLUS: Expr_SetDataType(this, dt); // String + string means concatenate. if (DATA_INT != dt && DATA_STRING != dt) res = RES_E_REQUIREINTSTRING; break; case BOT_NEQ: case BOT_EQ: Expr_SetDataType(this, DATA_BOOL); if (DATA_INT != dt && DATA_STRING != dt && DATA_BOOL != dt) res = RES_E_REQUIREINTSTRBOOL; break; case BOT_LEQ: case BOT_LT: case BOT_GEQ: case BOT_GT: Expr_SetDataType(this, DATA_BOOL); if (DATA_INT != dt) res = RES_E_REQUIREINT; break; case BOT_MINUS: case BOT_MULT: case BOT_DIV: Expr_SetDataType(this, DATA_INT); if (DATA_INT != dt) res = RES_E_REQUIREINT; break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } } if (RFAILED(res)) Stxerr_Add(hsaStxerr, SzFromBot(bot), Ast_GetLine(this), res); } } return res; } /*---------------------------------------------------------- Purpose: Typechecks a unary operator expression. Returns: RES_OK or some error result Cond: -- */ RES PRIVATE UnOpExpr_Typecheck( PEXPR this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); pexpr = UnOpExpr_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (RSUCCEEDED(res)) { UNOPTYPE uot = UnOpExpr_GetType(this); DATATYPE dt = Expr_GetDataType(pexpr); // Check the type of the expression switch (uot) { case UOT_NEG: Expr_SetDataType(this, DATA_INT); if (DATA_INT != dt) res = RES_E_REQUIREINT; break; case UOT_NOT: Expr_SetDataType(this, DATA_BOOL); if (DATA_BOOL != dt) res = RES_E_REQUIREBOOL; break; case UOT_GETIP: Expr_SetDataType(this, DATA_STRING); if (DATA_INT != dt) res = RES_E_REQUIREINT; break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } if (RFAILED(res)) Stxerr_Add(hsaStxerr, SzFromUot(uot), Ast_GetLine(this), res); } return res; } /*---------------------------------------------------------- Purpose: Typechecks an expression. Returns: RES_OK or some error result Cond: -- */ RES PRIVATE Expr_Typecheck( PEXPR this, PSYMTAB pst, HSA hsaStxerr) { RES res; ASSERT(this); ASSERT(hsaStxerr); switch (Ast_GetType(this)) { case AT_INT_EXPR: Expr_SetDataType(this, DATA_INT); res = RES_OK; break; case AT_STRING_EXPR: Expr_SetDataType(this, DATA_STRING); res = RES_OK; break; case AT_BOOL_EXPR: Expr_SetDataType(this, DATA_BOOL); res = RES_OK; break; case AT_VAR_EXPR: res = VarExpr_Typecheck(this, pst, hsaStxerr); break; case AT_UNOP_EXPR: res = UnOpExpr_Typecheck(this, pst, hsaStxerr); break; case AT_BINOP_EXPR: res = BinOpExpr_Typecheck(this, pst, hsaStxerr); break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } return res; } /*---------------------------------------------------------- Purpose: Typecheck the assignment statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE AssignStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; LPSTR pszIdent; DATATYPE dt; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_ASSIGN_STMT == Ast_GetType(this)); pszIdent = AssignStmt_GetIdent(this); res = Ident_Typecheck(pszIdent, 0, &dt, Ast_GetLine(this), pst, hsaStxerr); if (RSUCCEEDED(res)) { PEXPR pexpr = AssignStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); // Types must match if (dt != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "=", Ast_GetLine(pexpr), RES_E_TYPEMISMATCH); } } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'while' statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE WhileStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_WHILE_STMT == Ast_GetType(this)); pexpr = WhileStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (RSUCCEEDED(res)) { if (DATA_BOOL != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'while' expression", Ast_GetLine(pexpr), RES_E_REQUIREBOOL); } else { // Typecheck the statement block DWORD i; DWORD cstmts; HPA hpaStmts = WhileStmt_GetStmtBlock(this); res = RES_OK; cstmts = PAGetCount(hpaStmts); // Typecheck each statement for (i = 0; i < cstmts; i++) { PSTMT pstmt = PAFastGetPtr(hpaStmts, i); res = Stmt_Typecheck(pstmt, pst, hsaStxerr); if (RFAILED(res)) break; } } } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'if' statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE IfStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_IF_STMT == Ast_GetType(this)); pexpr = IfStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (RSUCCEEDED(res)) { if (DATA_BOOL != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'if' expression", Ast_GetLine(pexpr), RES_E_REQUIREBOOL); } else { // Typecheck the statement block DWORD i; DWORD cstmts; HPA hpaStmts = IfStmt_GetStmtBlock(this); res = RES_OK; cstmts = PAGetCount(hpaStmts); // Typecheck each statement for (i = 0; i < cstmts; i++) { PSTMT pstmt = PAFastGetPtr(hpaStmts, i); res = Stmt_Typecheck(pstmt, pst, hsaStxerr); if (RFAILED(res)) break; } } } return res; } /*---------------------------------------------------------- Purpose: Typecheck the label statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE LabelStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PSTE pste; LPSTR pszIdent; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_LABEL_STMT == Ast_GetType(this)); pszIdent = LabelStmt_GetIdent(this); if (RES_OK == Symtab_FindEntry(pst, pszIdent, STFF_DEFAULT, &pste, NULL)) { if (DATA_LABEL == STE_GetDataType(pste)) res = RES_OK; else res = Stxerr_Add(hsaStxerr, pszIdent, Ast_GetLine(this), RES_E_REQUIRELABEL); } else { // This should never get here ASSERT(0); res = RES_E_FAIL; } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'goto' statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE GotoStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { LPSTR pszIdent; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_GOTO_STMT == Ast_GetType(this)); pszIdent = GotoStmt_GetIdent(this); return Ident_Typecheck(pszIdent, DATA_LABEL, NULL, Ast_GetLine(this), pst, hsaStxerr); } /*---------------------------------------------------------- Purpose: Typecheck the transmit statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE TransmitStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_TRANSMIT_STMT == Ast_GetType(this)); pexpr = TransmitStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (DATA_STRING != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'transmit' parameter", Ast_GetLine(pexpr), RES_E_REQUIRESTRING); } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'waitfor' statement waitfor [ then IDENT { , then IDENT } ] [ until ] where: is a string IDENT is a label is an integer Returns: RES_OK or some error result Cond: -- */ RES PRIVATE WaitforStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res = RES_E_FAIL; PEXPR pexpr; HSA hsa; DWORD i; DWORD ccase; PWAITCASE pwc; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_WAITFOR_STMT == Ast_GetType(this)); // Typecheck that is of type string, and any // IDENTs are labels. hsa = WaitforStmt_GetCaseList(this); ccase = SAGetCount(hsa); for (i = 0; i < ccase; i++) { SAGetItemPtr(hsa, i, &pwc); ASSERT(pwc); // Typecheck res = Expr_Typecheck(pwc->pexpr, pst, hsaStxerr); if (DATA_STRING != Expr_GetDataType(pwc->pexpr)) { res = Stxerr_Add(hsaStxerr, "'waitfor' parameter", Ast_GetLine(pwc->pexpr), RES_E_REQUIRESTRING); break; } // Typecheck IDENT label. If there is only one , there // may not be an IDENT label. if (pwc->pszIdent) { res = Ident_Typecheck(pwc->pszIdent, DATA_LABEL, NULL, Ast_GetLine(pwc->pexpr), pst, hsaStxerr); if (RFAILED(res)) break; } else ASSERT(1 == ccase); } // 'until' expression is optional if (RSUCCEEDED(res) && NULL != (pexpr = WaitforStmt_GetUntilExpr(this))) { res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (DATA_INT != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'until' parameter", Ast_GetLine(pexpr), RES_E_REQUIREINT); } } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'delay' statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE DelayStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_DELAY_STMT == Ast_GetType(this)); pexpr = DelayStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (DATA_INT != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'delay' parameter", Ast_GetLine(pexpr), RES_E_REQUIREINT); } return res; } /*---------------------------------------------------------- Purpose: Typecheck the 'set' statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE SetStmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; PEXPR pexpr; ASSERT(this); ASSERT(hsaStxerr); ASSERT(AT_SET_STMT == Ast_GetType(this)); switch (SetStmt_GetType(this)) { case ST_IPADDR: pexpr = SetIPStmt_GetExpr(this); res = Expr_Typecheck(pexpr, pst, hsaStxerr); if (DATA_STRING != Expr_GetDataType(pexpr)) { res = Stxerr_Add(hsaStxerr, "'ipaddr' parameter", Ast_GetLine(pexpr), RES_E_REQUIRESTRING); } break; case ST_PORT: case ST_SCREEN: res = RES_OK; break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } return res; } /*---------------------------------------------------------- Purpose: Typecheck a statement Returns: RES_OK or some error result Cond: -- */ RES PRIVATE Stmt_Typecheck( PSTMT this, PSYMTAB pst, HSA hsaStxerr) { RES res; ASSERT(this); ASSERT(hsaStxerr); switch (Ast_GetType(this)) { case AT_ENTER_STMT: case AT_LEAVE_STMT: res = RES_OK; break; case AT_WHILE_STMT: res = WhileStmt_Typecheck(this, pst, hsaStxerr); break; case AT_IF_STMT: res = IfStmt_Typecheck(this, pst, hsaStxerr); break; case AT_ASSIGN_STMT: res = AssignStmt_Typecheck(this, pst, hsaStxerr); break; case AT_HALT_STMT: // Nothing to typecheck here res = RES_OK; break; case AT_TRANSMIT_STMT: res = TransmitStmt_Typecheck(this, pst, hsaStxerr); break; case AT_WAITFOR_STMT: res = WaitforStmt_Typecheck(this, pst, hsaStxerr); break; case AT_DELAY_STMT: res = DelayStmt_Typecheck(this, pst, hsaStxerr); break; case AT_LABEL_STMT: res = LabelStmt_Typecheck(this, pst, hsaStxerr); break; case AT_GOTO_STMT: res = GotoStmt_Typecheck(this, pst, hsaStxerr); break; case AT_SET_STMT: res = SetStmt_Typecheck(this, pst, hsaStxerr); break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } return res; } /*---------------------------------------------------------- Purpose: Typecheck a procedure declaration. Returns: RES_OK or some error result Cond: -- */ RES PRIVATE ProcDecl_Typecheck( PPROCDECL this, PSYMTAB pst, HSA hsaStxerr) { RES res = RES_OK; DWORD i; DWORD cstmts; ASSERT(this); ASSERT(hsaStxerr); cstmts = PAGetCount(this->hpaStmts); // Typecheck each statement for (i = 0; i < cstmts; i++) { PSTMT pstmt = PAFastGetPtr(this->hpaStmts, i); res = Stmt_Typecheck(pstmt, this->pst, hsaStxerr); if (RFAILED(res)) break; } return res; } /*---------------------------------------------------------- Purpose: Typecheck a module declaration. Returns: RES_OK or some error result Cond: -- */ RES PUBLIC ModuleDecl_Typecheck( PMODULEDECL this, HSA hsaStxerr) { RES res = RES_OK; DWORD i; DWORD cprocs; BOOL bFoundMain = FALSE; ASSERT(this); ASSERT(hsaStxerr); TRACE_MSG(TF_GENERAL, "Typechecking..."); cprocs = PAGetCount(this->hpaProcs); // Typecheck each proc for (i = 0; i < cprocs; i++) { PPROCDECL pprocdecl = PAFastGetPtr(this->hpaProcs, i); if (IsSzEqualC(ProcDecl_GetIdent(pprocdecl), "main")) bFoundMain = TRUE; res = ProcDecl_Typecheck(pprocdecl, this->pst, hsaStxerr); if (RFAILED(res)) break; } // There must be a main proc if (RSUCCEEDED(res) && !bFoundMain) res = Stxerr_AddTok(hsaStxerr, NULL, RES_E_MAINMISSING); return res; }