/// <summary> /// Tries to match provided rule one time. /// </summary> /// <param name="context">Parsing context</param> /// <returns> /// Original branch, if rule doesn't match, otherwise both - original branch and /// branch with consumed optional match. /// </returns> public async Task <IReadOnlyCollection <IParsingContext <TToken> > > Match(IParsingContext <TToken> context) { var branchesWithOptionalMatch = await rule.Match(context.Clone()); if (branchesWithOptionalMatch != null && branchesWithOptionalMatch.Count > 0) { return (new List <IParsingContext <TToken> >(branchesWithOptionalMatch) { context }); } // Optional rule didn't match, return original parsing context return(new[] { context }); }
/// <summary> /// Matches multiple parser rules in parallel and checks. /// </summary> /// <param name="context">Parsing context</param> /// <returns>All matching parsing branches or null.</returns> protected override async Task <IReadOnlyCollection <IParsingContext <TToken> > > MatchInternal( IParsingContext <TToken> context) { var activeBranches = new List <Task <IReadOnlyCollection <IParsingContext <TToken> > > >(rules.Length); var succeededBranches = new List <IParsingContext <TToken> >(); // Run lookahead on all branches in parallel for (var ruleIndex = 0; ruleIndex < rules.Length; ++ruleIndex) { var branchContext = context.Clone(); TraceDebug(branchContext, "Branch is matching \"{0}\"", rules[ruleIndex]); // In case of "any-of" we only need to match global decorators once, // so lets hope this rule itself will be decorated, so it's safe // to unwrap all alternatives var branchTask = GlobalRulesDecoratorRule <TToken> .Unwrap(rules[ruleIndex]) .Match(branchContext); if (activeBranches.Remove(branchTask)) { // We got cached task, which means our new task is already completed // with the same result as some other task. // We can process results of both right now and don't need to store them. var branchResult = await branchTask; if (branchResult != null && branchResult.Count > 0) { // Since it is a cached task, we know that both branches completed with the same result succeededBranches.AddRange(branchResult); } } else { activeBranches.Add(branchTask); } } if (activeBranches.Count > 0) { var branchResults = await Task.WhenAll(activeBranches); for (var branchIndex = 0; branchIndex < branchResults.Length; ++branchIndex) { var branchResult = branchResults[branchIndex]; if (branchResult != null && branchResult.Count > 0) { succeededBranches.AddRange(branchResult); } } } if (succeededBranches.Count <= 0) { // None of the branches matched successfully return(null); } return(succeededBranches); }