// // Copyright (c) Microsoft Corporation 1995 // // ast.c // // This file contains the abstract syntax tree functions. // // History: // 05-20-95 ScottH Created // #include "proj.h" #include "rcids.h" #define RetInt(ppv, x) (*((LPINT)*(ppv)) = (x)) #define RetStr(ppv, x) (*((LPSTR)*(ppv)) = (x)) #define RetBool(ppv, x) (*((LPBOOL)*(ppv)) = (x)) // // Wait case functions // #ifdef DEBUG /*---------------------------------------------------------- Purpose: Dump a waitcase structure Returns: -- Cond: -- */ void PRIVATE Waitcase_Dump( PWAITCASE this) { Ast_Dump((PAST)this->pexpr); if (this->pszIdent) { TRACE_MSG(TF_ALWAYS, " then %s", this->pszIdent); } } #endif /*---------------------------------------------------------- Purpose: Create a wait-case list. Returns: RES_OK RES_E_OUTOFMEMORY Cond: -- */ RES PUBLIC Waitcase_Create( PHSA phsa) { RES res = RES_OK; if ( !SACreate(phsa, sizeof(WAITCASE), 8) ) res = RES_E_OUTOFMEMORY; return res; } /*---------------------------------------------------------- Purpose: Add a case to the given wait-case list. Returns: RES_OK Cond: -- */ RES PUBLIC Waitcase_Add( HSA hsa, PEXPR pexpr, LPCSTR pszIdent, // This may be NULL DWORD dwFlags) { RES res = RES_OK; // assume success WAITCASE wc; ASSERT(hsa); ASSERT(pexpr); wc.pexpr = pexpr; wc.dwFlags = dwFlags; wc.pszIdent = NULL; // Copy pszIdent since the parameter is freed by the caller. if ( pszIdent && !GSetString(&wc.pszIdent, pszIdent) ) res = RES_E_OUTOFMEMORY; else if ( !SAInsertItem(hsa, SA_APPEND, &wc) ) res = RES_E_OUTOFMEMORY; return res; } /*---------------------------------------------------------- Purpose: Free the contents of the given pointer. Returns: -- Cond: Don't free the pointer itself! */ void CALLBACK Waitcase_FreeSA( PVOID pv, LPARAM lparam) { PWAITCASE pwc = (PWAITCASE)pv; if (pwc->pexpr) Expr_Delete(pwc->pexpr); if (pwc->pszIdent) GSetString(&pwc->pszIdent, NULL); // free } /*---------------------------------------------------------- Purpose: Destroy the wait case list. Returns: RES_OK Cond: -- */ RES PUBLIC Waitcase_Destroy( HSA hsa) { ASSERT(hsa); SADestroyEx(hsa, Waitcase_FreeSA, 0); return RES_OK; } // // Base level AST functions // #ifdef DEBUG /*---------------------------------------------------------- Purpose: Dump the AST Returns: -- Cond: -- */ void PUBLIC Ast_Dump( PAST this) { ASSERT(this); if (IsFlagSet(g_dwDumpFlags, DF_AST)) { switch (this->asttype) { case AT_BASE: TRACE_MSG(TF_ALWAYS, "Unknown AST"); break; case AT_MODULE_DECL: { PMODULEDECL pmd = (PMODULEDECL)this; DWORD i; DWORD cprocs = PAGetCount(pmd->hpaProcs); TRACE_MSG(TF_ALWAYS, "module"); for (i = 0; i < cprocs; i++) Ast_Dump(PAFastGetPtr(pmd->hpaProcs, i)); } break; case AT_PROC_DECL: { PPROCDECL ppd = (PPROCDECL)this; DWORD i; DWORD cstmts = PAGetCount(ppd->hpaStmts); TRACE_MSG(TF_ALWAYS, "proc %s", ProcDecl_GetIdent(ppd)); for (i = 0; i < cstmts; i++) Ast_Dump(PAFastGetPtr(ppd->hpaStmts, i)); TRACE_MSG(TF_ALWAYS, "endproc"); } break; case AT_ENTER_STMT: TRACE_MSG(TF_ALWAYS, "enter"); break; case AT_LEAVE_STMT: TRACE_MSG(TF_ALWAYS, "leave"); break; case AT_HALT_STMT: TRACE_MSG(TF_ALWAYS, "halt"); break; case AT_ASSIGN_STMT: TRACE_MSG(TF_ALWAYS, "%s = ", AssignStmt_GetIdent(this)); Ast_Dump((PAST)AssignStmt_GetExpr(this)); break; case AT_LABEL_STMT: TRACE_MSG(TF_ALWAYS, "%s:", LabelStmt_GetIdent(this)); break; case AT_GOTO_STMT: TRACE_MSG(TF_ALWAYS, "goto %s", GotoStmt_GetIdent(this)); break; case AT_WHILE_STMT: TRACE_MSG(TF_ALWAYS, "while "); TRACE_MSG(TF_ALWAYS, " do "); TRACE_MSG(TF_ALWAYS, "endwhile "); break; case AT_IF_STMT: TRACE_MSG(TF_ALWAYS, "if "); TRACE_MSG(TF_ALWAYS, " then "); TRACE_MSG(TF_ALWAYS, "endif "); break; case AT_DELAY_STMT: TRACE_MSG(TF_ALWAYS, "delay"); Ast_Dump((PAST)DelayStmt_GetExpr(this)); break; case AT_WAITFOR_STMT: { PWAITFORSTMT pws = (PWAITFORSTMT)this; DWORD ccase = SAGetCount(pws->hsa); DWORD i; TRACE_MSG(TF_ALWAYS, "waitfor"); for (i = 0; i < ccase; i++) { PVOID pv; SAGetItemPtr(pws->hsa, i, &pv); Waitcase_Dump(pv); } if (WaitforStmt_GetUntilExpr(this)) { TRACE_MSG(TF_ALWAYS, "until"); Ast_Dump((PAST)WaitforStmt_GetUntilExpr(this)); } } break; case AT_TRANSMIT_STMT: TRACE_MSG(TF_ALWAYS, "transmit"); Ast_Dump((PAST)TransmitStmt_GetExpr(this)); break; case AT_SET_STMT: switch (SetStmt_GetType(this)) { case ST_IPADDR: TRACE_MSG(TF_ALWAYS, "set ipaddr getip"); Ast_Dump((PAST)SetIPStmt_GetExpr(this)); break; case ST_PORT: if (IsFlagSet(SetPortStmt_GetFlags(this), SPF_DATABITS)) TRACE_MSG(TF_ALWAYS, "set port databits %u", SetPortStmt_GetDatabits(this)); if (IsFlagSet(SetPortStmt_GetFlags(this), SPF_STOPBITS)) TRACE_MSG(TF_ALWAYS, "set port stopbits %u", SetPortStmt_GetStopbits(this)); if (IsFlagSet(SetPortStmt_GetFlags(this), SPF_PARITY)) TRACE_MSG(TF_ALWAYS, "set port parity %u", SetPortStmt_GetParity(this)); break; case ST_SCREEN: if (IsFlagSet(SetScreenStmt_GetFlags(this), SPF_KEYBRD)) TRACE_MSG(TF_ALWAYS, "set screen keyboard %s", SetScreenStmt_GetKeybrd(this) ? "on" : "off"); break; default: ASSERT(0); break; } break; case AT_INT_EXPR: TRACE_MSG(TF_ALWAYS, " %d", IntExpr_GetVal(this)); break; case AT_STRING_EXPR: TRACE_MSG(TF_ALWAYS, " %s", StrExpr_GetStr(this)); break; case AT_BOOL_EXPR: TRACE_MSG(TF_ALWAYS, " %s", BoolExpr_GetVal(this) ? (LPSTR)"TRUE" : (LPSTR)"FALSE"); break; case AT_VAR_EXPR: TRACE_MSG(TF_ALWAYS, " %s", VarExpr_GetIdent(this)); break; case AT_BINOP_EXPR: { PBINOPEXPR pbo = (PBINOPEXPR)this; Ast_Dump((PAST)pbo->pexpr1); switch (BinOpExpr_GetType(this)) { case BOT_OR: TRACE_MSG(TF_ALWAYS, " or"); break; case BOT_AND: TRACE_MSG(TF_ALWAYS, " and"); break; case BOT_LT: TRACE_MSG(TF_ALWAYS, " <"); break; case BOT_LEQ: TRACE_MSG(TF_ALWAYS, " <="); break; case BOT_GT: TRACE_MSG(TF_ALWAYS, " >"); break; case BOT_GEQ: TRACE_MSG(TF_ALWAYS, " >="); break; case BOT_EQ: TRACE_MSG(TF_ALWAYS, " =="); break; case BOT_NEQ: TRACE_MSG(TF_ALWAYS, " !="); break; case BOT_PLUS: TRACE_MSG(TF_ALWAYS, " +"); break; case BOT_MINUS: TRACE_MSG(TF_ALWAYS, " -"); break; case BOT_MULT: TRACE_MSG(TF_ALWAYS, " *"); break; case BOT_DIV: TRACE_MSG(TF_ALWAYS, " /"); break; default: ASSERT(0); break; } Ast_Dump((PAST)pbo->pexpr2); } break; case AT_UNOP_EXPR: { PUNOPEXPR puo = (PUNOPEXPR)this; switch (UnOpExpr_GetType(this)) { case UOT_NEG: TRACE_MSG(TF_ALWAYS, " -"); break; case UOT_NOT: TRACE_MSG(TF_ALWAYS, " !"); break; case UOT_GETIP: TRACE_MSG(TF_ALWAYS, " getip"); break; default: ASSERT(0); break; } Ast_Dump((PAST)puo->pexpr); } break; default: ASSERT(0); break; } } } #endif // DEBUG /*---------------------------------------------------------- Purpose: Creates a new AST Returns: RES_OK RES_E_OUTOFMEMORY Cond: -- */ RES PUBLIC Ast_New( LPVOID * ppv, ASTTYPE asttype, DWORD cbSize, DWORD iLine) { PAST past; ASSERT(ppv); past = GAlloc(cbSize); if (past) { Ast_SetSize(past, cbSize); Ast_SetType(past, asttype); Ast_SetLine(past, iLine); } *ppv = past; return NULL != past ? RES_OK : RES_E_OUTOFMEMORY; } /*---------------------------------------------------------- Purpose: Destroys the given AST. Returns: Cond: -- */ void PUBLIC Ast_Delete( PAST this) { GFree(this); } /*---------------------------------------------------------- Purpose: Duplicate the given AST. Returns: RES_OK RES_E_OUTOFMEMORY Cond: -- */ RES PUBLIC Ast_Dup( PAST this, PAST * ppast) { PAST past; DWORD cbSize; ASSERT(this); ASSERT(ppast); cbSize = Ast_GetSize(this); past = GAlloc(cbSize); if (past) { BltByte(past, this, cbSize); } *ppast = past; return NULL != past ? RES_OK : RES_E_OUTOFMEMORY; } // // Expressions // /*---------------------------------------------------------- Purpose: Callback for PADestroyEx. Returns: -- Cond: -- */ void CALLBACK Expr_DeletePAPtr( LPVOID pv, LPARAM lparam) { Expr_Delete(pv); } /*---------------------------------------------------------- Purpose: Destroys an Expr. Returns: RES_OK RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC Expr_Delete( PEXPR this) { RES res; DBG_ENTER(Expr_Delete); if (this) { res = RES_OK; switch (this->ast.asttype) { case AT_INT_EXPR: case AT_BOOL_EXPR: // (Nothing to free inside) break; case AT_STRING_EXPR: { PSTREXPR ps = (PSTREXPR)this; if (ps->psz) GSetString(&ps->psz, NULL); // free } break; case AT_VAR_EXPR: { PVAREXPR ps = (PVAREXPR)this; if (ps->pszIdent) GSetString(&ps->pszIdent, NULL); // free } break; case AT_BINOP_EXPR: { PBINOPEXPR pbo = (PBINOPEXPR)this; if (pbo->pexpr1) Expr_Delete(pbo->pexpr1); if (pbo->pexpr2) Expr_Delete(pbo->pexpr2); } break; case AT_UNOP_EXPR: { PUNOPEXPR puo = (PUNOPEXPR)this; if (puo->pexpr) Expr_Delete(puo->pexpr); } break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } if (RSUCCEEDED(res)) { // Most of the time when the evaluated result // is a string, it is just a copy of the pointer // in the specific class structure. In these // cases it does not need to be freed again, // because it was freed above. if (this->er.psz && IsFlagSet(this->dwFlags, EF_ALLOCATED)) { ASSERT(DATA_STRING == Expr_GetDataType(this)); GSetString(&this->er.psz, NULL); // free } Ast_Delete((PAST)this); } } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(Expr_Delete, res); return res; } /*---------------------------------------------------------- Purpose: Creates a IntExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC IntExpr_New( PEXPR * ppexpr, int nVal, DWORD iLine) { RES res; DBG_ENTER(IntExpr_New); ASSERT(ppexpr); if (ppexpr) { PINTEXPR this; res = Ast_New(&this, AT_INT_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { IntExpr_SetVal(this, nVal); } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(IntExpr_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a StrExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC StrExpr_New( PEXPR * ppexpr, LPCSTR psz, DWORD iLine) { RES res; DBG_ENTER(StrExpr_New); ASSERT(ppexpr); ASSERT(psz); if (ppexpr) { PSTREXPR this; res = Ast_New(&this, AT_STRING_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; if (!GSetString(&this->psz, psz)) res = RES_E_OUTOFMEMORY; if (RFAILED(res)) { Ast_Delete((PAST)this); this = NULL; } } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(StrExpr_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a BoolExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC BoolExpr_New( PEXPR * ppexpr, BOOL bVal, DWORD iLine) { RES res; DBG_ENTER(BoolExpr_New); ASSERT(ppexpr); if (ppexpr) { PBOOLEXPR this; res = Ast_New(&this, AT_BOOL_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { BoolExpr_SetVal(this, bVal); } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(BoolExpr_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a VarExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC VarExpr_New( PEXPR * ppexpr, LPCSTR pszIdent, DWORD iLine) { RES res; DBG_ENTER(VarExpr_New); ASSERT(ppexpr); ASSERT(pszIdent); if (ppexpr) { PVAREXPR this; res = Ast_New(&this, AT_VAR_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; if (!GSetString(&this->pszIdent, pszIdent)) res = RES_E_OUTOFMEMORY; if (RFAILED(res)) { Ast_Delete((PAST)this); this = NULL; } } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(VarExpr_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a BinOpExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC BinOpExpr_New( PEXPR * ppexpr, BINOPTYPE binoptype, PEXPR pexpr1, PEXPR pexpr2, DWORD iLine) { RES res; DBG_ENTER(BinOpExpr_New); ASSERT(ppexpr); ASSERT(pexpr1); ASSERT(pexpr2); if (ppexpr) { PBINOPEXPR this; res = Ast_New(&this, AT_BINOP_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; BinOpExpr_SetType(this, binoptype); this->pexpr1 = pexpr1; this->pexpr2 = pexpr2; } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(BinOpExpr_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a UnOpExpr object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC UnOpExpr_New( PEXPR * ppexpr, UNOPTYPE unoptype, PEXPR pexpr, DWORD iLine) { RES res; DBG_ENTER(UnOpExpr_New); ASSERT(ppexpr); ASSERT(pexpr); if (ppexpr) { PUNOPEXPR this; res = Ast_New(&this, AT_UNOP_EXPR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { UnOpExpr_SetType(this, unoptype); this->pexpr = pexpr; } *ppexpr = (PEXPR)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(UnOpExpr_New, res); return res; } // // Stmt // /*---------------------------------------------------------- Purpose: Callback for PADestroyEx. Returns: -- Cond: -- */ void CALLBACK Stmt_DeletePAPtr( LPVOID pv, LPARAM lparam) { Stmt_Delete(pv); } /*---------------------------------------------------------- Purpose: Destroys a Stmt. Returns: RES_OK RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC Stmt_Delete( PSTMT this) { RES res; DBG_ENTER(Stmt_Delete); if (this) { PEXPR pexpr; HSA hsa; res = RES_OK; switch (this->ast.asttype) { case AT_ENTER_STMT: // (don't free pst -- it belongs to the decl structs) case AT_LEAVE_STMT: case AT_HALT_STMT: break; case AT_ASSIGN_STMT: { PASSIGNSTMT pls = (PASSIGNSTMT)this; if (pls->pszIdent) GSetString(&pls->pszIdent, NULL); // free pexpr = AssignStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); } break; case AT_WHILE_STMT: { PWHILESTMT pls = (PWHILESTMT)this; pexpr = WhileStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); if (pls->hpaStmts) PADestroyEx(pls->hpaStmts, Stmt_DeletePAPtr, 0); } break; case AT_IF_STMT: { PIFSTMT pls = (PIFSTMT)this; pexpr = IfStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); if (pls->hpaStmts) PADestroyEx(pls->hpaStmts, Stmt_DeletePAPtr, 0); } break; case AT_LABEL_STMT: { PLABELSTMT pls = (PLABELSTMT)this; if (pls->psz) GSetString(&pls->psz, NULL); // free } break; case AT_GOTO_STMT: { PGOTOSTMT pgs = (PGOTOSTMT)this; if (pgs->psz) GSetString(&pgs->psz, NULL); // free } break; case AT_DELAY_STMT: pexpr = DelayStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); break; case AT_TRANSMIT_STMT: pexpr = TransmitStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); break; case AT_WAITFOR_STMT: hsa = WaitforStmt_GetCaseList(this); if (hsa) Waitcase_Destroy(hsa); pexpr = WaitforStmt_GetUntilExpr(this); if (pexpr) Expr_Delete(pexpr); break; case AT_SET_STMT: switch (SetStmt_GetType(this)) { case ST_IPADDR: pexpr = SetIPStmt_GetExpr(this); if (pexpr) Expr_Delete(pexpr); break; case ST_PORT: case ST_SCREEN: break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } if (RSUCCEEDED(res)) Ast_Delete((PAST)this); } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(Stmt_Delete, res); return res; } // // Statements // /*---------------------------------------------------------- Purpose: Creates a WaitforStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC WaitforStmt_New( PSTMT * ppstmt, HSA hsa, PEXPR pexprUntil, // May be NULL DWORD iLine) { RES res; DBG_ENTER(WaitforStmt_New); ASSERT(ppstmt); ASSERT(hsa); if (ppstmt) { PWAITFORSTMT this; res = Ast_New(&this, AT_WAITFOR_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->hsa = hsa; this->pexprUntil = pexprUntil; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(WaitforStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a TransmitStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC TransmitStmt_New( PSTMT * ppstmt, PEXPR pexpr, DWORD dwFlags, DWORD iLine) { RES res; DBG_ENTER(TransmitStmt_New); ASSERT(ppstmt); ASSERT(pexpr); if (ppstmt) { PTRANSMITSTMT this; res = Ast_New(&this, AT_TRANSMIT_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->pexpr = pexpr; this->dwFlags = dwFlags; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(TransmitStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a DelayStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC DelayStmt_New( PSTMT * ppstmt, PEXPR pexpr, DWORD iLine) { RES res; DBG_ENTER(DelayStmt_New); ASSERT(ppstmt); ASSERT(pexpr); if (ppstmt) { PDELAYSTMT this; res = Ast_New(&this, AT_DELAY_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { this->pexprSecs = pexpr; res = RES_OK; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(DelayStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a HaltStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC HaltStmt_New( PSTMT * ppstmt, DWORD iLine) { RES res; DBG_ENTER(HaltStmt_New); ASSERT(ppstmt); if (ppstmt) { PHALTSTMT this; res = Ast_New(&this, AT_HALT_STMT, sizeof(*this), iLine); *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(HaltStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates an EnterStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC EnterStmt_New( PSTMT * ppstmt, PSYMTAB pst, DWORD iLine) { RES res; DBG_ENTER(EnterStmt_New); ASSERT(ppstmt); if (ppstmt) { PENTERSTMT this; res = Ast_New(&this, AT_ENTER_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { this->pst = pst; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(EnterStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates an LeaveStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC LeaveStmt_New( PSTMT * ppstmt, DWORD iLine) { RES res; DBG_ENTER(LeaveStmt_New); ASSERT(ppstmt); if (ppstmt) { PLEAVESTMT this; res = Ast_New(&this, AT_LEAVE_STMT, sizeof(*this), iLine); *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(LeaveStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates an AssignStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC AssignStmt_New( PSTMT * ppstmt, LPCSTR pszIdent, PEXPR pexpr, DWORD iLine) { RES res; DBG_ENTER(AssignStmt_New); ASSERT(ppstmt); ASSERT(pszIdent); ASSERT(pexpr); if (ppstmt) { PASSIGNSTMT this; res = Ast_New(&this, AT_ASSIGN_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success if (!GSetString(&this->pszIdent, pszIdent)) res = RES_E_OUTOFMEMORY; else this->pexpr = pexpr; if (RFAILED(res)) { Ast_Delete((PAST)this); this = NULL; } } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(AssignStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a LabelStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC LabelStmt_New( PSTMT * ppstmt, LPCSTR psz, DWORD iLine) { RES res; DBG_ENTER(LabelStmt_New); ASSERT(ppstmt); ASSERT(psz); if (ppstmt) { PLABELSTMT this; res = Ast_New(&this, AT_LABEL_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success if (!GSetString(&this->psz, psz)) { res = RES_E_OUTOFMEMORY; Ast_Delete((PAST)this); this = NULL; } } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(LabelStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a GotoStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC GotoStmt_New( PSTMT * ppstmt, LPCSTR psz, DWORD iLine) { RES res; DBG_ENTER(GotoStmt_New); ASSERT(ppstmt); ASSERT(psz); if (ppstmt) { PGOTOSTMT this; res = Ast_New(&this, AT_GOTO_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success if (!GSetString(&this->psz, psz)) { res = RES_E_OUTOFMEMORY; Ast_Delete((PAST)this); this = NULL; } } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(GotoStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a WhileStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC WhileStmt_New( PSTMT * ppstmt, PEXPR pexpr, HPA hpa, LPCSTR pszTopLabel, LPCSTR pszEndLabel, DWORD iLine) { RES res; ASSERT(ppstmt); ASSERT(hpa); ASSERT(pexpr); ASSERT(pszTopLabel); ASSERT(pszEndLabel); if (ppstmt) { PWHILESTMT this; res = Ast_New(&this, AT_WHILE_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->pexpr = pexpr; this->hpaStmts = hpa; lstrcpyn(this->szTopLabel, pszTopLabel, sizeof(this->szTopLabel)); lstrcpyn(this->szEndLabel, pszEndLabel, sizeof(this->szEndLabel)); } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; return res; } /*---------------------------------------------------------- Purpose: Creates an IfStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC IfStmt_New( PSTMT * ppstmt, PEXPR pexpr, HPA hpa, LPCSTR pszElseLabel, LPCSTR pszEndLabel, DWORD iLine) { RES res; ASSERT(ppstmt); ASSERT(hpa); ASSERT(pexpr); ASSERT(pszElseLabel); ASSERT(pszEndLabel); if (ppstmt) { PIFSTMT this; res = Ast_New(&this, AT_IF_STMT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->pexpr = pexpr; this->hpaStmts = hpa; lstrcpyn(this->szElseLabel, pszElseLabel, sizeof(this->szElseLabel)); lstrcpyn(this->szEndLabel, pszEndLabel, sizeof(this->szEndLabel)); } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; return res; } /*---------------------------------------------------------- Purpose: Creates a SetStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PRIVATE SetStmt_New( PVOID * ppv, SETTYPE settype, DWORD cbSize, DWORD iLine) { RES res; DBG_ENTER(SetStmt_New); ASSERT(ppv); ASSERT(sizeof(SETSTMT) <= cbSize); if (ppv) { PSETSTMT this; res = Ast_New(&this, AT_SET_STMT, cbSize, iLine); if (RSUCCEEDED(res)) { SetStmt_SetType(this, settype); res = RES_OK; } *ppv = this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(SetStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a SetIPStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC SetIPStmt_New( PSTMT * ppstmt, PEXPR pexpr, DWORD iLine) { RES res; DBG_ENTER(SetIPStmt_New); ASSERT(ppstmt); ASSERT(pexpr); if (ppstmt) { PSETIPSTMT this; res = SetStmt_New(&this, ST_IPADDR, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->pexpr = pexpr; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(SetIPStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a SetPortStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC SetPortStmt_New( PSTMT * ppstmt, PPORTSTATE pstate, DWORD iLine) { RES res; DBG_ENTER(SetPortStmt_New); ASSERT(ppstmt); ASSERT(pstate); if (ppstmt && pstate) { PSETPORTSTMT this; res = SetStmt_New(&this, ST_PORT, sizeof(*this), iLine); if (RSUCCEEDED(res)) { DWORD dwFlags = pstate->dwFlags; res = RES_OK; // assume success this->portstate.dwFlags = dwFlags; if (IsFlagSet(dwFlags, SPF_DATABITS)) this->portstate.nDatabits = pstate->nDatabits; if (IsFlagSet(dwFlags, SPF_STOPBITS)) this->portstate.nStopbits = pstate->nStopbits; if (IsFlagSet(dwFlags, SPF_PARITY)) this->portstate.nParity = pstate->nParity; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(SetPortStmt_New, res); return res; } /*---------------------------------------------------------- Purpose: Creates a SetScreenStmt object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC SetScreenStmt_New( PSTMT * ppstmt, PSCREENSET pstate, DWORD iLine) { RES res; DBG_ENTER(SetScreenStmt_New); ASSERT(ppstmt); ASSERT(pstate); if (ppstmt && pstate) { PSETSCREENSTMT this; res = SetStmt_New(&this, ST_SCREEN, sizeof(*this), iLine); if (RSUCCEEDED(res)) { DWORD dwFlags = pstate->dwFlags; res = RES_OK; // assume success this->screenset.dwFlags = dwFlags; if (IsFlagSet(dwFlags, SPF_KEYBRD)) this->screenset.fKBOn = pstate->fKBOn; } *ppstmt = (PSTMT)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(SetScreenStmt_New, res); return res; } // // Decl // /*---------------------------------------------------------- Purpose: Callback for PADestroyEx. Returns: -- Cond: -- */ void CALLBACK Decl_DeletePAPtr( LPVOID pv, LPARAM lparam) { Decl_Delete(pv); } /*---------------------------------------------------------- Purpose: Destroys a Decl. Returns: RES_OK RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC Decl_Delete( PDECL this) { RES res; DBG_ENTER(Decl_Delete); if (this) { res = RES_OK; switch (this->ast.asttype) { case AT_MODULE_DECL: { PMODULEDECL pmd = (PMODULEDECL)this; if (pmd->hpaProcs) PADestroyEx(pmd->hpaProcs, Decl_DeletePAPtr, 0); if (pmd->pst) Symtab_Destroy(pmd->pst); } break; case AT_PROC_DECL: { PPROCDECL ppd = (PPROCDECL)this; if (ppd->hpaStmts) PADestroyEx(ppd->hpaStmts, Stmt_DeletePAPtr, 0); if (ppd->pst) Symtab_Destroy(ppd->pst); if (ppd->pszIdent) GSetString(&ppd->pszIdent, NULL); // free } break; default: ASSERT(0); res = RES_E_INVALIDPARAM; break; } if (RSUCCEEDED(res)) Ast_Delete((PAST)this); } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(Decl_Delete, res); return res; } // // ProcDecl // /*---------------------------------------------------------- Purpose: Creates a ProcDecl object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC ProcDecl_New( PDECL * ppdecl, LPCSTR pszIdent, HPA hpa, PSYMTAB pst, DWORD iLine) { RES res; DBG_ENTER(ProcDecl_New); ASSERT(ppdecl); ASSERT(hpa); ASSERT(pst); if (ppdecl) { PPROCDECL this; res = Ast_New(&this, AT_PROC_DECL, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success if (!GSetString(&this->pszIdent, pszIdent)) res = RES_E_OUTOFMEMORY; else { this->hpaStmts = hpa; this->pst = pst; } if (RFAILED(res)) { Decl_Delete((PDECL)this); this = NULL; } } *ppdecl = (PDECL)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(ProcDecl_New, res); return res; } // // ModuleDecl // /*---------------------------------------------------------- Purpose: Creates a ModuleDecl object. Returns: RES_OK RES_E_OUTOFMEMORY RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC ModuleDecl_New( PDECL * ppdecl, HPA hpa, PSYMTAB pst, DWORD iLine) { RES res; DBG_ENTER(ModuleDecl_New); ASSERT(ppdecl); ASSERT(hpa); ASSERT(pst); if (ppdecl) { PMODULEDECL this; res = Ast_New(&this, AT_MODULE_DECL, sizeof(*this), iLine); if (RSUCCEEDED(res)) { res = RES_OK; // assume success this->hpaProcs = NULL; if ( !PAClone(&this->hpaProcs, hpa) ) res = RES_E_OUTOFMEMORY; else { this->pst = pst; } if (RFAILED(res)) { Decl_Delete((PDECL)this); this = NULL; } } *ppdecl = (PDECL)this; } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(ModuleDecl_New, res); return res; } // // AST Exec block // #define SZ_SUCCESS "$SUCCESS" #define SZ_FAILURE "$FAILURE" /*---------------------------------------------------------- Purpose: Initialize the AST exec block. Returns: RES_OK Cond: -- */ RES PUBLIC Astexec_Init( PASTEXEC this, HANDLE hport, PSESS_CONFIGURATION_INFO psci, HSA hsaStxerr) { RES res; ASSERT(this); ASSERT(psci); ASSERT(hsaStxerr); // For this first version, we only support one module and one // main procedure, so set the starting point on the first // statement in that procedure. if (this) { ZeroInit(this, ASTEXEC); this->hport = hport; this->psci = psci; // Don't free hsaStxerr -- it belongs to the caller this->hsaStxerr = hsaStxerr; if ( !PACreate(&this->hpaPcode, 8) ) res = RES_E_OUTOFMEMORY; else { res = Symtab_Create(&this->pstSystem, NULL); if (RSUCCEEDED(res)) { // Add the system variables PSTE pste; struct { LPCSTR pszIdent; DATATYPE dt; EVALRES er; } s_rgvars[] = { { "$USERID", DATA_STRING, psci->szUserName }, { "$PASSWORD", DATA_STRING, psci->szPassword }, { SZ_SUCCESS, DATA_BOOL, (LPSTR)TRUE }, { SZ_FAILURE, DATA_BOOL, (LPSTR)FALSE }, }; int i; for (i = 0; i < ARRAY_ELEMENTS(s_rgvars); i++) { res = STE_Create(&pste, s_rgvars[i].pszIdent, s_rgvars[i].dt); if (RFAILED(res)) break; pste->er.dw = s_rgvars[i].er.dw; res = Symtab_InsertEntry(this->pstSystem, pste); if (RFAILED(res)) break; } } } // Did something fail above? if (RFAILED(res)) { // Yes; clean up Astexec_Destroy(this); } } else res = RES_E_INVALIDPARAM; return res; } /*---------------------------------------------------------- Purpose: Destroys the AST exec block. Returns: RES_OK Cond: -- */ RES PUBLIC Astexec_Destroy( PASTEXEC this) { RES res; if (this) { if (this->hpaPcode) { PADestroy(this->hpaPcode); this->hpaPcode = NULL; } if (this->pstSystem) { Symtab_Destroy(this->pstSystem); this->pstSystem = NULL; } // ('this' was not allocated. Do not free it.) // (hsaStxerr is not owned by this class. Do not free it.) res = RES_OK; } else res = RES_E_INVALIDPARAM; return res; } /*---------------------------------------------------------- Purpose: Sets the success/failure code Returns: -- Cond: -- */ void PUBLIC Astexec_SetError( PASTEXEC this, BOOL bSuccess, // TRUE: success BOOL bFailure) { PSTE pste; ASSERT(this); if (RES_OK == Symtab_FindEntry(this->pstSystem, SZ_SUCCESS, STFF_DEFAULT, &pste, NULL)) { // Set the code for success pste->er.bVal = bSuccess; if (RES_OK == Symtab_FindEntry(this->pstSystem, SZ_FAILURE, STFF_DEFAULT, &pste, NULL)) { // Set the code for failure pste->er.bVal = bFailure; } else ASSERT(0); } else ASSERT(0); } /*---------------------------------------------------------- Purpose: Adds the statement to the executable list. Returns: RES_OK RES_E_OUTOFMEMORY Cond: -- */ RES PUBLIC Astexec_Add( PASTEXEC this, PSTMT pstmt) { RES res; ASSERT(this); ASSERT(pstmt); if (PAInsertPtr(this->hpaPcode, PA_APPEND, pstmt)) res = RES_OK; else res = RES_E_OUTOFMEMORY; return res; } /*---------------------------------------------------------- Purpose: Inserts a label into the executable list by recording the current ipaCur into the label entry in the symbol table. Returns: RES_OK Cond: -- */ RES PUBLIC Astexec_InsertLabel( PASTEXEC this, LPCSTR pszIdent, PSYMTAB pst) { RES res; DWORD ipa; PSTE pste; ASSERT(this); ASSERT(pszIdent); ASSERT(pst); ipa = PAGetCount(this->hpaPcode); if (RES_OK == Symtab_FindEntry(pst, pszIdent, STFF_DEFAULT, &pste, NULL)) { // Set the current code location in the symbol table pste->er.dw = ipa; res = RES_OK; } else { ASSERT(0); res = RES_E_FAIL; } return res; } /*---------------------------------------------------------- Purpose: Jumps to the given label. Returns: RES_OK Cond: -- */ RES PUBLIC Astexec_JumpToLabel( PASTEXEC this, LPCSTR pszIdent) { RES res; PSTE pste; ASSERT(pszIdent); ASSERT(this); ASSERT(this->pstCur); if (RES_OK == Symtab_FindEntry(this->pstCur, pszIdent, STFF_DEFAULT, &pste, NULL)) { EVALRES er; STE_GetValue(pste, &er); // Set instruction pointer Astexec_SetIP(this, (DWORD) er.dw); res = RES_OK; } else { // The label should have been in the symbol table! ASSERT(0); res = RES_E_FAIL; } return res; } /*---------------------------------------------------------- Purpose: Sends psz to the port (via hwnd) Returns: -- Cond: -- */ void PUBLIC Astexec_SendString( PASTEXEC this, LPCSTR pszSend, BOOL bRaw) // TRUE: send unformatted { // Send string LPCSTR psz; char ch; HWND hwnd = this->hwnd; // Send unformatted? if (bRaw) { // Yes for (psz = pszSend; *psz; ) { ch = *psz; psz++; SendByte(hwnd, ch); } } else { // No DWORD dwFlags = 0; for (psz = pszSend; *psz; ) { psz = MyNextChar(psz, &ch, &dwFlags); SendByte(hwnd, ch); } } } /*---------------------------------------------------------- Purpose: Destroy the find format handle Returns: RES_OK Cond: -- */ RES PUBLIC Astexec_DestroyFindFormat( PASTEXEC this) { // Reset the pending statement so we can handle multiple // expressions that can pend in a single evaluation. Astexec_SetPending(this, NULL); DestroyFindFormat(this->hFindFmt); this->hFindFmt = NULL; return RES_OK; } /*---------------------------------------------------------- Purpose: Make another pass at finding a string. Returns: RES_OK (if string was found) RES_FALSE (if the string was not found yet) Cond: -- */ RES PUBLIC Astexec_FindFormat( PASTEXEC this, LPDWORD piFound) { RES res; ASSERT(piFound); while (TRUE) { // Did we get the IP address? res = FindFormat(this->hwnd, this->hFindFmt, piFound); if (RES_OK == res) { // Yes this->nIter--; ASSERT(0 <= this->nIter); // Is this the right one? if (0 >= this->nIter) { // Yes; reset the pending statement so we // can handle multiple pending expressions // in a single evaluation. Astexec_DestroyFindFormat(this); break; } } else { // No; return read-pending RES_FALSE if (RES_E_MOREDATA == res) { TRACE_MSG(TF_GENERAL, "Buffer to FindFormat is too small"); res = RES_OK; // don't blow up } break; } } ASSERT(RSUCCEEDED(res)); return res; } /*---------------------------------------------------------- Purpose: Sets the IP address. Returns: RES_OK RES_E_FAIL (if IP address cannot be set) Cond: -- */ RES PUBLIC Astexec_SetIPAddr( PASTEXEC this, LPCSTR psz) { DWORD dwRet; ASSERT(this); ASSERT(psz); TRACE_MSG(TF_GENERAL, "Setting IP address to {%s}", psz); #ifndef WINNT_RAS // // On NT, the IP address is set by calling RxSetIPAddress, // which writes a new value to the phonebook if the connection uses SLIP. // dwRet = TerminalSetIP(this->hwnd, psz); #else // !WINNT_RAS dwRet = RxSetIPAddress(((SCRIPTDATA*)this->hwnd)->hscript, psz); #endif // !WINNT_RAS return ERROR_SUCCESS == dwRet ? RES_OK : RES_E_FAIL; } #define Astexec_Validate(this) ((this)->hpaPcode && (this)->psci) /*---------------------------------------------------------- Purpose: Returns the source line number of the current command that is executing. Returns: see above Cond: -- */ DWORD PUBLIC Astexec_GetCurLine( PASTEXEC this) { DWORD iLine; if (Astexec_Validate(this) && (this->ipaCur < PAGetCount(this->hpaPcode))) { PSTMT pstmt = PAFastGetPtr(this->hpaPcode, this->ipaCur); iLine = Ast_GetLine(pstmt); } else iLine = 0; return iLine; } /*---------------------------------------------------------- Purpose: Execute a statement and process the results. Returns: RES_OK other error values Cond: -- */ RES PRIVATE Astexec_ProcessStmt( PASTEXEC this, PSTMT pstmt) { RES res; ASSERT(this); ASSERT(pstmt); // (Re-)Execute the (possibly pending) statement res = Stmt_Exec(pstmt, this); // Set the pending statement based on the return value if (RES_OK == res) Astexec_SetPending(this, NULL); else if (RES_FALSE == res) { // (Re-set the current pending statement since // it could have been reset in Stmt_Exec. For // example, the evaluation of an expression could // have continued on to the next sub-expression // that caused another pending read.) Astexec_SetPending(this, pstmt); res = RES_OK; } else if (RFAILED(res)) { Stxerr_ShowErrors(this->hsaStxerr, this->hwnd); // Halt script SetFlag(this->dwFlags, AEF_HALT); } return res; } /*---------------------------------------------------------- Purpose: Executes the next command in the AST. Returns: RES_OK RES_FALSE (if at end of script) RES_HALT (if at end of script) RES_E_FAIL (invalid command -- should never happen) RES_E_INVALIDPARAM Cond: -- */ RES PUBLIC Astexec_Next( PASTEXEC this) { RES res; DBG_ENTER(Astexec_Next); if (this) { if (!Astexec_Validate(this)) { // No script res = RES_E_FAIL; } else if (Astexec_IsDone(this) || Astexec_IsHalted(this)) { res = RES_HALT; } else if (Astexec_IsReadPending(this)) { PSTMT pstmt = Astexec_GetPending(this); // ("Read pending" and "Paused" are mutually exclusive) ASSERT( !Astexec_IsPaused(this) ); res = Astexec_ProcessStmt(this, pstmt); } else if (Astexec_IsPaused(this)) { // ("Read pending" and "Paused" are mutually exclusive) ASSERT( !Astexec_IsReadPending(this) ); // Do nothing while we're paused res = RES_OK; } else if (this->ipaCur < PAGetCount(this->hpaPcode)) { PSTMT pstmt = PAFastGetPtr(this->hpaPcode, this->ipaCur++); res = Astexec_ProcessStmt(this, pstmt); } else { // We reach here if there is an error in the script. TRACE_MSG(TF_ASTEXEC, "Exec: (reached end of script)"); SetFlag(this->dwFlags, AEF_DONE); res = RES_HALT; } } else res = RES_E_INVALIDPARAM; DBG_EXIT_RES(Astexec_Next, res); return res; }