/// <summary> /// Applies the suppled production rule starting at token index /// This will recursively call further production rules /// </summary> /// <returns><c>true</c>, if rule was applied, <c>false</c> otherwise.</returns> /// <param name="r">The red component.</param> /// <param name="tokenIndex">Token index.</param> /// <param name="indent">Indent.</param> bool ApplyRule(ProductionRule r, ref int tokenIndex, int indent = 0) { Log("Applying " + r.ToString() + " to " + string.Join(", ", _tokens.GetRange(tokenIndex, _tokens.Count - tokenIndex).Select(t => $"{t.TokenType.Name} ({t.Value})")), indent); _rulesStack.Push(r.Name); var incomingTokenIndex = tokenIndex; // In order to pass this rule one of the RuleParts must evaluate bool evaluatedRuleSuccessfully = false; ProductionRulePart successfullyEvaluatedRulePart = null; for (int i = 0; i < r.Rhs.Count; i++) { // Take a copy of tokens. if (ApplyRulePart(r.Rhs[i], ref tokenIndex, indent)) { // We have passed - so can break out. // Otherwise, try the next rule part evaluatedRuleSuccessfully = true; successfullyEvaluatedRulePart = r.Rhs[i]; break; } } if (evaluatedRuleSuccessfully) { var nodePosition = GetRuleNodePosition(tokenIndex, incomingTokenIndex); _treeNodeStack.Push( new AbstractSyntaxRuleNode( r, r.Rhs.IndexOf(successfullyEvaluatedRulePart), nodePosition, _source.Substring(nodePosition.Index, nodePosition.Length) ) ); } else { // restore the index - we need to try a different rule tokenIndex = incomingTokenIndex; } _rulesStack.Pop(); return(evaluatedRuleSuccessfully); }
/// <summary> /// Applies the rule part starting at the given token index /// </summary> /// <returns><c>true</c>, if rule part was applyed, <c>false</c> otherwise.</returns> /// <param name="rulePart">Rule part.</param> /// <param name="tokenIndex">Token index.</param> /// <param name="indent">Indent.</param> bool ApplyRulePart(ProductionRulePart rulePart, ref int tokenIndex, int indent) { Log("Testing " + rulePart.ToString() + " " + string.Join(", ", _tokens.GetRange(tokenIndex, _tokens.Count - tokenIndex).Select(t => $"{t.TokenType.Name} ({t.Value})")), indent); var incomingTokenIndex = tokenIndex; foreach (var target in rulePart) { // Get the current token at the start of each loop - index may have changed. var currentToken = GetToken(tokenIndex); if (IsTerminal(target)) { var symbolParts = target.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string terminalName = symbolParts.First(); string value = symbolParts.Length != 2 ? string.Empty : symbolParts.Last().Trim('\''); if (currentToken != null && currentToken.TokenType.Name == terminalName) { Log($"- Matched {target} to Token {currentToken.TokenType.Name} {currentToken.Value}", indent); // TODO - These could be orphaned if this target succeeds but others fail // Not a problem but consider tidying up? _treeNodeStack.Push( new AbstractSyntaxTerminalNode( currentToken.TokenType, currentToken.Value, currentToken.Position, _source.Substring(currentToken.Position.Index, currentToken.Position.Length) ) ); tokenIndex++; } else if (currentToken != null) { Log($"- Failed Match Expected {target} Token {GetToken(tokenIndex).TokenType.Name} {GetToken(tokenIndex).Value}", indent); // Since this was the wrong terminal then the RulePart has failed. var rulesList = _rulesStack.ToList(); rulesList.Reverse(); _errors.Add(new ParserError($"Expected {target}. Encountered {GetToken(tokenIndex).TokenType.Name} {GetToken(tokenIndex).Value} at {GetToken(tokenIndex).Position}", tokenIndex, rulesList)); tokenIndex = incomingTokenIndex; return(false); } else { Log($"- Failed Match Expected {target} Found nothing", indent); // Since this was the wrong terminal then the RulePart has failed. var rulesList = _rulesStack.ToList(); rulesList.Reverse(); _errors.Add(new ParserError($"Expected {target}. Encountered EOF", tokenIndex, rulesList)); tokenIndex = incomingTokenIndex; return(false); } } else { var nextRule = _grammar.ProductionRules.Where(cr => cr.Name == target).First(); if (!ApplyRule(nextRule, ref tokenIndex, indent + 1)) { // Hmm, this rule faile - reset token index tokenIndex = incomingTokenIndex; return(false); } else { // This rule succeeded - but there may be more - we should use the current token index returned from this moving forwards } } } // If we've got here then this rule passed return(true); }