public static LNode UnpackTuple(LNode node, IMacroContext context) { var a = node.Args; if (a.Count == 2 && a[0].CallsMin(S.Tuple, 1)) { var output = new WList <LNode>(); var tuple = a[0].Args; var rhs = a[1]; // Avoid evaluating rhs more than once, if it doesn't look like a simple variable rhs = LeMP.ecs.StandardMacros.MaybeAddTempVarDecl(context, rhs, output); for (int i = 0; i < tuple.Count; i++) { var itemi = F.Dot(rhs, F.Id(GSymbol.Get("Item" + (i + 1)))); if (tuple[i].Calls(S.Var, 2)) { output.Add(F.Var(tuple[i].Args[0], tuple[i].Args[1], itemi)); } else { output.Add(F.Call(S.Assign, tuple[i], itemi)); } } return(F.Call(S.Splice, output.ToVList())); } return(null); }
private WList <LNode> GenerateExtraMatchingCode(Pair <LNode, string>[] matchingCode, int separateCount, ref Symbol loopType) { var extraMatching = new WList <LNode>(); if (separateCount != 0) { for (int i = 0; i < matchingCode.Length; i++) { if (matchingCode[i].B != null) // split out this case { var label = F.Id(matchingCode[i].B); // break/continue; matchN: matchingCode[i].A; if (extraMatching.Count > 0) { extraMatching.Add(GetContinueStmt(loopType)); } extraMatching.Add(F.Call(S.Label, label)); extraMatching.Add(matchingCode[i].A); //skipCount++; // put @@{ goto matchN; } in prediction tree matchingCode[i].A = F.Call(S.Goto, label); } } } return(extraMatching); }
public static LNode unroll(LNode var, LNode cases, LNode body, IMessageSink sink) { if (!cases.Calls(S.Tuple) && !cases.Calls(S.Braces)) return Reject(sink, cases, "unroll: the right-hand side of 'in' should be a tuple"); // Maps identifiers => replacements. The integer counts how many times replacement occurred. var replacements = InternalList<Triplet<Symbol, LNode, int>>.Empty; if (var.IsId && !var.HasPAttrs()) { replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0)); } else { var vars = var.Args; if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs())) { replacements = new Triplet<Symbol, LNode, int>[vars.Count].AsInternalList(); for (int i = 0; i < vars.Count; i++) { replacements.InternalArray[i].A = vars[i].Name; // Check for duplicate names for (int j = 0; j < i; j++) if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_") sink.Write(Severity.Error, vars[i], "unroll: duplicate name in the left-hand tuple"); // non-fatal } } else return Reject(sink, cases, "unroll: the left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers."); } UnrollCtx ctx = new UnrollCtx { Replacements = replacements }; WList<LNode> output = new WList<LNode>(); int iteration = 0; foreach (LNode replacement in cases.Args) { iteration++; bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces); int count = tuple ? replacement.ArgCount : 1; if (replacements.Count != count) { sink.Write(Severity.Error, replacement, "unroll, iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count); if (count < replacements.Count) continue; // too few } for (int i = 0; i < replacements.Count; i++) replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement; if (body.Calls(S.Braces)) { foreach (LNode stmt in body.Args) output.Add(ctx.Replace(stmt).Value); } else output.Add(ctx.Replace(body).Value); } foreach (var r in replacements) if (r.C == 0 && !r.A.Name.StartsWith("_")) sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A); return body.With(S.Splice, output.ToVList()); }
[LexicalMacro("match (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct an object against a \"pattern\", such as a tuple or an algebraic data type. Example:\n" + "match (obj) { \n" + " case is Shape(ShapeType.Circle, $size, Location: $p is Point<int>($x, $y)): \n" + " Circle(size, x, y); \n" + "}\n\n" + "This is translated to the following C# code: \n" + "do { \n" + " Point<int> p; \n" + " Shape tmp1; \n" + " if (obj is Shape) { \n" + " var tmp1 = (Shape)obj; \n" + " if (tmp1.Item1 == ShapeType.Circle) { \n" + " var size = tmp1.Item2; \n" + " var tmp2 = tmp1.Location; \n" + " if (tmp2 is Point<int>) { \n" + " var p = (Point<int>)tmp2; \n" + " var x = p.Item1; \n" + " var y = p.Item2; \n" + " Circle(size, x, y); \n" + " break; \n" + " } \n" + " }\n" + " }\n" + "} while(false); \n" + "`break` is not expected at the end of each handler (`case` code block), but it can " + "be used to exit early from a `case`. You can associate multiple patterns with the same " + "handler using `case pattern1, pattern2:` in EC#, but please note that (due to a " + "limitation of plain C#) this causes code duplication since the handler will be repeated " + "for each pattern.")] public static LNode match(LNode node, IMacroContext context) { { LNode input; VList <LNode> contents; if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) { contents = node.Args[1].Args; var outputs = new WList <LNode>(); input = MaybeAddTempVarDecl(input, outputs); int next_i = 0; for (int case_i = 0; case_i < contents.Count; case_i = next_i) { var @case = contents[case_i]; if (!IsCaseLabel(@case)) { return(Reject(context, contents[0], "In 'match': expected 'case' statement")); } for (next_i = case_i + 1; next_i < contents.Count; next_i++) { var stmt = contents[next_i]; if (IsCaseLabel(stmt)) { break; } if (stmt.Calls(S.Break, 0)) { next_i++; break; } } var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1))); if (@case.Calls(S.Case) && @case.Args.Count > 0) { var codeGen = new CodeGeneratorForMatchCase(context, input, handler); foreach (var pattern in @case.Args) { outputs.Add(codeGen.GenCodeForPattern(pattern)); } } else { outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement)); if (next_i < contents.Count) { context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement."); } } } return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false)))); } } return(null); }
public static LNode match(LNode node, IMacroContext context) { { LNode input; VList <LNode> contents; if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) { contents = node.Args[1].Args; var outputs = new WList <LNode>(); input = MaybeAddTempVarDecl(context, input, outputs); int next_i = 0; for (int case_i = 0; case_i < contents.Count; case_i = next_i) { var @case = contents[case_i]; if (!IsCaseLabel(@case)) { return(Reject(context, contents[0], "In 'match': expected 'case' statement")); } for (next_i = case_i + 1; next_i < contents.Count; next_i++) { var stmt = contents[next_i]; if (IsCaseLabel(stmt)) { break; } if (stmt.Calls(S.Break, 0)) { next_i++; break; } } var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1))); if (@case.Calls(S.Case) && @case.Args.Count > 0) { var codeGen = new CodeGeneratorForMatchCase(context, input, handler); foreach (var pattern in @case.Args) { outputs.Add(codeGen.GenCodeForPattern(pattern)); } } else { outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement)); if (next_i < contents.Count) { context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement."); } } } return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false)))); } } return(null); }
private void AddSwitchHandler(LNode branch, WList <LNode> stmts) { stmts.SpliceAdd(branch, S.Splice); if (EndMayBeReachable(branch)) { stmts.Add(F.Call(S.Break)); } }
private void AddUserAction(VList <LNode> actions) { int i = _target.Count; _target.AddRange(actions); if (actions.Any(stmt => stmt.Range.StartIndex > 0)) { 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); var newline = F.Id(S.TriviaNewline); int line = 0; for (; i < _target.Count; i++, line++) { var r = _target[i].Range; if (line != r.Start.Line) { line = r.Start.Line; if (line <= 0) // sometimes occurs for generated `return result` statement { _target.Insert(i++, F.Trivia(S.CsPPRawText, "#line default")); } else { _target.Insert(i++, F.Trivia(S.CsPPRawText, "#line " + line + " " + Ecs.EcsNodePrinter.PrintString(filename, '"'))); } } } if (line > 1) { _target.Add(F.Trivia(S.CsPPRawText, "#line default")); } } else { _target[i] = _target[i].PlusAttr(F.Trivia(S.TriviaSLComment, string.Format(" line {0}", _target[i].Range.Start.Line))); } } }
// Used to avoid evaluating `value` more than once by creating a // declaration in `output` of a temporary variable to hold the value. // If `value` looks simple (according to LooksLikeSimpleValue), this // fn returns value and leaves output unchanged. protected static LNode MaybeAddTempVarDecl(IMacroContext ctx, LNode value, WList<LNode> output) { if (!LooksLikeSimpleValue(value)) { LNode tmpId; output.Add(TempVarDecl(ctx, value, out tmpId)); return tmpId; } return value; }
// Used to avoid evaluating `value` more than once by creating a // declaration in `output` of a temporary variable to hold the value. // If `value` looks simple (according to LooksLikeSimpleValue), this // fn returns value and leaves output unchanged. protected static LNode MaybeAddTempVarDecl(LNode value, WList <LNode> output) { if (!LooksLikeSimpleValue(value)) { LNode tmpId; output.Add(TempVarDecl(value, out tmpId)); return(tmpId); } return(value); }
// Used to avoid evaluating `value` more than once by creating a // declaration in `output` of a temporary variable to hold the value. // If `value` looks simple (according to LooksLikeSimpleValue), this // fn returns value and leaves output unchanged. protected internal static LNode MaybeAddTempVarDecl(IMacroContext ctx, LNode value, WList <LNode> output) { if (!LooksLikeSimpleValue(value)) { LNode tmpId; output.Add(TempVarDecl(ctx, value, out tmpId)); return(tmpId); } return(value); }
public static void SpliceAdd(this WList <LNode> list, LNode node, Symbol listName = null) { if (node.Calls(listName ?? CodeSymbols.Splice)) { list.AddRange(node.Args); } else { list.Add(node); } }
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))); } } }
[LexicalMacro("match (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct an object against a \"pattern\", such as a tuple or an algebraic data type. Example:\n" + "match (obj) { \n" + " case is Shape(ShapeType.Circle, $size, Location: $p is Point<int>($x, $y)): \n" + " Circle(size, x, y); \n" + "}\n\n" + "This is translated to the following C# code: \n" + "do { \n" + " Point<int> p; \n" + " Shape tmp1; \n" + " if (obj is Shape) { \n" + " var tmp1 = (Shape)obj; \n" + " if (tmp1.Item1 == ShapeType.Circle) { \n" + " var size = tmp1.Item2; \n" + " var tmp2 = tmp1.Location; \n" + " if (tmp2 is Point<int>) { \n" + " var p = (Point<int>)tmp2; \n" + " var x = p.Item1; \n" + " var y = p.Item2; \n" + " Circle(size, x, y); \n" + " break; \n" + " } \n" + " }\n" + " }\n" + "} while(false); \n" + "`break` is not expected at the end of each handler (`case` code block), but it can " + "be used to exit early from a `case`. You can associate multiple patterns with the same " + "handler using `case pattern1, pattern2:` in EC#, but please note that (due to a " + "limitation of plain C#) this causes code duplication since the handler will be repeated " + "for each pattern.")] public static LNode match(LNode node, IMacroContext context) { { LNode input; VList<LNode> contents; if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) { contents = node.Args[1].Args; var outputs = new WList<LNode>(); input = MaybeAddTempVarDecl(input, outputs); int next_i = 0; for (int case_i = 0; case_i < contents.Count; case_i = next_i) { var @case = contents[case_i]; if (!IsCaseLabel(@case)) return Reject(context, contents[0], "In 'match': expected 'case' statement"); for (next_i = case_i + 1; next_i < contents.Count; next_i++) { var stmt = contents[next_i]; if (IsCaseLabel(stmt)) break; if (stmt.Calls(S.Break, 0)) { next_i++; break; } } var handler = new VList<LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1))); if (@case.Calls(S.Case) && @case.Args.Count > 0) { var codeGen = new CodeGeneratorForMatchCase(context, input, handler); foreach (var pattern in @case.Args) outputs.Add(codeGen.GenCodeForPattern(pattern)); } else { outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement)); if (next_i < contents.Count) context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement."); } } return LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false))); } } return null; }
protected virtual Symbol GenerateSetDecl(IPGTerminalSet set) { Symbol setName; if (_setDeclNames.TryGetValue(set, out setName)) { return(setName); } setName = GenerateSetName(_currentRule); _classBody.Add(GenerateSetDecl(set, setName)); return(_setDeclNames[set] = setName); }
public virtual LNode GenerateSwitch(IPGTerminalSet[] branchSets, LNode[] branchCode, MSet <int> casesToInclude, LNode defaultBranch, LNode laVar) { Debug.Assert(branchSets.Length == branchCode.Length); Debug.Assert(casesToInclude.Count <= branchCode.Length); WList <LNode> stmts = new WList <LNode>(); for (int i = 0; i < branchCode.Length; i++) { if (casesToInclude.Contains(i)) { int index = -1; foreach (LNode value in GetCases(branchSets[i])) { var label = F.Call(S.Case, value); if (++index > 0 && (index % 4) != 0) // write 4 cases per line { label = label.PlusAttr(F.Id(S.TriviaAppendStatement)); } stmts.Add(label); if (stmts.Count > 65535) // sanity check { throw new InvalidOperationException("switch is too large to generate"); } } AddSwitchHandler(branchCode[i], stmts); } } if (!defaultBranch.IsIdNamed(GSymbol.Empty)) { stmts.Add(F.Call(S.Label, F.Id(S.Default))); AddSwitchHandler(defaultBranch, stmts); } return(F.Call(S.Switch, (LNode)laVar, F.Braces(stmts.ToVList()))); }
public static LNode match(LNode node, IMacroContext context) { { LNode input; VList<LNode> contents; if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) { contents = node.Args[1].Args; var outputs = new WList<LNode>(); input = MaybeAddTempVarDecl(context, input, outputs); int next_i = 0; for (int case_i = 0; case_i < contents.Count; case_i = next_i) { var @case = contents[case_i]; if (!IsCaseLabel(@case)) return Reject(context, contents[0], "In 'match': expected 'case' statement"); for (next_i = case_i + 1; next_i < contents.Count; next_i++) { var stmt = contents[next_i]; if (IsCaseLabel(stmt)) break; if (stmt.Calls(S.Break, 0)) { next_i++; break; } } var handler = new VList<LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1))); if (@case.Calls(S.Case) && @case.Args.Count > 0) { var codeGen = new CodeGeneratorForMatchCase(context, input, handler); foreach (var pattern in @case.Args) outputs.Add(codeGen.GenCodeForPattern(pattern)); } else { // default: // Note: the extra {braces} around the handler are rarely // needed. They are added just in case the handler declares a // variable and a different handler declares another variable // by the same name, which is illegal unless we add braces. outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement)); if (next_i < contents.Count) context.Sink.Error(contents[next_i], "The default branch must be the final branch in a 'match' statement."); } } return LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false))); } } return null; }
/// <summary> /// Spawns a random <see cref="Vehicle"/>, wraps it and adds it to a specific <see cref="WList{TEntity}"/>. /// </summary> /// <param name="list">The wrapped list.</param> /// <param name="position">The position.</param> /// <param name="heading">The heading of the vehicle.</param> /// <returns></returns> public static WVehicle CreateRandomVehicle(this WList <WVehicle> list, Vector3 position, float heading = 0f) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (position == null) { throw new ArgumentNullException(nameof(position)); } var vehicle = new WVehicle(list.CallbackManager, World.CreateRandomVehicle(position, heading)); list.Add(vehicle); return(vehicle); }
/// <summary> /// Spawns a <see cref="Ped"/>, wraps it and adds it to a specific <see cref="WList{TEntity}"/>. /// </summary> /// <param name="list">The wrapped list.</param> /// <param name="modelHash">The world model.</param> /// <param name="position">The position.</param> /// <param name="heading">The heading of the ped.</param> /// <returns></returns> public static WPed CreatePed(this WList <WPed> list, PedHash modelHash, Vector3 position, float heading = 0f) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (position == null) { throw new ArgumentNullException(nameof(position)); } var ped = new WPed(list.CallbackManager, World.CreatePed(modelHash, position, heading)); list.Add(ped); return(ped); }
protected LNode SingleExprInside(Token group, string stmtType, WList <LNode> list = null, bool allowUnassignedVarDecl = false) { list = list ?? new WList <LNode>(); int oldCount = list.Count; AppendExprsInside(group, list, false, allowUnassignedVarDecl); if (list.Count != oldCount + 1) { if (list.Count <= oldCount) { LNode result = F.Id(S.Missing, group.StartIndex + 1, group.StartIndex + 1); list.Add(result); Error(result, "Missing expression inside '{0}'", stmtType); return(result); } else { Error(list[1], "There should be only one expression inside '{0}'", stmtType); list.Resize(oldCount + 1); } } return(list[0]); }
/// <summary> /// Spawns a <see cref="Prop"/>, wraps it and adds it to a specific <see cref="WList{TEntity}"/>. /// </summary> /// <param name="list">The wrapped list.</param> /// <param name="model">The world model.</param> /// <param name="position">The position.</param> /// <param name="rotation">The rotation.</param> /// <param name="dynamic">Whether the prop should be dynamic.</param> /// <param name="placeOnGround">Whether to place the prop on the ground.</param> /// <returns></returns> public static WProp CreateProp(this WList <WProp> list, Model model, Vector3 position, Vector3 rotation = default(Vector3), bool dynamic = true, bool placeOnGround = true) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (position == null) { throw new ArgumentNullException(nameof(position)); } if (rotation == null) { throw new ArgumentNullException(nameof(rotation)); } var prop = new WProp(list.CallbackManager, World.CreateProp(model, position, rotation, dynamic, placeOnGround)); list.Add(prop); return(prop); }
Mode = MacroMode.ProcessChildrenBefore)] // post-normal-macro-expansion public static LNode with(LNode fn, IMacroContext context) { LNode braces; if (fn.ArgCount != 2 || !(braces = fn.Args[1]).Calls(S.Braces)) { return(null); } LNode tmp = F.Id(NextTempName(context)); WList <LNode> stmts = braces.Args.ToWList(); stmts = stmts.SmartSelect(stmt => stmt.ReplaceRecursive(expr => { if (expr.Calls(S.Dot, 1)) { return(expr.WithArgs(new VList <LNode>(tmp, expr.Args.Last))); } else if (expr.IsIdNamed("#")) { return(tmp); } return(null); })); stmts.Insert(0, F.Var(null, tmp.Name, fn.Args[0])); if (IsExpressionContext(context)) { stmts.Add(tmp); return(F.Call("#runSequence", stmts.ToVList())); } else { return(F.Braces(stmts.ToVList())); } }
public static LNode unroll(LNode var, VList <LNode> cases, LNode body, IMessageSink sink) { // Maps identifiers => replacements. The integer counts how many times replacement occurred. var replacements = InternalList <Triplet <Symbol, LNode, int> > .Empty; if (var.IsId && !var.HasPAttrs()) { replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0)); } else { var vars = var.Args; if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs())) { replacements = new Triplet <Symbol, LNode, int> [vars.Count].AsInternalList(); for (int i = 0; i < vars.Count; i++) { replacements.InternalArray[i].A = vars[i].Name; // Check for duplicate names for (int j = 0; j < i; j++) { if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_") { sink.Error(vars[i], "Duplicate name in the left-hand tuple"); // non-fatal } } } } else { return(Reject(sink, var, "The left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers.")); } } UnrollCtx ctx = new UnrollCtx { Replacements = replacements }; WList <LNode> output = new WList <LNode>(); int iteration = 0; foreach (LNode replacement in cases) { iteration++; bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces); int count = tuple ? replacement.ArgCount : 1; if (replacements.Count != count) { sink.Error(replacement, "iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count); if (count < replacements.Count) { continue; // too few } } for (int i = 0; i < replacements.Count; i++) { replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement; } if (body.Calls(S.Braces)) { foreach (LNode stmt in body.Args) { output.Add(ctx.Replace(stmt).Value); } } else { output.Add(ctx.Replace(body).Value); } } foreach (var r in replacements) { if (r.C == 0 && !r.A.Name.StartsWith("_")) { sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A); } } return(body.With(S.Splice, output.ToVList())); }
protected LNode GeneratePredictionTreeCode(PredictionTree tree, Pair <LNode, string>[] matchingCode, ref Symbol haveLoop) { var braces = F.Braces(); Debug.Assert(tree.Children.Count >= 1); var alts = (Alts)_currentPred; if (tree.Children.Count == 1) { return(GetPredictionSubtreeCode(tree.Children[0], matchingCode, ref haveLoop)); } // From the prediction table, we can generate either an if-else chain: // // if (la0 >= '0' && la0 <= '7') sub_tree_1(); // else if (la0 == '-') sub_tree_2(); // else break; // // or a switch statement: // // switch(la0) { // case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': // sub_tree_1(); // break; // case '-': // sub_tree_2(); // break; // default: // goto breakfor; // } // // Assertion levels always need an if-else chain; lookahead levels // consider the complexity of switch vs if and decide which is most // appropriate. Generally "if" is slower, but a switch may require // too many labels since it doesn't support ranges like "la0 >= 'a' // && la0 <= 'z'". // // This class makes if-else chains directly (using IPGTerminalSet. // GenerateTest() to generate the test expressions), but the code // generation helper (CGH) is used to generate switch statements // because the required code may be more complex. // // We may or may not be generating code inside a for(;;) loop. If we // decide to generate a switch() statement, one of the branches will // usually need to break out of the for loop, but "break" can only // break out of the switch(). In that case, add "stop:" after the // switch() and use "goto stop" instead of "break". WList <LNode> block = new WList <LNode>(); LNode laVar = null; MSet <int> switchCases = new MSet <int>(); IPGTerminalSet[] branchSets = null; bool should = false; if (tree.UsesLA()) { laVar = F.Id("la" + tree.Lookahead.ToString()); if (!tree.IsAssertionLevel) { IPGTerminalSet covered = CGH.EmptySet; branchSets = tree.Children.Select(branch => { var set = branch.Set.Subtract(covered); covered = covered.Union(branch.Set); return(set); }).ToArray(); should = CGH.ShouldGenerateSwitch(branchSets, switchCases, tree.Children.Last.IsErrorBranch); if (!should) { switchCases.Clear(); } else if (should && haveLoop == S.For) { // Can't "break" out of the for-loop when there is a nested switch, haveLoop = GSymbol.Get(NextStopLabel()); // so use "goto stop". } } } LNode[] branchCode = new LNode[tree.Children.Count]; for (int i = 0; i < tree.Children.Count; i++) { if (tree.Children[i].IsErrorBranch) { if (_recognizerMode) { branchCode[i] = F.Call(S.Return, F.False); } else if (alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value) { Debug.Assert(matchingCode.Length == alts.Arms.Count + 1); branchCode[i] = matchingCode[alts.Arms.Count].A; } else { branchCode[i] = CGH.ErrorBranch(tree.TotalCoverage, tree.Lookahead); } } else { branchCode[i] = GetPredictionSubtreeCode(tree.Children[i], matchingCode, ref haveLoop); } } var code = GenerateIfElseChain(tree, branchCode, ref laVar, switchCases); if (laVar != null) { block.Insert(0, F.Assign(laVar, CGH.LA(tree.Lookahead))); _laVarsNeeded |= 1ul << tree.Lookahead; } else if (should) { laVar = CGH.LA(tree.Lookahead); } if (should) { Debug.Assert(switchCases.Count != 0); code = CGH.GenerateSwitch(branchSets, branchCode, switchCases, code ?? F.Missing, laVar); } block.Add(code); return(F.Braces(block.ToVList())); }
public static LNode match(LNode node, IMacroContext context) { { LNode input; VList <LNode> contents; if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) { contents = node.Args[1].Args; var outputs = new WList <LNode>(); input = MaybeAddTempVarDecl(context, input, outputs); // Process the braced block, one case at a time int next_i = 0; for (int case_i = 0; case_i < contents.Count; case_i = next_i) { var @case = contents[case_i]; if (!IsCaseLabel(@case) // `case ...:` or `default:` ) { return(Reject(context, contents[0], "In 'match': expected 'case' statement")); } // Find the end of the current case/default block for (next_i = case_i + 1; next_i < contents.Count; next_i++) { var stmt = contents[next_i]; if (IsCaseLabel(stmt)) { break; } if (stmt.Calls(S.Break, 0)) { next_i++; break; } } // handler: the list of statements underneath `case` var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1))); if (@case.Calls(S.Case) && @case.Args.Count > 0) { var codeGen = new CodeGeneratorForMatchCase(context, input, handler); foreach (var pattern in @case.Args) { outputs.Add(codeGen.GenCodeForPattern(pattern)); } } else // default: // Note: the extra {braces} around the handler are rarely // needed. They are added just in case the handler declares a // variable and a different handler declares another variable // by the same name, which is illegal unless we add braces. { outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement)); if (next_i < contents.Count) { context.Sink.Error(contents[next_i], "The default branch must be the final branch in a 'match' statement."); } } } return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false)))); } } return(null); }
public static LNode UnpackTuple(LNode node, IMessageSink sink) { var a = node.Args; if (a.Count == 2 && a[0].CallsMin(S.Tuple, 1)) { var output = new WList<LNode>(); var tuple = a[0].Args; var rhs = a[1]; // Avoid evaluating rhs more than once, if it doesn't look like a simple variable rhs = MaybeAddTempVarDecl(rhs, output); for (int i = 0; i < tuple.Count; i++) { var itemi = F.Dot(rhs, F.Id(GSymbol.Get("Item" + (i + 1)))); if (tuple[i].Calls(S.Var, 2)) output.Add(F.Var(tuple[i].Args[0], tuple[i].Args[1], itemi)); else output.Add(F.Call(S.Assign, tuple[i], itemi)); } return F.Call(S.Splice, output.ToVList()); } return null; }
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); 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(); // 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 = @case.Value.AsLNode(S.Braces); 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.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol) "VList"), LNode.Id((Symbol) "LNode"))).SetStyle(NodeStyle.Operator); 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()); } }
private void AddSwitchHandler(LNode branch, WList<LNode> stmts) { stmts.SpliceAdd(branch, S.Splice); if (EndMayBeReachable(branch)) stmts.Add(F.Call(S.Break)); }
private WList<LNode> GenerateExtraMatchingCode(Pair<LNode, string>[] matchingCode, int separateCount, ref Symbol loopType) { var extraMatching = new WList<LNode>(); if (separateCount != 0) { for (int i = 0; i < matchingCode.Length; i++) { if (matchingCode[i].B != null) // split out this case { var label = F.Id(matchingCode[i].B); // break/continue; matchN: matchingCode[i].A; if (extraMatching.Count > 0) extraMatching.Add(GetContinueStmt(loopType)); extraMatching.Add(F.Call(S.Label, label)); extraMatching.Add(matchingCode[i].A); //skipCount++; // put @@{ goto matchN; } in prediction tree matchingCode[i].A = F.Call(S.Goto, label); } } } return extraMatching; }
[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 virtual LNode GenerateSwitch(IPGTerminalSet[] branchSets, MSet<int> casesToInclude, LNode[] branchCode, LNode defaultBranch, LNode laVar) { Debug.Assert(branchSets.Length == branchCode.Length); WList<LNode> stmts = new WList<LNode>(); for (int i = 0; i < branchSets.Length; i++) { if (casesToInclude.Contains(i)) { foreach (LNode value in GetCases(branchSets[i])) { stmts.Add(F.Call(S.Case, value)); if (stmts.Count > 65535) // sanity check throw new InvalidOperationException("switch is too large to generate"); } AddSwitchHandler(branchCode[i], stmts); } } if (!defaultBranch.IsIdNamed(S.Missing)) { stmts.Add(F.Call(S.Label, F.Id(S.Default))); AddSwitchHandler(defaultBranch, stmts); } return F.Call(S.Switch, (LNode)laVar, F.Braces(stmts.ToVList())); }
private void MakeTestExpr(LNode pattern, LNode candidate, out Symbol varArgSym, out LNode varArgCond) { varArgSym = null; varArgCond = null; // is this a $substitutionVar? LNode condition; bool isParams, refExistingVar; var nodeVar = DecodeSubstitutionExpr(pattern, out condition, out isParams, out refExistingVar); // Unless the candidate is a simple variable name, avoid repeating // it by creating a temporary variable to hold its value int predictedTests = pattern.Attrs.Count + (nodeVar != null ? 0 : pattern.Args.Count) + (!pattern.HasSimpleHeadWithoutPAttrs() ? 1 : 0); if (predictedTests > 1) { candidate = MaybePutCandidateInTempVar(candidate.IsCall, candidate); } MatchAttributes(pattern, candidate); // Look for @[$(...var)] // case $_ if (nodeVar != null) { if (nodeVar != __ || condition != null) { if (!refExistingVar) { AddVar(nodeVar, isParams, errAt: pattern); } if (!isParams) { var assignment = LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(nodeVar), candidate)).SetStyle(NodeStyle.Operator); Tests.Add(LNode.Call(CodeSymbols.NotEq, LNode.List(assignment.PlusAttrs(LNode.List(LNode.InParensTrivia)), LNode.Literal(null))).SetStyle(NodeStyle.Operator)); Tests.Add(condition); } } if (isParams) { varArgSym = nodeVar; varArgCond = condition; return; } } else if (pattern.IsId) { Tests.Add(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"IsIdNamed"))).SetStyle(NodeStyle.Operator), LNode.List(LNode.Call(CodeSymbols.Cast, LNode.List(F.Literal(pattern.Name.Name), LNode.Id((Symbol)"Symbol"))).SetStyle(NodeStyle.Operator)))); } else if (pattern.IsLiteral) { if (pattern.Value == null) { Tests.Add(LNode.Call(CodeSymbols.Eq, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Value"))).SetStyle(NodeStyle.Operator), LNode.Literal(null))).SetStyle(NodeStyle.Operator)); } else { Tests.Add(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(pattern, LNode.Id((Symbol)"Equals"))).SetStyle(NodeStyle.Operator), LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Value"))).SetStyle(NodeStyle.Operator)))); } } else // call(...) { int?varArgAt; int fixedArgC = GetFixedArgCount(pattern.Args, out varArgAt); // Test if the call target matches var pTarget = pattern.Target; if (pTarget.IsId && !pTarget.HasPAttrs()) { var quoteTarget = QuoteSymbol(pTarget.Name); LNode targetTest; if (varArgAt.HasValue && fixedArgC == 0) { targetTest = LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Calls"))).SetStyle(NodeStyle.Operator), LNode.List(quoteTarget)); } else if (varArgAt.HasValue) { targetTest = LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"CallsMin"))).SetStyle(NodeStyle.Operator), LNode.List(quoteTarget, F.Literal(fixedArgC))); } else { targetTest = LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Calls"))).SetStyle(NodeStyle.Operator), LNode.List(quoteTarget, F.Literal(fixedArgC))); } Tests.Add(targetTest); } else { if (fixedArgC == 0) { Tests.Add(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"IsCall"))).SetStyle(NodeStyle.Operator)); if (!varArgAt.HasValue) { Tests.Add(LNode.Call(CodeSymbols.Eq, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"Count"))).SetStyle(NodeStyle.Operator), LNode.Literal(0))).SetStyle(NodeStyle.Operator)); } } else { var op = varArgAt.HasValue ? S.GE : S.Eq; Tests.Add(LNode.Call(op, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"Count"))).SetStyle(NodeStyle.Operator), F.Literal(fixedArgC)))); } int i = Tests.Count; MakeTestExpr(pTarget, LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Target"))).SetStyle(NodeStyle.Operator)); } MakeArgListTests(pattern.Args, ref candidate); } }
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)); } }
public static LNode @try(LNode node, IMessageSink sink) { if (!node.IsCall) return null; // try(code, catch, Exception::e, handler, catch, ..., finally, handler) // ...becomes... // #try(#{ stmt1; stmt2; ... }, #catch(#var(Exception, e), handler), #finally(handler)) LNode finallyCode = null; WList<LNode> clauses = new WList<LNode>(); var parts = node.Args; for (int i = parts.Count-2; i >= 1; i -= 2) { var p = parts[i]; if (p.IsIdNamed(_finally)) { if (clauses.Count != 0 || finallyCode != null) sink.Write(Severity.Error, p, "The «finally» clause must come last, there can only be one of them."); finallyCode = parts[i+1]; } else if (p.Name == _catch) { if (p.ArgCount > 0) { if (p.ArgCount > 1) sink.Write(Severity.Error, p, "Expected catch() to take one argument."); // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, p.Args[0], F.Missing, parts[i + 1])); } else { // This is a catch-all clause (the type argument is missing) if (clauses.Count != 0) sink.Write(Severity.Error, p, "The catch-all clause must be the last «catch» clause."); clauses.Add(F.Call(S.Catch, F.Missing, F.Missing, parts[i + 1])); } } else if (i > 1 && parts[i-1].IsIdNamed(_catch)) { // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, AutoRemoveParens(p), F.Missing, parts[i+1])); i--; } else { return Reject(sink, p, "Expected «catch» or «finally» clause here. Clause is missing or malformed."); } if (i == 2) return Reject(sink, parts[1], "Expected «catch» or «finally» clause here. Clause is missing or malformed."); } if (clauses.Count == 0 && finallyCode == null) { Debug.Assert(node.ArgCount <= 1); return Reject(sink, node, "Missing «catch, Type, Code» or «finally, Code» clause"); } if (finallyCode != null) clauses.Add(F.Call(S.Finally, finallyCode)); clauses.Insert(0, node.Args[0]); return node.With(S.Try, clauses.ToVList()); }
[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())); } }
public static LNode @try(LNode node, IMessageSink sink) { if (!node.IsCall) { return(null); } // try(code, catch, Exception::e, handler, catch, ..., finally, handler) // ...becomes... // #try(#{ stmt1; stmt2; ... }, #catch(#var(Exception, e), handler), #finally(handler)) LNode finallyCode = null; WList <LNode> clauses = new WList <LNode>(); var parts = node.Args; for (int i = parts.Count - 2; i >= 1; i -= 2) { var p = parts[i]; if (p.IsIdNamed(_finally)) { if (clauses.Count != 0 || finallyCode != null) { sink.Write(Severity.Error, p, "The «finally» clause must come last, there can only be one of them."); } finallyCode = parts[i + 1]; } else if (p.Name == _catch) { if (p.ArgCount > 0) { if (p.ArgCount > 1) { sink.Write(Severity.Error, p, "Expected catch() to take one argument."); } // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, p.Args[0], F.Missing, parts[i + 1])); } else { // This is a catch-all clause (the type argument is missing) if (clauses.Count != 0) { sink.Write(Severity.Error, p, "The catch-all clause must be the last «catch» clause."); } clauses.Add(F.Call(S.Catch, F.Missing, F.Missing, parts[i + 1])); } } else if (i > 1 && parts[i - 1].IsIdNamed(_catch)) { // This is a normal catch clause clauses.Insert(0, F.Call(S.Catch, AutoRemoveParens(p), F.Missing, parts[i + 1])); i--; } else { return(Reject(sink, p, "Expected «catch» or «finally» clause here. Clause is missing or malformed.")); } if (i == 2) { return(Reject(sink, parts[1], "Expected «catch» or «finally» clause here. Clause is missing or malformed.")); } } if (clauses.Count == 0 && finallyCode == null) { Debug.Assert(node.ArgCount <= 1); return(Reject(sink, node, "Missing «catch, Type, Code» or «finally, Code» clause")); } if (finallyCode != null) { clauses.Add(F.Call(S.Finally, finallyCode)); } clauses.Insert(0, node.Args[0]); return(node.With(S.Try, clauses.ToVList())); }
public override LNode GenerateMatchExpr(IPGTerminalSet set_, bool savingResult, bool recognizerMode) { var set = (PGIntSet)set_; LNode call; var type = set.ChooseMatchType(2, 4); if (type != PGIntSet.Match.Set) { var args = new WList<LNode>(); if (type == PGIntSet.Match.Ranges) { // Use MatchRange or MatchExceptRange foreach (var r in set) { if (!set.IsInverted || r.Lo != EOF_int || r.Hi != EOF_int) { args.Add((LNode)set.MakeLiteral(r.Lo)); args.Add((LNode)set.MakeLiteral(r.Hi)); } } var target = recognizerMode ? (set.IsInverted ? _TryMatchExceptRange : _TryMatchRange) : (set.IsInverted ? _MatchExceptRange : _MatchRange); call = ApiCall(target, args); } else { // Use Match or MatchExcept foreach (var r in set) { for (int c = r.Lo; c <= r.Hi; c++) { if (!set.IsInverted || c != EOF_int) args.Add((LNode)set.MakeLiteral(c)); } } var target = recognizerMode ? (set.IsInverted ? _TryMatchExcept : _TryMatch) : (set.IsInverted ? _MatchExcept : _Match); call = ApiCall(target, args.ToVList()); } } else { var setName = GenerateSetDecl(set); if (set.IsInverted) call = ApiCall(recognizerMode ? _TryMatchExcept : _MatchExcept, F.Id(setName)); else call = ApiCall(recognizerMode ? _TryMatch : _Match, F.Id(setName)); } return call; }
protected LNode GeneratePredictionTreeCode(PredictionTree tree, Pair<LNode, string>[] matchingCode, ref Symbol haveLoop) { var braces = F.Braces(); Debug.Assert(tree.Children.Count >= 1); var alts = (Alts)_currentPred; if (tree.Children.Count == 1) return GetPredictionSubtreeCode(tree.Children[0], matchingCode, ref haveLoop); // From the prediction table, we can generate either an if-else chain: // // if (la0 >= '0' && la0 <= '7') sub_tree_1(); // else if (la0 == '-') sub_tree_2(); // else break; // // or a switch statement: // // switch(la0) { // case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': // sub_tree_1(); // break; // case '-': // sub_tree_2(); // break; // default: // goto breakfor; // } // // Assertion levels always need an if-else chain; lookahead levels // consider the complexity of switch vs if and decide which is most // appropriate. Generally "if" is slower, but a switch may require // too many labels since it doesn't support ranges like "la0 >= 'a' // && la0 <= 'z'". // // This class makes if-else chains directly (using IPGTerminalSet. // GenerateTest() to generate the test expressions), but the code // snippet generator (CSG) is used to generate switch statements // because the required code may be more complex. // // We may or may not be generating code inside a for(;;) loop. If we // decide to generate a switch() statement, one of the branches will // usually need to break out of the for loop, but "break" can only // break out of the switch(). In that case, add "stop:" after the // switch() and use "goto stop" instead of "break". WList<LNode> block = new WList<LNode>(); LNode laVar = null; MSet<int> switchCases = new MSet<int>(); IPGTerminalSet[] branchSets = null; bool should = false; if (tree.UsesLA()) { laVar = F.Id("la" + tree.Lookahead.ToString()); if (!tree.IsAssertionLevel) { IPGTerminalSet covered = CGH.EmptySet; branchSets = tree.Children.Select(branch => { var set = branch.Set.Subtract(covered); covered = covered.Union(branch.Set); return set; }).ToArray(); should = CGH.ShouldGenerateSwitch(branchSets, switchCases, tree.Children.Last.IsErrorBranch); if (!should) switchCases.Clear(); else if (should && haveLoop == S.For) // Can't "break" out of the for-loop when there is a nested switch, haveLoop = GSymbol.Get(NextStopLabel()); // so use "goto stop". } } LNode[] branchCode = new LNode[tree.Children.Count]; for (int i = 0; i < tree.Children.Count; i++) if (tree.Children[i].IsErrorBranch) { if (alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value) { Debug.Assert(matchingCode.Length == alts.Arms.Count + 1); branchCode[i] = matchingCode[alts.Arms.Count].A; } else branchCode[i] = CGH.ErrorBranch(tree.TotalCoverage, tree.Lookahead); } else branchCode[i] = GetPredictionSubtreeCode(tree.Children[i], matchingCode, ref haveLoop); var code = GenerateIfElseChain(tree, branchCode, ref laVar, switchCases); if (laVar != null) { block.Insert(0, F.Assign(laVar, CGH.LA(tree.Lookahead))); _laVarsNeeded |= 1ul << tree.Lookahead; } else if (should) laVar = CGH.LA(tree.Lookahead); if (should) { Debug.Assert(switchCases.Count != 0); code = CGH.GenerateSwitch(branchSets, switchCases, branchCode, code, laVar); } block.Add(code); return F.Braces(block.ToVList()); }
public static LNode @var(LNode node, IMessageSink sink) { var parts = node.Args; if (parts.Count == 0) { return(Reject(sink, node, "A variable definition must have the form var(Name::Type), var(Name = value), or var(Name::Type = value)")); } if (parts[0].IsId) { return(null); // e.g. this is true for "static readonly x::Foo" } WList <LNode> varStmts = null; LNode varStmt = null; for (int i = 0; i < parts.Count; i++) { LNode part = parts[i], type = null, init = null; if (part.Calls(S.Assign, 2)) { init = part.Args[1]; part = part.Args[0]; } if (part.Calls(S.ColonColon, 2)) { type = part.Args[1]; part = part.Args[0]; } if (init == null && part.Calls(S.Assign, 2)) { init = part.Args[1]; part = part.Args[0]; } if (!part.IsId) { return(Reject(sink, part, "Expected a simple variable name here")); } if (type != null && !IsComplexId(type)) { return(Reject(sink, type, "Expected a type name here")); } type = type ?? F.Missing; var nameAndInit = init == null ? part : F.Call(S.Assign, part, init); if (varStmt != null && varStmt.Args[0].Equals(type)) { // same type used again, e.g. (var x::int y::int) => (#var int x y) varStmt = varStmt.WithArgs(varStmt.Args.Add(nameAndInit)); } else { // first item (var x::int => #var int x) or type changed (var a::A b::B => #var A a; #var B b) if (varStmt != null) { varStmts = varStmts ?? new WList <LNode>(); varStmts.Add(varStmt); } varStmt = node.With(S.Var, type, nameAndInit); } } // Return a single statement or a list of them if necessary if (varStmts != null) { varStmts.Add(varStmt); return(F.Call(S.Splice, varStmts.ToVList())); } else { return(varStmt); } }