private static NonTerminal GetAliasedSymbol(NonTerminal symbol, IReadOnlyDictionary <NonTerminal, ImmutableList <Rule> > rules)
        {
            var referencingRules = rules.SelectMany(kvp => kvp.Value)
                                   .Where(r => r.Symbols.Contains(symbol))
                                   .ToArray();

            return(referencingRules.Length == 1 && referencingRules.Single().Symbols.Count == 1
                ? referencingRules.Single().Produced
                : null);
        }
        private LeftRecursionRewriter RewriteOne()
        {
            if (this.aliases.Count != 0)
            {
                throw new InvalidOperationException("sanity check");
            }

            // Example with all right associative (left-associative just replaces (+ E)? with (+ EX)*)
            //
            // START
            // E = -E | E * E | E + E | ID
            //
            // ONE REWRITE
            // E = E0 (* E)? | E + E
            // E0 = -E0 | ID
            //
            // TWO REWRITES
            // E = E1 (+ E)?
            // E1 = E0 (* E)?
            // E0 = -E0 | ID

            // pick the highest precedence left-recursive rule we can find
            var leftRecursiveRule = this.rulesByProduced.SelectMany(kvp => kvp.Value)
                                    .FirstOrDefault(IsSimpleLeftRecursive);

            if (leftRecursiveRule == null)
            {
                return(this);
            }

            var isBinary = IsSimpleRightRecursive(leftRecursiveRule);

            var replacements = new List <RuleReplacement>();

            var newSymbol = new NonTerminal($"{leftRecursiveRule.Produced}_{this.rulesByProduced.Count(kvp => kvp.Key.Name.StartsWith(leftRecursiveRule.Produced.Name + "_"))}");

            // determine which rules get pushed to the new symbol. These will be any rules that are neither left or simple right recursive
            // as well as any simple right-recursive rules that are higher precedence than the current rule. Note: we don't use ToDictionary
            // here because we want to preserve order
            var leftRecursiveRuleIndex = this.rulesByProduced[leftRecursiveRule.Produced].IndexOf(leftRecursiveRule);

            replacements.AddRange(
                this.rulesByProduced[leftRecursiveRule.Produced]
                .Select(
                    // this check is simpler than the requirement stated above, because we leverage the fact that we know the current
                    // rule is the highest-precedence left-recursive rule for the symbol
                    (r, index) => index < leftRecursiveRuleIndex || (!IsSimpleLeftRecursive(r) && !IsSimpleRightRecursive(r))
                    // note that right-recursive rules have their last symbol replaced by the new symbol as well. This is to indicate the fact
                    // that E => -E can't parse as -(1 + 1) because unary minus binds tighter
                            ? new RuleReplacement(r, new Rule(newSymbol, r.Symbols.Select((s, i) => i == r.Symbols.Count - 1 && s == leftRecursiveRule.Produced ? newSymbol : s)))
                            : null
                    )
                .Where(t => t != null)
                );

            // associativity is only a binary concept
            var isLeftAssociative = isBinary && !this.rightAssociativeRules.Contains(leftRecursiveRule);

            if (!isLeftAssociative)
            {
                // for E ? E : E, we have E = T | T ? E : E
                // the former is a no-op
                replacements.AddRange(
                    new[]
                {
                    new RuleReplacement(null, new Rule(leftRecursiveRule.Produced, newSymbol)),
                    new RuleReplacement(leftRecursiveRule, new Rule(leftRecursiveRule.Produced, leftRecursiveRule.Symbols.Select((s, i) => i == 0 ? newSymbol : s))),
                }
                    );
            }
            else
            {
                // for left associativity, we do:
                // E = E + E
                // E = T List<+T>
                // every parse of "+ T" maps to E + E rule
                var suffixSymbol     = new NonTerminal($"'{string.Join(" ", leftRecursiveRule.Symbols.Skip(1).Take(leftRecursiveRule.Symbols.Count - 2).Append(newSymbol))}'");
                var suffixListSymbol = new NonTerminal($"List<{suffixSymbol}>");

                replacements.AddRange(
                    new[]
                {
                    // E = T List<+T>
                    new RuleReplacement(null, new Rule(leftRecursiveRule.Produced, newSymbol, suffixListSymbol)),

                    // List<+T> = +T List<+T> | empty
                    new RuleReplacement(null, new Rule(suffixListSymbol, suffixSymbol, suffixListSymbol)),
                    new RuleReplacement(null, new Rule(suffixListSymbol)),

                    // +T = + T
                    new RuleReplacement(leftRecursiveRule, new Rule(suffixSymbol, leftRecursiveRule.Symbols.Select((s, i) => i == leftRecursiveRule.Symbols.Count - 1 ? newSymbol : s).Skip(1)))
                }
                    );
            }

            return(this.Replace(replacements));
        }
 private bool IsAliasOf(Symbol a, NonTerminal b) =>
 a != b && Traverse.Along(a as NonTerminal, s => this.aliases.GetValueOrDefault(s)).Contains(b);