/// <summary> /// Возможно ли расчитать числовое значение данного токена. Если да, то <see cref="Calculate"/> вернет данное значение /// </summary> public bool CanBeCalculated(RuntimeDataPackage package = null) { //Можно посчиатать если: // 1. Значение уже подсчитано // или // 2. Нету дочерных токенов // Если дочерные токены есть, то можно если: // 3. Все значениея дочерных уже подсчиатыны // или // 4. Если ссылки на дочерные установлены // 4.1. И если ссылки - переменные, то если все переменные есть в mz // или // 5. Все дочерные значения примитивные, и их можно подсчитать // // Но подсчитать нельзя если: // Некоторые дочерние токены имеют унарные функции или операторы if (_valueSet) { return(true); } if (Subtokens != null) { bool referenceConditional = true; bool hasRefsToVar = Subtokens.Exists(p => p.Reference != null && p.Reference.Type == ReferenceType.Variable); if (hasRefsToVar) { referenceConditional = package != null && Subtokens .FindAll(p => p.Reference != null && p.Reference.Type == ReferenceType.Variable) .All(p => package.ContainsVarialbe(p.Reference.Index)); } if (!referenceConditional) { return(false); } return((Subtokens.All(p => p._valueSet) || Subtokens.All(p => p.IsSimple)) && (Subtokens.Exists(p => p.UnaryFunction == null) && Subtokens.Exists(p => p.UnaryOperator == null))); } else if (_referenceSet && Reference.Type == ReferenceType.Variable) { return(package != null && package.ContainsVarialbe(Reference.Index)); } else { return(true); } }
/// <summary> /// Расчитывает числовое значение данного токена. Возможно только в случае если <see cref="CanBeCalculated"/> <see cref="true"/> /// </summary> /// <returns></returns> public Constant Calculate(RuntimeDataPackage package) { Steps = 0; if (_valueSet) { return(Value); } if (IsSimple) { CalculateValue(Parse(package), this); _valueSet = true; return(Value); } //1. Найти пару с найбольшим приоритетом //2. Удалить ее, заменив ее решением //Посторять 1-2 пункты пока не останится последний токен //3. Применить унарный оператор //Создаем копию саб токенов. Нам не нужно портить изначальный массив. var subTokens = Subtokens.Select(p => (Token)p.Clone()).ToList(); while (subTokens.Count != 1) { //Индексы в массиве операнов при самом приоритетном операторе int maxLeftIndex = -1; int maxRightIndex = -1; //Приоритет самого приоритетного оператора int maxPriority = 0; //Самый приоритетный оператор Operator op = null; //Ищем оператор for (int i = 0; i < subTokens.Count; i++) { //Ищем по левому, бикоз вай нот if (subTokens[i].LeftSideOperator != null) { if (subTokens[i].LeftSideOperator.Priority > maxPriority) { //Запоминаем наши индексы и прочее maxLeftIndex = i - 1; maxRightIndex = i; maxPriority = subTokens[i].LeftSideOperator.Priority; op = subTokens[i].LeftSideOperator; } } } //Хз, можно убрать наверное /*if (!subTokens[maxLeftIndex].IsPrimitive) * throw new Exception("Token must be primitive"); * * if (!subTokens[maxRightIndex].IsPrimitive) * throw new Exception("Token must be primitive");*/ //Получаем числовые значение //Учитываем, что операнды могут иметь свои унарные операции и функции. //Их приоритет всегда выше бинарных, потому сразу выполняем их CalculateValue(subTokens[maxLeftIndex].Parse(package), subTokens[maxLeftIndex]); CalculateValue(subTokens[maxRightIndex].Parse(package), subTokens[maxRightIndex]); var value = op.BinaryFunc(subTokens[maxLeftIndex].Value, subTokens[maxRightIndex].Value); Steps += op.OperatorSteps; //Дебага ради создаем новое строковое значение var newRawValue = value.ToString(); //Важно грамотно сохранить левые и правые токены var newToken = new Token(newRawValue) { LeftSideOperator = subTokens[maxLeftIndex].LeftSideOperator, LeftSideToken = subTokens[maxLeftIndex].LeftSideToken, RightSideOperator = subTokens[maxRightIndex].RightSideOperator, RightSideToken = subTokens[maxRightIndex].RightSideToken, Value = value, _valueSet = true }; //Подставляем в существующие токены наш новы if (maxLeftIndex - 1 >= 0) { subTokens[maxLeftIndex - 1].RightSideToken = newToken; } if (maxRightIndex + 1 < subTokens.Count) { subTokens[maxRightIndex + 1].LeftSideToken = newToken; } //Удаляем 2 старых subTokens.RemoveAt(maxRightIndex); subTokens.RemoveAt(maxLeftIndex); //Заменяя его на новый subTokens.Insert(maxLeftIndex, newToken); } CalculateValue(subTokens[0].Value, this); return(Value); }