private void AddUserAction(LNode action) { int i = _target.Count; _target.SpliceAdd(action, S.Splice); if (action.Range.StartIndex > 0 && _target.Count > i) { if (LLPG.AddCsLineDirectives) { // Remove folder name. This only makes sense if the output // file and input file are in the same folder; sadly we have // no access to the output file name, but as of 2015-05 it's // always true that the output file will be in the same folder. string filename = System.IO.Path.GetFileName(_target[i].Range.Source.FileName); int line = 0; for (; i < _target.Count; i++, line++) { var r = _target[i].Range; if (line != r.Start.Line) { line = r.Start.Line; _target[i] = _target[i].PlusAttr(F.Trivia(S.TriviaRawTextBefore, string.Format("#line {0} {1}\n", line, Ecs.EcsNodePrinter.PrintString(filename, '"')))); } } _target.Add(F.Trivia(S.RawText, "#line default")); } else { _target[i] = _target[i].PlusAttr(F.Trivia(S.TriviaSLCommentBefore, string.Format(" line {0}", _target[i].Range.Start.Line))); } } }
private void AddSwitchHandler(LNode branch, WList <LNode> stmts) { stmts.SpliceAdd(branch, S.Splice); if (EndMayBeReachable(branch)) { stmts.Add(F.Call(S.Break)); } }
public void Generate(Rule rule) { CGH.BeginRule(rule); _currentRule = rule; _target = new WList <LNode>(); _laVarsNeeded = 0; _separatedMatchCounter = _stopLabelCounter = 0; _recognizerMode = rule.IsRecognizer; _labelsInUse.Clear(); Visit(rule.Pred); if (_laVarsNeeded != 0) { LNode laVars = F.Call(S.Var, CGH.LAType()); for (int i = 0; _laVarsNeeded != 0; i++, _laVarsNeeded >>= 1) { if ((_laVarsNeeded & 1) != 0) { laVars = laVars.PlusArg(F.Id("la" + i.ToString())); } } _target.Insert(0, laVars); } LNode method; if (rule.TryWrapperName != null) { Debug.Assert(rule.IsRecognizer); method = F.OnNewLine(CGH.CreateTryWrapperForRecognizer(rule)); _classBody.SpliceAdd(method, S.Splice); } method = CGH.CreateRuleMethod(rule, _target.ToVList()); if (!rule.IsRecognizer) { method = F.OnNewLine(method); } _classBody.SpliceAdd(method, S.Splice); }
// 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); } } }
private void AddSwitchHandler(LNode branch, WList<LNode> stmts) { stmts.SpliceAdd(branch, S.Splice); if (EndMayBeReachable(branch)) stmts.Add(F.Call(S.Break)); }