165 lines
4 KiB
C++
165 lines
4 KiB
C++
// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Implementation of ExprBlock.
|
|
//
|
|
|
|
#include "stdinc.h"
|
|
#include "engexpr.h"
|
|
|
|
HRESULT
|
|
Expression::Generate()
|
|
{
|
|
HRESULT hr = InfixToPostfix();
|
|
if (FAILED(hr))
|
|
{
|
|
// clean up the working stack
|
|
while (!m_stack.empty())
|
|
m_stack.pop();
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
int Precedence(Token t)
|
|
{
|
|
switch(t)
|
|
{
|
|
case TOKEN_op_pow: return 10;
|
|
case TOKEN_sub: return 9; // unary - (negation)
|
|
case TOKEN_op_mult: return 8;
|
|
case TOKEN_op_div: return 7;
|
|
case TOKEN_op_mod: return 6;
|
|
case TOKEN_op_plus:
|
|
case TOKEN_op_minus: return 5;
|
|
case TOKEN_op_lt:
|
|
case TOKEN_op_leq:
|
|
case TOKEN_op_gt:
|
|
case TOKEN_op_geq:
|
|
case TOKEN_op_eq:
|
|
case TOKEN_op_neq:
|
|
case TOKEN_is: return 4;
|
|
case TOKEN_op_not: return 3;
|
|
case TOKEN_and: return 2;
|
|
case TOKEN_or: return 1;
|
|
case TOKEN_lparen: return 0;
|
|
default:
|
|
assert(false);
|
|
return 12;
|
|
}
|
|
}
|
|
|
|
// Infix to postfix conversion is performed by a single scan of the infix expression blocks.
|
|
// A stack is used to hold some of the blocks before they eventually are appended to the postfix expression.
|
|
//
|
|
// The algorithm follows the following rules:
|
|
// * If the current item is an value it is immediately appended.
|
|
// * If the current item is an operator, pop and append each operator on the stack until one is encountered that:
|
|
// - has lower precedence than the current operator OR
|
|
// - is a left paren OR
|
|
// - is a unary operator and the current item is also unary
|
|
// Once done with this popping, push the current item onto the stack.
|
|
// * If the current item is a left paren, push it onto the stack.
|
|
// * If the current item is a right paren, pop and append all the operators until the matching left paren is found.
|
|
// Discard the left and right paren as parens are not needed in postfix.
|
|
// * After scanning all items of the input, pop and append any operators that remain on the stack.
|
|
//
|
|
// Before working with this code, try out a few expressions on paper to see how this works.
|
|
|
|
HRESULT
|
|
Expression::InfixToPostfix()
|
|
{
|
|
assert(m_stack.empty());
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ExprBlocks::index iLast = m_e.Next();
|
|
assert(iLast > 0);
|
|
for (ExprBlocks::index i = 0; i < iLast; ++i)
|
|
{
|
|
const ExprBlock &b = m_e[i];
|
|
if (b.k == ExprBlock::_val || b.k == ExprBlock::_call)
|
|
{
|
|
// this is an operand -- send it directly to the postfix output
|
|
hr = m_eblocks.Add(b);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
if (b.op == TOKEN_rparen)
|
|
{
|
|
// pop whatever's left until the matching lparen
|
|
for (;;)
|
|
{
|
|
if (m_stack.empty())
|
|
{
|
|
assert(false);
|
|
return E_FAIL;
|
|
}
|
|
|
|
ExprBlock bPop = m_stack.top();
|
|
if (bPop.op == TOKEN_lparen)
|
|
{
|
|
m_stack.pop();
|
|
break;
|
|
}
|
|
hr = m_eblocks.Add(bPop);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
m_stack.pop();
|
|
}
|
|
continue;
|
|
}
|
|
else if (b.op != TOKEN_lparen)
|
|
{
|
|
// Pop all operators of lower precedence off the stack. (This won't pass a left paren because its precedence is set to 0.)
|
|
// Exception: don't pop a unary operator if the new one is also unary.
|
|
int iNewPrecidence = Precedence(b.op);
|
|
bool fNewUnary = b.op == TOKEN_sub || b.op == TOKEN_op_not;
|
|
while (!m_stack.empty()) // note that there's a break inside the loop as well
|
|
{
|
|
ExprBlock bPop = m_stack.top();
|
|
if (Precedence(bPop.op) < iNewPrecidence)
|
|
break;
|
|
|
|
if (fNewUnary && (bPop.op == TOKEN_sub || bPop.op == TOKEN_op_not))
|
|
break;
|
|
|
|
hr = m_eblocks.Add(bPop);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
m_stack.pop();
|
|
}
|
|
}
|
|
|
|
// now push the new operator onto the stack
|
|
hr = m_stack.push(b);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
while (!m_stack.empty())
|
|
{
|
|
ExprBlock bPop = m_stack.top();
|
|
if (bPop.op == TOKEN_lparen)
|
|
{
|
|
assert(false);
|
|
return E_FAIL;
|
|
}
|
|
hr = m_eblocks.Add(bPop);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
m_stack.pop();
|
|
}
|
|
|
|
// Add the teminating (_end) block
|
|
hr = m_eblocks.Add(ExprBlock(ExprBlock::cons_end()));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return S_OK;
|
|
}
|