/// <summary> /// Extracts the individual expression elements, or tokens, from a string /// representation of a mathematical expression before they are sent to /// the calculation unit for final processing. /// </summary> /// <returns> A <see cref="System.Collections.Generic.List{DotNetAsm.ExpressionElement}"/> /// </returns> /// <param name="expression">The mathematical expression.</param> public List <ExpressionElement> ParseElements(string expression) { var elements = new List <ExpressionElement>(); StringBuilder elementBuilder = new StringBuilder(); ExpressionElement currentElement = new ExpressionElement(); for (int i = 0; i < expression.Length; i++) { var c = expression[i]; if (char.IsWhiteSpace(c)) { AddElement(); } else if (c.IsOperator() || c == ',') { char next = i < expression.Length - 1 ? expression[i + 1] : char.MinValue; bool nextIsOperand = char.IsLetterOrDigit(next) || next == '_' || next == '.' || next == '#'; if (currentElement.type != ExpressionElement.Type.Operator) { AddElement(); currentElement.type = ExpressionElement.Type.Operator; if (c == ',') { currentElement.subType = ExpressionElement.Subtype.Binary; } else { if (currentElement.subType == ExpressionElement.Subtype.Open) { if (c.IsRadixOperator() && nextIsOperand) { currentElement.type = ExpressionElement.Type.Operand; currentElement.subType = ExpressionElement.Subtype.None; } else { currentElement.subType = ExpressionElement.Subtype.Unary; } } else { currentElement.subType = ExpressionElement.Subtype.Binary; } } } else if (!_compounds.Contains(elementBuilder.ToString() + c)) { currentElement.subType = ExpressionElement.Subtype.Binary; AddElement(); if (c.IsRadixOperator()) { currentElement.type = ExpressionElement.Type.Operand; currentElement.subType = ExpressionElement.Subtype.None; } else { currentElement.subType = ExpressionElement.Subtype.Unary; } } elementBuilder.Append(c); } else if (c == '(' || c == ')') { AddElement(); if (c == '(' && elements.Count > 0 && (currentElement.type == ExpressionElement.Type.Operand && char.IsLetter(currentElement.word[0]))) { // Convert operand expressions to functions where appropriate elementBuilder.Append(elements.Last().word); currentElement.type = ExpressionElement.Type.Function; elements.RemoveAt(elements.Count - 1); AddElement(); } currentElement.type = ExpressionElement.Type.Group; if (c == '(') { currentElement.subType = ExpressionElement.Subtype.Open; } else { currentElement.subType = ExpressionElement.Subtype.Close; } elementBuilder.Append(c); } else { if (currentElement.type != ExpressionElement.Type.Operand) { AddElement(); currentElement.type = ExpressionElement.Type.Operand; currentElement.subType = ExpressionElement.Subtype.None; } elementBuilder.Append(c); } if (i == expression.Length - 1) { AddElement(); } } return(elements); void AddElement() { if (elementBuilder.Length > 0) { currentElement.word = elementBuilder.ToString(); elementBuilder.Clear(); elements.Add(currentElement); } } }
double Calculate(List <ExpressionElement> parsedElements) { var operators = new Stack <ExpressionElement>(); Stack <double> result = new Stack <double>(); int openParens = 0; for (int i = 0; i < parsedElements.Count; i++) { var element = parsedElements[i]; if (openParens > 0) { int parmsPassed = 1; int start = i + 1, len = 0; for (i++; i < parsedElements.Count && openParens > 0; i++) { element = parsedElements[i]; if (element.word.Equals(",") && openParens < 2) { if (len == 0) { throw new Exception(); // we did a f(,n) thing } result.Push(Calculate(parsedElements.GetRange(start, len))); parmsPassed++; start = i + 1; len = 0; } else { if (element.subType == ExpressionElement.Subtype.Open) { openParens++; } else if (element.subType == ExpressionElement.Subtype.Close) { openParens--; } if (openParens > 0) { len++; } } } if (len == 0) { throw new Exception(); // we did a f()/f(n,) thing } i--; result.Push(Calculate(parsedElements.GetRange(start, len))); result.Push(parmsPassed); } else if (element.type == ExpressionElement.Type.Operand) { if (element.word[0].IsRadixOperator()) { var hexbin = element.word.Substring(1); int radix; if (element.word[0] == '%') { radix = 2; hexbin = Regex.Replace(hexbin, @"^([#.]+)$", m => m.Groups[1].Value.Replace("#", "1").Replace(".", "0")); } else { radix = 16; } result.Push(Convert.ToInt64(hexbin, radix)); } else { result.Push(double.Parse(element.word)); } } else if (element.type == ExpressionElement.Type.Function || element.subType == ExpressionElement.Subtype.Open) { operators.Push(element); if (element.type == ExpressionElement.Type.Function) { openParens = 1; } else if (openParens > 0) { openParens++; // we're in a function track opening parens } } else if (element.type == ExpressionElement.Type.Operator) { if (operators.Count > 0) { ExpressionElement topElement = new ExpressionElement(); var elemOrder = _operators[element].Item2; topElement = operators.Peek(); while (topElement.type == ExpressionElement.Type.Function || topElement.type == ExpressionElement.Type.Operator || topElement.subType == ExpressionElement.Subtype.Open) { var topOrder = topElement.type == ExpressionElement.Type.Operator ? _operators[topElement].Item2 : int.MaxValue; if (topElement.subType != ExpressionElement.Subtype.Open && topOrder >= elemOrder) { operators.Pop(); DoOperation(topElement); if (operators.Count > 0) { topElement = operators.Peek(); } else { break; } } else { break; } } } operators.Push(element); } else if (element.subType == ExpressionElement.Subtype.Close) { if (operators.Count > 0) { var topElement = operators.Peek(); while (topElement.subType != ExpressionElement.Subtype.Open) { operators.Pop(); DoOperation(topElement); if (operators.Count == 0) { throw new Exception(); } topElement = operators.Peek(); } if (topElement.subType == ExpressionElement.Subtype.Open) { operators.Pop(); } } } } if (openParens > 0) { throw new Exception(); } while (operators.Count > 0) { DoOperation(operators.Pop()); } void DoOperation(ExpressionElement op) { OperationDef operation = null; List <double> parms = new List <double> { result.Pop() }; if (op.type == ExpressionElement.Type.Function) { operation = _functions[op.word]; var parmcount = operation.Item2; if (parmcount != (int)parms.Last()) { throw new Exception(); // parms passed does not match function's definition } parms.Clear(); while (parmcount-- > 0) { parms.Add(result.Pop()); } } else { operation = _operators[op]; if (op.subType == ExpressionElement.Subtype.Binary) { parms.Add(result.Pop()); } if (op.arithmeticType == ExpressionElement.ArithmeticType.Boolean && parms.Any(p => !(p.AlmostEquals(1) || p.AlmostEquals(0)))) { throw new Exception(); } if (op.arithmeticType == ExpressionElement.ArithmeticType.Integral && parms.Any(p => !p.AlmostEquals(Math.Round(p)))) { throw new Exception(); } } result.Push(operation.Item1(parms)); } if (result.Count > 1) { throw new Exception(); } return(result.Pop()); }
/// <summary> /// Translates all special symbols in the expression into a /// <see cref="System.Collections.Generic.List{DotNetAsm.ExpressionElement}"/> /// for use by the evualator. /// </summary> /// <returns>The expression symbols.</returns> /// <param name="line">The current source line.</param> /// <param name="expression">The expression to evaluate.</param> /// <param name="scope">The current scope.</param> /// <param name="errorOnNotFound">If set to <c>true</c> raise an error /// if a symbol encountered in the expression was not found.</param> public List <ExpressionElement> TranslateExpressionSymbols(SourceLine line, string expression, string scope, bool errorOnNotFound) { char lastTokenChar = char.MinValue; StringBuilder translated = new StringBuilder(), symbolBuilder = new StringBuilder(); for (int i = 0; i < expression.Length; i++) { char c = expression[i]; if (c == '\'' || c == '"') { var literal = expression.GetNextQuotedString(i); var unescaped = literal.TrimOnce(c); if (unescaped.Contains("\\")) { unescaped = Regex.Unescape(unescaped); } var bytes = Assembler.Encoding.GetBytes(unescaped); if (bytes.Length > sizeof(int)) { throw new OverflowException(literal); } if (bytes.Length < sizeof(int)) { Array.Resize(ref bytes, sizeof(int)); } var encodedValue = BitConverter.ToInt32(bytes, 0); translated.Append(encodedValue); i += literal.Length - 1; lastTokenChar = '0'; // can be any operand } else if ((c == '*' || c == '-' || c == '+') && (lastTokenChar.IsOperator() || lastTokenChar == '(' || lastTokenChar == char.MinValue)) { bool isSpecial = false; if (c == '*' && (lastTokenChar == '(' || i == 0 || expression[i - 1] != '*')) { isSpecial = true; translated.Append(Assembler.Output.LogicalPC.ToString()); } else if (lastTokenChar == '(' || lastTokenChar == char.MinValue || (lastTokenChar.IsOperator()) && char.IsWhiteSpace(expression[i - 1])) { int j = i, k; for (; j < expression.Length && expression[j] == c; j++) { symbolBuilder.Append(c); } for (k = j; k < expression.Length && char.IsWhiteSpace(expression[k]); k++) { } if (j >= expression.Length || (lastTokenChar == '(' && expression[k] == ')') || ((lastTokenChar == char.MinValue || lastTokenChar.IsOperator()) && char.IsWhiteSpace(expression[k - 1]) && expression[k].IsOperator())) { isSpecial = true; translated.Append(ConvertAnonymous(symbolBuilder.ToString(), line, errorOnNotFound)); i = j - 1; } symbolBuilder.Clear(); } if (isSpecial) { lastTokenChar = translated[translated.Length - 1]; } else { lastTokenChar = c; translated.Append(c); } } else { if (!char.IsWhiteSpace(c)) { lastTokenChar = c; } translated.Append(c); } } var elements = Assembler.Evaluator.ParseElements(translated.ToString()).ToList(); for (int i = 0; i < elements.Count; i++) { if (elements[i].type == ExpressionElement.Type.Operand && (elements[i].word[0] == '_' || char.IsLetter(elements[i].word[0]))) { var symbol = elements[i].word; if (_constants.ContainsKey(symbol)) { symbol = _constants[symbol].ToString(); } else { symbol = GetNamedSymbolValue(symbol, line, scope); } if (symbol[0] == '-') { elements.Insert(i, new ExpressionElement { word = "-", type = ExpressionElement.Type.Operator, subType = ExpressionElement.Subtype.Unary }); i++; symbol = symbol.Substring(1); } elements[i] = new ExpressionElement { word = symbol, type = ExpressionElement.Type.Operand, subType = ExpressionElement.Subtype.None }; } } return(elements); }