private void AssertRuleReplacement( string ruleText, int[] sourceSymbols = null, float[][] sourceParameters = null, string expectedReplacementText = null, string axiom = null, int ruleParamMemoryStartIndex = 0, int matchIndex = 0, int paramTempMemorySize = 0, float[] globalParams = null, string[] globalParamNames = null, int expectedReplacementPatternIndex = 0, string includedSymbols = "[]ABCDE" ) { var totalIncluded = new HashSet <int>(includedSymbols.Select(x => (int)x)); globalParamNames = globalParamNames ?? new string[0]; using var symbols = new DependencyTracker <SymbolString <float> >( axiom == null ? new SymbolString <float>(sourceSymbols, sourceParameters) : SymbolString <float> .FromString(axiom, Allocator.Persistent) ); using var expectedReplacement = SymbolString <float> .FromString(expectedReplacementText, Allocator.Persistent); var ruleFromString = new BasicRule( RuleParser.ParseToRule(ruleText, x => x, globalParameters: globalParamNames), '[', ']'); using var ruleNativeData = new SystemLevelRuleNativeData(new[] { ruleFromString }); var nativeWriter = new SymbolSeriesMatcherNativeDataWriter(); ruleFromString.WriteDataIntoMemory(ruleNativeData, nativeWriter); globalParams = globalParams ?? new float[0]; using var globalNative = new NativeArray <float>(globalParams, Allocator.Persistent); var expectedTotalParamReplacement = expectedReplacement.parameters.data.Length; using var paramMemory = new NativeArray <float>(paramTempMemorySize, Allocator.Persistent); using var branchCache = new SymbolStringBranchingCache('[', ']', new[] { totalIncluded }, ruleNativeData); branchCache.BuildJumpIndexesFromSymbols(symbols); var random = new Unity.Mathematics.Random(); var matchSingleData = new LSystemSingleSymbolMatchData { isTrivial = false, tmpParameterMemorySpace = JaggedIndexing.GetWithNoLength(ruleParamMemoryStartIndex) }; var potentialMatch = ruleFromString.AsBlittable().PreMatchCapturedParametersWithoutConditional( branchCache, symbols.Data, matchIndex, paramMemory, matchSingleData.tmpParameterMemorySpace.index, ref matchSingleData, new TmpNativeStack <SymbolStringBranchingCache.BranchEventData>(5), globalNative, ruleNativeData.dynamicOperatorMemory, ref random, ruleNativeData.ruleOutcomeMemorySpace ); Assert.IsTrue(potentialMatch); Assert.AreEqual(expectedReplacementPatternIndex, matchSingleData.selectedReplacementPattern); Assert.AreEqual(paramTempMemorySize, matchSingleData.tmpParameterMemorySpace.length, "parameter temp memory size mismatch"); Assert.AreEqual(expectedReplacement.symbols.Length, matchSingleData.replacementSymbolIndexing.length, "replacement symbols size mismatch"); Assert.AreEqual(expectedReplacement.parameters.data.Length, matchSingleData.replacementParameterIndexing.length, "replacement parameter size mismatch"); matchSingleData.replacementSymbolIndexing.index = 0; matchSingleData.replacementParameterIndexing.index = 0; using var resultSymbols = new SymbolString <float>( expectedReplacement.symbols.Length, expectedReplacement.parameters.data.Length, Allocator.Persistent); ruleFromString.AsBlittable().WriteReplacementSymbols( globalNative, paramMemory, resultSymbols, matchSingleData, ruleNativeData.dynamicOperatorMemory, ruleNativeData.replacementsSymbolMemorySpace, ruleNativeData.ruleOutcomeMemorySpace, ruleNativeData.structExpressionMemorySpace ); Assert.AreEqual(expectedReplacementText, resultSymbols.ToString()); Assert.IsTrue(expectedReplacement.Equals(resultSymbols)); }
/* Function: ScoreScopeInterpretation * A function used by <ScoreInterpretation()> to determine the C and S fields of the score for the passed interpretation * using only the scope. Only those fields and the trailing 1 will be set in the returned score. If the interpretation doesn't * match using the scope, it will return zero. */ private long ScoreScopeInterpretation(Topic topic, Link link, SymbolString interpretation) { // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------1 // C - Whether the topic and link's capitalization match if it matters to the language. // S - How high on the scope list the symbol match is. Language topicLanguage = EngineInstance.Languages.FromID(topic.LanguageID); CommentType commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); // Values of C: // Natural Docs links: // 1 - Topic is documentation, case matches // 1 - Topic is documentation, case differs // 1 - Topic is file, case matches // 1 - Topic is file, case differs // 1 - Topic is code, topic language is case sensitive, case matches // 0 - Topic is code, topic language is case sensitive, case differs // 1 - Topic is code, topic language is case insensitive, case matches // 1 - Topic is code, topic language is case insensitive, case differs // Type/Class Parent links: // Assuming they're the same language... // X - Topic is documentation, case matches // X - Topic is documentation, case differs // X - Topic is file, case matches // X - Topic is file, case differs // 1 - Topic is code, language is case sensitive, case matches // X - Topic is code, language is case sensitive, case differs // 1 - Topic is code, language is case insensitive, case matches // 1 - Topic is code, language is case insensitive, case differs bool caseFlagged; bool caseRequired; if (link.Type == LinkType.NaturalDocs) { caseRequired = false; caseFlagged = (commentType.IsCode && topicLanguage.CaseSensitive); } else { if (commentType.IsCode == false) { return(0); } caseRequired = topicLanguage.CaseSensitive; caseFlagged = false; } // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------1 // Our baseline. long score = 0x0000000000000001; int scopeListIndex; // If we match as a global symbol... if (string.Compare(topic.Symbol, interpretation, !caseRequired) == 0) { if (link.Context.ScopeIsGlobal) { scopeListIndex = 0; } else { // Conceptually, we had to walk down the entire hierachy to get to global: // Scope A.B.C = A.B.C.Name, A.B.Name, A.Name, Name = Index 3 // so the scope list index is the number of dividers in the scope plus one. int linkScopeIndex, linkScopeLength; link.Context.GetRawTextScope(out linkScopeIndex, out linkScopeLength); int dividers = link.Context.RawText.Count(SymbolString.SeparatorChar, linkScopeIndex, linkScopeLength); scopeListIndex = dividers + 1; } // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------= // Apply C if (!caseFlagged || string.Compare(topic.Symbol, interpretation, false) == 0) { score |= 0x2000000000000000; } } // If the topic ends with the interepretation, such as "A.B.C.Name" and "Name"... else if (topic.Symbol.EndsWith(interpretation, !caseRequired)) { string topicSymbolString = topic.Symbol.ToString(); int topicScopeIndex = 0; int topicScopeLength = topicSymbolString.Length - interpretation.ToString().Length - 1; // See if the link's scope can completely encompass the remaining scope: // Topic A.B.C.Name + Link Name + Link Scope A.B.C = yes // Topic A.B.C.Name + Link Name + Link Scope A.B = no // Topic A.B.C.Name + Link Name + Link Scope A.B.C.D = yes, it can walk up the hierarchy // Topic A.B.C.Name + Link Name + Link Scope A.B.CC = no, can't split a word // Topic A.B.C.Name + Link Name + Link Scope X.Y.Z = no string linkContextString = link.Context.RawText; int linkScopeIndex, linkScopeLength; link.Context.GetRawTextScope(out linkScopeIndex, out linkScopeLength); // If the remaining topic scope is a substring or equal to the link scope... if (topicScopeLength <= linkScopeLength && string.Compare(linkContextString, linkScopeIndex, topicSymbolString, topicScopeIndex, topicScopeLength, !caseRequired) == 0) { if (topicScopeLength == linkScopeLength) { // If it's an exact match, this is considered the first entry on our conceptual scope list. scopeListIndex = 0; } else // topicScopeLength < linkScopeLength { // If the scope was a substring, the next character needs to be a separator so we don't split a word. if (linkContextString[topicScopeLength] != SymbolString.SeparatorChar) { return(0); } // The scope list index is the number of separators we trimmed off: // Link scope: A.B.C.D // Remaining topic scope: A.B // Scope list: // 0 - A.B.C.D // 1 - A.B.C // 2 - A.B // 3 - A // 4 - global scopeListIndex = linkContextString.Count(SymbolString.SeparatorChar, linkScopeIndex + topicScopeLength, linkScopeLength - topicScopeLength); } // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------= // Apply C if (!caseFlagged || (topicSymbolString.EndsWith(interpretation) == true && string.Compare(linkContextString, linkScopeIndex, topicSymbolString, topicScopeIndex, topicScopeLength, false) == 0)) { score |= 0x2000000000000000; } } else { return(0); } } else { return(0); } // --=----- -------- -------- -SSSSSSS SSS----- -------- -------- -------= // Encode the scope index. We want lower indexes to have a higher score. if (scopeListIndex > 1023) { scopeListIndex = 1023; } long scopeListBits = 1023 - scopeListIndex; scopeListBits <<= 29; score |= scopeListBits; return(score); }
/* Function: ScoreUsingInterpretation * A function used by <ScoreInterpretation()> to determine the C and S fields of the score for the passed interpretation * using only the using statements. Only those fields and the trailing 1 will be set in the returned score. If the interpretation * doesn't match using the using statements, it will return zero. */ private long ScoreUsingInterpretation(Topic topic, Link link, SymbolString interpretation) { // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------1 // C - Whether the topic and link's capitalization match if it matters to the language. // S - How high on the scope list the symbol match is. IList <UsingString> usingStrings = link.Context.GetUsingStatements(); if (usingStrings == null || usingStrings.Count == 0) { return(0); } Language topicLanguage = EngineInstance.Languages.FromID(topic.LanguageID); CommentType commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); // Values of C: // Natural Docs links: // 1 - Topic is documentation, case matches // 1 - Topic is documentation, case differs // 1 - Topic is file, case matches // 1 - Topic is file, case differs // 1 - Topic is code, topic language is case sensitive, case matches // 0 - Topic is code, topic language is case sensitive, case differs // 1 - Topic is code, topic language is case insensitive, case matches // 1 - Topic is code, topic language is case insensitive, case differs // Type/Class Parent links: // Assuming they're the same language... // X - Topic is documentation, case matches // X - Topic is documentation, case differs // X - Topic is file, case matches // X - Topic is file, case differs // 1 - Topic is code, language is case sensitive, case matches // X - Topic is code, language is case sensitive, case differs // 1 - Topic is code, language is case insensitive, case matches // 1 - Topic is code, language is case insensitive, case differs bool caseFlagged; bool caseRequired; if (link.Type == LinkType.NaturalDocs) { caseRequired = false; caseFlagged = (commentType.IsCode && topicLanguage.CaseSensitive); } else { if (commentType.IsCode == false) { return(0); } caseRequired = topicLanguage.CaseSensitive; caseFlagged = false; } // Find the scope list index to start at, since the actual scopes come before the using statements. // Scope list: // 0 - A.B.C.Link // 1 - A.B.Link // 2 - A.Link // 3 - Link // 4 - Link + first using statement // So if there's a scope, the starting index is the number of separators in the scope + 2. Otherwise it's one. // Scope list: // 0 - Link // 1 - Link + first using statement int scopeListIndex; if (link.Context.ScopeIsGlobal) { scopeListIndex = 1; } else { int scopeIndex, scopeLength; link.Context.GetRawTextScope(out scopeIndex, out scopeLength); scopeListIndex = link.Context.RawText.Count(SymbolString.SeparatorChar, scopeIndex, scopeLength) + 2; } // Go through each using statement looking for the best score. long bestScore = 0; foreach (var usingString in usingStrings) { SymbolString newInterpretation; bool newInterpretationPossible; if (usingString.Type == UsingString.UsingType.AddPrefix) { newInterpretation = usingString.PrefixToAdd + interpretation; newInterpretationPossible = true; } else if (usingString.Type == UsingString.UsingType.ReplacePrefix) { SymbolString prefixToRemove = usingString.PrefixToRemove; string prefixToRemoveString = prefixToRemove.ToString(); string interpretationString = interpretation.ToString(); if (interpretationString.Length > prefixToRemoveString.Length && interpretation.StartsWith(prefixToRemove, !caseRequired)) { newInterpretation = usingString.PrefixToAdd + SymbolString.FromExportedString(interpretationString.Substring(prefixToRemoveString.Length + 1)); newInterpretationPossible = true; } else { newInterpretation = new SymbolString(); // to make the compiler shut up newInterpretationPossible = false; } } else { throw new NotImplementedException(); } if (newInterpretationPossible && string.Compare(newInterpretation, topic.Symbol, !caseRequired) == 0) { // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------1 // Our baseline. long score = 0x0000000000000001; // --C----- -------- -------- -SSSSSSS SSS----- -------- -------- -------= // Encode the scope index. We want lower indexes to have a higher score. if (scopeListIndex > 1023) { scopeListIndex = 1023; } long scopeListBits = 1023 - scopeListIndex; scopeListBits <<= 29; score |= scopeListBits; // --C----- -------- -------- -======= ===----- -------- -------- -------= // Determine C. If C is set we can quit early because it would be impossible for a later using statement to // generate a higher score. if (!caseFlagged || string.Compare(newInterpretation, topic.Symbol, false) == 0) { score |= 0x2000000000000000; bestScore = score; break; } else { if (score > bestScore) { bestScore = score; } } } scopeListIndex++; } return(bestScore); }
public override string OutputOf(IList <string> commands) { StringBuilder output = new StringBuilder(); bool lastWasLineBreak = true; Topic topic = new Topic(EngineInstance.CommentTypes); topic.LanguageID = EngineInstance.Languages.FromName("C#").ID; topic.CommentTypeID = EngineInstance.CommentTypes.FromKeyword("Function").ID; Link link = new Engine.Links.Link(); link.LanguageID = EngineInstance.Languages.FromName("C#").ID; link.Type = Engine.Links.LinkType.NaturalDocs; bool linkIsPlainText = true; string show = "all"; for (int i = 0; i < commands.Count; i++) { string command = commands[i]; if (command.EndsWith(";")) { command = command.Substring(0, command.Length - 1).TrimEnd(); } Match match = GetPropertyRegex.Match(command); string target, property, value, valueString; Match addPrefixMatch = null; Match replacePrefixMatch = null; if (match.Success) { target = match.Groups[1].ToString().ToLower(); property = match.Groups[2].ToString().ToLower(); value = match.Groups[3].ToString(); if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"') { valueString = value.Substring(1, value.Length - 2).Trim(); } else { value = value.ToLower(); valueString = null; } } else { addPrefixMatch = AddPrefixRegex.Match(command); replacePrefixMatch = ReplacePrefixRegex.Match(command); if (addPrefixMatch.Success || replacePrefixMatch.Success) { target = "link"; property = "using"; value = null; valueString = null; } else { target = null; property = null; value = null; valueString = null; } } var lcCommand = command.ToLower(); try { if (command == "") { if (!lastWasLineBreak) { output.AppendLine(); lastWasLineBreak = true; } } else if (command.StartsWith("//")) { output.AppendLine(command); lastWasLineBreak = false; } // Topic properties else if (target == "topic") { if (property == "languagename") { if (valueString == null) { throw new Exception("Topic.LanguageName must be set to a string value."); } var language = EngineInstance.Languages.FromName(valueString); if (language == null) { throw new Exception("\"" + valueString + "\" is not recognized as a language."); } topic.LanguageID = language.ID; } else if (property == "keyword") { if (valueString == null) { throw new Exception("Topic.Keyword must be set to a string value."); } var commentType = EngineInstance.CommentTypes.FromKeyword(valueString); if (commentType == null) { throw new Exception("\"" + valueString + "\" is not recognized as a keyword."); } topic.CommentTypeID = commentType.ID; } else if (property == "title") { if (valueString == null) { throw new Exception("Topic.Title must be set to a string value."); } if (valueString == "") { throw new Exception("Topic.Title cannot be set to an empty string."); } topic.Title = valueString; } else if (property == "body") { if (value == "null") { topic.Body = null; } else if (valueString == null) { throw new Exception("Topic.Body must be set to null or a string value."); } else { topic.Body = valueString; } } else if (property == "prototype") { if (value == "null") { topic.Prototype = null; } else if (valueString == null) { throw new Exception("Topic.Prototype must be set to null or a string value."); } else { topic.Prototype = valueString; } } else if (property == "scope") { if (value == "null") { ContextString temp = topic.PrototypeContext; temp.Scope = new SymbolString(); topic.PrototypeContext = temp; } else if (valueString == null) { throw new Exception("Topic.Scope must be set to null or a string value."); } else { ContextString temp = topic.PrototypeContext; temp.Scope = SymbolString.FromPlainText_NoParameters(valueString); topic.PrototypeContext = temp; } } else { throw new Exception("\"" + property + "\" is not a recognized Topic property."); } // Leave lastWasLineBreak alone since we're not generating output. } // Link properties else if (target == "link") { if (property == "languagename") { if (valueString == null) { throw new Exception("Link.LanguageName must be set to a string value."); } var language = EngineInstance.Languages.FromName(valueString); if (language == null) { throw new Exception("\"" + valueString + "\" is not recognized as a language."); } link.LanguageID = language.ID; } else if (property == "type") { if (valueString == null) { throw new Exception("Link.Type must be set to a string value."); } string lcValueString = valueString.ToLower(); if (lcValueString == "naturaldocs" || lcValueString == "natural docs") { link.Type = LinkType.NaturalDocs; } else if (lcValueString == "type") { link.Type = LinkType.Type; } else if (lcValueString == "classparent" || lcValueString == "class parent") { link.Type = LinkType.ClassParent; } else { throw new Exception("\"" + valueString + "\" is not recognized as a link type."); } } else if (property == "text") { if (valueString == null) { throw new Exception("Link.Text must be set to a string value"); } if (valueString == "") { throw new Exception("Link.Text cannot be set to an empty string."); } link.TextOrSymbol = valueString; linkIsPlainText = true; } else if (property == "scope") { if (value == "null") { ContextString temp = link.Context; temp.Scope = new SymbolString(); link.Context = temp; } else if (valueString == null) { throw new Exception("Link.Scope must be set to null or a string value."); } else if (valueString == "") { throw new Exception("Link.Scope cannot be set to an empty string."); } else { ContextString temp = link.Context; temp.Scope = SymbolString.FromPlainText_NoParameters(valueString); link.Context = temp; } } else if (property == "using") { if (value == "null") { ContextString temp = link.Context; temp.ClearUsingStatements(); link.Context = temp; } else if (addPrefixMatch != null && addPrefixMatch.Success) { string op = addPrefixMatch.Groups[1].ToString(); string add = addPrefixMatch.Groups[2].ToString(); ContextString temp = link.Context; if (op == "=") { temp.ClearUsingStatements(); } temp.AddUsingStatement( UsingString.FromParameters( UsingString.UsingType.AddPrefix, SymbolString.FromPlainText_NoParameters(add) ) ); link.Context = temp; } else if (replacePrefixMatch != null && replacePrefixMatch.Success) { string op = replacePrefixMatch.Groups[1].ToString(); string remove = replacePrefixMatch.Groups[2].ToString(); string add = replacePrefixMatch.Groups[3].ToString(); ContextString temp = link.Context; if (op == "=") { temp.ClearUsingStatements(); } temp.AddUsingStatement( UsingString.FromParameters( UsingString.UsingType.ReplacePrefix, SymbolString.FromPlainText_NoParameters(add), SymbolString.FromPlainText_NoParameters(remove) ) ); link.Context = temp; } else { throw new Exception("\"" + command + "\" is not a recognized Link.Using statement."); } } else { throw new Exception("\"" + property + "\" is not recognized as a link property."); } // Leave lastWasLineBreak alone since we're not generating output. } // Show else if (lcCommand.StartsWith("show")) { show = lcCommand.Substring(4); } // Score else if (lcCommand == "score") { // Validate fields if (topic.Title == null) { throw new Exception("You didn't set Topic.Title."); } if (link.TextOrSymbol == null) { throw new Exception("You didn't set Link.Text."); } // Calculate fields string parametersString; SymbolString topicSymbol = SymbolString.FromPlainText(topic.Title, out parametersString); var commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); if (commentType.Scope == Engine.CommentTypes.CommentType.ScopeValue.Normal && topic.PrototypeContext.ScopeIsGlobal == false) { topicSymbol = topic.PrototypeContext.Scope + topicSymbol; } topic.Symbol = topicSymbol; if (link.Type == LinkType.Type || link.Type == LinkType.ClassParent) { if (linkIsPlainText) { SymbolString linkSymbol = SymbolString.FromPlainText_NoParameters(link.TextOrSymbol); link.TextOrSymbol = linkSymbol.ToString(); link.EndingSymbol = linkSymbol.EndingSymbol; linkIsPlainText = false; } } else { string ignore; SymbolString linkSymbol = SymbolString.FromPlainText(link.TextOrSymbol, out ignore); link.EndingSymbol = linkSymbol.EndingSymbol; } // Show topic if (!lastWasLineBreak) { output.AppendLine(); } var topicLanguage = EngineInstance.Languages.FromID(topic.LanguageID); commentType = EngineInstance.CommentTypes.FromID(topic.CommentTypeID); output.AppendLine(topicLanguage.Name + " " + commentType.Name + " Topic: " + topic.Title); output.AppendLine(" Symbol: " + topic.Symbol.FormatWithSeparator('.')); if (topic.TitleParameters != null) { output.AppendLine(" Title Parameters: " + topic.TitleParameters.ToString().Replace(Engine.Symbols.ParameterString.SeparatorChar, ',')); } if (topic.PrototypeParameters != null) { output.AppendLine(" Prototype Parameters: " + topic.PrototypeParameters.ToString().Replace(Engine.Symbols.ParameterString.SeparatorChar, ',')); } if (topic.Prototype != null) { output.AppendLine(" Prototype: " + topic.Prototype); } if (topic.Body != null) { output.AppendLine(" Body: " + topic.Body); } output.AppendLine(); // Show link var linkLanguage = EngineInstance.Languages.FromID(link.LanguageID); output.AppendLine(linkLanguage.Name + " " + link.Type + " Link: " + link.TextOrSymbol.Replace(Engine.Symbols.SymbolString.SeparatorChar, '*')); if (link.Context.ScopeIsGlobal) { output.AppendLine(" Scope: Global"); } else { output.AppendLine(" Scope: " + link.Context.Scope.FormatWithSeparator('.')); } var usingStatements = link.Context.GetUsingStatements(); if (usingStatements != null) { foreach (var usingStatement in usingStatements) { if (usingStatement.Type == UsingString.UsingType.AddPrefix) { output.AppendLine(" Using: Add Prefix " + usingStatement.PrefixToAdd.FormatWithSeparator('.')); } else if (usingStatement.Type == UsingString.UsingType.ReplacePrefix) { output.AppendLine(" Using: Replace Prefix " + usingStatement.PrefixToRemove.FormatWithSeparator('.') + " with " + usingStatement.PrefixToAdd.FormatWithSeparator('.')); } else { throw new NotImplementedException("Unexpected using type " + usingStatement.Type); } } } output.AppendLine(); // Show score List <Engine.Links.LinkInterpretation> interpretations = null; if (link.Type == LinkType.NaturalDocs) { string ignore; interpretations = EngineInstance.Comments.NaturalDocsParser.LinkInterpretations(link.TextOrSymbol, Engine.Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowNamedLinks | Engine.Comments.Parsers.NaturalDocs.LinkInterpretationFlags.AllowPluralsAndPossessives, out ignore); } long score = EngineInstance.CodeDB.ScoreLink(link, topic, 0, interpretations); if (score <= 0) { output.AppendLine("☓☓☓ No Match ☓☓☓"); } else { output.Append("Match score:"); if (show.Contains("all") || show.Contains("rawscore")) { string scoreString = score.ToString("X16"); output.Append(" " + scoreString.Substring(0, 4) + " " + scoreString.Substring(4, 4) + " " + scoreString.Substring(8, 4) + " " + scoreString.Substring(12, 4)); } output.AppendLine(); // DEPENDENCY: This code depends on the format generated by Engine.CodeDB.ScoreLink(). // Format: // 0LCETPPP PPPPPPPP PPPPPPPP PSSSSSSS SSSIIIII IBFFFFFF Rbbbbbbb brrrrrr1 // L - Whether the topic matches the link's language. // C - Whether the topic and link's capitalization match if it matters to the language. // E - Whether the text is an exact match with no plural or possessive conversions applied. // T - Whether the link parameters exactly match the topic title parameters. // P - How well the parameters match. // S - How high on the scope list the symbol match is. // I - How high on the interpretation list (named/plural/possessive) the match is. // B - Whether the topic has a body // F - How high on the list of topics that define the same symbol in the same file this is. // R - Whether the topic has a prototype. // b - The length of the body divided by 16. // r - The length of the prototype divided by 16. long LValue = (score & 0x4000000000000000) >> 62; long CValue = (score & 0x2000000000000000) >> 61; long EValue = (score & 0x1000000000000000) >> 60; long TValue = (score & 0x0800000000000000) >> 59; long PValue = (score & 0x07FFFF8000000000) >> 39; long SValue = (score & 0x0000007FE0000000) >> 29; long IValue = (score & 0x000000001F800000) >> 23; long BValue = (score & 0x0000000000400000) >> 22; long FValue = (score & 0x00000000003F0000) >> 16; long RValue = (score & 0x0000000000008000) >> 15; long bValue = (score & 0x0000000000007F80) >> 7; long rValue = (score & 0x000000000000007E) >> 1; if (show.Contains("all") || show.Contains("language")) { output.AppendLine(" " + (LValue == 1 ? "☒" : "☐") + " - Language"); } if (show.Contains("all") || show.Contains("capitalization")) { output.AppendLine(" " + (CValue == 1 ? "☒" : "☐") + " - Capitalization"); } if (show.Contains("all") || show.Contains("interpretation")) { output.AppendLine(" " + (EValue == 1 ? "☒" : "☐") + " - Exact text"); } if (show.Contains("all") || show.Contains("parameters")) { output.AppendLine(" " + (TValue == 1 ? "☒" : "☐") + " - Topic title parameters"); output.Append(" "); for (int shift = 18; shift >= 0; shift -= 2) { long individualPValue = PValue >> shift; individualPValue &= 0x0000000000000003; switch (individualPValue) { case 3: output.Append("☒"); break; case 2: output.Append("↑"); break; case 1: output.Append("↓"); break; case 0: output.Append("☐"); break; } } output.AppendLine(" - Parameters"); } if (show.Contains("all") || show.Contains("scope")) { output.AppendLine(" " + (1023 - SValue) + " - Scope index"); output.AppendLine(" (" + SValue + " score)"); } if (show.Contains("all") || show.Contains("interpretation")) { output.AppendLine(" " + (63 - IValue) + " - Interpretation index"); output.AppendLine(" (" + IValue + " score)"); } if (show.Contains("all") || show.Contains("body")) { output.AppendLine(" " + (BValue == 1 ? "☒" : "☐") + " - Body"); if (BValue == 1) { output.Append(" (" + bValue + " score, " + (bValue * 16)); if (bValue == 0xFF) { output.Append('+'); } else { output.Append("-" + ((bValue * 16) + 15)); } output.AppendLine(" characters)"); } } if (show.Contains("all") || show.Contains("prototype")) { output.AppendLine(" " + (RValue == 1 ? "☒" : "☐") + " - Prototype"); if (RValue == 1) { output.Append(" (" + rValue + " score, " + (rValue * 16)); if (rValue == 0x3F) { output.Append('+'); } else { output.Append("-" + ((rValue * 16) + 15)); } output.AppendLine(" characters)"); } } if (show.Contains("all") || show.Contains("samesymbol")) { output.AppendLine(" " + (63 - FValue) + " - Same symbol in same file index"); output.AppendLine(" (" + FValue + " score)"); } } output.AppendLine(); lastWasLineBreak = true; } else { throw new Exception("Unknown command " + command); } } catch (Exception e) { output.AppendLine("Command: " + command); output.AppendLine("Exception: " + e.Message); output.AppendLine("(" + e.GetType().ToString() + ")"); lastWasLineBreak = false; } } return(output.ToString()); }