You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1394 lines
35 KiB
1394 lines
35 KiB
//***************************************************************************
|
|
//
|
|
// SQL_1.CPP
|
|
//
|
|
// Level 1 Syntax SQL Parser
|
|
//
|
|
// Implements the syntax described in SQL_1.BNF. This translates the input
|
|
// into an RPN stream of tokens.
|
|
//
|
|
// 21-Jun-96 Created.
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
|
|
#include <genlex.h>
|
|
#include <sqllex.h>
|
|
#include <sql_1.h>
|
|
|
|
//#define trace(x) printf x
|
|
#define trace(x)
|
|
|
|
static DWORD TranslateIntrinsic(LPWSTR pFuncName)
|
|
{
|
|
if (_wcsicmp(pFuncName, L"UPPER") == 0)
|
|
return SQL_LEVEL_1_TOKEN::IFUNC_UPPER;
|
|
if (_wcsicmp(pFuncName, L"LOWER") == 0)
|
|
return SQL_LEVEL_1_TOKEN::IFUNC_LOWER;
|
|
return SQL_LEVEL_1_TOKEN::IFUNC_NONE;
|
|
}
|
|
|
|
SQL1_Parser::SQL1_Parser(CGenLexSource *pSrc)
|
|
{
|
|
m_pLexer = new CGenLexer(Sql_1_LexTable, pSrc);
|
|
m_nLine = 0;
|
|
m_pTokenText = 0;
|
|
m_nCurrentToken = 0;
|
|
|
|
// Semantic transfer variables.
|
|
// ============================
|
|
m_nRelOp = 0;
|
|
VariantInit(&m_vTypedConst);
|
|
m_dwPropFunction = 0;
|
|
m_dwConstFunction = 0;
|
|
m_pIdent = 0;
|
|
m_bConstIsStrNumeric = FALSE;
|
|
|
|
m_pExpression = new SQL_LEVEL_1_RPN_EXPRESSION;
|
|
}
|
|
|
|
SQL1_Parser::~SQL1_Parser()
|
|
{
|
|
VariantClear(&m_vTypedConst);
|
|
delete m_pIdent;
|
|
delete m_pLexer;
|
|
delete m_pExpression;
|
|
}
|
|
|
|
|
|
int SQL1_Parser::GetQueryClass(
|
|
LPWSTR pDestBuf,
|
|
int nBufLen
|
|
)
|
|
{
|
|
// Scan until 'FROM' and then get the class name.
|
|
// ==============================================
|
|
|
|
for (;;)
|
|
{
|
|
m_nCurrentToken = m_pLexer->NextToken();
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_EOF)
|
|
{
|
|
m_pLexer->Reset();
|
|
return FAILED;
|
|
}
|
|
|
|
if (_wcsicmp(m_pLexer->GetTokenText(), L"from") == 0)
|
|
{
|
|
m_nCurrentToken = m_pLexer->NextToken();
|
|
if (m_nCurrentToken != SQL_1_TOK_IDENT)
|
|
{
|
|
m_pLexer->Reset();
|
|
return FAILED;
|
|
}
|
|
|
|
// If here, we have the class name.
|
|
// ================================
|
|
if (wcslen(m_pLexer->GetTokenText()) >= (size_t)nBufLen)
|
|
{
|
|
m_pLexer->Reset();
|
|
return BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
wcscpy(pDestBuf, m_pLexer->GetTokenText());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Reset the scanner.
|
|
// ==================
|
|
m_pLexer->Reset();
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//
|
|
// NOTE: the caller must delete the output expression.
|
|
//
|
|
|
|
int SQL1_Parser::Parse(SQL_LEVEL_1_RPN_EXPRESSION **pOutput)
|
|
{
|
|
*pOutput = 0;
|
|
|
|
int nRes = parse();
|
|
if (nRes)
|
|
return nRes;
|
|
|
|
*pOutput = m_pExpression;
|
|
m_pExpression = 0;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
LPSTR ToAnsi(LPWSTR Src)
|
|
{
|
|
static char buf[256];
|
|
WideCharToMultiByte(CP_ACP, NULL, Src, -1, buf, 256, NULL, NULL);
|
|
return buf;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// Next()
|
|
//
|
|
// Advances to the next token and recognizes keywords, etc.
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOL SQL1_Parser::Next()
|
|
{
|
|
m_nCurrentToken = m_pLexer->NextToken();
|
|
if (m_nCurrentToken == SQL_1_TOK_ERROR)
|
|
return FALSE;
|
|
|
|
m_nLine = m_pLexer->GetLineNum();
|
|
m_pTokenText = m_pLexer->GetTokenText();
|
|
if (m_nCurrentToken == SQL_1_TOK_EOF)
|
|
m_pTokenText = L"<end of file>";
|
|
|
|
// Keyword check.
|
|
// ==============
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_IDENT)
|
|
{
|
|
if (_wcsicmp(m_pTokenText, L"select") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_SELECT;
|
|
else if (_wcsicmp(m_pTokenText, L"from") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_FROM;
|
|
else if (_wcsicmp(m_pTokenText, L"where") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_WHERE;
|
|
else if (_wcsicmp(m_pTokenText, L"like") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_LIKE;
|
|
else if (_wcsicmp(m_pTokenText, L"or") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_OR;
|
|
else if (_wcsicmp(m_pTokenText, L"and") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_AND;
|
|
else if (_wcsicmp(m_pTokenText, L"not") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_NOT;
|
|
else if (_wcsicmp(m_pTokenText, L"IS") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_IS;
|
|
else if (_wcsicmp(m_pTokenText, L"NULL") == 0)
|
|
m_nCurrentToken = SQL_1_TOK_NULL;
|
|
else if (_wcsicmp(m_pTokenText, L"TRUE") == 0)
|
|
{
|
|
m_nCurrentToken = SQL_1_TOK_INT;
|
|
m_pTokenText = L"65535";
|
|
}
|
|
else if (_wcsicmp(m_pTokenText, L"FALSE") == 0)
|
|
{
|
|
m_nCurrentToken = SQL_1_TOK_INT;
|
|
m_pTokenText = L"0";
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <parse> ::= SELECT <prop_list> FROM <classname> WHERE <expr>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int SQL1_Parser::parse()
|
|
{
|
|
int nRes;
|
|
|
|
// SELECT
|
|
// ======
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (m_nCurrentToken != SQL_1_TOK_SELECT)
|
|
return SYNTAX_ERROR;
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
// <prop_list>
|
|
// ===========
|
|
if (nRes = prop_list())
|
|
return nRes;
|
|
|
|
// FROM
|
|
// ====
|
|
if (m_nCurrentToken != SQL_1_TOK_FROM)
|
|
return SYNTAX_ERROR;
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
// <classname>
|
|
// ===========
|
|
if (nRes = class_name())
|
|
return nRes;
|
|
|
|
// WHERE clause.
|
|
// =============
|
|
return opt_where();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <opt_where> ::= WHERE <expr>;
|
|
// <opt_where> ::= <>;
|
|
//
|
|
//***************************************************************************
|
|
int SQL1_Parser::opt_where()
|
|
{
|
|
int nRes;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_EOF)
|
|
{
|
|
trace(("No WHERE clause\n"));
|
|
return SUCCESS;
|
|
}
|
|
|
|
if (m_nCurrentToken != SQL_1_TOK_WHERE)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
// <expr>
|
|
// ======
|
|
if (nRes = expr())
|
|
return nRes;
|
|
|
|
// Verify that the current token is SQL_1_TOK_EOF.
|
|
// ===============================================
|
|
if (m_nCurrentToken != SQL_1_TOK_EOF)
|
|
return SYNTAX_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <prop_list> ::= <property_name> <prop_list_2>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::prop_list()
|
|
{
|
|
int nRes;
|
|
|
|
if (m_nCurrentToken != SQL_1_TOK_ASTERISK &&
|
|
m_nCurrentToken != SQL_1_TOK_IDENT)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (nRes = property_name())
|
|
return nRes;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return prop_list_2();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <prop_list_2> ::= COMMA <prop_list>;
|
|
// <prop_list_2> ::= <>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::prop_list_2()
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_COMMA)
|
|
{
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
return prop_list();
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <property_name> ::= PROPERTY_NAME_STRING;
|
|
// <property_name> ::= ASTERISK;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::property_name()
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_ASTERISK)
|
|
{
|
|
trace(("Asterisk\n"));
|
|
m_pExpression->nNumberOfProperties = 0;
|
|
// This signals 'all properties' to the evaluator
|
|
return SUCCESS;
|
|
}
|
|
|
|
// Else a property name.
|
|
// =====================
|
|
|
|
trace(("Property name %S\n", m_pTokenText));
|
|
|
|
m_pExpression->AddProperty(m_pTokenText);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <classname> ::= CLASS_NAME_STRING;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::class_name()
|
|
{
|
|
if (m_nCurrentToken != SQL_1_TOK_IDENT)
|
|
return SYNTAX_ERROR;
|
|
|
|
trace(("Class name is %S\n", m_pTokenText));
|
|
m_pExpression->bsClassName = SysAllocString(m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <expr> ::= <term> <expr2>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::expr()
|
|
{
|
|
int nRes;
|
|
|
|
if (nRes = term())
|
|
return nRes;
|
|
|
|
if (nRes = expr2())
|
|
return nRes;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <expr2> ::= OR <term> <expr2>;
|
|
// <expr2> ::= <>;
|
|
//
|
|
// Entry: Assumes token OR already current.
|
|
// Exit: Advances a token
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::expr2()
|
|
{
|
|
int nRes;
|
|
|
|
while (1)
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_OR)
|
|
{
|
|
trace(("Token OR\n"));
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (nRes = term())
|
|
return nRes;
|
|
|
|
SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN;
|
|
pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_OR;
|
|
m_pExpression->AddToken(pNewTok);
|
|
}
|
|
else break;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <term> ::= <simple_expr> <term2>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::term()
|
|
{
|
|
int nRes;
|
|
if (nRes = simple_expr())
|
|
return nRes;
|
|
|
|
if (nRes = term2())
|
|
return nRes;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <term2> ::= AND <simple_expr> <term2>;
|
|
// <term2> ::= <>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::term2()
|
|
{
|
|
int nRes;
|
|
|
|
while (1)
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_AND)
|
|
{
|
|
trace(("Token AND\n"));
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (nRes = simple_expr())
|
|
return nRes;
|
|
|
|
// Add the AND token.
|
|
// ==================
|
|
SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN;
|
|
pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_AND;
|
|
m_pExpression->AddToken(pNewTok);
|
|
}
|
|
else break;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <simple_expr> ::= NOT <expr>;
|
|
// <simple_expr> ::= OPEN_PAREN <expr> CLOSE_PAREN;
|
|
// <simple_expr> ::= IDENTIFIER <leading_ident_expr> <finalize>;
|
|
// <simple_expr> ::= VARIANT <rel_operator> <trailing_prop_expr> <finalize>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
int SQL1_Parser::simple_expr()
|
|
{
|
|
int nRes;
|
|
|
|
// NOT <expr>
|
|
// ==========
|
|
if (m_nCurrentToken == SQL_1_TOK_NOT)
|
|
{
|
|
trace(("Operator NOT\n"));
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (nRes = simple_expr())
|
|
return nRes;
|
|
|
|
SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN;
|
|
pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::TOKEN_NOT;
|
|
m_pExpression->AddToken(pNewTok);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// OPEN_PAREN <expr> CLOSE_PAREN
|
|
// =============================
|
|
else if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN)
|
|
{
|
|
trace(("Open Paren: Entering subexpression\n"));
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (expr())
|
|
return SYNTAX_ERROR;
|
|
if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN)
|
|
return SYNTAX_ERROR;
|
|
trace(("Close paren: Exiting subexpression\n"));
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// IDENTIFIER <leading_ident_expr> <finalize>
|
|
// ==========================================
|
|
else if (m_nCurrentToken == SQL_1_TOK_IDENT)
|
|
{
|
|
trace((" Identifier <%S>\n", m_pTokenText));
|
|
|
|
m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1];
|
|
if ( !m_pIdent )
|
|
{
|
|
return FAILED;
|
|
}
|
|
|
|
wcscpy(m_pIdent, m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (nRes = leading_ident_expr())
|
|
return SYNTAX_ERROR;
|
|
|
|
return finalize();
|
|
}
|
|
|
|
// <typed_constant> <rel_operator> <trailing_prop_expr> <finalize>
|
|
// ======================================================
|
|
else if (m_nCurrentToken == SQL_1_TOK_INT ||
|
|
m_nCurrentToken == SQL_1_TOK_REAL ||
|
|
m_nCurrentToken == SQL_1_TOK_QSTRING
|
|
)
|
|
{
|
|
if (nRes = typed_constant())
|
|
return nRes;
|
|
|
|
if (nRes = rel_operator())
|
|
return nRes;
|
|
|
|
if (nRes = trailing_prop_expr())
|
|
return nRes;
|
|
|
|
return finalize();
|
|
}
|
|
|
|
return SYNTAX_ERROR;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <trailing_prop_expr> ::= IDENTIFIER <trailing_prop_expr2>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
int SQL1_Parser::trailing_prop_expr()
|
|
{
|
|
if (m_nCurrentToken != SQL_1_TOK_IDENT)
|
|
return SYNTAX_ERROR;
|
|
|
|
m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1];
|
|
if ( !m_pIdent )
|
|
{
|
|
return FAILED;
|
|
}
|
|
|
|
wcscpy(m_pIdent, m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return trailing_prop_expr2();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <trailing_prop_expr2> ::= OPEN_PAREN IDENTIFIER CLOSE_PAREN;
|
|
// <trailing_prop_expr2> ::= <>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
int SQL1_Parser::trailing_prop_expr2()
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN)
|
|
{
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
// If we got to this point, the string pointed to by m_pIdent
|
|
// was an intrinsic function and not a property name, and we
|
|
// are about to get the property name, so we have to translate
|
|
// the function name to its correct code before overwriting it.
|
|
// ============================================================
|
|
trace(("Translating intrinsic function %S\n", m_pIdent));
|
|
m_dwPropFunction = TranslateIntrinsic(m_pIdent);
|
|
delete m_pIdent;
|
|
|
|
m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1];
|
|
if ( !m_pIdent )
|
|
{
|
|
return FAILED;
|
|
}
|
|
|
|
wcscpy(m_pIdent, m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
}
|
|
|
|
trace(("Property name is %S\n", m_pIdent));
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <leading_ident_expr> ::= OPEN_PAREN <unknown_func_expr>;
|
|
// <leading_ident_expr> ::= <comp_operator> <trailing_const_expr>;
|
|
// <leading_ident_expr> ::= <equiv_operator> <trailing_or_null>;
|
|
// <leading_ident_expr> ::= <is_operator> NULL;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
int SQL1_Parser::leading_ident_expr()
|
|
{
|
|
int nRes;
|
|
if (m_nCurrentToken == SQL_1_TOK_OPEN_PAREN)
|
|
{
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
return unknown_func_expr();
|
|
}
|
|
if (SUCCESS == comp_operator())
|
|
{
|
|
return trailing_const_expr();
|
|
}
|
|
else if(SUCCESS == equiv_operator())
|
|
return trailing_or_null();
|
|
nRes = is_operator();
|
|
if(nRes != SUCCESS)
|
|
return nRes;
|
|
if (m_nCurrentToken != SQL_1_TOK_NULL)
|
|
return LEXICAL_ERROR;
|
|
if (Next())
|
|
return SUCCESS;
|
|
else
|
|
return LEXICAL_ERROR;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <unknown_func_expr> ::= IDENTIFIER CLOSE_PAREN
|
|
// <rel_operator> <trailing_const_expr>;
|
|
//
|
|
// <unknown_func_expr> ::= <typed_constant> CLOSE_PAREN
|
|
// <rel_operator> <trailing_prop_expr>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
int SQL1_Parser::unknown_func_expr()
|
|
{
|
|
int nRes;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_IDENT)
|
|
{
|
|
m_dwPropFunction = TranslateIntrinsic(m_pIdent);
|
|
delete m_pIdent;
|
|
|
|
m_pIdent = new wchar_t[wcslen(m_pTokenText) + 1];
|
|
if ( !m_pIdent )
|
|
{
|
|
return FAILED;
|
|
}
|
|
|
|
wcscpy(m_pIdent, m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN)
|
|
return SYNTAX_ERROR;
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (nRes = rel_operator())
|
|
return nRes;
|
|
return trailing_const_expr();
|
|
}
|
|
|
|
// Else the other production.
|
|
// ==========================
|
|
|
|
if (nRes = typed_constant())
|
|
return nRes;
|
|
|
|
// If here, we know that the leading ident was
|
|
// an intrinsic function.
|
|
// ===========================================
|
|
|
|
m_dwConstFunction = TranslateIntrinsic(m_pIdent);
|
|
delete m_pIdent;
|
|
m_pIdent = 0;
|
|
|
|
if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (nRes = rel_operator())
|
|
return nRes;
|
|
|
|
return trailing_prop_expr();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <trailing_or_null> ::= NULL;
|
|
// <trailing_or_null> ::= <trailing_const_expr>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::trailing_or_null()
|
|
{
|
|
if (m_nCurrentToken == SQL_1_TOK_NULL)
|
|
{
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
else
|
|
{
|
|
V_VT(&m_vTypedConst) = VT_NULL;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
return trailing_const_expr();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <trailing_const_expr> ::= IDENTIFIER OPEN_PAREN
|
|
// <typed_constant> CLOSE_PAREN;
|
|
// <trailing_const_expr> ::= <typed_constant>;
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
int SQL1_Parser::trailing_const_expr()
|
|
{
|
|
int nRes;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_IDENT)
|
|
{
|
|
trace(("Function applied to typed const = %S\n", m_pTokenText));
|
|
|
|
m_dwConstFunction = TranslateIntrinsic(m_pTokenText);
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
if (m_nCurrentToken != SQL_1_TOK_OPEN_PAREN)
|
|
return SYNTAX_ERROR;
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (nRes = typed_constant())
|
|
return nRes;
|
|
|
|
if (m_nCurrentToken != SQL_1_TOK_CLOSE_PAREN)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
return typed_constant();
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <finalize> ::= <>;
|
|
//
|
|
// This composes the SQL_LEVEL_1_TOKEN for a simple relational expression,
|
|
// complete with any associated intrinsic functions. All of the other
|
|
// parse functions help isolate the terms of the expression, but only
|
|
// this function builds the token.
|
|
//
|
|
// To build the token, the following member variables are used:
|
|
// m_pPropName
|
|
// m_vTypedConst
|
|
// m_dwPropFunction
|
|
// m_dwConstFunction
|
|
// m_nRelOp;
|
|
//
|
|
// After the token is built, these are cleared/deallocated as appropriate.
|
|
// No tokens are consumed and the input is not advanced.
|
|
//
|
|
//***************************************************************************
|
|
int SQL1_Parser::finalize()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// At this point, we have all the info needed for a token.
|
|
// =======================================================
|
|
|
|
SQL_LEVEL_1_TOKEN *pNewTok = new SQL_LEVEL_1_TOKEN;
|
|
if ( pNewTok == NULL )
|
|
{
|
|
return FAILED;
|
|
}
|
|
|
|
pNewTok->nTokenType = SQL_LEVEL_1_TOKEN::OP_EXPRESSION;
|
|
pNewTok->pPropertyName = SysAllocString(m_pIdent);
|
|
pNewTok->nOperator = m_nRelOp;
|
|
|
|
VariantInit(&pNewTok->vConstValue);
|
|
hr = VariantCopy(&pNewTok->vConstValue, &m_vTypedConst);
|
|
if ( FAILED( hr ) )
|
|
{
|
|
throw WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pNewTok->dwPropertyFunction = m_dwPropFunction;
|
|
pNewTok->dwConstFunction = m_dwConstFunction;
|
|
pNewTok->bConstIsStrNumeric = m_bConstIsStrNumeric;
|
|
|
|
m_pExpression->AddToken(pNewTok);
|
|
|
|
// Cleanup.
|
|
// ========
|
|
VariantClear(&m_vTypedConst);
|
|
delete m_pIdent;
|
|
m_pIdent = 0;
|
|
m_nRelOp = 0;
|
|
m_dwPropFunction = 0;
|
|
m_dwConstFunction = 0;
|
|
m_bConstIsStrNumeric = FALSE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <typed_constant> ::= VARIANT;
|
|
//
|
|
// Ouput: m_vTypedConst is set to the value of the constant. The only
|
|
// supported types are VT_I4, VT_R8 and VT_BSTR.
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::typed_constant()
|
|
{
|
|
trace((" Typed constant <%S> ", m_pTokenText));
|
|
VariantClear(&m_vTypedConst);
|
|
m_bConstIsStrNumeric = FALSE;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_INT)
|
|
{
|
|
trace((" Integer\n"));
|
|
DWORD x = wcslen(m_pTokenText);
|
|
|
|
if (*m_pTokenText == L'-')
|
|
{
|
|
//negative
|
|
|
|
if ((x < 11) ||
|
|
((x == 11) && (wcscmp(m_pTokenText, L"-2147483648") <= 0)))
|
|
{
|
|
V_VT(&m_vTypedConst) = VT_I4;
|
|
V_I4(&m_vTypedConst) = _wtol(m_pTokenText);
|
|
}
|
|
else
|
|
{
|
|
trace((" Actually Integer String\n"));
|
|
V_VT(&m_vTypedConst) = VT_BSTR;
|
|
V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText);
|
|
m_bConstIsStrNumeric = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//positive
|
|
|
|
if ((x < 10) ||
|
|
((x == 10) && (wcscmp(m_pTokenText, L"2147483647") <= 0)))
|
|
{
|
|
V_VT(&m_vTypedConst) = VT_I4;
|
|
V_I4(&m_vTypedConst) = _wtol(m_pTokenText);
|
|
}
|
|
else
|
|
{
|
|
trace((" Actually Integer String\n"));
|
|
V_VT(&m_vTypedConst) = VT_BSTR;
|
|
V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText);
|
|
m_bConstIsStrNumeric = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_QSTRING)
|
|
{
|
|
trace((" String\n"));
|
|
V_VT(&m_vTypedConst) = VT_BSTR;
|
|
V_BSTR(&m_vTypedConst) = SysAllocString(m_pTokenText);
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_REAL)
|
|
{
|
|
trace((" Real\n"));
|
|
V_VT(&m_vTypedConst) = VT_R8;
|
|
V_R8(&m_vTypedConst) = 0.0;
|
|
|
|
if (m_pTokenText)
|
|
{
|
|
VARIANT varFrom;
|
|
varFrom.vt = VT_BSTR;
|
|
varFrom.bstrVal = SysAllocString(m_pTokenText);
|
|
|
|
if(varFrom.bstrVal != NULL)
|
|
{
|
|
VariantClear(&m_vTypedConst);
|
|
VariantInit(&m_vTypedConst);
|
|
SCODE sc = VariantChangeTypeEx(&m_vTypedConst, &varFrom, 0, 0x409, VT_R8);
|
|
VariantClear(&varFrom);
|
|
|
|
if(sc != S_OK)
|
|
{
|
|
VariantClear(&m_vTypedConst);
|
|
VariantInit(&m_vTypedConst);
|
|
return LEXICAL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Else, not a typed constant.
|
|
else
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <rel_operator> ::= <equiv_operator>;
|
|
// <rel_operator> ::= <comp_operator>;
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::rel_operator()
|
|
{
|
|
if(SUCCESS == equiv_operator())
|
|
return SUCCESS;
|
|
else if (SUCCESS == comp_operator())
|
|
return SUCCESS;
|
|
else return LEXICAL_ERROR;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <equiv_operator> ::= EQUIV_OPERATOR; // =, !=
|
|
//
|
|
// Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN.
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::equiv_operator()
|
|
{
|
|
m_nRelOp = 0;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_EQ)
|
|
{
|
|
trace((" REL OP =\n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUAL;
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_NE)
|
|
{
|
|
trace((" REL OP <> (!=) \n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL;
|
|
}
|
|
else
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <is_operator> ::= IS_OPERATOR; // is, isnot
|
|
//
|
|
// Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN.
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::is_operator()
|
|
{
|
|
m_nRelOp = 0;
|
|
if (m_nCurrentToken != SQL_1_TOK_IS)
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_NOT)
|
|
{
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL;
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
trace((" REL OP IS NOT \n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_NOT_EQUAL;
|
|
return SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
trace((" REL OP IS \n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUAL;
|
|
return SUCCESS;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// <comp_operator> ::= COMP_OPERATOR; // <=, >=, <, >, like
|
|
//
|
|
// Output: m_nRelOp is set to the correct operator for a SQL_LEVEL_1_TOKEN.
|
|
//
|
|
//***************************************************************************
|
|
|
|
int SQL1_Parser::comp_operator()
|
|
{
|
|
m_nRelOp = 0;
|
|
|
|
if (m_nCurrentToken == SQL_1_TOK_LE)
|
|
{
|
|
trace((" REL OP <=\n"));
|
|
|
|
if (m_pIdent)
|
|
{
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN;
|
|
}
|
|
else
|
|
{
|
|
trace((" REL OP changed to >=\n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN;
|
|
}
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_LT)
|
|
{
|
|
trace((" REL OP <\n"));
|
|
|
|
if (m_pIdent)
|
|
{
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LESSTHAN;
|
|
}
|
|
else
|
|
{
|
|
trace((" REL OP changed to >\n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_GREATERTHAN;
|
|
}
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_GE)
|
|
{
|
|
trace((" REL OP >=\n"));
|
|
|
|
if (m_pIdent)
|
|
{
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorGREATERTHAN;
|
|
}
|
|
else
|
|
{
|
|
trace((" REL OP changed to <=\n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_EQUALorLESSTHAN;
|
|
}
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_GT)
|
|
{
|
|
trace((" REL OP >\n"));
|
|
|
|
if (m_pIdent)
|
|
{
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_GREATERTHAN;
|
|
}
|
|
else
|
|
{
|
|
trace((" REL OP changed to <\n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LESSTHAN;
|
|
}
|
|
}
|
|
else if (m_nCurrentToken == SQL_1_TOK_LIKE)
|
|
{
|
|
trace((" REL OP 'like' \n"));
|
|
m_nRelOp = SQL_LEVEL_1_TOKEN::OP_LIKE;
|
|
}
|
|
else
|
|
return SYNTAX_ERROR;
|
|
|
|
if (!Next())
|
|
return LEXICAL_ERROR;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// Expression and token structure methods.
|
|
//
|
|
//***************************************************************************
|
|
|
|
SQL_LEVEL_1_RPN_EXPRESSION::SQL_LEVEL_1_RPN_EXPRESSION()
|
|
{
|
|
nNumTokens = 0;
|
|
pArrayOfTokens = 0;
|
|
bsClassName = 0;
|
|
nNumberOfProperties = 0;
|
|
pbsRequestedPropertyNames = 0;
|
|
nCurSize = 32;
|
|
nCurPropSize = 32;
|
|
pArrayOfTokens = new SQL_LEVEL_1_TOKEN[nCurSize];
|
|
pbsRequestedPropertyNames = new BSTR[nCurPropSize];
|
|
}
|
|
|
|
SQL_LEVEL_1_RPN_EXPRESSION::~SQL_LEVEL_1_RPN_EXPRESSION()
|
|
{
|
|
delete [] pArrayOfTokens;
|
|
if (bsClassName)
|
|
SysFreeString(bsClassName);
|
|
for (int i = 0; i < nNumberOfProperties; i++)
|
|
SysFreeString(pbsRequestedPropertyNames[i]);
|
|
delete pbsRequestedPropertyNames;
|
|
}
|
|
|
|
void SQL_LEVEL_1_RPN_EXPRESSION::AddToken(SQL_LEVEL_1_TOKEN *pTok)
|
|
{
|
|
AddToken(*pTok);
|
|
delete pTok;
|
|
pTok = NULL;
|
|
}
|
|
|
|
void SQL_LEVEL_1_RPN_EXPRESSION::AddToken(SQL_LEVEL_1_TOKEN &pTok)
|
|
{
|
|
if (nCurSize == nNumTokens)
|
|
{
|
|
nCurSize += 32;
|
|
SQL_LEVEL_1_TOKEN *pTemp = new SQL_LEVEL_1_TOKEN[nCurSize];
|
|
for (int i = 0; i < nNumTokens; i++)
|
|
pTemp[i] = pArrayOfTokens[i];
|
|
delete [] pArrayOfTokens;
|
|
pArrayOfTokens = pTemp;
|
|
}
|
|
|
|
pArrayOfTokens[nNumTokens++] = pTok;
|
|
}
|
|
|
|
void SQL_LEVEL_1_RPN_EXPRESSION::AddProperty(LPWSTR pProp)
|
|
{
|
|
if (nCurPropSize == nNumberOfProperties)
|
|
{
|
|
nCurPropSize += 32;
|
|
|
|
BSTR * pTemp = new BSTR[ nCurPropSize ];
|
|
if ( !pTemp )
|
|
{
|
|
return;
|
|
}
|
|
|
|
memcpy(pTemp, pbsRequestedPropertyNames,
|
|
sizeof(BSTR) * nNumberOfProperties);
|
|
delete pbsRequestedPropertyNames;
|
|
pbsRequestedPropertyNames = pTemp;
|
|
}
|
|
|
|
pbsRequestedPropertyNames[nNumberOfProperties++] = SysAllocString(pProp);
|
|
}
|
|
|
|
void SQL_LEVEL_1_RPN_EXPRESSION::Dump(const char *pszTextFile)
|
|
{
|
|
FILE *f = fopen(pszTextFile, "wt");
|
|
if (!f)
|
|
return;
|
|
|
|
fprintf(f, "----RPN Expression----\n");
|
|
fprintf(f, "Class name = %S\n", bsClassName);
|
|
fprintf(f, "Properties selected: ");
|
|
|
|
if (!nNumberOfProperties)
|
|
{
|
|
fprintf(f, "* = all properties selected\n");
|
|
}
|
|
else for (int i = 0; i < nNumberOfProperties; i++)
|
|
{
|
|
fprintf(f, "%S ", pbsRequestedPropertyNames[i]);
|
|
}
|
|
fprintf(f, "\n------------------\n");
|
|
fprintf(f, "Tokens:\n");
|
|
|
|
for (int i = 0; i < nNumTokens; i++)
|
|
pArrayOfTokens[i].Dump(f);
|
|
|
|
fprintf(f, "---end of expression---\n");
|
|
fclose(f);
|
|
}
|
|
|
|
SQL_LEVEL_1_TOKEN::SQL_LEVEL_1_TOKEN()
|
|
{
|
|
nTokenType = 0;
|
|
pPropertyName = 0;
|
|
nOperator = 0;
|
|
VariantInit(&vConstValue);
|
|
dwPropertyFunction = 0;
|
|
dwConstFunction = 0;
|
|
bConstIsStrNumeric = FALSE;
|
|
}
|
|
|
|
SQL_LEVEL_1_TOKEN::SQL_LEVEL_1_TOKEN(SQL_LEVEL_1_TOKEN &Src)
|
|
{
|
|
nTokenType = 0;
|
|
pPropertyName = 0;
|
|
nOperator = 0;
|
|
VariantInit(&vConstValue);
|
|
dwPropertyFunction = 0;
|
|
dwConstFunction = 0;
|
|
bConstIsStrNumeric = FALSE;
|
|
|
|
*this = Src;
|
|
}
|
|
|
|
SQL_LEVEL_1_TOKEN& SQL_LEVEL_1_TOKEN::operator =(SQL_LEVEL_1_TOKEN &Src)
|
|
{
|
|
//first clear any old values...
|
|
if (pPropertyName)
|
|
SysFreeString(pPropertyName);
|
|
|
|
VariantClear(&vConstValue);
|
|
|
|
nTokenType = Src.nTokenType;
|
|
pPropertyName = SysAllocString(Src.pPropertyName);
|
|
nOperator = Src.nOperator;
|
|
HRESULT hr = VariantCopy(&vConstValue, &Src.vConstValue);
|
|
if ( FAILED( hr ) )
|
|
{
|
|
throw WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
dwPropertyFunction = Src.dwPropertyFunction;
|
|
dwConstFunction = Src.dwConstFunction;
|
|
bConstIsStrNumeric = Src.bConstIsStrNumeric;
|
|
return *this;
|
|
}
|
|
|
|
SQL_LEVEL_1_TOKEN::~SQL_LEVEL_1_TOKEN()
|
|
{
|
|
nTokenType = 0;
|
|
if (pPropertyName)
|
|
SysFreeString(pPropertyName);
|
|
|
|
nOperator = 0;
|
|
VariantClear(&vConstValue);
|
|
}
|
|
|
|
void SQL_LEVEL_1_TOKEN::Dump(FILE *f)
|
|
{
|
|
switch (nTokenType)
|
|
{
|
|
case OP_EXPRESSION:
|
|
fprintf(f, "OP_EXPRESSION ");
|
|
break;
|
|
case TOKEN_AND:
|
|
fprintf(f, "TOKEN_AND ");
|
|
break;
|
|
case TOKEN_OR:
|
|
fprintf(f, "TOKEN_OR ");
|
|
break;
|
|
case TOKEN_NOT:
|
|
fprintf(f, "TOKEN_NOT ");
|
|
break;
|
|
default:
|
|
fprintf(f, "Error: no token type specified\n");
|
|
}
|
|
|
|
if (nTokenType == OP_EXPRESSION)
|
|
{
|
|
char *pOp = "<no op>";
|
|
switch (nOperator)
|
|
{
|
|
case OP_EQUAL: pOp = "OP_EQUAL"; break;
|
|
case OP_NOT_EQUAL: pOp = "OP_NOT_EQUAL"; break;
|
|
case OP_EQUALorGREATERTHAN: pOp = "OP_EQUALorGREATERTHAN"; break;
|
|
case OP_EQUALorLESSTHAN: pOp = "OP_EQUALorLESSTHAN"; break;
|
|
case OP_LESSTHAN: pOp = "OP_LESSTHAN"; break;
|
|
case OP_GREATERTHAN: pOp = "OP_GREATERTHAN"; break;
|
|
case OP_LIKE: pOp = "OP_LIKE"; break;
|
|
}
|
|
|
|
fprintf(f, " Property = %S\n", pPropertyName);
|
|
fprintf(f, " Operator = %s\n", pOp);
|
|
fprintf(f, " Value = ");
|
|
|
|
switch (V_VT(&vConstValue))
|
|
{
|
|
case VT_I4:
|
|
fprintf(f, "VT_I4 = %d\n", V_I4(&vConstValue));
|
|
break;
|
|
case VT_BSTR:
|
|
fprintf(f, "VT_BSTR = %S\n", V_BSTR(&vConstValue));
|
|
break;
|
|
case VT_R8:
|
|
fprintf(f, "VT_R8 = %f\n", V_R8(&vConstValue));
|
|
break;
|
|
default:
|
|
fprintf(f, "<unknown>\n");
|
|
}
|
|
|
|
switch (dwPropertyFunction)
|
|
{
|
|
case IFUNC_NONE:
|
|
break;
|
|
case IFUNC_LOWER:
|
|
fprintf(f, "Intrinsic function LOWER() applied to property\n");
|
|
break;
|
|
case IFUNC_UPPER:
|
|
fprintf(f, "Intrinsic function UPPER() applied to property\n");
|
|
break;
|
|
}
|
|
switch (dwConstFunction)
|
|
{
|
|
case IFUNC_NONE:
|
|
break;
|
|
case IFUNC_LOWER:
|
|
fprintf(f, "Intrinsic function LOWER() applied to const value\n");
|
|
break;
|
|
case IFUNC_UPPER:
|
|
fprintf(f, "Intrinsic function UPPER() applied to const value\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(f, " <end of token>\n");
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Algorithm for evaluating the expression, assuming that it has been
|
|
// tokenized and translated to Reverse Polish.
|
|
//
|
|
// Starting point: (a) An array of SQL tokens.
|
|
// (b) An empty boolean token stack.
|
|
//
|
|
// 1. Read Next Token
|
|
//
|
|
// 2. If a SIMPLE EXPRESSION, evaluate it to TRUE or FALSE, and
|
|
// place this boolean result on the stack. Go to 1.
|
|
//
|
|
// 3. If an OR operator, then pop a boolean token into A,
|
|
// pop another boolean token into B. If either A or B are TRUE,
|
|
// stack TRUE. Else stack FALSE.
|
|
// Go to 1.
|
|
//
|
|
// 4. If an AND operator, then pop a boolean token into A,
|
|
// and pop another into B. If both are TRUE, stack TRUE.
|
|
// Else stack FALSE.
|
|
// Go to 1.
|
|
//
|
|
// 5. If a NOT operator, reverse the value of the top-of-stack boolean.
|
|
// Go to 1.
|
|
//
|
|
// At end-of-input, the result is at top-of-stack.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|