Run Code
|
API
|
Code Wall
|
Misc
|
Feedback
|
Login
|
Theme
|
Privacy
|
Patreon
jjj4
//Rextester.Program.Main is the entry point for your code. Don't change it. //Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5 using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Globalization; namespace Rextester { public enum ScalarType { INT, FLOAT, TEXT, DATE }; class ApplicationException : Exception { public ApplicationException(String msg) : base(msg) { } } class FormulaException : Exception { public FormulaException(String msg) : base(msg) { } } class FuncNotRecognizedException : Exception { public FuncNotRecognizedException(String msg) : base(msg) { } } class CellAddressNotRecognizedException : Exception { public CellAddressNotRecognizedException(String msg) : base(msg) { } } class MismatchedParenException : Exception { public MismatchedParenException(String msg) : base(msg) { } } public struct CellAddress { public int mRow, mCol; public bool mHasFixedRow, mHasFixedCol; public CellAddress GetCopy() { CellAddress oOut = new CellAddress(); oOut.mCol = this.mCol; oOut.mRow = this.mRow; oOut.mHasFixedCol = this.mHasFixedCol; oOut.mHasFixedRow = this.mHasFixedRow; return oOut; } } public class ScalarValue { ScalarType mType; Int32 mValueInt; double mValueFloat; DateTime mValueDate; String mValueText; bool mIsValueSet; public ScalarValue() { mType = ScalarType.TEXT; mValueInt = 0; mValueFloat = 0; //mValueDate = algo; mValueText = String.Empty; mIsValueSet = false; } // copy constructor public ScalarValue(ScalarValue sv) { this.mType = sv.mType; this.mValueInt = sv.mValueInt; this.mValueFloat = sv.mValueFloat; this.mValueDate = sv.mValueDate; this.mValueText = sv.mValueText; this.mIsValueSet = sv.mIsValueSet; } public ScalarValue(Int32 val) { SetInt(val); } public ScalarValue(double val) { SetFloat(val); } public ScalarValue(DateTime val) { SetDate(val); } public ScalarValue(String val) { SetText(val); } public ScalarValue(String val, bool bCheck) { TextToSpecificType(val); } public ScalarType GetScalarType() { return mType; } public Int32 GetInt() { return this.mValueInt; } public double GetFloat() { return this.mValueFloat; } public DateTime GetDate() { return this.mValueDate; } public String GetText() { return this.mValueText; } public double GetAsFloat() { return (this.mValueFloat + this.mValueInt); } public void SetInt(Int32 iVal) { Clear(); this.mValueInt = iVal; mType = ScalarType.INT; mIsValueSet = true; } public void SetFloat(double fVal) { Clear(); this.mValueFloat = fVal; mType = ScalarType.FLOAT; mIsValueSet = true; } public void SetDate(DateTime dVal) { Clear(); this.mValueDate = dVal; mType = ScalarType.DATE; mIsValueSet = true; } public void SetText(String sVal) { Clear(); this.mValueText = sVal; mType = ScalarType.TEXT; mIsValueSet = true; } // return true if INT or FLOAT public bool IsNumeric() { return (mType == ScalarType.INT || mType == ScalarType.FLOAT); } public void Clear() { mIsValueSet = false; mValueInt = 0; mValueFloat = 0; mValueDate = DateTime.Now; mValueText = String.Empty; } public void SetTextWithCulture(CultureInfo culture) { if (mType == ScalarType.DATE) this.mValueText = Convert.ToString(this.mValueDate, culture); else if (mType == ScalarType.FLOAT) this.mValueText = Convert.ToString(this.mValueFloat, culture); else if (mType == ScalarType.INT) this.mValueText = Convert.ToString(this.mValueInt, culture); else if (mType == ScalarType.TEXT) ; else throw new ApplicationException("SetTextWithCulture(): type not implemented"); } // Return an int representation of the current value public Int32 ToInt() { if (mType == ScalarType.DATE) return 0; else if (mType == ScalarType.FLOAT) return ((Int32)Math.Round(mValueFloat)); else if (mType == ScalarType.INT) return this.mValueInt; else if (mType == ScalarType.TEXT) { try { Int32 i = Convert.ToInt32(mValueText); return i; } catch (FormatException e) { return 0; } } else throw new ApplicationException("ToInt(): type not implemented"); } // Return a float representation of the current value public double ToFloat() { if (mType == ScalarType.DATE) return 0; else if (mType == ScalarType.FLOAT) return mValueFloat; else if (mType == ScalarType.INT) return 1.0 * this.mValueInt; else if (mType == ScalarType.TEXT) { try { double f = Convert.ToDouble(mValueText); return f; } catch (FormatException e) { return 0; } } else throw new ApplicationException("ToFloat(): type not implemented"); } // Return a date representation of the current value public DateTime ToDate() { if (mType == ScalarType.DATE) return mValueDate; else if (mType == ScalarType.FLOAT) return DateTime.Now; else if (mType == ScalarType.INT) return DateTime.Now; else if (mType == ScalarType.TEXT) { try { DateTime d = Convert.ToDateTime(mValueText); return d; } catch (FormatException e) { return DateTime.Now; } } else throw new ApplicationException("ToDate(): type not implemented"); } public String ToText() { if (mType == ScalarType.DATE) return Convert.ToString(this.mValueDate); else if (mType == ScalarType.FLOAT) return Convert.ToString(this.mValueFloat); else if (mType == ScalarType.INT) return Convert.ToString(this.mValueInt); else if (mType == ScalarType.TEXT) return this.mValueText; else return String.Empty; } // Set mType. // The value variables have to be reset accordingly. public void SetType(ScalarType new_type) { if (mIsValueSet == false) { mType = new_type; return; } ScalarType oldType = mType; if (oldType == ScalarType.INT) { if (new_type == ScalarType.INT) ; // same type, done else if (new_type == ScalarType.FLOAT) SetFloat(this.ToFloat()); else if (new_type == ScalarType.DATE) SetDate(this.ToDate()); else if (new_type == ScalarType.TEXT) SetText(this.ToText()); } else if (oldType == ScalarType.FLOAT) { if (new_type == ScalarType.INT) SetInt(ToInt()); else if (new_type == ScalarType.FLOAT) ; // same type, done else if (new_type == ScalarType.DATE) SetDate(this.ToDate()); else if (new_type == ScalarType.TEXT) SetText(this.ToText()); } else if (oldType == ScalarType.DATE) { if (new_type == ScalarType.INT) SetInt(ToInt()); else if (new_type == ScalarType.FLOAT) SetFloat(this.ToFloat()); else if (new_type == ScalarType.DATE) ; // same type, done else if (new_type == ScalarType.TEXT) SetText(this.ToText()); } else if (oldType == ScalarType.TEXT) { if (new_type == ScalarType.INT) SetInt(ToInt()); else if (new_type == ScalarType.FLOAT) SetFloat(this.ToFloat()); else if (new_type == ScalarType.DATE) SetDate(this.ToDate()); else if (new_type == ScalarType.TEXT) ; // same type, done } } // Take string and attempt to store it as INT, FLOAT, or DATE. If unsuccessful, save as TEXT public void TextToSpecificType(String txt) { mIsValueSet = false; String trimmed = txt.Trim(); try { Int32 i = Convert.ToInt32(trimmed); SetInt(i); return; } catch (FormatException e) { } try { double f = Convert.ToDouble(trimmed); SetFloat(f); return; } catch (FormatException e) { } try { DateTime d = Convert.ToDateTime(trimmed); SetDate(d); return; } catch (FormatException e) { } SetText(trimmed); } public ScalarValue Add(ScalarValue sv) { if (!this.IsNumeric() || !sv.IsNumeric()) throw new FormulaException("Addition is only defined for numeric types."); if (this.mType == ScalarType.INT && sv.mType == ScalarType.INT) return new ScalarValue(this.mValueInt + sv.mValueInt); else if (this.mType == ScalarType.FLOAT && sv.mType == ScalarType.FLOAT) return new ScalarValue(this.mValueFloat + sv.mValueFloat); else return new ScalarValue(this.GetAsFloat() + sv.GetAsFloat()); } public ScalarValue Subtract(ScalarValue sv) { if (!this.IsNumeric() || !sv.IsNumeric()) throw new FormulaException("Subtraction is only defined for numeric types."); if (this.mType == ScalarType.INT && sv.mType == ScalarType.INT) return new ScalarValue(this.mValueInt - sv.mValueInt); else if (this.mType == ScalarType.FLOAT && sv.mType == ScalarType.FLOAT) return new ScalarValue(this.mValueFloat - sv.mValueFloat); else return new ScalarValue(this.GetAsFloat() - sv.GetAsFloat()); } public ScalarValue Multiply(ScalarValue sv) { if (!this.IsNumeric() || !sv.IsNumeric()) throw new FormulaException("Multiplication is only defined for numeric types."); if (this.mType == ScalarType.INT && sv.mType == ScalarType.INT) return new ScalarValue(this.mValueInt * sv.mValueInt); else if (this.mType == ScalarType.FLOAT && sv.mType == ScalarType.FLOAT) return new ScalarValue(this.mValueFloat * sv.mValueFloat); else return new ScalarValue(this.GetAsFloat() * sv.GetAsFloat()); } public ScalarValue Divide(ScalarValue sv) { if (!this.IsNumeric() || !sv.IsNumeric()) throw new FormulaException("Division is only defined for numeric types."); if (this.mType == ScalarType.INT && sv.mType == ScalarType.INT) return new ScalarValue(this.mValueInt / sv.mValueInt); else if (this.mType == ScalarType.FLOAT && sv.mType == ScalarType.FLOAT) return new ScalarValue(this.mValueFloat / sv.mValueFloat); else return new ScalarValue(this.GetAsFloat() / sv.GetAsFloat()); } public ScalarValue Power(ScalarValue sv) { if (!this.IsNumeric() || !sv.IsNumeric()) throw new FormulaException("Exponentiation is only defined for numeric types."); return new ScalarValue(Math.Pow(this.GetAsFloat(), sv.GetAsFloat())); } public static ScalarValue AddJJJ(ScalarValue[] pParams) { if (!pParams[0].IsNumeric() || !pParams[1].IsNumeric()) throw new FormulaException("Addition is only defined for numeric types."); if (pParams[0].mType == ScalarType.INT && pParams[1].mType == ScalarType.INT) return new ScalarValue(pParams[0].mValueInt + pParams[1].mValueInt); else return new ScalarValue(pParams[0].GetAsFloat() + pParams[1].GetAsFloat()); } public static ScalarValue SubtractJJJ(ScalarValue[] pParams) { if (!pParams[0].IsNumeric() || !pParams[1].IsNumeric()) throw new FormulaException("Subtraction is only defined for numeric types."); if (pParams[0].mType == ScalarType.INT && pParams[1].mType == ScalarType.INT) return new ScalarValue(pParams[0].mValueInt - pParams[1].mValueInt); else return new ScalarValue(pParams[0].GetAsFloat() - pParams[1].GetAsFloat()); } public static ScalarValue MultiplyJJJ(ScalarValue[] pParams) { if (!pParams[0].IsNumeric() || !pParams[1].IsNumeric()) throw new FormulaException("Multiplication is only defined for numeric types."); if (pParams[0].mType == ScalarType.INT && pParams[1].mType == ScalarType.INT) return new ScalarValue(pParams[0].mValueInt * pParams[1].mValueInt); else return new ScalarValue(pParams[0].GetAsFloat() * pParams[1].GetAsFloat()); } public static ScalarValue DivideJJJ(ScalarValue[] pParams) { if (!pParams[0].IsNumeric() || !pParams[1].IsNumeric()) throw new FormulaException("Division is only defined for numeric types."); if (pParams[0].mType == ScalarType.INT && pParams[1].mType == ScalarType.INT) return new ScalarValue(pParams[0].mValueInt / pParams[1].mValueInt); else return new ScalarValue(pParams[0].GetAsFloat() / pParams[1].GetAsFloat()); } public static ScalarValue FuncLengthJJJ(ScalarValue[] pParams) { ScalarValue sv = pParams[0]; if (sv.mType == ScalarType.TEXT) { return new ScalarValue(sv.GetText().Length); } else throw new FormulaException("Length is only defined for text."); } public static ScalarValue FuncLnJJJ(ScalarValue[] pParams) { ScalarValue sv = pParams[0]; if (sv.IsNumeric()) { return new ScalarValue(Math.Log(sv.GetAsFloat())); } else throw new FormulaException("LN is only defined for numbers."); } public static ScalarValue FuncLogJJJ(ScalarValue[] pParams) { ScalarValue sv = pParams[0]; if (sv.IsNumeric()) { return new ScalarValue(Math.Log10(sv.GetAsFloat())); } else throw new FormulaException("LOG is only defined for numbers."); } } public enum TokenType { OPERATION, SCALAR, PAREN_OPEN, PAREN_CLOSE, FUNCTION, ADDRESS, RANGE, QUOTATION, COMMA, PARAMETER_LIST, UNKNOWN }; public class Token { TokenType mType; String mTokenText; CellAddress mAddress; ScalarValue mScalar; public readonly static String[] LITERAL_LIST = { "E", "PI" }; public readonly static double[] LITERAL_VALUES = { Math.E, Math.PI }; readonly static String[] FUNCTION_LIST = { "E", "PI", "LENGTH", "LN", "LOG", "LOWER", "TRIM", "UPPER" }; readonly static String[] OPERATION_LIST = { "+", "-", "*", "/", "^" }; //public Token(TokenType t) //{ // mType = t; // if (mType != TokenType.PAREN_CLOSE && mType != TokenType.PAREN_OPEN) // throw new FormulaException("Using wrong version of the Token constructor. This one is for parenthesis."); //} public Token(ScalarValue scalar) { mType = TokenType.SCALAR; mScalar = scalar; ScalarToText(); } public Token(TokenType t, String txt) { mType = t; mTokenText = txt; switch (mType) { case TokenType.ADDRESS: this.mAddress = new CellAddress(); CheckAddress(txt); break; case TokenType.FUNCTION: CheckFunction(txt); break; case TokenType.OPERATION: CheckOperation(txt); break; case TokenType.RANGE: //CheckRange(txt); break; case TokenType.SCALAR: CheckScalar(txt); ScalarToText(); break; case TokenType.PAREN_OPEN: break; case TokenType.PAREN_CLOSE: break; case TokenType.QUOTATION: break; case TokenType.COMMA: break; case TokenType.UNKNOWN: break; default: throw new FormulaException("How did this happen? Unrecognized type in Token constructor."); } } private Token() { } public Token(Token src) { Token oToken = new Token(); oToken.mScalar = new ScalarValue(src.mScalar); oToken.mType = src.mType; oToken.mAddress = src.mAddress.GetCopy(); oToken.mTokenText = src.mTokenText; } public TokenType GetTokenType() { return mType; } public String GetTokenText() { return mTokenText; } public ScalarType GetScalarType() { return this.mScalar.GetScalarType(); } public double GetFloat() { ScalarType type = mScalar.GetScalarType(); if (mScalar.IsNumeric() == false) throw new ApplicationException("GetFloat(): called on non-numeric"); return (mScalar.GetInt() + mScalar.GetFloat()); } // return a copy of mScalar public ScalarValue GetScalar() { return new ScalarValue(mScalar); } private void ScalarToText() { if (mType == TokenType.SCALAR) this.mTokenText = mScalar.ToText(); } public CellAddress GetAddress() { return this.mAddress.GetCopy(); } private static void ParseAddress(String address, ref CellAddress addrStruct) { const char cFixedChar = '$'; int iRowIndex = -1; int iStartIndex = 0; if (address[0] == cFixedChar) { iStartIndex++; addrStruct.mHasFixedCol = true; } else addrStruct.mHasFixedCol = false; for (int i = iStartIndex; i < address.Length && iRowIndex < 0; i++) if (address[i] == '0' || address[i] == '1' || address[i] == '2' || address[i] == '3' || address[i] == '4' || address[i] == '5' || address[i] == '6' || address[i] == '7' || address[i] == '8' || address[i] == '9') iRowIndex = i; if (iRowIndex < 0) throw new CellAddressNotRecognizedException("ParseAddress did not recognize the address. No digits."); if ((addrStruct.mHasFixedCol && iRowIndex == 1) || (!addrStruct.mHasFixedCol && iRowIndex == 0)) throw new CellAddressNotRecognizedException("ParseAddress did not recognize the address. No column."); if (address[iRowIndex - 1] == cFixedChar) addrStruct.mHasFixedRow = true; else addrStruct.mHasFixedRow = false; String sRowText = address.Substring(iRowIndex); String sColumnText = address.Substring(0, iRowIndex); try { addrStruct.mRow = Convert.ToInt16(sRowText); } catch (FormatException e) { throw new CellAddressNotRecognizedException("ParseAddress did not recognize the address. Could not convert row number."); } Int64 iCol = 0; for (int i = 0; i < sColumnText.Length; i++) { if (sColumnText[i] == 'A') iCol = iCol * 26 + 1; else if (sColumnText[i] == 'B') iCol = iCol * 26 + 2; else if (sColumnText[i] == 'C') iCol = iCol * 26 + 3; else if (sColumnText[i] == 'D') iCol = iCol * 26 + 4; else if (sColumnText[i] == 'E') iCol = iCol * 26 + 5; else if (sColumnText[i] == 'F') iCol = iCol * 26 + 6; else if (sColumnText[i] == 'G') iCol = iCol * 26 + 7; else if (sColumnText[i] == 'H') iCol = iCol * 26 + 8; else if (sColumnText[i] == 'I') iCol = iCol * 26 + 9; else if (sColumnText[i] == 'J') iCol = iCol * 26 + 10; else if (sColumnText[i] == 'K') iCol = iCol * 26 + 11; else if (sColumnText[i] == 'L') iCol = iCol * 26 + 12; else if (sColumnText[i] == 'M') iCol = iCol * 26 + 13; else if (sColumnText[i] == 'N') iCol = iCol * 26 + 14; else if (sColumnText[i] == 'O') iCol = iCol * 26 + 15; else if (sColumnText[i] == 'P') iCol = iCol * 26 + 16; else if (sColumnText[i] == 'Q') iCol = iCol * 26 + 17; else if (sColumnText[i] == 'R') iCol = iCol * 26 + 18; else if (sColumnText[i] == 'S') iCol = iCol * 26 + 19; else if (sColumnText[i] == 'T') iCol = iCol * 26 + 20; else if (sColumnText[i] == 'U') iCol = iCol * 26 + 21; else if (sColumnText[i] == 'V') iCol = iCol * 26 + 22; else if (sColumnText[i] == 'W') iCol = iCol * 26 + 23; else if (sColumnText[i] == 'X') iCol = iCol * 26 + 24; else if (sColumnText[i] == 'Y') iCol = iCol * 26 + 25; else if (sColumnText[i] == 'Z') iCol = iCol * 26 + 26; else throw new CellAddressNotRecognizedException("CheckAddress did not recognize the address. Could not convert column name: " + sColumnText); } if (iCol > Int16.MaxValue) throw new CellAddressNotRecognizedException("CheckAddress did not recognize the address. Column too large: " + sColumnText); addrStruct.mCol = (Int16)iCol; // convert to zero-based index addrStruct.mRow--; addrStruct.mCol--; } private void CheckAddress(String address) { Token.ParseAddress(address, ref this.mAddress); } private void CheckFunction(String func) { bool bFound = false; foreach (String f in Token.FUNCTION_LIST) { if (f == func) { bFound = true; break; } } if (!bFound) throw new FuncNotRecognizedException("CheckFunction did not recognize the function name."); } private void CheckOperation(String op) { bool bFound = false; foreach (String o in Token.OPERATION_LIST) { if (o == op) { bFound = true; break; } } if (!bFound) throw new FormulaException("CheckOperation did not recognize the operator."); } private void CheckScalar(String txt) { try { this.mScalar = new ScalarValue(txt, true); } catch (FormulaException e) { throw e; } } private void CheckRange(String range) { throw new FormulaException("CheckRange should not be called."); //int iColonIndex = -1; //for (int i = 0; i < range.Length; i++) //{ // if (range[i] == ':') // if (iColonIndex < 0) // iColonIndex = i; // else // throw new FormulaException("CheckRange found more than one colon."); //} //if (iColonIndex < 0) // throw new FormulaException("CheckRange did not find a colon."); //String sAddr1 = range.Substring(0, iColonIndex); //String sAddr2 = range.Substring(iColonIndex); //ParseAddress(sAddr1, ref this.mAddress1); //ParseAddress(sAddr2, ref this.mAddress2); } private static void CopyArrayToTokenList(Token[] pArray, ref List<Token> pList) { for (int i = 0; i < pArray.Length; i++) pList.Add(pArray[i]); } //Go through the token list. In UNKNOWN tokens, replace the given char with a token of a certain type. private static List<Token> ReplaceCharWithToken(List<Token> pList, char symbol, TokenType type) { // list 2 to contain the result of the replacement List<Token> oTokenList2 = new List<Token>(); // cycle through the symbols and parse foreach (Token oToken in pList) { if (oToken.GetTokenType() == TokenType.UNKNOWN) { String sTmpFormula = oToken.GetTokenText(); String sTmp = String.Empty; foreach (char c in sTmpFormula) { if (c == symbol) { if (sTmp.Length > 0) oTokenList2.Add(new Token(TokenType.UNKNOWN, sTmp)); sTmp = String.Empty; oTokenList2.Add(new Token(type, String.Empty + symbol)); } else sTmp = sTmp + c; } // add the last token, if it should be added if (sTmp.Length > 0) oTokenList2.Add(new Token(TokenType.UNKNOWN, sTmp)); } else oTokenList2.Add(oToken); } return oTokenList2; } public static String RemoveAllSpaces(String txt) { String sOut = String.Empty; foreach (char c in txt) if (!char.IsWhiteSpace(c)) sOut = sOut + c; return sOut; } // Parse the formula into an array of tokens public static Token[] ParseFormula(String formula) { formula = RemoveAllSpaces(formula); List<Token> oTokenList = new List<Token>(); // Check for quotation marks first. There could be a '+' inside quotes. { bool bQuotesPaired = true; int iIndex = 0; for (int i=0; i<formula.Length; i++) { if (formula[i] == '"') { bQuotesPaired = !bQuotesPaired; String s1 = formula.Substring(iIndex, i - iIndex); if (s1.Length > 0) if (bQuotesPaired) { // save the quoted text as a text SCALAR so it will not be further parsed ScalarValue sv = new ScalarValue(s1); oTokenList.Add(new Token(sv)); } else oTokenList.Add(new Token(TokenType.UNKNOWN, s1)); Token oToken = new Token(TokenType.QUOTATION, "\""); oTokenList.Add(oToken); iIndex = i + 1; } } // insert the last token String s = formula.Substring(iIndex, formula.Length - iIndex); if (s.Length > 0) oTokenList.Add(new Token(TokenType.UNKNOWN, s)); if (!bQuotesPaired) throw new FormulaException("Mismatched quotation marks."); } // Now check for commas, operators, and parentheses if (true) { oTokenList = ReplaceCharWithToken(oTokenList, ',', TokenType.COMMA); oTokenList = ReplaceCharWithToken(oTokenList, '(', TokenType.PAREN_OPEN); oTokenList = ReplaceCharWithToken(oTokenList, ')', TokenType.PAREN_CLOSE); oTokenList = ReplaceCharWithToken(oTokenList, '+', TokenType.OPERATION); oTokenList = ReplaceCharWithToken(oTokenList, '-', TokenType.OPERATION); oTokenList = ReplaceCharWithToken(oTokenList, '*', TokenType.OPERATION); oTokenList = ReplaceCharWithToken(oTokenList, '/', TokenType.OPERATION); oTokenList = ReplaceCharWithToken(oTokenList, '^', TokenType.OPERATION); oTokenList = ReplaceCharWithToken(oTokenList, ':', TokenType.RANGE); } else { String[] aSymbolArray = { "(", ")", "+", "-", "*", "/", "^", ":" }; TokenType[] aTokenTypeArray = { TokenType.PAREN_OPEN, TokenType.PAREN_CLOSE, TokenType.OPERATION , TokenType.OPERATION , TokenType.OPERATION , TokenType.OPERATION , TokenType.OPERATION, TokenType.RANGE }; if (aSymbolArray.Length != aTokenTypeArray.Length) throw new FormulaException("Programming error in ParseFormula. Sizes do not match."); int n = aSymbolArray.Length; for (int i = 0; i < n; i++) { // list 2 to contain the result of the parsing List<Token> oTokenList2 = new List<Token>(); char symbol = aSymbolArray[i][0]; TokenType type = aTokenTypeArray[i]; // cycle through the symbols and parse for (int j = 0; j < oTokenList.Count; j++) { Token oToken = oTokenList[j]; if (oToken.GetTokenType() == TokenType.UNKNOWN) { String sTmpFormula = oTokenList[j].GetTokenText(); // and replace it with the result of parsing Token[] oTmpArray = ParseFormula(sTmpFormula, symbol, type, 0); CopyArrayToTokenList(oTmpArray, ref oTokenList2); } else oTokenList2.Add(oToken); } oTokenList = oTokenList2; } } // check for mismatched parentheses { int iCount = 0; for (int j = 0; j < oTokenList.Count; j++) { Token oToken = oTokenList[j]; if (oToken.GetTokenType() == TokenType.PAREN_OPEN) iCount++; else if (oToken.GetTokenType() == TokenType.PAREN_CLOSE) iCount--; if (iCount < 0) throw new MismatchedParenException("Close parenthesis found with no matching open parenthesis."); } if (iCount != 0) throw new MismatchedParenException("There are mismatched parentheses."); } // Operations, parentheses, and quotation marks are parsed. // Now look at the UNKNOWN tokens. They are functions, scalars, or cells // Check in that order. TokenType[] aTypeToCheck = { TokenType.FUNCTION, TokenType.SCALAR, TokenType.ADDRESS }; for (int i = 0; i < oTokenList.Count; i++) { Token oToken = oTokenList[i]; if (oToken.GetTokenType() == TokenType.UNKNOWN) { bool bConverted = false; for (int t = 0; t < aTypeToCheck.Length && !bConverted; t++) { Token oNewToken = null; TokenType type = aTypeToCheck[t]; String text = oToken.GetTokenText(); try { oNewToken = new Token(type, text); bConverted = true; if (type == TokenType.SCALAR && oNewToken.GetScalarType() == ScalarType.TEXT) // conversion to numeric type failed bConverted = false; } catch (FuncNotRecognizedException e) { // not a serious exception. Part of control flow bConverted = false; } catch (CellAddressNotRecognizedException e) { // not a serious exception. Part of control flow bConverted = false; } // if the conversion succeeded, replace the token if (bConverted) { oTokenList[i] = oNewToken; //bConverted = false; } } if (!bConverted) throw new FuncNotRecognizedException("Text not recognized: " + oToken.GetTokenText()); } } return oTokenList.ToArray(); } // Recursive procedure to parse the formula private static Token[] ParseFormula(String formula, char symbol, TokenType type, int recursionLevel) { if (recursionLevel++ > 100) throw new FormulaException("ParseFormula exceeded maximum recursion. Is there a circular definition?"); List<Token> oTokenList = new List<Token>(); // Check for symbol int n = formula.Length; int iSymbolIndex = -1; // find the first location of the symbol, if there is one for (int i = 0; i < n && iSymbolIndex < 0; i++) { if (formula[i] == symbol) iSymbolIndex = i; } // if the symbol is not found, return a single token if (iSymbolIndex < 0) oTokenList.Add(new Token(TokenType.UNKNOWN, formula)); else { // Parse the text before the symbol String s1 = formula.Substring(0, iSymbolIndex); if (s1.Length > 0) { Token[] oTmpArray = ParseFormula(s1, symbol, type, recursionLevel); CopyArrayToTokenList(oTmpArray, ref oTokenList); } // Add the symbol token oTokenList.Add(new Token(type, new String(symbol, 1))); // Parse the text after the parenthesis if ((iSymbolIndex + 1) < n) { String s2 = formula.Substring(iSymbolIndex + 1); Token[] oTmpArray = ParseFormula(s2, symbol, type, recursionLevel); CopyArrayToTokenList(oTmpArray, ref oTokenList); } } return oTokenList.ToArray(); } } struct LiteralInfoStruct { public String mName; public ScalarValue mValue; } delegate ScalarValue ScalarCalcProc(ScalarValue[] pParams); struct FunctionInfoStruct { public String mName; public int mNumParams; public ScalarCalcProc mCalcProc; } struct BinaryOperationInfoStruct { public String mName; public ScalarCalcProc mCalcProc; public int mPrecedence; } struct PostFixUnaryOperationStruct { public String mName; public ScalarCalcProc mCalcProc; public int mPrecedence; } public class TokenJJJ { TokenType mType; String mTokenText; CellAddress mAddress; ScalarValue mScalar; List<TokenJJJ> mParameterList; List<LiteralInfoStruct> mListLiterals; List<FunctionInfoStruct> mListFunctions; List<BinaryOperationInfoStruct> mListBinaryOps; List<PostFixUnaryOperationStruct> mListPostFixUnaryOps; void FillLists() { mListLiterals = new List<LiteralInfoStruct>(); { LiteralInfoStruct str = new LiteralInfoStruct(); str.mName = "E"; str.mValue = new ScalarValue(2.7); mListLiterals.Add(str); str = new LiteralInfoStruct(); str.mName = "PI"; str.mValue = new ScalarValue(3.14); mListLiterals.Add(str); } mListFunctions = new List<FunctionInfoStruct>(); { FunctionInfoStruct str = new FunctionInfoStruct(); str.mName = "LENGTH"; str.mNumParams = 1; str.mCalcProc = Rextester.ScalarValue.FuncLengthJJJ; str.mPrecedence = 6; mListFunctions.Add(str); str = new FunctionInfoStruct(); str.mName = "LN"; str.mNumParams = 1; str.mCalcProc = ScalarValue.FuncLnJJJ; str.mPrecedence = 6; mListFunctions.Add(str); str = new FunctionInfoStruct(); str.mName = "LOG"; str.mNumParams = 1; str.mCalcProc = ScalarValue.FuncLogJJJ; str.mPrecedence = 6; mListFunctions.Add(str); } mListBinaryOps = new List<BinaryOperationInfoStruct>(); { BinaryOperationInfoStruct str = new BinaryOperationInfoStruct(); str.mName = "+"; str.mCalcProc = ScalarValue.AddJJJ; str.mPrecedence = 1; mListBinaryOps.Add(str); str = new BinaryOperationInfoStruct(); str.mName = "-"; str.mCalcProc = ScalarValue.SubtractJJJ; str.mPrecedence = 1; mListBinaryOps.Add(str); str = new BinaryOperationInfoStruct(); str.mName = "*"; str.mCalcProc = ScalarValue.MultiplyJJJ; str.mPrecedence = 2; mListBinaryOps.Add(str); str = new BinaryOperationInfoStruct(); str.mName = "/"; str.mCalcProc = ScalarValue.DivideJJJ; str.mPrecedence = 2; mListBinaryOps.Add(str); } } public TokenJJJ(int i) { mType = TokenType.SCALAR; mScalar = new ScalarValue(i); } public TokenJJJ(double f) { mType = TokenType.SCALAR; mScalar = new ScalarValue(f); } public TokenJJJ(String s) { mType = TokenType.SCALAR; mScalar = new ScalarValue(s); } public TokenJJJ(TokenType t) { mType = t; } public TokenJJJ(TokenType t, String s) { mType = t; mScalar = new ScalarValue(s); } // public enum TokenType { OPERATION, SCALAR, PAREN_OPEN, PAREN_CLOSE, FUNCTION, ADDRESS, RANGE, QUOTATION, COMMA, PARAMETER_LIST, UNKNOWN }; // TokenType ADDRESS UNKNOWN COMMA RANGE QUOTATION FUNCTION OPERATION PAREN_OPEN, PAREN_CLOSE, //Calculate the tokens enclosed by a pair of parentheses. // pParenIndex is the index of the open parenthesis private static TokenJJJ Calc1( ref List<TokenJJJ> pTokenList, bool pParenBlock) { bool bCloseParenFoundOrEnd = false; Stack<TokenJJJ> oScalarStack = new Stack<TokenJJJ>(); Stack<TokenJJJ> oOperationStack = new Stack<TokenJJJ>(); TokenJJJ oToken = null; // Get next token, and remove from list if (pTokenList.Count > 0) { oToken = pTokenList[0]; pTokenList.RemoveAt(0); } while (oToken is not null) { bool bScalarJustPushed = false; // processing if (oToken.GetType() == TokenType.PAREN_CLOSE) { if (!pParenBlock) throw new Exception("Unmatched close parenthesis"); oToken = null; bProcessAllOperations = true; } else if (oToken.GetType() == TokenType.PAREN_OPEN) { iParenDepth++; pScalarStack.Push(oToken); } else if if (oToken.GetType() == TokenType.SCALAR) { bScalarJustPushed = true; pScalarStack.Push(oToken); } else if if (oToken.GetType() == TokenType.COMMA) { pOpStack.Push(oToken); } // public enum TokenType { OPERATION, FUNCTION, ADDRESS, RANGE, QUOTATION, PARAMETER_LIST, UNKNOWN }; } } public static TokenJJJ Calculate(List<TokenJJJ> pTokenList) { //Enclose the list with parentheses // pTokenList.Insert(0, new TokenJJJ(TokenType.PAREN_OPEN)); // pTokenList.Add(new TokenJJJ(TokenType.PAREN_CLOSE)); Stack<TokenJJJ> oScalarStack = new Stack<TokenJJJ>(); Stack<TokenJJJ> oOperationStack = new Stack<TokenJJJ>(); return Calc1(pTokenList, oScalarStack, oOperationStack); } } public class Program { public static void Main(string[] args) { int i = 0; TokenJJJ[] t = new TokenJJJ[5]; t[i++] = new TokenJJJ(1); t[i++] = new TokenJJJ(TokenType.OPERATION, "+"); t[i++] = new TokenJJJ(2); t[i++] = new TokenJJJ(TokenType.OPERATION, "*"); t[i++] = new TokenJJJ(3); //Your code goes here Console.WriteLine("Hello, world!"); } } }
run
|
edit
|
history
|
help
0
Generic hierarchy
Vignere Cipher
hacker
encryption
d
TEST 1
Events
length
Fórum ➡ GroupJoin'ing Books and Orders ♦
Random number maker block (better version)