void __ExprCalcOperatorBinary(_OP op, ref _EVAL A, _EVAL B) { if (A.op < B.op) { A.op = B.op; } switch (A.op) { case _OP.OperandInt: A.value = (ulong)__ExprCalcOperatorBinary(op, (int)A.value, (int)B.value); break; case _OP.OperandUint: A.value = __ExprCalcOperatorBinary(op, (uint)A.value, (uint)B.value); break; case _OP.OperandLong: A.value = (ulong)__ExprCalcOperatorBinary(op, (long)A.value, (long)B.value); break; case _OP.OperandUlong: A.value = __ExprCalcOperatorBinary(op, A.value, B.value); break; } }
//int _debugNFailed; #region CALC_UNARY void __ExprCalcOperatorUnary(_OP op, ref _EVAL A) { switch (A.op) { case _OP.OperandInt: A.value = (ulong)__ExprCalcOperatorUnary(op, (int)A.value); break; case _OP.OperandUint: A.value = __ExprCalcOperatorUnary(op, (uint)A.value); break; case _OP.OperandLong: A.value = (ulong)__ExprCalcOperatorUnary(op, (long)A.value); break; case _OP.OperandUlong: A.value = __ExprCalcOperatorUnary(op, A.value); break; } }
bool __ExprCalcCast(int i, ref _EVAL A) { //AOutput.Write(_tok[i]); _Symbol x; if (!_TryFindSymbol(i, out x, false)) { //AOutput.Write(_tok[i]); //0 in SDK //_Err(i, "unknown"); return(false); } if (0 != _Unalias(i, ref x) || _TokIsChar(i + 1, '*')) { //support expressions like '#define RT_BITMAP (LPWSTR)((ULONG_PTR)((WORD)(2)))' if (A.op <= _OP.OperandUint) { A.op = _OP.OperandInt; return(true); } //_Err(i, "stop"); //0 in SDK return(false); } var ct = x as _CppType; if (ct == null) { //support LPARAM, HWND if (A.op > _OP.OperandUint) { return(false); //0 in SDK } if (x == _sym_LPARAM || x == _sym_Wnd) { return(true); } //support enum, callback if (x is _Enum || x is _Callback) { return(true); } //_Err(i, "stop 2"); //0 in SDK return(false); } switch (ct.sizeBytesCpp) { case 8: if (ct.csTypename[0] == 'd') { return(false); //double } if (ct.csTypename[0] == 'I') //IntPtr //AOutput.Write(_DebugGetLine(i)); { if (A.op > _OP.OperandUint) { return(false); //0 in SDK } break; //let be the same type as in 32-bit mode (no long). All in SDK are like #define HKEY_CURRENT_USER (IntPtr)0x80000001. } A.op = ct.isUnsigned ? _OP.OperandUlong : _OP.OperandLong; return(true); case 4: if (ct.csTypename[0] == 'f') { return(false); //float } A.value = (uint)A.value; break; case 2: A.value = (ushort)A.value; break; case 1: if (ct.csTypename == "bool") { A.value = A.value == 0U ? 0U : 1U; } else { A.value = (byte)A.value; } break; default: return(false); } A.op = ct.isUnsigned ? _OP.OperandUint : _OP.OperandInt; return(true); }
void __ExprDebugOutValue(_EVAL v) { AOutput.Write($" {v.op}, { __ExprValueToString(v.value, v.op)}"); }
_ExpressionResult _Expression(int iFrom, int iTo, string debugConstName = null) { Debug.Assert(iTo > iFrom); int nTok = iTo - iFrom, iLast = iTo - 1; string sValue, sType; bool isHex = false; //if(debugConstName == "[]") return new _ExpressionResult(_TokToString(iFrom, iTo), null, true); //AOutput.Write($"{debugConstName} {_TokToString(iFrom, iTo)}"); //Unenclose whole expression (not parts). while (nTok >= 3 && _TokIsChar(iFrom, '(') && _TokIsChar(iLast, ')')) { if (nTok == 3 || _SkipEnclosed(iFrom) == iLast) { iFrom++; iLast--; iTo--; nTok -= 2; } else { break; } } //Simple cases. if (nTok == 1) { ulong value; _OP type; if (_TokIsNumber(iFrom)) { if (__ExprNumber(iFrom, out value, out type, ref isHex)) { type = __ExprMakeUintIfHex(type, isHex); sValue = __ExprValueToString(value, type); sType = __ExprTypeToString(type); return(new _ExpressionResult(sValue, sType, false, value)); } else { sValue = _TokToString(iFrom); sType = sValue.Ends("f", true) ? "float" : "double"; return(new _ExpressionResult(sValue, sType, false)); } } if (_TokIsChar(iFrom, '\"')) { return(new _ExpressionResult(_TokToString(iFrom), "string", false)); } if (_TokIsChar(iFrom, '\'')) { //is like 'ABCD'? int len = _tok[iFrom].len - 2; if (len > 1 && len <= 4) { char *s = T(iFrom) + 1; if (*s != '\\') { //AOutput.Write(_DebugGetLine(iFrom)); uint k = 0; for (int j = 0; j < len; j++) { k = (k << 8) | ((uint)s[j] & 0xff); } return(new _ExpressionResult($"0x{k:X}", "uint", false, k)); } } //string k=_TokToString(iFrom).Re return(new _ExpressionResult(_TokToString(iFrom), "char", false)); } if (_TokIsIdent(iFrom)) { if (_EnumFindValue(iFrom, out value, out type)) { sValue = __ExprValueToString(value, type); sType = __ExprTypeToString(type); return(new _ExpressionResult(sValue, sType, false, value)); } //AOutput.Write($"<><c 0xff0000>{_TokToString(iFrom, iTo)}</c>"); } else { //AOutput.Write($"<><c 0xff0000>{_TokToString(iFrom, iTo)}</c>"); //0 in SDK } return(new _ExpressionResult(_TokToString(iFrom), null, true)); } //Calculate expression. //OutList(debugConstName, _TokToString(iFrom, iTo)); //Part 1 - convert the expression from normal infix form to RPN form which is easy to calculate. //Use the shunting-yard algorithm: https://en.wikipedia.org/wiki/Shunting-yard_algorithm //Put operands and operators in _eRPN, which is the "output queue" of the algorithm. int nRPN = 0; //number of elements in the output queue int nOp = 0; //number of elements in the operator stack int i; bool wasOperand = false; ////Debug //if(debugConstName == "WTS_CURRENT_SERVER") { // AOutput.Write(debugConstName); // int stop = 0; //} for (i = iFrom; i < iTo; i++) { int iTok = i; _OP op; char c = *T(i); if (_IsCharIdent(c) || c == '\'') //digit, identifier or 'character' { if (wasOperand) { goto gFail; //cannot be 2 operands } wasOperand = true; //If the token is a number, then add it to the output queue. ulong value; _OP type = _OP.OperandInt; if (_IsCharDigit(c)) //number { if (!__ExprNumber(i, out value, out type, ref isHex)) { //_Err(i, "bad number"); //0 in SDK goto gFail; } } else if (c == '\'') { char *s = T(i) + 1; int len = _tok[i].len - 2; if (len > 1) { if (len == 2 && *s == '\\') { s++; } else { goto gFail; } } value = *s; } else //identifier { if (_TokIs(i, "sizeof")) { if (!_TokIsChar(++i, '(')) { goto gFail; } int rightParen = __ExprIsType(++i); if (rightParen != i + 1) { goto gFail; //if not like a type, or with * } if (!__ExprSizeof(i, out value)) { goto gFail; } i++; } else { if (!_EnumFindValue(i, out value, out type)) { goto gFail; } } } _eRPN[nRPN++] = new _EVAL(i, type, value); } else if (_IsCharOperator(c) || c == '(') //operator or cast or ( { if (c == '(') { //is cast? int rightParen = __ExprIsType(i + 1); bool isCast = rightParen > 0; if (isCast && rightParen - i == 2 && _enumValues.ContainsKey(_tok[i + 1])) { isCast = false; //is '(enumValue)'? } if (isCast) //cast { iTok = i + 1; i = rightParen; op = _OP.Cast; //cast is an operator } else { iTok = i; op = _OP.LeftParen; } } else { char c2 = *T(i + 1); op = 0; if (wasOperand) //this must be a binary operator { //note: cannot be +=, ++ etc because they can be used only with variables, not with constants. wasOperand = false; switch (c) { case '+': op = _OP.Plus; break; case '-': op = _OP.Minus; break; case '*': op = _OP.Mul; break; case '/': op = _OP.Div; break; case '%': op = _OP.Mod; break; case '&': if (c2 == '&') { i++; op = _OP.LogAnd; } else { op = _OP.BitAnd; } break; case '|': if (c2 == '|') { i++; op = _OP.LogOr; } else { op = _OP.BitOr; } break; case '^': op = _OP.BitXor; break; case '<': if (c2 == '<') { i++; op = _OP.BitLshift; } else if (c2 == '=') { i++; op = _OP.LtEq; } else { op = _OP.Lt; } break; case '>': if (c2 == '>') { i++; op = _OP.BitRshift; } else if (c2 == '=') { i++; op = _OP.GtEq; } else { op = _OP.Gt; } break; case '=': if (c2 == '=') { i++; op = _OP.CompareEq; } else { goto gFail; } break; case '!': if (c2 == '=') { i++; op = _OP.CompareNotEq; } else { goto gFail; } break; //case '?': // op = _OPERATOR.Ternary; // break; default: goto gFail; } } else //this must be a prefix operator //note: cannot be ++, & etc because they can be used only with variables, not with constants. { switch (c) { case '+': continue; case '-': op = _OP.UnaryMinus; break; case '!': op = _OP.UnaryLogNot; break; case '~': op = _OP.UnaryBitNot; break; default: goto gFail; } } } //If the token is an operator (op), then: if (op != _OP.LeftParen) { //While there is an operator (op2) at the top of the stack, and either // op is left-associative and its precedence is less than or equal to that of op2, or // op is right associative, and has precedence less than that of o2, // pop op2 off the stack, onto the output queue. for (; nOp > 0; nOp--) { int n = nOp - 1; _OP op2 = _eOp[n].op; if (op2 == _OP.LeftParen) { break; //is an operator at the top of the stack? } bool pop = false, opRTL = (op & _OP._FlagRightToLeft) != 0; int prec1 = (int)(op & _OP._MaskPrecedence), prec2 = (int)(op2 & _OP._MaskPrecedence); //OutList(" ", op, prec1, opRTL, op2, prec2); if (!opRTL) { pop = prec1 >= prec2; //op is left-associative and its precedence is less than or equal to that of op2 } else { pop = prec1 > prec2; //op is right associative, and has precedence less than that of o2 } if (!pop) { break; } _eRPN[nRPN++] = _eOp[n]; } } //Else if the token is a left parenthesis (i.e. "("), then push it onto the stack. //push op to the stack _eOp[nOp++] = new _EVAL(iTok, op); } else if (c == ')') { //If the token is a right parenthesis (i.e. ")"): // Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue. for (; nOp > 0; nOp--) { int n = nOp - 1; if (_eOp[n].op == _OP.LeftParen) { break; } _eRPN[nRPN++] = _eOp[n]; } //If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. if (nOp == 0) { goto gFail; } //Pop the left parenthesis from the stack, but not onto the output queue. nOp--; } else { //"string" //0 in SDK goto gFail; } } //When there are no more tokens to read: // While there are still operator tokens in the stack: // If the operator token on the top of the stack is a parenthesis: error, there are mismatched parentheses. // Else pop the operator onto the output queue. for (; nOp > 0; nOp--) { int n = nOp - 1; if (_eOp[n].op == _OP.LeftParen) { goto gFail; } _eRPN[nRPN++] = _eOp[n]; } ////Debug //if(debugConstName == "WTS_CURRENT_SERVER") { // var deb = new StringBuilder(debugConstName); // deb.Append(" = "); // deb.Append(_TokToString(iFrom, iTo)); // deb.Append(" -> "); // for(i = 0; i < nRPN; i++) { // deb.Append(" "); // if(_eRPN[i].op < _OP._FirstOperator) deb.Append(_eRPN[i].value); // else deb.Append(_eRPN[i].op); // } // AOutput.Write(deb); // int stop = 0; //} //Part 2 - calculate the RPN expression. //Use the postfix algorithm: https://en.wikipedia.org/wiki/Reverse_Polish_notation int nCalc = 0; //_eCalc stack length for (i = 0; i < nRPN; i++) { if (_eRPN[i].op < _OP._FirstOperator) //operand //If the token is a value (operand): push it onto the stack. { _eCalc[nCalc++] = _eRPN[i]; } else //operator //It is already known that the operator takes n arguments. { int nOperands = (int)(_eRPN[i].op & _OP._MaskNOperands) >> 8; //If there are fewer than n values on the stack: error, the user has not input sufficient values in the expression. if (nCalc < nOperands) { goto gFail; } if (nOperands == 1) { //Pop the top n values from the stack. //A = _eCalc[--nCalc]; //operand (we change it in-place without pop/push) //Evaluate the operator, with the values as arguments. _OP op = _eRPN[i].op; if (op == _OP.Cast) { if (!__ExprCalcCast(_eRPN[i].iTok, ref _eCalc[nCalc - 1])) { goto gFail; } } else { __ExprCalcOperatorUnary(op, ref _eCalc[nCalc - 1]); } } else //2 operands //Pop the top n values from the stack. { _EVAL B = _eCalc[--nCalc]; //rigt operand //A = _eCalc[--nCalc]; //left operand (we change it in-place without pop/push) //Evaluate the operator, with the values as arguments. __ExprCalcOperatorBinary(_eRPN[i].op, ref _eCalc[nCalc - 1], B); //__ExprDebugOutValue(_eCalc[nCalc - 1]); } //Push the returned results, if any, back onto the stack. //_eCalc[nCalc++] = A; //we change it in-place without pop/push } } //If there is only one value in the stack, it is the result of the calculation. //Otherwise, there are more values in the stack: error, the user input has too many values. if (nCalc != 1) { goto gFail; } _OP rType = __ExprMakeUintIfHex(_eCalc[0].op, isHex); sValue = __ExprValueToString(_eCalc[0].value, rType); sType = __ExprTypeToString(rType); return(new _ExpressionResult(sValue, sType, false, _eCalc[0].value)); gFail: //AOutput.Write($"<><c 0xff>{debugConstName} {_TokToString(iFrom, iTo)}</c>"); //if(++_debugNFailed > 10) _Err(iFrom, "debug"); return(new _ExpressionResult(_TokToString(iFrom, iTo), null, true)); //never mind: need functions: //LongToHandle //HRESULT_FROM_WIN32 //never mind: sizeof pointers, IntPtr, LPARAM and AWnd. }