private void GatherCandidatesFromNonterminal(SqlGrammarRuleSequenceNonTerminal nonTerminal, ICollection <TerminalCandidate> candidates) { foreach (var sequence in nonTerminal.TargetRule.Sequences) { foreach (ISqlGrammarRuleSequenceItem item in sequence.Items) { GatherCandidatesFromGrammarItem(item, candidates); if (item.IsRequired) { break; } } } }
private static ParseResult ProceedNonTerminal(ParseContext context, SqlGrammarRuleSequenceNonTerminal nonTerminal, int level, int tokenStartOffset, bool tokenReverted, ReservedWordScope scope) { if (nonTerminal.TargetRule.Scope != ReservedWordScope.Inherit) { scope = nonTerminal.TargetRule.Scope; } var bestCandidateNodes = new List <StatementGrammarNode>(); var workingNodes = new List <StatementGrammarNode>(); var nonTerminalId = nonTerminal.Id; var result = new ParseResult { NodeId = nonTerminalId, Nodes = workingNodes, BestCandidates = bestCandidateNodes, }; var workingTerminalCount = 0; var bestCandidateTerminalCount = 0; var totalTokenCount = context.TokenBuffer.Count; var isPlSqlStatement = String.Equals(nonTerminalId, OracleGrammarDescription.NonTerminals.PlSqlStatementType); if (isPlSqlStatement) { context.PlSqlStatementTokenIndex.Push(tokenStartOffset); } foreach (var sequence in nonTerminal.TargetRule.Sequences) { context.CancellationToken.ThrowIfCancellationRequested(); result.Status = ParseStatus.Success; workingNodes.Clear(); workingTerminalCount = 0; var bestCandidatesCompatible = false; var isSequenceValid = true; foreach (ISqlGrammarRuleSequenceItem item in sequence.Items) { var tokenOffset = tokenStartOffset + workingTerminalCount; var isNodeRequired = item.IsRequired; if (tokenOffset >= totalTokenCount && !isNodeRequired) { continue; } var childNodeId = item.Id; if (!isNodeRequired && workingTerminalCount == 0 && String.Equals(childNodeId, nonTerminalId)) { continue; } var bestCandidateOffset = tokenStartOffset + bestCandidateTerminalCount; var tryBestCandidates = bestCandidatesCompatible && !tokenReverted && bestCandidateTerminalCount > workingTerminalCount; if (item is SqlGrammarRuleSequenceNonTerminal childNonTerminal) { var nestedResult = ProceedNonTerminal(context, childNonTerminal, level + 1, tokenOffset, false, scope); var optionalTokenReverted = TryRevertOptionalToken(optionalTerminalCount => ProceedNonTerminal(context, childNonTerminal, level + 1, tokenOffset - optionalTerminalCount, true, scope), ref nestedResult, workingNodes); workingTerminalCount -= optionalTokenReverted; TryParseInvalidGrammar(tryBestCandidates, () => ProceedNonTerminal(context, childNonTerminal, level + 1, bestCandidateOffset, false, scope), ref nestedResult, workingNodes, bestCandidateNodes, ref workingTerminalCount); var isNestedNodeValid = nestedResult.Status == ParseStatus.Success; if (isNodeRequired || isNestedNodeValid) { result.Status = nestedResult.Status; } var nestedNode = new StatementGrammarNode(NodeType.NonTerminal, context.Statement, null) { Id = childNodeId, Level = level, IsRequired = isNodeRequired, IsGrammarValid = isNestedNodeValid }; var alternativeNode = nestedNode.Clone(); int currentTerminalCount; if (nestedResult.BestCandidates.Count > 0 && ((currentTerminalCount = workingTerminalCount + nestedResult.BestCandidateTerminalCount) > bestCandidateTerminalCount || (currentTerminalCount == bestCandidateTerminalCount && isNestedNodeValid))) { alternativeNode.AddChildNodes(ResolveAlternativeNodes(nestedResult)); if (optionalTokenReverted > 0 || !isNestedNodeValid || workingNodes.Count != bestCandidateNodes.Count) { bestCandidateTerminalCount = CreateNewNodeList(workingNodes, bestCandidateNodes); } bestCandidateNodes.Add(alternativeNode); bestCandidateTerminalCount += alternativeNode.TerminalCount; bestCandidatesCompatible = true; } if (isNestedNodeValid && nestedResult.Nodes.Count > 0) { nestedNode.AddChildNodes(nestedResult.Nodes); workingNodes.Add(nestedNode); workingTerminalCount += nestedResult.TerminalCount; } if (result.Status == ParseStatus.SequenceNotFound) { if (workingNodes.Count == 0) { break; } isSequenceValid = false; workingNodes.Add(alternativeNode.Clone()); workingTerminalCount += alternativeNode.TerminalCount; } } else { var terminalReference = (SqlGrammarRuleSequenceTerminal)item; var terminalResult = IsTokenValid(context, terminalReference, level, tokenOffset, scope); TryParseInvalidGrammar(tryBestCandidates && isNodeRequired, () => IsTokenValid(context, terminalReference, level, bestCandidateOffset, scope), ref terminalResult, workingNodes, bestCandidateNodes, ref workingTerminalCount); if (terminalResult.Status == ParseStatus.SequenceNotFound) { if (isNodeRequired) { result.Status = ParseStatus.SequenceNotFound; break; } continue; } workingTerminalCount++; bestCandidateTerminalCount++; var terminalNode = terminalResult.Nodes[0]; workingNodes.Add(terminalNode); bestCandidateNodes.Add(terminalNode.Clone()); } } if (result.Status == ParseStatus.Success) { #region CASE WHEN issue if (bestCandidateTerminalCount > workingTerminalCount) { var currentTerminalCount = bestCandidateNodes.SelectMany(n => n.Terminals).TakeWhile(t => !t.Id.IsIdentifierOrAlias() && !t.Id.IsLiteral()).Count(); if (currentTerminalCount > workingTerminalCount && workingNodes.FirstOrDefault()?.FirstTerminalNode.Id.IsIdentifierOrAlias() == true) { workingNodes.ForEach(n => n.IsGrammarValid = false); } } #endregion if (isSequenceValid) { break; } } } if (isPlSqlStatement) { context.PlSqlStatementTokenIndex.Pop(); } result.BestCandidates = bestCandidateNodes; result.TerminalCount = workingTerminalCount; result.BestCandidateTerminalCount = bestCandidateTerminalCount; return(result); }
private static ParseResult ProceedNonTerminal(ParseContext context, SqlGrammarRuleSequenceNonTerminal nonTerminal, int level, int tokenStartOffset, bool tokenReverted, ReservedWordScope scope) { if (nonTerminal.TargetRule.Scope != ReservedWordScope.Inherit) { scope = nonTerminal.TargetRule.Scope; } var bestCandidateNodes = new List<StatementGrammarNode>(); var workingNodes = new List<StatementGrammarNode>(); var nonTerminalId = nonTerminal.Id; var result = new ParseResult { NodeId = nonTerminalId, Nodes = workingNodes, BestCandidates = bestCandidateNodes, }; var workingTerminalCount = 0; var bestCandidateTerminalCount = 0; var isPlSqlStatement = String.Equals(nonTerminalId, OracleGrammarDescription.NonTerminals.PlSqlStatementType); if (isPlSqlStatement) { context.PlSqlStatementTokenIndex.Push(tokenStartOffset); } foreach (var sequence in nonTerminal.TargetRule.Sequences) { context.CancellationToken.ThrowIfCancellationRequested(); result.Status = ParseStatus.Success; workingNodes.Clear(); workingTerminalCount = 0; var bestCandidatesCompatible = false; var isSequenceValid = true; foreach (ISqlGrammarRuleSequenceItem item in sequence.Items) { var tokenOffset = tokenStartOffset + workingTerminalCount; var isNodeRequired = item.IsRequired; if (tokenOffset >= context.TokenBuffer.Count && !isNodeRequired) { continue; } var childNodeId = item.Id; if (!isNodeRequired && workingTerminalCount == 0 && String.Equals(childNodeId, nonTerminalId)) { continue; } var bestCandidateOffset = tokenStartOffset + bestCandidateTerminalCount; var tryBestCandidates = bestCandidatesCompatible && !tokenReverted && bestCandidateTerminalCount > workingTerminalCount; var childNonTerminal = item as SqlGrammarRuleSequenceNonTerminal; if (childNonTerminal != null) { var nestedResult = ProceedNonTerminal(context, childNonTerminal, level + 1, tokenOffset, false, scope); var optionalTokenReverted = TryRevertOptionalToken(optionalTerminalCount => ProceedNonTerminal(context, childNonTerminal, level + 1, tokenOffset - optionalTerminalCount, true, scope), ref nestedResult, workingNodes); workingTerminalCount -= optionalTokenReverted; TryParseInvalidGrammar(tryBestCandidates, () => ProceedNonTerminal(context, childNonTerminal, level + 1, bestCandidateOffset, false, scope), ref nestedResult, workingNodes, bestCandidateNodes, ref workingTerminalCount); var isNestedNodeValid = nestedResult.Status == ParseStatus.Success; if (isNodeRequired || isNestedNodeValid) { result.Status = nestedResult.Status; } var nestedNode = new StatementGrammarNode(NodeType.NonTerminal, context.Statement, null) { Id = childNodeId, Level = level, IsRequired = isNodeRequired, IsGrammarValid = isNestedNodeValid }; var alternativeNode = nestedNode.Clone(); int currentTerminalCount; if (nestedResult.BestCandidates.Count > 0 && ((currentTerminalCount = workingTerminalCount + nestedResult.BestCandidateTerminalCount) > bestCandidateTerminalCount || (currentTerminalCount == bestCandidateTerminalCount && isNestedNodeValid))) { var bestCandidatePosition = new Dictionary<int, StatementGrammarNode>(); // Candidate nodes can be multiplied or terminals can be spread among different nonterminals, // therefore we fetch the node with most terminals or the later (when nodes contain same terminals). foreach (var candidate in nestedResult.BestCandidates) { StatementGrammarNode storedNode; if (!bestCandidatePosition.TryGetValue(candidate.SourcePosition.IndexStart, out storedNode) || storedNode.SourcePosition.IndexEnd <= candidate.SourcePosition.IndexEnd) { bestCandidatePosition[candidate.SourcePosition.IndexStart] = candidate; } } alternativeNode.AddChildNodes(bestCandidatePosition.Values); if (optionalTokenReverted > 0 || !isNestedNodeValid || workingNodes.Count != bestCandidateNodes.Count) { bestCandidateTerminalCount = CreateNewNodeList(workingNodes, bestCandidateNodes); } bestCandidateNodes.Add(alternativeNode); bestCandidateTerminalCount += alternativeNode.TerminalCount; bestCandidatesCompatible = true; } if (isNestedNodeValid && nestedResult.Nodes.Count > 0) { nestedNode.AddChildNodes(nestedResult.Nodes); workingNodes.Add(nestedNode); workingTerminalCount += nestedResult.TerminalCount; } if (result.Status == ParseStatus.SequenceNotFound) { if (workingNodes.Count == 0) { break; } isSequenceValid = false; workingNodes.Add(alternativeNode.Clone()); workingTerminalCount += alternativeNode.TerminalCount; } } else { var terminalReference = (SqlGrammarRuleSequenceTerminal)item; var terminalResult = IsTokenValid(context, terminalReference, level, tokenOffset, scope); TryParseInvalidGrammar(tryBestCandidates && isNodeRequired, () => IsTokenValid(context, terminalReference, level, bestCandidateOffset, scope), ref terminalResult, workingNodes, bestCandidateNodes, ref workingTerminalCount); if (terminalResult.Status == ParseStatus.SequenceNotFound) { if (isNodeRequired) { result.Status = ParseStatus.SequenceNotFound; break; } continue; } workingTerminalCount++; bestCandidateTerminalCount++; var terminalNode = terminalResult.Nodes[0]; workingNodes.Add(terminalNode); bestCandidateNodes.Add(terminalNode.Clone()); } } if (result.Status == ParseStatus.Success) { #region CASE WHEN issue if (bestCandidateTerminalCount > workingTerminalCount) { var currentTerminalCount = bestCandidateNodes.SelectMany(n => n.Terminals).TakeWhile(t => !t.Id.IsIdentifierOrAlias() && !t.Id.IsLiteral()).Count(); if (currentTerminalCount > workingTerminalCount && workingNodes.FirstOrDefault()?.FirstTerminalNode.Id.IsIdentifierOrAlias() == true) { workingNodes.ForEach(n => n.IsGrammarValid = false); } } #endregion if (isSequenceValid) { break; } } } if (isPlSqlStatement) { context.PlSqlStatementTokenIndex.Pop(); } result.BestCandidates = bestCandidateNodes; result.TerminalCount = workingTerminalCount; result.BestCandidateTerminalCount = bestCandidateTerminalCount; return result; }
private void GatherCandidatesFromNonterminal(SqlGrammarRuleSequenceNonTerminal nonTerminal, ICollection<TerminalCandidate> candidates) { foreach (var sequence in nonTerminal.TargetRule.Sequences) { foreach (ISqlGrammarRuleSequenceItem item in sequence.Items) { GatherCandidatesFromGrammarItem(item, candidates); if (item.IsRequired) { break; } } } }