// 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; }