static void AddCapture(MMap <Symbol, LNode> captures, Symbol capName, LNode candidate) { LNode oldCap = captures.TryGetValue(capName, null); captures[capName] = LNode.MergeLists(oldCap, candidate, 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); } } }
public static LNode MergeActions(LNode action, LNode action2) { return(LNode.MergeLists(action, action2, S.Splice)); }
public static LNode matchCode(LNode node, IMacroContext context) { if (node.AttrNamed(S.Static) != null) { return(null); // this case is handled by static_matchCode macro } var args_body = context.GetArgsAndBody(false); LNodeList args = args_body.Item1, body = args_body.Item2; if (args.Count != 1 || body.Count < 1) { return(null); } var cases = GetCases(body, context.Sink); if (cases.IsEmpty) { return(null); } var output = new WList <LNode>(); var var = MaybeAddTempVarDecl(context, args[0], output); var ifClauses = new List <Pair <LNode, LNode> >(); var cmc = new CodeMatchContext { Context = context }; foreach (var @case in cases) { cmc.ThenClause.Clear(); // e.g. case [$(..._)] Foo($x + 1, $y) => // LNode x, y, tmp9; // if (var.Calls((Symbol) "Foo", 2) && (tmp9 = var.Args[0]).Calls(CodeSymbols.Plus, 2) // && (x = tmp9.Args[0]) != null // this will never be null, but we want to put it the assignment in the 'if' statement // && 1.Equals(tmp9.Args[1].Value) && (y = var.Args[1]) != null) { ... } LNode testExpr = null; if (@case.Key.Count > 0) { if (cmc.IsMultiCase = @case.Key.Count > 1) { cmc.UsageCounters.Clear(); testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => { test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, var), S.Or); return(test); }); foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count)) { if (cmc.NodeVars.ContainsKey(pair.Key)) { cmc.NodeVars[pair.Key] = true; } if (cmc.ListVars.ContainsKey(pair.Key)) { cmc.ListVars[pair.Key] = true; } } } else { testExpr = cmc.MakeTopTestExpr(@case.Key[0], var); } } var handler = F.Braces(@case.Value); if (cmc.ThenClause.Count > 0) { handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces); } ifClauses.Add(Pair.Create(testExpr, handler)); } LNode ifStmt = null; for (int i = ifClauses.Count - 1; i >= 0; i--) { if (ifClauses[i].Item1 == null) { if (ifStmt == null) { ifStmt = ifClauses[i].Item2; } else { context.Sink.Error(node, "The default case must appear last, and there can be only one."); } } else { if (ifStmt == null) { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2); } else { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt); } } } if (cmc.NodeVars.Count > 0) { output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat( cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key))))); } if (cmc.ListVars.Count > 0) { LNode type = LNode.Id((Symbol)"LNodeList"); output.Add(F.Call(S.Var, ListExt.Single(type).Concat( cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key))))); } if (output.Count == 0) { return(ifStmt.IncludingTriviaFrom(node)); } else { output.Add(ifStmt); return(F.Braces(output.ToVList()).IncludingTriviaFrom(node)); } }
[LexicalMacro("matchCode (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct a Loyc tree against a series of cases with patterns, e.g. " + "`case $a + $b:` expects a tree that calls `+` with two parameters, placed in new variables called a and b. " + "`break` is not required or recognized at the end of each case's handler (code block). " + "Use `$(...x)` to gather zero or more parameters into a list `x`. " + "Use `case pattern1, pattern2:` in EC# to handle multiple cases with the same handler.")] public static LNode matchCode(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); VList <LNode> args = args_body.Item1, body = args_body.Item2; if (args.Count != 1 || body.Count < 1) { return(null); } var cases = GetCases(body, context.Sink); if (cases.IsEmpty) { return(null); } var output = new WList <LNode>(); var @var = MaybeAddTempVarDecl(args[0], output); var ifClauses = new List <Pair <LNode, LNode> >(); var cmc = new CodeMatchContext { Context = context }; foreach (var @case in cases) { cmc.ThenClause.Clear(); LNode testExpr = null; if (@case.Key.Count > 0) { if (cmc.IsMultiCase = @case.Key.Count > 1) { cmc.UsageCounters.Clear(); testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => { test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or); return(test); }); foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count)) { if (cmc.NodeVars.ContainsKey(pair.Key)) { cmc.NodeVars[pair.Key] = true; } if (cmc.ListVars.ContainsKey(pair.Key)) { cmc.ListVars[pair.Key] = true; } } } else { testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var); } } var handler = @case.Value; if (cmc.ThenClause.Count > 0) { handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces); } ifClauses.Add(Pair.Create(testExpr, handler)); } LNode ifStmt = null; for (int i = ifClauses.Count - 1; i >= 0; i--) { if (ifClauses[i].Item1 == null) { if (ifStmt == null) { ifStmt = ifClauses[i].Item2; } else { context.Sink.Write(Severity.Error, node, "The default case must appear last, and there can be only one."); } } else { if (ifStmt == null) { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2); } else { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt); } } } if (cmc.NodeVars.Count > 0) { output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key))))); } if (cmc.ListVars.Count > 0) { LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode"))); output.Add(F.Call(S.Var, ListExt.Single(type).Concat(cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key))))); } if (output.Count == 0) { return(ifStmt); } else { output.Add(ifStmt); return(F.Braces(output.ToVList())); } }
public static LNode matchCode(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); VList <LNode> args = args_body.Item1, body = args_body.Item2; if (args.Count != 1 || body.Count < 1) { return(null); } var cases = GetCases(body, context.Sink); if (cases.IsEmpty) { return(null); } var output = new WList <LNode>(); var @var = MaybeAddTempVarDecl(context, args[0], output); var ifClauses = new List <Pair <LNode, LNode> >(); var cmc = new CodeMatchContext { Context = context }; foreach (var @case in cases) { cmc.ThenClause.Clear(); LNode testExpr = null; if (@case.Key.Count > 0) { if (cmc.IsMultiCase = @case.Key.Count > 1) { cmc.UsageCounters.Clear(); testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => { test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or); return(test); }); foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count)) { if (cmc.NodeVars.ContainsKey(pair.Key)) { cmc.NodeVars[pair.Key] = true; } if (cmc.ListVars.ContainsKey(pair.Key)) { cmc.ListVars[pair.Key] = true; } } } else { testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var); } } var handler = @case.Value; if (cmc.ThenClause.Count > 0) { handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces); } ifClauses.Add(Pair.Create(testExpr, handler)); } LNode ifStmt = null; for (int i = ifClauses.Count - 1; i >= 0; i--) { if (ifClauses[i].Item1 == null) { if (ifStmt == null) { ifStmt = ifClauses[i].Item2; } else { context.Sink.Write(Severity.Error, node, "The default case must appear last, and there can be only one."); } } else { if (ifStmt == null) { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2); } else { ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt); } } } if (cmc.NodeVars.Count > 0) { output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key))))); } if (cmc.ListVars.Count > 0) { LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode"))); output.Add(F.Call(S.Var, ListExt.Single(type).Concat(cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key))))); } if (output.Count == 0) { return(ifStmt); } else { output.Add(ifStmt); return(F.Braces(output.ToVList())); } }