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); }
[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); }
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; }
[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; }