/// <inheritdoc /> public string EvaluateExpression(string expression) { string[] exprParts = expression.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); StringBuilder sb = new StringBuilder(1024); for (int i = -1, ilen = exprParts.Length; ++i != ilen;) { string exprPart = exprParts[i]; IPatternParameter patternParameter = _sequence[i]; string result = patternParameter.EvaluateExpression(exprPart); sb.Append($"{result}, "); } return(sb.Length == 0 ? string.Empty : sb.ToString(0, sb.Length - 2) ); }
/// <summary> /// Returns an instance of <see cref="IPatternParameter"/> that combines the given /// pattern parameter instances. /// </summary> private static IPatternParameter Combine(IPatternParameter pp1, IPatternParameter pp2) { if (pp1 == null) { return(pp2); } if (pp2 == null) { return(pp1); } if (pp1 is CombinedPatternParameter cpp1) { cpp1.Add(pp2); } else if (pp2 is CombinedPatternParameter cpp2) { cpp2.Add(pp1); } return(new CombinedPatternParameter(pp1, pp2)); }
/// <summary> /// Evaluates the given <paramref name="instruction"/> using the given <paramref name="instructionInfo"/>. Any resolved parameter is added /// to <paramref name="paramCache"/>. /// </summary> /// <param name="instruction">Directive to evaluate</param> /// <param name="instructionInfo">Instruction that provides the meta data</param> /// <param name="paramCache">Parameter cache</param> /// <returns>Evaluation result</returns> public IInstructionEvaluationResult Evaluate(string instruction, InstructionTranslationInfo instructionInfo, IDictionary <string, IPatternParameter> paramCache) { if (string.IsNullOrEmpty(instruction)) { throw new ArgumentNullException(nameof(instruction)); } if (instructionInfo == null) { throw new ArgumentNullException(nameof(instructionInfo)); } string wordSequence = instruction; // Remove dot if (wordSequence.EndsWith(".")) { wordSequence = wordSequence.Substring(0, wordSequence.Length - 1); } // Value sequence recognition string valueSequenceString = null; wordSequence = Regex.Replace(wordSequence, @"(?<top>""[^""]+"")(?<nav>\s(?<child>""[^""]+""))*\s(?<value>""[^""]+"")", match => { string prime = match.Groups["top"].Value; string sslValue = string.Join(", ", match.Groups["child"].Captures.ToArray()); string value = match.Groups["value"].Value; string[] joinSet = string.IsNullOrEmpty(sslValue) ? new[] { prime, value } : new[] { prime, sslValue, value }; valueSequenceString = string.Join(", ", joinSet); return(ValueSequenceToken); }); // Split to words string[] words = wordSequence.SplitToBuddyTokens(); if (words.Length == 0) { return(EvaluationResult.Error(wordSequence, instructionInfo.Instruction, "Instruction is empty after split")); } words = words.StripSpecialCharacters(); if (words.Length == 0) { return(EvaluationResult.Error(wordSequence, instructionInfo.Instruction, "Instruction is empty after strip")); } // Normalize variants if (instructionInfo.Instruction is IVariantInstruction variantInstruction) { words = variantInstruction.Normalize(words); } // Iterate words and check for match int wordIndex = 0; for (IEnumerator <IInstructionPatternToken> tokenItr = instructionInfo.GetInstructionPatternTokenEnumerator(); tokenItr.MoveNext();) { IInstructionPatternToken token = tokenItr.Current; if (token == null) { throw new InvalidOperationException("Token iterator provided NULL token"); } string word; // Take the word from the sequence if (wordIndex < words.Length) { word = words[wordIndex]; } else // We have more tokens than words // All remaining tokens must be not mandatory or we have an error // So they must handle an unset value { word = PatternParameter.Unset; } // If we have a match, everything is fine if (token.DoesMatch(word)) { wordIndex++; // Resolve parametrized tokens if (token is IParametrizedInstructionPatternToken parametrizedToken) { IPatternParameter patternParameter = parametrizedToken.Value; string parameterName = patternParameter.Name; // If the value has been replaced by a ValueSequenceToken before, we have to re-replace it now if (valueSequenceString != null) // Something must have been replaced { if (patternParameter.Value.Value.Equals(ValueSequenceToken)) // Is this the correct parameter? { ((AbstractPatternParameter)patternParameter).SetValue(valueSequenceString); } } // Add value to cache paramCache.Add(parameterName, patternParameter); } continue; } // If the token is not mandatory, we can go forward if (!token.IsMandatory) { continue; } return(EvaluationResult.Error( wordSequence, instructionInfo.Instruction, $"{FormatWordIndex(wordIndex)} word of instruction " + $"does not match expected token '{token.ToHumanReadableString()}'. " + $"Word is '{word}'" )); } return(EvaluationResult.Ok); }
/// <summary> /// Creates a string representing a directive based on the given <paramref name="actionStep"/> and the /// given <paramref name="parameters"/>. /// </summary> /// <param name="actionStep">Action step to format</param> /// <param name="parameters">Parameters to use</param> /// <returns>String representing a directive based on the given action step</returns> /// <exception cref="ArgumentNullException">If any parameter is NULL</exception> /// <exception cref="InstructionFormattingException">If not all declared parameters could be replaced</exception> public virtual string ToString(IBuddyTranslationInstruction actionStep, IDictionary <string, IPatternParameter> parameters) { if (actionStep == null) { throw new ArgumentNullException(nameof(actionStep)); } if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } string tdilPattern = actionStep.TdilPattern; string result = tdilPattern; if (actionStep is IReferableInstruction referableInstruction) { string resultReference = referableInstruction.ResultReferencePattern; resultReference = resultReference.Replace("#", "1"); result = $"{resultReference} = {result}"; } // Process all parameter references MatchCollection matches = _tdilPatternParameterRegex.Matches(result); for (IEnumerator matchItr = matches.GetEnumerator(); matchItr.MoveNext();) { Match match = (Match)matchItr.Current; if (match == null) { continue; } Group paramRefGrp = match.Groups["paramRef"]; Group closureGrp = match.Groups["closure"]; Group nameGrp = match.Groups["name"]; EClosureType closureType = EClosureType.None; string parameterExpression = paramRefGrp.Value; string[] pureParamNames; // Do we have a closure with alternatives? if (closureGrp.Success) { string coreFunction = parameterExpression.Substring(2, parameterExpression.Length - 3); if (coreFunction.IndexOf('|') != -1) { closureType = EClosureType.Alternative; pureParamNames = coreFunction.Split('|'); } else { closureType = EClosureType.Conjunction; pureParamNames = coreFunction.Split('&'); } } else // Plain old reference { pureParamNames = new[] { nameGrp.Value }; } // Look up referenced pattern parameter string parameterValueFunction = null; IPatternParameter patternParameter = null; for (int i = -1; ++i != pureParamNames.Length;) { string pureParamName = pureParamNames[i]; string reducedParamName = pureParamName.Split('.')[0]; if (parameters.TryGetValue(reducedParamName, out var locPatternParameter)) { string assignedValueFunction = $"${pureParamName}"; // When this is a conjunction, we have to combine all pattern parameter if (closureType == EClosureType.Conjunction) { parameterValueFunction = string.Concat(parameterValueFunction ?? string.Empty, ",", assignedValueFunction); patternParameter = Combine(patternParameter, locPatternParameter); continue; } // In all other cases, just resolve the first matching pattern parameter patternParameter = locPatternParameter; parameterValueFunction = assignedValueFunction; break; } } // Do we have one? if (patternParameter == null) { throw new InstructionFormattingException($"The TDIL pattern '{tdilPattern}' contains a mandatory parameter '{parameterExpression}' that could not be resolved!"); } // Calculate value string valueStr = patternParameter.EvaluateExpression(parameterValueFunction); if (valueStr == null) { throw new InstructionFormattingException($"Expression '{parameterExpression}' could not be evaluated by pattern parameter of '{patternParameter}'"); } // Insert and replace result = result.Replace(parameterExpression, valueStr); } // Ensure all parameters have been replaced if (result.Contains('~')) { throw new InstructionFormattingException($"At least one parameter could not be set. Pattern is '{tdilPattern}'. Result is '{result}'."); } return(result); }
/// <summary> Adds the given instance to the sequence. </summary> public void Add(IPatternParameter pp) { _sequence.Add(pp); }
/// <summary> Creates a new instance by combining the given instances. </summary> public CombinedPatternParameter(IPatternParameter pp1, IPatternParameter pp2) { _sequence.Add(pp1); _sequence.Add(pp2); }