/// <summary> /// Processes the given instruction pattern <paramref name="token"/> and creates a meta information object. /// </summary> /// <param name="token">Token to process</param> /// <param name="instructionInfo">Instruction meta info to extend</param> /// <exception cref="InvalidInstructionTranslationPatternException">If the instruction pattern is invalid</exception> private void ProcessToken(string token, InstructionTranslationInfo instructionInfo) { if (string.IsNullOrEmpty(token)) { throw new InvalidInstructionTranslationPatternException(string.Format("Token of directive pattern of instruction '{0}' is empty. ", instructionInfo.Instruction.GetType())); } char tokenHead = token[0]; // Token is special char if (tokenHead == '<') { CreateSpecialCharacterToken(token, instructionInfo, "<", ">"); return; } if (tokenHead == '[') { CreateSpecialCharacterToken(token, instructionInfo, "[", "]"); return; } // Token is a parameter if (tokenHead == '{') { CreateParameterToken(token, instructionInfo); return; } throw new InvalidInstructionTranslationPatternException(string.Format("Token of instruction pattern '{0}' contains an unexpected character '{1}'", instructionInfo.Instruction.GetType(), tokenHead)); }
/// <summary> /// Processes the given <paramref name="token"/> in the case of a special character token. /// </summary> /// <param name="token">Token to process</param> /// <param name="instructionInfo">Instruction info to extend</param> /// <param name="tokenHead">Token head</param> /// <param name="tokenTail">Token tail</param> private void CreateSpecialCharacterToken(string token, InstructionTranslationInfo instructionInfo, string tokenHead, string tokenTail) { if (!token.EndsWith(tokenTail)) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern token does not finish with '{1}'. Token is '{0}'", token, tokenTail)); } string tokenCore = token.Substring(1, token.Length - 2); // Head instructionInfo.AddToken(new SpecialCharacterToken { Value = tokenHead }); // Content ProcessToken(tokenCore, instructionInfo); // Tail instructionInfo.AddToken(new SpecialCharacterToken { Value = tokenTail }); }
/// <summary> /// Processes and evaluates the given <paramref name="instruction"/> to create the instruction meta data. /// </summary> /// <param name="instruction">Instruction to process</param> /// <exception cref="InvalidInstructionTranslationPatternException">If the instruction pattern is invalid</exception> private void CreateMetaInfo(IBuddyTranslationInstruction instruction) { string directivePattern = instruction.InstructionPattern; if (string.IsNullOrEmpty(directivePattern)) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern of instruction '{0}' is NULL or empty", instruction.GetType())); } string[] directiveTokens = directivePattern.SplitToBuddyTokens(); if (directiveTokens.Length == 0) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern of instruction '{0}' has no tokens. Pattern is '{1}'", instruction.GetType(), directivePattern)); } string leadingWord = directiveTokens[0]; if (string.IsNullOrEmpty(leadingWord)) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern of instruction '{0}' has an empty leading word. Pattern is '{1}'", instruction.GetType(), directivePattern)); } InstructionTranslationInfo instructionInfo = this; // Add leading word token instructionInfo.AddToken(new WordToken { Value = leadingWord }); // Process remaining tokens for (int i = 0; ++i != directiveTokens.Length;) { string token = directiveTokens[i]; ProcessToken(token, instructionInfo); } }
/// <summary> /// Processes the given <paramref name="token"/> in the case of a parameter token. /// </summary> /// <param name="token">Token to process</param> /// <param name="instructionInfo">Instruction info to extend</param> private void CreateParameterToken(string token, InstructionTranslationInfo instructionInfo) { if (!token.EndsWith("}")) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern token does not finish with '}}'. Token is '{0}'", token)); } string tokenCore = token.Substring(1, token.Length - 2); string[] parameterParts = tokenCore.Split(','); if (parameterParts.Length < 2) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern token does not match expected token structure. Token is '{0}'", token)); } Dictionary <string, string> valueSet = new Dictionary <string, string>(); for (int i = -1; ++i != parameterParts.Length;) { string parameterPart = parameterParts[i]; string[] keyValueSet = parameterPart.Split(':'); if (keyValueSet.Length != 2) { throw new InvalidInstructionTranslationPatternException(string.Format("Directive pattern parameter token does not declare values in 'key:value' format!. Token is '{0}'", token)); } string key = keyValueSet[0]; string value = keyValueSet[1]; valueSet.Add(key, value); } IPatternParameter patternParameter = new PatternParameterFactory().CreatePatternParameter(valueSet); instructionInfo.AddToken(new ParameterToken { Value = patternParameter }); }
/// <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); }