Exemplo n.º 1
0
            // GENERATED CODE EXAMPLE: The methods in this region generate
            // the for(;;) loop in this example and everything inside it, except
            // the calls to Match() which are generated by Visit(TerminalPred).
            // The generated code uses "goto" and "match" blocks in some cases
            // to avoid code duplication. This occurs when the matching code
            // requires multiple statements AND appears more than once in the
            // prediction tree. Otherwise, matching is done "inline" during
            // prediction. We generate a for(;;) loop for (...)*, and in certain
            // cases, we generates a do...while(false) loop for (...)?.
            //
            // rule Foo @{ (('a'|'A') 'A')* 'a'..'z' 'a'..'z' };
            // public void Foo()
            // {
            //     int la0, la1;
            //     for (;;) {
            //         la0 = LA(0);
            //         if (la0 == 'a') {
            //             la1 = LA(1);
            //             if (la1 == 'A')
            //                 goto match1;
            //             else
            //                 break;
            //         } else if (la0 == 'A')
            //             goto match1;
            //         else
            //             break;
            //         match1:
            //         {
            //             Match('A', 'a');
            //             Match('A');
            //         }
            //     }
            //     MatchRange('a', 'z');
            //     MatchRange('a', 'z');
            // }

            private void GenerateCodeForAlts(Alts alts, Dictionary <int, int> timesUsed, PredictionTree tree)
            {
                bool needError = LLPG.NeedsErrorBranch(tree, alts);

                if (!needError && alts.ErrorBranch != null)
                {
                    LLPG.Output(Warning, alts, "The error branch will not be used because the other alternatives are exhaustive (cover all cases)");
                }
                bool userDefinedError = needError && !_recognizerMode && alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value;

                // Generate matching code for each arm. the "string" in each pair
                // becomes non-null if the matching code for that branch needs to be
                // split out (separated) from the prediction tree because it appears
                // multiple times in the tree. The string is the goto-label name.
                Pair <LNode, string>[] matchingCode = new Pair <LNode, string> [alts.Arms.Count + (userDefinedError ? 1: 0)];
                MSet <int>             unreachable  = new MSet <int>();
                int separateCount = 0;

                for (int i = 0; i < alts.Arms.Count; i++)
                {
                    if (!timesUsed.ContainsKey(i))
                    {
                        unreachable.Add(i);
                        continue;
                    }

                    var codeForThisArm = new WList <LNode>();
                    VisitWithNewTarget(alts.Arms[i], codeForThisArm);

                    matchingCode[i].A = F.Braces(codeForThisArm.ToVList());
                    if (timesUsed[i] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A))
                    {
                        separateCount++;
                        matchingCode[i].B = alts.Arms[i].ChooseGotoLabel()
                                            ?? "match" + (i + 1).ToString();
                    }
                }

                // Add matching code for the error branch, if present. Note: the
                // default error branch, which is produced by IPGCodeGenHelper.
                // ErrorBranch() is handled differently: default error code can
                // differ at each error point in the prediction tree. Therefore
                // we generate it later, on-demand.
                if (userDefinedError)
                {
                    int i            = alts.Arms.Count;
                    var errorHandler = new WList <LNode>();
                    VisitWithNewTarget(alts.ErrorBranch, errorHandler);
                    matchingCode[i].A = F.Braces(errorHandler.ToVList());
                    if (timesUsed[ErrorAlt] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A))
                    {
                        matchingCode[i].B = "error";
                        separateCount++;
                    }
                }

                // Print unreachability warnings
                if (unreachable.Count == 1)
                {
                    LLPG.Output(Warning, alts, string.Format("Branch {{{0}}} is unreachable.", alts.AltName(unreachable.First())));
                }
                else if (unreachable.Count > 1)
                {
                    LLPG.Output(Warning, alts, string.Format("Branches {{{0}}} are unreachable.", unreachable.Select(i => alts.AltName(i)).Join(", ")));
                }
                if (!timesUsed.ContainsKey(ExitAlt) && alts.Mode != LoopMode.None)
                {
                    LLPG.Output(Warning, alts, "Infinite loop. The exit branch is unreachable.");
                }

                Symbol loopType = null;

                // Choose a loop type for (...)* or (...)?:
                if (alts.Mode == LoopMode.Star)
                {
                    loopType = S.For;
                }
                else if (alts.Mode == LoopMode.Opt)
                {
                    if (alts.HasErrorBranch(LLPG) || alts.NonExitDefaultArmRequested())
                    {
                        loopType = S.DoWhile;
                    }
                }

                // If the code for an arm is nontrivial and appears multiple times
                // in the prediction table, it will have to be split out into a
                // labeled block and reached via "goto". I'd rather just do a goto
                // from inside one "if" statement to inside another, but in C#
                // (unlike in CIL, and unlike in C) that is prohibited :(
                DeduplicateLabels(matchingCode);
                var extraMatching = GenerateExtraMatchingCode(matchingCode, separateCount, ref loopType);

                if (separateCount != 0)
                {
                    loopType = loopType ?? S.DoWhile;
                }

                Symbol breakMode = loopType;                 // used to request a "goto" label in addition to the loop
                LNode  code      = GeneratePredictionTreeCode(tree, matchingCode, ref breakMode);

                // Add break/continue between prediction tree and extra matching code,
                // if necessary.
                if (extraMatching.Count != 0 && CodeGenHelperBase.EndMayBeReachable(code))
                {
                    loopType = loopType ?? S.DoWhile;
                    extraMatching.Insert(0, GetContinueStmt(loopType));
                }

                if (!extraMatching.IsEmpty)
                {
                    code = LNode.MergeLists(code, F.Braces(extraMatching), S.Braces);
                }

                if (loopType == S.For)
                {
                    // (...)* => for (;;) {}
                    code = F.Call(S.For, F.List(), F.Missing, F.List(), code);
                }
                else if (loopType == S.DoWhile)
                {
                    // (...)? becomes "do {...} while(false);" IF the exit branch is NOT the default.
                    // If the exit branch is the default, then no loop and no "break" is needed.
                    code = F.Call(S.DoWhile, code, F.@false);
                }
                if (breakMode != loopType && breakMode != null)
                {
                    // Add "stop:" label (plus extra ";" for C# compatibility, in
                    // case the label ends the block in which it is located.)
                    var stopLabel = F.Call(S.Label, F.Id(breakMode))
                                    .PlusTrailingTrivia(F.Trivia(S.TriviaRawText, ";"));
                    code = LNode.MergeLists(code, stopLabel, S.Braces);
                }

                int oldCount = _target.Count;

                _target.SpliceAdd(code, S.Braces);

                // Add comment before code
                if (LLPG.AddComments)
                {
                    var pos     = alts.Basis.Range.Start;
                    var comment = F.Trivia(S.TriviaSLComment, string.Format(" Line {0}: {1}", pos.Line, alts.ToString()));
                    if (_target.Count > oldCount)
                    {
                        _target[oldCount] = _target[oldCount].PlusAttr(comment);
                    }
                }
            }
Exemplo n.º 2
0
            /// <summary>Extends each level of the prediction tree so that it has
            /// total coverage. For example, a typicaly prediction tree might have
            /// branches for 'a'..'z' and '0..'9'; this method will add coverage for
            /// all other possible inputs. It does this either by adding an error
            /// branch, or by extending the set handled by the default branch of
            /// each level.</summary>
            private void AddElseCases(Alts alts, PredictionTree tree)
            {
                foreach (var branch in tree.Children)
                {
                    if (branch.Sub.Tree != null)
                    {
                        AddElseCases(alts, branch.Sub.Tree);
                    }
                }

                if (tree.IsAssertionLevel)
                {
                    tree.Children.Last.AndPreds.Clear();
                    tree.Children.Last.AndPreds.Add(Set <AndPred> .Empty);
                }
                else if (!tree.TotalCoverage.ContainsEverything)
                {
                    var rest = tree.TotalCoverage.Inverted();
                    if (alts.HasErrorBranch(LLPG))
                    {
                        tree.Children.Add(new PredictionBranch(rest, new PredictionTreeOrAlt {
                            Alt = ErrorAlt
                        }, tree.TotalCoverage));
                    }
                    else
                    {
                        // No error branch, so use default arm
                                        #if false
                        // First try: this tends to produce less intuitive code. Neither
                        // version is objectively better; sometimes this version gives
                        // faster code and sometimes the other version gives faster code.
                        int defaultArm = alts.DefaultArmInt();
                        foreach (PredictionBranch branch in tree.Children)
                        {
                            if (branch.Sub.Tree == null && branch.Sub.Alt == defaultArm)
                            {
                                branch.Set = branch.Set.Union(rest);
                                goto done;
                            }
                        }
                        if (alts.DefaultArm != null)
                        {
                            tree.Children.Add(new PredictionBranch(rest, defaultArm, tree.TotalCoverage));
                        }
                        else
                        {
                            tree.Children.Last.Set = tree.Children.Last.Set.Union(rest);
                        }
                        done :;
                                        #else
                        PredictionBranch last = tree.Children.Last;
                        if (alts.DefaultArm != null && (last.Sub.Tree != null || last.Sub.Alt != alts.DefaultArm.Value))
                        {
                            tree.Children.Add(new PredictionBranch(rest, alts.DefaultArm.Value, tree.TotalCoverage));
                        }
                        else
                        {
                            last.Set = last.Set.Union(rest);
                        }
                                        #endif
                    }
                }
            }