|
|
//
// 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 <Expr> [ then IDENT { , <Expr> then IDENT } ] [ until <UntilExpr> ] where: <Expr> is a string IDENT is a label <UntilExpr> 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 <Expr> 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 <Expr>
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 <Expr>, 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; }
|