static LNode ToLNodeCore(ILNode node) { var attrs = VList <LNode> .Empty; for (int i = node.Min; i < -1; i++) { attrs.Add(ToLNodeCore(node[i])); } switch (node.Kind) { case LNodeKind.Id: return(LNode.Id(attrs, node.Name, node.Range, node.Style)); case LNodeKind.Literal: return(LNode.Literal(attrs, node.Value, node.Range, node.Style)); default: var args = VList <LNode> .Empty; for (int i = 0, max = node.Max; i <= max; i++) { args.Add(ToLNodeCore(node[i])); } var target = ToLNodeCore(node.Target); return(LNode.Call(attrs, target, args, node.Range, node.Style)); } }
// Parse the list of variables provided in the GUI public static Dictionary <Symbol, LNode> ParseVarList(IEnumerable <LNode> varList) { var vars = new Dictionary <Symbol, LNode>(); foreach (LNode assignment in varList) { { LNode expr, @var; if (assignment.Calls(CodeSymbols.Assign, 2) && (@var = assignment.Args[0]) != null && (expr = assignment.Args[1]) != null) { if ([email protected]) { throw new ArgumentException("Left-hand side of '=' must be a variable name: {0}".Localized(@var)); } // For efficiency, try to evaluate the expression in advance try { expr = LNode.Literal(Eval(expr, vars)); } catch { } // it won't work if expression uses X or Y vars.Add(@var.Name, expr); } else { throw new ArgumentException("Expected assignment expression: {0}".Localized(assignment)); } }; } return(vars); }
public static LNode getScopedProperty(LNode node, IMacroContext context) { LNode key; if (node.ArgCount >= 1 && !(key = context.PreProcess(node.Args[0])).IsCall) { var keyValue = key.IsId ? key.Name : key.Value; var @default = node.Args[1, node]; var result = context.ScopedProperties.TryGetValue(keyValue, @default); if (result == node) { context.Write(Severity.Error, key, "The specified property does not exist."); } if (result is LNode) { return((LNode)result); } else { return(LNode.Literal(result, node)); } } context.Write(Severity.Error, node, "Expected one argument, a key literal, with the default code as an optional second argument."); return(null); }
/// <summary>Converts <see cref="ILNode"/> to <see cref="LNode"/> recursively.</summary> public static LNode ToLNode(this ILNode node) { if (node is LNode n) { return(n); } var attrs = LNodeList.Empty; for (int i = node.Min; i < -1; i++) { attrs.Add(ToLNode(node[i])); } switch (node.Kind) { case LNodeKind.Id: return(LNode.Id(attrs, node.Name, new SourceRange(node.Range), node.Style)); case LNodeKind.Literal: return(LNode.Literal(attrs, node.Value, new SourceRange(node.Range), node.Style)); default: var args = LNodeList.Empty; for (int i = 0, max = node.Max; i <= max; i++) { args.Add(ToLNode(node[i])); } var target = ToLNode(node.Target); return(LNode.Call(attrs, target, args, new SourceRange(node.Range), node.Style)); } }
public static LNode _set(LNode node, IMacroContext context) { var lhs = node.Args[0, LNode.Missing]; var name = lhs.Name; bool isSnippet = name == _hash_snippet; if ((isSnippet || name == _hash_set) && node.ArgCount == 2 && lhs.IsId) { Symbol newTarget = isSnippet ? _hash_setScopedPropertyQuote : _hash_setScopedProperty; var stmts = node.Args.Slice(1).Select(key => { LNode value = F.@true; if (key.Calls(S.Assign, 2)) { value = key.Args[1]; value = context.PreProcess(value); key = key.Args[0]; if (isSnippet && value.Calls(S.Braces)) { value = value.Args.AsLNode(S.Splice); } } if (!key.IsId) { context.Write(Severity.Error, key, "Invalid key; expected an identifier."); } return((LNode)node.With(newTarget, LNode.Literal(key.Name, key), value)); }); return(F.Call(S.Splice, stmts)); } return(null); }
public static LNode ArrayLiteral(LNode node, IMacroContext context) { var value = node.Value; if (value is Array array) { Type elementType = value.GetType().GetElementType(); string elementTypeName = elementType.NameWithGenericArgs(); LNode elementTypeN = LNode.Call(S.CsRawText, LNode.List(LNode.Literal(elementTypeName))); Func <object, LNode, LNode> newLiteral = (el, pnode) => LNode.Literal(el, pnode); // Reduce output text size by preventing the printer from using casts // e.g. print `23` instead of `(byte) 23` or `(short) 23`. Also, unbox // ints to save memory (ideally we'd do this for all Value Types) if (elementType == typeof(byte)) { newLiteral = (el, pnode) => LNode.Literal((int)(byte)el, pnode); } if (elementType == typeof(sbyte)) { newLiteral = (el, pnode) => LNode.Literal((int)(sbyte)el, pnode); } if (elementType == typeof(short)) { newLiteral = (el, pnode) => LNode.Literal((int)(short)el, pnode); } if (elementType == typeof(ushort)) { newLiteral = (el, pnode) => LNode.Literal((int)(ushort)el, pnode); } if (elementType == typeof(int)) { newLiteral = (el, pnode) => LNode.Literal((int)(int)el, pnode); } if (array.Rank == 1) { var initializers = new List <LNode>(); int count = 0; foreach (object element in array) { LNode elemNode = newLiteral(element, node); if ((count++ & 7) == 0 && array.Length > 8) { elemNode = elemNode.PlusAttr(LNode.Id(S.TriviaNewline)); } initializers.Add(elemNode); } return(LNode.Call(CodeSymbols.New, LNode.List().Add(LNode.Call(LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id(CodeSymbols.Array), elementTypeN)))).AddRange(initializers))); } else { return(null); // TODO //Stmt("int[,] Foo = new[,] { {\n 0 }, {\n 1,\n 2, }, };", F.Call(S.Var, F.Of(S.TwoDimensionalArray, S.Int32), // F.Call(S.Assign, Foo, F.Call(S.New, F.Call(S.TwoDimensionalArray), F.Braces(zero), F.Braces(one, two))))); } } return(null); }
private static LNode TranslateLiteral(LNode node, IMessageSink sink, object literal) { if (!node.IsId) { return(null); } return(LNode.Literal(literal, node)); }
public static LNode overrideTarget(LNode outerNode, IMacroContext context1) { // Test the indirect way to register a macro return(LNode.Call((Symbol)"#registerMacro", LNode.List(LNode.Literal( new MacroInfo(null, outerNode[0].Name.Name, (node, context2) => { return node.WithTarget(outerNode[1]); }) { Mode = MacroMode.PriorityOverride // needed by the unit test })))); }
public void TestTwoDimensionalArrayLiterals() { var lemp = NewLemp(10, null); Test(LNode.Literal(new String[, ] { { "hello" }, { "!" } }), lemp, "new String[,] { { \"hello\" }, { \"!\" } };", EcsLanguageService.Value); Test(LNode.Literal(new byte[, ] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 254, 255, 0 } }), lemp, "new byte[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 254, 255, 0 } };", EcsLanguageService.Value); }
public void TestCustomPrinters() { var lht = new LiteralHandlerTable(); // Can't add printer for null Assert.IsFalse(lht.AddPrinter(true, (Type)null, PrintTrim)); Assert.IsFalse(lht.AddPrinter(true, (Symbol)null, PrintTrim)); // Add printer for "trim" marker and string Assert.IsTrue(lht.AddPrinter(false, (Symbol)"trim", PrintTrim)); Assert.IsTrue(lht.AddPrinter(false, typeof(string), PrintTrim)); Assert.IsFalse(lht.AddPrinter(false, typeof(string), PrintTrim)); Assert.IsTrue(lht.AddPrinter(true, typeof(string), PrintTrim)); Assert.IsTrue(lht.AddPrinter(true, typeof(string), PrintTrim)); // Originally there is no printer for int var sb = new StringBuilder(); Assert.IsTrue(lht.TryPrint(LNode.Literal(123), sb).Right.HasValue); Assert.IsTrue(lht.TryPrint(LNode.Literal(123), sb).Right.Value.Format.Contains("no printer for type 'System.Int32'")); Assert.AreEqual("", sb.ToString()); // Add a printer for int and try it Assert.IsTrue(lht.AddPrinter(false, typeof(int), PrintTrim)); Assert.AreEqual("hovercraft", lht.TryPrint(CL(123, "fail"), sb).Left.Value.Name); Assert.AreEqual("123", sb.ToString()); // Add printer for "fail" marker and try it. Assert.IsTrue(lht.AddPrinter(false, (Symbol)"fail", PrintFail)); Assert.AreEqual("Always fails", lht.TryPrint(CL(new int[5], "fail"), sb).Right.Value.Format); Assert.AreEqual("FAIL", sb.ToString()); // TypeMarker printers take priority, so "fail" runs at first. Then the handler for int runs. Assert.AreEqual("hovercraft", lht.TryPrint(CL(999, "fail"), sb).Left.Value.Name); Assert.AreEqual("999", sb.ToString()); // Try our string and trim modes Assert.AreEqual("hovercraft", lht.TryPrint(LNode.Literal(" hi! "), sb).Left.Value.Name); Assert.AreEqual("hi!", sb.ToString()); Assert.AreEqual("hovercraft", lht.TryPrint(CL(new StringBuilder(" bye! "), "trim"), sb).Left.Value.Name); Assert.AreEqual("bye!", sb.ToString()); // TypeMarker printers take priority, so "firstChar" runs in preference to the string handler. Assert.IsTrue(lht.AddPrinter(false, (Symbol)"firstChar", PrintFirstChar)); Assert.AreEqual("eels", lht.TryPrint(CL("hi! ", "firstChar"), sb).Left.Value.Name); Assert.AreEqual("h", sb.ToString()); }
public static CalculatorCore New(LNode expr, Dictionary <Symbol, LNode> vars, CalcRange xRange, CalcRange yRange) { // Find out if the expression uses the variable "y" (or is an equation with '=' or '==') // As an (unnecessary) side effect, this throws if an unreferenced var is used bool isEquation = expr.Calls(CodeSymbols.Assign, 2) || expr.Calls(CodeSymbols.Eq, 2), usesY = false; if (!isEquation) { LNode zero = LNode.Literal((double)0); Func <Symbol, double> lookup = null; lookup = name => name == sy_x || (usesY |= name == sy_y) ? 0 : Eval(vars[name], lookup); Eval(expr, lookup); } if (isEquation || usesY) { return(new Calculator3D(expr, vars, xRange, yRange)); } else { return(new Calculator2D(expr, vars, xRange)); } }
public void TestOneDimensionalArrayLiterals() { var lemp = NewLemp(10, null); Test(LNode.Literal(new Int32[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }), lemp, "new Int32[] { 1, 2, 3, 4, 5, 6, 7, 8,\n 9, 10 };", EcsLanguageService.Value); Test(LNode.Literal(new Int32[] { }), lemp, "new Int32[] { };", EcsLanguageService.Value); Test(LNode.Literal(new SByte[] { -1, 2, 3, 4 }), lemp, "new SByte[] { -1, 2, 3, 4 };", EcsLanguageService.Value); Test(LNode.Literal(new Byte[] { 0, 255 }), lemp, "new Byte[] { 0, 255 };", EcsLanguageService.Value); Test(LNode.Literal(new Int16[] { -32768, 0, 32767 }), lemp, "new Int16[] { -32768, 0, 32767 };", EcsLanguageService.Value); Test(LNode.Literal(new Int16[] { -0x8000, 0x0, 0x7FFF }).SetBaseStyle(NodeStyle.HexLiteral), lemp, "new Int16[] { -0x8000, 0x0, 0x7FFF };", EcsLanguageService.Value); Test(LNode.Literal(new UInt16[] { 0, 1, 2, 3, 65535 }), lemp, "new UInt16[] { 0, 1, 2, 3, 65535 };", EcsLanguageService.Value); Test(LNode.Literal(new String[] { "hello", "!" }), lemp, "new String[] { \"hello\", \"!\" };", EcsLanguageService.Value); Test(LNode.Literal(new String[][] { new String[] { "hello" }, new String[] { "!" } }), lemp, "new String[][] { new String[] { \"hello\" }, new String[] { \"!\" } };", EcsLanguageService.Value); }
private void MakeArgListTests(VList <LNode> patternArgs, ref LNode candidate) { Symbol varArgSym = null; LNode varArgCond = null; int i; for (i = 0; i < patternArgs.Count; i++) { MakeTestExpr(patternArgs[i], LNode.Call(CodeSymbols.IndexBracks, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), F.Literal(i))), out varArgSym, out varArgCond); if (varArgSym != null) { break; } } int i2 = i + 1; for (int left = patternArgs.Count - i2; i2 < patternArgs.Count; i2++) { Symbol varArgSym2 = null; LNode varArgCond2 = null; MakeTestExpr(patternArgs[i2], LNode.Call(CodeSymbols.IndexBracks, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Call(CodeSymbols.Sub, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Id((Symbol)"Count"))), F.Literal(left))).SetStyle(NodeStyle.Operator))), out varArgSym2, out varArgCond2); if (varArgSym2 != null) { Context.Sink.Write(Severity.Error, patternArgs[i2], "More than a single $(...varargs) variable is not supported in a single argument list."); break; } left--; } if (varArgSym != null && (varArgSym != __ || varArgCond != null)) { LNode varArgSymId = F.Id(varArgSym); LNode grabVarArgs; if (i == 0 && patternArgs.Count == 1) { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))))).SetStyle(NodeStyle.Operator); } else if (i == 0 && patternArgs.Count > 1) { var fixedArgsLit = F.Literal(patternArgs.Count - 1); grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Id((Symbol)"WithoutLast"))), LNode.List(fixedArgsLit)))).SetStyle(NodeStyle.Operator); } else { var varArgStartLit = F.Literal(i); var fixedArgsLit = F.Literal(patternArgs.Count - 1); if (i + 1 == patternArgs.Count) { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode"))), LNode.List(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Id((Symbol)"Slice"))), LNode.List(varArgStartLit)))))))).SetStyle(NodeStyle.Operator); } else { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode"))), LNode.List(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Id((Symbol)"Slice"))), LNode.List(varArgStartLit, LNode.Call(CodeSymbols.Sub, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))), LNode.Id((Symbol)"Count"))), fixedArgsLit)).SetStyle(NodeStyle.Operator))))))))).SetStyle(NodeStyle.Operator); } } if (varArgCond != null || IsMultiCase) { Tests.Add(LNode.Call(CodeSymbols.OrBits, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(grabVarArgs.PlusAttrs(LNode.List(LNode.InParensTrivia)), LNode.Id((Symbol)"IsEmpty"))), LNode.Literal(true))).SetStyle(NodeStyle.Operator)); Tests.Add(varArgCond); } else { ThenClause.Add(grabVarArgs); } } }
[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); }
void GenCodeForPattern(LNode input, LNode pattern) { bool refExistingVar; LNode varBinding, cmpExpr, isType, inRange; VList <LNode> subPatterns, conditions; GetPatternComponents(pattern, out varBinding, out refExistingVar, out cmpExpr, out isType, out inRange, out subPatterns, out conditions); if (isType != null) { if ((cmpExpr ?? inRange ?? varBinding) != null) { if (!LooksLikeSimpleValue(input)) { PutStmt(TempVarDecl(input, out input)); } } PutCond(LNode.Call(CodeSymbols.Is, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)); if (varBinding == null && ((cmpExpr ?? inRange) != null || subPatterns.Count > 0)) { varBinding = LNode.Id(NextTempName(), isType); } } if (varBinding != null) { if (isType != null) { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(isType, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)))))); } } else { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input)).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Missing, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input))))); } } input = varBinding; } if (cmpExpr != null) { if (cmpExpr.Value == null) { PutCond(LNode.Call(CodeSymbols.Eq, LNode.List(input, LNode.Literal(null))).SetStyle(NodeStyle.Operator)); } else { PutCond(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(cmpExpr, LNode.Id((Symbol)"Equals"))), LNode.List(input))); } } for (int itemIndex = 0; itemIndex < subPatterns.Count; itemIndex++) { var subPattern = subPatterns[itemIndex]; LNode propName; if (subPattern.Calls(S.NamedArg, 2) || subPattern.Calls(S.Colon, 2)) { propName = subPattern[0]; subPattern = subPattern[1]; } else { propName = LNode.Id("Item" + (itemIndex + 1), subPattern); } GenCodeForPattern(LNode.Call(CodeSymbols.Dot, LNode.List(input, propName)), subPattern); } if (inRange != null) { PutCond(LNode.Call(CodeSymbols.In, LNode.List(input, inRange)).SetStyle(NodeStyle.Operator)); } foreach (var cond in conditions) { PutCond(cond); } }
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); }
void ProcessAttribute(LNode attr, Symbol mode, LNode exceptionType, LNode variableName, bool isPropSetter) { var conditions = attr.Args; object haveCCRewriter = Context.ScopedProperties.TryGetValue(_haveContractRewriter, null); _haveCCRewriter = haveCCRewriter is bool?(bool)haveCCRewriter : false; // #notnull is equivalent to either requires(_ != null) or ensures(_ != null) if (mode == sy_notnull) { if (attr.Args.Count != 0) { Context.Sink.Warning(attr, "'#notnull' does not expect arguments."); } if (variableName != null // argument ) { mode = sy_requires; } else { // return value mode = sy_ensures; } conditions.Add(LNode.Call(CodeSymbols.Neq, LNode.List(LNode.Id((Symbol)"_"), LNode.Literal(null))).SetStyle(NodeStyle.Operator)); } else if (!attr.IsCall) { Context.Sink.Warning(attr, "'{0}' expects a list of conditions.", attr.Name); } if (mode == sy_requires || mode == sy_assert) { ProcessRequiresAttribute(conditions, mode, variableName); } else // mode == @@ensures || mode == @@ensuresFinally || mode == @@ensuresOnThrow { if (variableName != null && !isPropSetter) { Context.Sink.Error(attr, "The '{0}' attribute does not apply to method arguments.", mode); } else { ProcessEnsuresAttribute(conditions, mode, exceptionType, variableName); } } }
private void MatchAttributes(LNode pattern, LNode candidate) { LNode condition; bool isParams, refExistingVar; Symbol listVar; var pAttrs = pattern.PAttrs(); if (pAttrs.Count == 1 && (listVar = DecodeSubstitutionExpr(pAttrs[0], out condition, out isParams, out refExistingVar)) != null && isParams) { if (listVar != __ || condition != null) { if (!refExistingVar) { AddVar(listVar, true, errAt: pattern); } Tests.Add(LNode.Call(CodeSymbols.OrBits, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(LNode.List(LNode.InParensTrivia), CodeSymbols.Assign, LNode.List(F.Id(listVar), LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Attrs"))).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"IsEmpty"))).SetStyle(NodeStyle.Operator), LNode.Literal(true))).SetStyle(NodeStyle.Operator)); if (condition != null) { Tests.Add(condition); } } } else if (pAttrs.Count != 0) { Context.Sink.Error(pAttrs[0], "Currently, Attribute matching is very limited; you can only use `[$(...varName)]`"); } }
public static LNode CompileMacro(LNode pattern, LNode body, IMacroContext context, LNodeList attrs) { var modeNodes = attrs.Where(a => Enum.TryParse(a.Name.Name, out MacroMode _)); // unwrap braces (they're not part of the pattern, they just enable statement syntax in EC#) var pattern_apos = pattern.UnwrapBraces(); MacroMode modes = GetMacroMode(ref attrs, pattern_apos); // compileTime {...} can recognize macro method definitions. // Take advantage of this by generating a macro method which it will register for us. LNode macroName = pattern_apos.Target ?? pattern_apos; LNode syntax = F.Literal(pattern_apos.ToString()); LNode description = attrs.FirstOrDefault(a => a.Value is string) ?? F.Literal("User-defined macro at {0}".Localized(pattern.Range.Start)); attrs = attrs.SmartWhere(a => !(a.Value is string)); // remove docstring, if any var extraArgs = LNode.List(); if (macroName.IsId) { extraArgs.Add(F.Literal(macroName.Name.Name)); } else { Debug.Assert((modes & (MacroMode.MatchEveryCall | MacroMode.MatchEveryIdentifier | MacroMode.MatchEveryLiteral)) != 0); } // ensure operator macros like `'+` are not printed as `operator+` which C# will reject if (EcsValidators.IsOperator(macroName.Name)) { macroName = F.Id(EcsValidators.SanitizeIdentifier(macroName.Name.Name)); } LNode modesExpr = null; foreach (LNode mode in modeNodes) { modesExpr = LNode.MergeBinary(modesExpr, LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.ColonColon, LNode.List(LNode.Id((Symbol)"global"), LNode.Id((Symbol)"LeMP"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"MacroMode"))).SetStyle(NodeStyle.Operator), mode)).SetStyle(NodeStyle.Operator), S.OrBits); } if (modesExpr != null) { extraArgs.Add(LNode.Call(CodeSymbols.Assign, LNode.List(LNode.Id((Symbol)"Mode"), modesExpr)).SetStyle(NodeStyle.Operator)); } LNode lmAttribute = LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.ColonColon, LNode.List(LNode.Id((Symbol)"global"), LNode.Id((Symbol)"LeMP"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"LexicalMacroAttribute"))).SetStyle(NodeStyle.Operator), LNode.List().Add(syntax).Add(description).AddRange(extraArgs)); if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.StatementBlock); } body = context.PreProcess(body); // Look for "using" statements above the macro() call LNodeList usingDirectives = LNode.List(context.PreviousSiblings.Where(n => n.Calls(S.Import))); // Look for "using" and "#r" statements at the beginning of the body if (body.Calls(S.Braces)) { var bodyUsings = body.Args.TakeNowWhile(stmt => stmt.Calls(S.Import) || stmt.Calls(S.CsiReference)); usingDirectives.AddRange(bodyUsings); body = body.WithArgs(body.Args.Slice(bodyUsings.Count)); } // Create a matchCode statement unless the pattern is MacroName($(.._)), which always matches if (!(pattern_apos.HasSimpleHeadWithoutPAttrs() && pattern_apos.Target.IsId && pattern_apos.ArgCount == 1 && pattern_apos[0].Equals(LNode.Call(CodeSymbols.Substitute, LNode.List(LNode.Call(CodeSymbols.DotDot, LNode.List(LNode.Id((Symbol)"_"))).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)))) { // Note: the body is already preprocessed; #noLexicalMacros prevents double-processing body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call((Symbol)"matchCode", LNode.List(LNode.Id((Symbol)"#node"), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Case, LNode.List(pattern)), LNode.Call((Symbol)"#noLexicalMacros", LNode.List(body.AsList(S.Braces))))).SetStyle(NodeStyle.StatementBlock))).SetStyle(NodeStyle.Special), LNode.Call(CodeSymbols.Return, LNode.List(LNode.Literal(null))))).SetStyle(NodeStyle.StatementBlock); } return(LNode.Call((Symbol)"compileTime", LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(usingDirectives).Add(LNode.Call(LNode.List().Add(lmAttribute).AddRange(attrs).Add(LNode.Id(CodeSymbols.Public)).Add(LNode.Id(CodeSymbols.Static)), CodeSymbols.Fn, LNode.List(LNode.Id((Symbol)"LNode"), macroName, LNode.Call(CodeSymbols.AltList, LNode.List(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Id((Symbol)"LNode"), LNode.Id((Symbol)"#node"))), LNode.Call(CodeSymbols.Var, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.ColonColon, LNode.List(LNode.Id((Symbol)"global"), LNode.Id((Symbol)"LeMP"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"IMacroContext"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"#context"))))), body)))).SetStyle(NodeStyle.StatementBlock))).SetStyle(NodeStyle.Special)); }
public static string PrintLiteral(object value, NodeStyle style = 0) => PrintLiteral(LNode.Literal(value, null, style));
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); } }
void GenCodeForPattern(LNode input, LNode pattern) { // Get the parts of the pattern, e.g. `$x is T(sp)` => varBinding=x, isType=T, sp is returned bool refExistingVar; LNode varBinding, cmpExpr, isType, inRange; VList <LNode> subPatterns, conditions; GetPatternComponents(pattern, out varBinding, out refExistingVar, out cmpExpr, out isType, out inRange, out subPatterns, out conditions); // For a pattern like `(varBinding is IsType in A...B)(subPatterns) && conds`, // our goal is to generate code like this: // // var tmp_1 = $input; // temp var created unless $input looks simple // if (tmp_1 is IsType) { // Type varBinding = (IsType)tmp_1; // if (varBinding >= A && varBinding <= B && /* code for matching subPatterns */) // if (conds) // $handler; // } if (isType != null) { if ((cmpExpr ?? inRange ?? varBinding) != null) { // input will be used multiple times, so consider making a tmp var. if (!LooksLikeSimpleValue(input)) { PutStmt(TempVarDecl(_context, input, out input)); } } PutCond(LNode.Call(CodeSymbols.Is, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)); if (varBinding == null && ((cmpExpr ?? inRange) != null || subPatterns.Count > 0)) { // we'll need another temp variable to hold the same value, casted. varBinding = LNode.Id(NextTempName(_context), isType); } } if (varBinding != null) { if (isType != null) { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(isType, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)))))); } } else { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input)).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Missing, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input))))); } } input = varBinding; } if (cmpExpr != null) // do equality test { if (cmpExpr.Value == null) { PutCond(LNode.Call(CodeSymbols.Eq, LNode.List(input, LNode.Literal(null))).SetStyle(NodeStyle.Operator)); } else { PutCond(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(cmpExpr, LNode.Id((Symbol)"Equals"))).SetStyle(NodeStyle.Operator), LNode.List(input))); } } // Generate code for subpatterns for (int itemIndex = 0; itemIndex < subPatterns.Count; itemIndex++) { var subPattern = subPatterns[itemIndex]; LNode propName; // Recognize `propName:` in front of the subpattern (fun fact: we // can't use `matchCode` to detect a named parameter here, because if // we write `case { $propName: $subPattern; }:` it is parsed as a // goto-label, not as a named parameter.) if (subPattern.Calls(S.NamedArg, 2) || subPattern.Calls(S.Colon, 2)) { propName = subPattern[0]; subPattern = subPattern[1]; } else { propName = LNode.Id("Item" + (itemIndex + 1), subPattern); } GenCodeForPattern(LNode.Call(CodeSymbols.Dot, LNode.List(input, propName)).SetStyle(NodeStyle.Operator), subPattern); } if (inRange != null) { PutCond(LNode.Call(CodeSymbols.In, LNode.List(input, inRange)).SetStyle(NodeStyle.Operator)); } foreach (var cond in conditions) { PutCond(cond); } }
// Used for optimization, to avoid writing complicated expressions in the output: // e.g. instead of code.Args[0].Target.Args.Count == 1 && code.Args[0].Target.Args[0].IsIdNamed((Symbol) "_") // we might write (tmp_5 = code.Args[0].Target) != null && tmp_5.Args.Count == 1 && tmp_5.Args[0].IsIdNamed((Symbol) "_") LNode MaybePutCandidateInTempVar(bool condition, LNode candidate) { if (condition) { var targetTmp = NextTempName(Context); var targetTmpId = F.Id(targetTmp); AddVar(targetTmp, false, errAt: candidate); Tests.Add(LNode.Call(CodeSymbols.NotEq, LNode.List(LNode.Call(LNode.List(LNode.InParensTrivia), CodeSymbols.Assign, LNode.List(targetTmpId, candidate)).SetStyle(NodeStyle.Operator), LNode.Literal(null))).SetStyle(NodeStyle.Operator)); return(targetTmpId); } else { return(candidate); } }
LNode EliminateSequenceExpressionsInExecStmt(LNode stmt) { { LNode block, collection, cond, init, initValue, loopVar, name, tmp_11, tmp_12, type; VList <LNode> attrs, incs, inits; if (stmt.Calls(CodeSymbols.Braces)) { return(stmt.WithArgs(EliminateSequenceExpressions(stmt.Args, false))); } else if (stmt.CallsMin(CodeSymbols.If, 1) || stmt.Calls(CodeSymbols.UsingStmt, 2) || stmt.Calls(CodeSymbols.Lock, 2) || stmt.Calls(CodeSymbols.Switch, 2) && stmt.Args[1].Calls(CodeSymbols.Braces)) { return(ProcessBlockCallStmt(stmt, 1)); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fixed, 2) && (init = stmt.Args[0]) != null && (block = stmt.Args[1]) != null) { init = EliminateSequenceExpressionsInExecStmt(init); block = EliminateSequenceExpressionsInChildStmt(block); if (init.CallsMin(__numrunSequence, 1)) { return(LNode.Call(LNode.List(attrs), CodeSymbols.Braces, LNode.List().AddRange(init.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.Fixed, LNode.List(init.Args.Last, block)))).SetStyle(NodeStyle.Statement)); } else { return(stmt.WithArgChanged(1, block)); } } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.While, 2) && (cond = stmt.Args[0]) != null && (block = stmt.Args[1]) != null) { cond = BubbleUpBlocks(cond); block = EliminateSequenceExpressionsInChildStmt(block); if (cond.CallsMin(__numrunSequence, 1)) { return(LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList), LNode.Missing, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(cond.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.If, LNode.List(cond.Args.Last, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement)))); } else { return(stmt.WithArgChanged(1, block)); } } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.DoWhile, 2) && (block = stmt.Args[0]) != null && (cond = stmt.Args[1]) != null) { block = EliminateSequenceExpressionsInChildStmt(block); cond = BubbleUpBlocks(cond); if (cond.CallsMin(__numrunSequence, 1)) { var continue_N = F.Id(NextTempName(Context, "continue_")); var bodyStmts = block.AsList(S.Braces); bodyStmts.AddRange(cond.Args.WithoutLast(1)); bodyStmts.Add(LNode.Call(CodeSymbols.Assign, LNode.List(continue_N, cond.Args.Last)).SetStyle(NodeStyle.Operator)); return(LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Id(CodeSymbols.Bool), LNode.Call(CodeSymbols.Assign, LNode.List(continue_N, LNode.Literal(true))))))), continue_N, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(bodyStmts)).SetStyle(NodeStyle.Statement)))); } else { return(stmt.WithArgChanged(0, block)); } } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.For, 4) && stmt.Args[0].Calls(CodeSymbols.AltList) && (cond = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (block = stmt.Args[3]) != null) { inits = stmt.Args[0].Args; incs = stmt.Args[2].Args; return(ESEInForLoop(stmt, attrs, inits, cond, incs, block)); } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.ForEach, 3) && (tmp_11 = stmt.Args[0]) != null && tmp_11.Calls(CodeSymbols.Var, 2) && (type = tmp_11.Args[0]) != null && (loopVar = tmp_11.Args[1]) != null && (collection = stmt.Args[1]) != null && (block = stmt.Args[2]) != null) { block = EliminateSequenceExpressionsInChildStmt(block); collection = BubbleUpBlocks(collection); if (collection.CallsMin(__numrunSequence, 1)) { return(LNode.Call(LNode.List(attrs), CodeSymbols.Braces, LNode.List().AddRange(collection.Args.WithoutLast(1)).Add(LNode.Call(CodeSymbols.ForEach, LNode.List(LNode.Call(CodeSymbols.Var, LNode.List(type, loopVar)), collection.Args.Last, block)))).SetStyle(NodeStyle.Statement)); } else { return(stmt.WithArgChanged(stmt.Args.Count - 1, block)); } } else if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Var, 2) && (type = stmt.Args[0]) != null && (tmp_12 = stmt.Args[1]) != null && tmp_12.Calls(CodeSymbols.Assign, 2) && (name = tmp_12.Args[0]) != null && (initValue = tmp_12.Args[1]) != null) { var initValue_apos = BubbleUpBlocks(initValue); if (initValue_apos != initValue) { { LNode last; VList <LNode> stmts; if (initValue_apos.CallsMin((Symbol)"#runSequence", 1) && (last = initValue_apos.Args[initValue_apos.Args.Count - 1]) != null) { stmts = initValue_apos.Args.WithoutLast(1); return(LNode.Call((Symbol)"#runSequence", LNode.List().AddRange(stmts).Add(LNode.Call(LNode.List(attrs), CodeSymbols.Var, LNode.List(type, LNode.Call(CodeSymbols.Assign, LNode.List(name, last))))))); } else { return(LNode.Call(LNode.List(attrs), CodeSymbols.Var, LNode.List(type, LNode.Call(CodeSymbols.Assign, LNode.List(name, initValue_apos))))); } } } } else if (stmt.CallsMin(S.Try, 2)) { return(ESEInTryStmt(stmt)); } else if (stmt.HasSpecialName && stmt.ArgCount >= 1 && stmt.Args.Last.Calls(S.Braces)) { return(ProcessBlockCallStmt(stmt, stmt.ArgCount - 1)); } else { // Ordinary expression statement return(BubbleUpBlocks(stmt, stmtContext: true)); } } return(stmt); }
private void MakeArgListTests(LNodeList patternArgs, ref LNode candidate) { // Note: at this point we can assume that the quantity of // arguments has already been checked and is not too small. Symbol varArgSym = null; LNode varArgCond = null; int i; for (i = 0; i < patternArgs.Count; i++) { MakeTestExpr(patternArgs[i], LNode.Call(CodeSymbols.IndexBracks, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), F.Literal(i))).SetStyle(NodeStyle.Operator), out varArgSym, out varArgCond); if (varArgSym != null) { break; } } int i2 = i + 1; for (int left = patternArgs.Count - i2; i2 < patternArgs.Count; i2++) { Symbol varArgSym2 = null; LNode varArgCond2 = null; MakeTestExpr(patternArgs[i2], LNode.Call(CodeSymbols.IndexBracks, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.Sub, 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(left))).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator), out varArgSym2, out varArgCond2); if (varArgSym2 != null) { Context.Sink.Error(patternArgs[i2], "More than a single $(...varargs) variable is not supported in a single argument list."); break; } left--; } if (varArgSym != null && (varArgSym != __ || varArgCond != null)) { // Extract variable arg list LNode varArgSymId = F.Id(varArgSym); LNode grabVarArgs; if (i == 0 && patternArgs.Count == 1) { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator); } else if (i == 0 && patternArgs.Count > 1) { var fixedArgsLit = F.Literal(patternArgs.Count - 1); grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"WithoutLast"))).SetStyle(NodeStyle.Operator), LNode.List(fixedArgsLit)))).SetStyle(NodeStyle.Operator); } else { var varArgStartLit = F.Literal(i); var fixedArgsLit = F.Literal(patternArgs.Count - 1); if (i + 1 == patternArgs.Count) { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.New, LNode.List(LNode.Call((Symbol)"LNodeList", LNode.List(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"Slice"))).SetStyle(NodeStyle.Operator), LNode.List(varArgStartLit)))))))).SetStyle(NodeStyle.Operator); } else { grabVarArgs = LNode.Call(CodeSymbols.Assign, LNode.List(varArgSymId, LNode.Call(CodeSymbols.New, LNode.List(LNode.Call((Symbol)"LNodeList", LNode.List(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(candidate, LNode.Id((Symbol)"Args"))).SetStyle(NodeStyle.Operator), LNode.Id((Symbol)"Slice"))).SetStyle(NodeStyle.Operator), LNode.List(varArgStartLit, LNode.Call(CodeSymbols.Sub, 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), fixedArgsLit)).SetStyle(NodeStyle.Operator))))))))).SetStyle(NodeStyle.Operator); } } // Add an extra condition on the $(...list) if requested by user if (varArgCond != null || IsMultiCase) { Tests.Add(LNode.Call(CodeSymbols.OrBits, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(grabVarArgs.PlusAttrs(LNode.List(LNode.InParensTrivia)), LNode.Id((Symbol)"IsEmpty"))).SetStyle(NodeStyle.Operator), LNode.Literal(true))).SetStyle(NodeStyle.Operator)); Tests.Add(varArgCond); } else { ThenClause.Add(grabVarArgs); } } }
public static LNode concat(LNode node, IMacroContext context) { var result = ConcatCore(node, out var attrs, context.Sink); return(result == null ? null : LNode.Literal(result.ToString(), node).WithAttrs(attrs)); }
void GenCodeForPattern(LNode input, LNode pattern, string defaultPropName = null) { // Get the parts of the pattern, e.g. `$x is T(sp)` => varBinding=x, isType=T, sp is returned bool refExistingVar; LNode varBinding, cmpExpr, isType, inRange, propName; VList <LNode> subPatterns, conditions; GetPatternComponents(pattern, out propName, out varBinding, out refExistingVar, out cmpExpr, out isType, out inRange, out subPatterns, out conditions); if (defaultPropName == null) // Outermost pattern { if (propName != null) { _context.Sink.Error(propName, "match: property name not allowed on outermost pattern"); } } else { if ((propName = propName ?? LNode.Id(defaultPropName, pattern)) != null) { input = LNode.Call(CodeSymbols.Dot, LNode.List(input, propName)).SetStyle(NodeStyle.Operator); } } // For a pattern like `is Type varBinding(subPatterns) in A...B && conds`, // our goal is to generate code like this: // // var tmp_1 = $input; // temp var created unless $input looks simple // if (tmp_1 is Type) { // Type varBinding = (Type)tmp_1; // if (varBinding >= A && varBinding <= B && /* code for matching subPatterns */) // if (conds) // $handler; // } if (isType != null) { if ((cmpExpr ?? inRange ?? varBinding) != null) { // input will be used multiple times, so consider making a tmp var. if (!LooksLikeSimpleValue(input)) { PutStmt(TempVarDecl(_context, input, out input)); } } PutCond(LNode.Call(CodeSymbols.Is, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)); if (varBinding == null && ((cmpExpr ?? inRange) != null || subPatterns.Count > 0)) { // we'll need another temp variable to hold the same value, casted. varBinding = LNode.Id(NextTempName(_context), isType); } } if (varBinding != null) { if (isType != null) { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(isType, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, LNode.Call(CodeSymbols.Cast, LNode.List(input, isType)).SetStyle(NodeStyle.Operator)))))); } } else { if (refExistingVar) { PutStmt(LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input)).SetStyle(NodeStyle.Operator)); } else { PutStmt(LNode.Call(CodeSymbols.Var, LNode.List(LNode.Missing, LNode.Call(CodeSymbols.Assign, LNode.List(varBinding, input))))); } } input = varBinding; } if (cmpExpr != null) // do equality test { if (cmpExpr.Value == null) { PutCond(LNode.Call(CodeSymbols.Eq, LNode.List(input, LNode.Literal(null))).SetStyle(NodeStyle.Operator)); } else { PutCond(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(cmpExpr, LNode.Id((Symbol)"Equals"))).SetStyle(NodeStyle.Operator), LNode.List(input))); } } // Generate code for subpatterns for (int itemIndex = 0; itemIndex < subPatterns.Count; itemIndex++) { var subPattern = subPatterns[itemIndex]; GenCodeForPattern(input, subPattern, "Item" + (itemIndex + 1)); } if (inRange != null) { PutCond(LNode.Call(CodeSymbols.In, LNode.List(input, inRange)).SetStyle(NodeStyle.Operator)); } foreach (var cond in conditions) { PutCond(cond); } }
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 Either <Symbol, ILogMessage> TryPrint(object value, Symbol typeMarker, out StringBuilder sb, NodeStyle style = NodeStyle.Default) => TryPrint(LNode.Literal(SourceRange.Synthetic, new LiteralValue(value, typeMarker), style), out sb);
static LiteralNode CL(object value, string symbol) => LNode.Literal(SourceRange.Synthetic, new LiteralValue(value, (Symbol)symbol));