public static LNode static_if(LNode @if, IMacroContext context) { if (!Range.IsInRange(@if.ArgCount, 2, 3)) { return(null); } LNode cond = context.PreProcess(@if.Args[0]); object @bool; if ((@bool = cond.Value) is bool) { LNode output = (bool)@bool ? @if.Args[1] : @if.Args.TryGet(2, null) ?? F.Call(S.Splice); if (output.Calls(S.Braces)) { return(output.WithTarget(S.Splice)); } else { return(output); } } else { return(Reject(context, @if.Args[0], "'static if' is incredibly limited right now. Currently it only supports a literal boolean or (x `tree==` y)")); } }
public static LNode unroll(LNode node, IMacroContext context) { LNode clause; // unroll (X, Y) \in ((X, Y), (Y, X)) {...} // unroll ((X, Y) in ((X, Y), (Y, X))) {...} if (node.ArgCount == 2 && ((clause = node.Args[0]).Calls(@in, 2) || clause.Calls(S.In, 2))) { LNode identifiers = clause.Args[0], cases = clause.Args[1]; if (!cases.Calls(S.Tuple) && !cases.Calls(S.Braces) && !cases.Calls(S.Splice)) { cases = context.PreProcess(cases); if (!cases.Calls(S.Tuple) && !cases.Calls(S.Braces) && !cases.Calls(S.Splice)) { return(Reject(context, cases, "The right-hand side of 'in' should be a tuple or braced block.")); } } var result = unroll(identifiers, cases.Args, node.Args[1], context.Sink); if (result != null && node.HasPAttrs()) { context.Sink.Warning(result.Attrs[0], "'unroll' does not support attributes."); } return(result); } return(null); }
public static Symbol NextTempName(IMacroContext ctx, LNode value) { string prefix = value.Name.Name; prefix = EcsValidators.IsPlainCsIdentifier(prefix) ? prefix + "_" : "tmp_"; return(NextTempName(ctx, prefix)); }
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); }
public static LNode In(LNode node, IMacroContext context) { { LNode range, x; if (node.Calls(CodeSymbols.In, 2) && (x = node.Args[0]) != null && (range = node.Args[1]) != null) { LNode parens; range = range.WithoutAttrNamed(S.TriviaInParens, out parens); if (parens == null) { { LNode hi, lo; if (range.Calls(CodeSymbols.DotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRangeExcludeHi"))).SetStyle(NodeStyle.Operator), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (range.Calls(CodeSymbols.DotDotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRange"))).SetStyle(NodeStyle.Operator), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); } } return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(range, LNode.Id((Symbol) "Contains"))).SetStyle(NodeStyle.Operator), LNode.List(x)); } } return null; }
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); }
public static LNode ForwardProperty(LNode prop, IMacroContext context) { LNode name, fwd, body; if (prop.ArgCount != 4) { return(null); } LNode target = GetForwardingTarget(fwd = prop.Args[3], name = prop.Args[1]); if (target != null) { body = F.Braces(F.Call(S.get, F.Braces(F.Call(S.Return, target)))); return(prop.WithArgChanged(3, body)); } else if ((body = fwd).Calls(S.Braces)) { var body2 = body.WithArgs(stmt => { if (stmt.Calls(S.get, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) { return(stmt.WithArgs(new VList <LNode>(F.Braces(F.Call(S.Return, target))))); } if (stmt.Calls(S.set, 1) && (target = GetForwardingTarget(stmt.Args[0], name)) != null) { return(stmt.WithArgs(new VList <LNode>(F.Braces(F.Call(S.Assign, target, F.Id(S.value)))))); } return(stmt); }); if (body2 != body) { return(prop.WithArgChanged(3, body2)); } } return(null); }
public static LNode LLLPG_lexer(LNode node, IMacroContext context) { return(LllpgMacro(node, context, _lexer, lexerCfg => { var helper = new IntStreamCodeGenHelper(); foreach (var option in MacroContext.GetOptions(lexerCfg.Args)) { LNode value = option.Value; string key = (option.Key ?? (Symbol)"??").Name; switch (key.ToLowerInvariant()) { case "inputsource": helper.InputSource = value; break; case "inputclass": helper.InputClass = value; break; case "terminaltype": helper.TerminalType = value; break; case "settype": helper.SetType = value; break; case "listinitializer": helper.SetListInitializer(value); break; default: context.Write(Severity.Error, value, "Unrecognized option '{0}'. Available options: " + "inputSource: var, inputClass: type, terminalType: type, setType: type, listInitializer: var _ = new List<T>()", key); break; } } return helper; })); }
[LexicalMacro("x in lo..hi; x in lo...hi; x in ..hi; x in lo..._; x in range", "Converts an 'in' expression to a normal C# expression using the following rules " + "(keeping in mind that the EC# parser treats `..<` as an alias for `..`):\n" + "1. `x in _..hi` and `x in ..hi` become `x.IsInRangeExcl(hi)`\n" + "2. `x in _...hi` and `x in ...hi` become `x.IsInRangeIncl(hi)`\n" + "3. `x in lo.._` and `x in lo..._` become simply `x >= lo`\n" + "4. `x in lo..hi` becomes `x.IsInRangeExcludeHi(lo, hi)`\n" + "5. `x in lo...hi` becomes `x.IsInRange(lo, hi)`\n" + "6. `x in range` becomes `range.Contains(x)`\n" + "The first applicable rule is used.", "#in")] public static LNode In(LNode node, IMacroContext context) { { LNode range, x; if (node.Calls(CodeSymbols.In, 2) && (x = node.Args[0]) != null && (range = node.Args[1]) != null) { LNode parens; range = range.WithoutAttrNamed(S.TriviaInParens, out parens); if (parens == null) { { LNode hi, lo; if (range.Calls(CodeSymbols.DotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRangeExcludeHi"))), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LT, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (range.Calls(CodeSymbols.DotDotDot, 2) && (lo = range.Args[0]) != null && (hi = range.Args[1]) != null) if (lo.IsIdNamed(__)) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); else if (hi.IsIdNamed(__)) return LNode.Call(CodeSymbols.GE, LNode.List(x, lo)).SetStyle(NodeStyle.Operator); else return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(x, LNode.Id((Symbol) "IsInRange"))), LNode.List(lo, hi)); else if (range.Calls(CodeSymbols.DotDotDot, 1) && (hi = range.Args[0]) != null) return LNode.Call(CodeSymbols.LE, LNode.List(x, hi)).SetStyle(NodeStyle.Operator); } } return LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(range, LNode.Id((Symbol) "Contains"))), LNode.List(x)); } } return null; }
static Symbol GetFnAndClassName(IMacroContext context, out LNode @class, out LNode fn) { @class = fn = null; var anc = context.Ancestors; for (int i = anc.Count - 1; i >= 0; i--) { var name = anc[i].Name; if (anc[i].ArgCount >= 2) { if (fn == null) { if (name == S.Fn || name == S.Property || name == S.Constructor || name == S.Event) { fn = anc[i][1]; } } if (name == S.Struct || name == S.Class || name == S.Namespace || name == S.Interface || name == S.Trait || name == S.Alias) { @class = anc[i][0]; return(name); } } } return(null); }
public static LNode on_throw_catch(LNode node, IMacroContext context) { VList <LNode> rest; LNode firstArg, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); return(TransformOnCatch(node, firstArg, F.Braces(rest), on_handler)); }
public static LNode on_finally(LNode node, IMacroContext context) { LNode firstArg, rest, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) return null; return node.With(S.Try, rest, node.With(S.Finally, on_handler)); }
public static LNode saveAndRestore(LNode node, IMacroContext context) { var tmp_10 = context.GetArgsAndBody(true); var args = tmp_10.Item1; var body = tmp_10.Item2; if (args.Count == 1) { LNode newValue = null; { var tmp_11 = args[0]; LNode property; if (tmp_11.Calls(CodeSymbols.Assign, 2) && (property = tmp_11.Args[0]) != null && (newValue = tmp_11.Args[1]) != null || (property = tmp_11) != null) { string mainProp = KeyNameComponentOf(property).Name; string varPrefix = "old" + mainProp + "_"; LNode varName, varDecl = TempVarDecl(context, property, out varName, varPrefix); LNode tryFinally = LNode.Call(CodeSymbols.Try, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(body)).SetStyle(NodeStyle.StatementBlock), LNode.Call(CodeSymbols.Finally, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Assign, LNode.List(property, varName)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.StatementBlock))))); if (newValue != null) { return(LNode.Call(CodeSymbols.Splice, LNode.List(varDecl, LNode.Call(CodeSymbols.Assign, LNode.List(property, newValue)).SetStyle(NodeStyle.Operator), tryFinally)).IncludingTriviaFrom(node)); } else { return(LNode.Call(CodeSymbols.Splice, LNode.List(varDecl, tryFinally)).IncludingTriviaFrom(node)); } } } } return(null); }
private static LNode StmtToCSharp(LNode stmt, IMacroContext context, bool execContext, Symbol parentConstruct) { if (!stmt.IsCall) { return(stmt); } if (stmt.Calls(S.Braces)) { return(null); } var helpers = RVList <LNode> .Empty; ExecInfo info; if (!StatementTypes.TryGetValueSafe(stmt.Name, out info)) { info = new ExecInfo(-1, execContext); } var args = stmt.Args; for (int i = info.IgnoreMax; i < stmt.Args.Count; i++) { if (i == info.BraceIndex) { continue; } } return(stmt); }
public static LNode @nameof(LNode nameof, IMacroContext context) { if (nameof.ArgCount != 1) return null; Symbol expr = EcsValidators.KeyNameComponentOf(nameof.Args[0]); return F.Literal(expr.Name); }
public static LNode NullDot(LNode node, IMacroContext context) { if (!node.Calls(S.NullDot, 2)) return null; var a = node.Args; LNode leftSide = a[0], rightSide = a[1]; // So our input will be something like a.b?.c().d<x>, which is parsed // (a.b) ?. (c().d<x>) // in EC# we would transform this to // a.b::tmp != null ? tmp.c().d<x> : null // but there's no EC# compiler yet, so instead use code that plain C# // can support: // a.b != null ? (a.b).c().d<x> : null LNode condition, thenExpr; if (StandardMacros.LooksLikeSimpleValue(leftSide)) { condition = F.Call(S.Neq, leftSide, F.@null); thenExpr = ConvertToNormalDot(leftSide, rightSide); } else { LNode tempVar = F.Id(StandardMacros.NextTempName(context, leftSide)); condition = F.Call(S.Neq, F.Var(F.Missing, tempVar, leftSide), F.@null); thenExpr = ConvertToNormalDot(tempVar, rightSide); } return F.InParens(F.Call(S.QuestionMark, condition, thenExpr, F.Null)); }
public static LNode static_matchCode(LNode node, IMacroContext context) { if (node.AttrNamed(S.Static) == null && !node.HasSpecialName) return null; // handled by normal matchCode macro var args_body = context.GetArgsAndBody(false); VList<LNode> args = args_body.Item1, body = args_body.Item2; if (args.Count != 1) return Reject(context, args[1], "Expected only one expression to match"); var expression = context.PreProcess(AutoStripBraces(args[0])); var cases = GetCases(body, context.Sink); // The `default:` case is represented by an empty list of patterns. if (cases.WithoutLast(1).Any(pair => pair.Key.IsEmpty)) context.Write(Severity.Error, node, "The `default:` case must be the last one, because the cases are tested in the order they appear, so no case after `default:` can be matched."); MMap<Symbol, LNode> captures = new MMap<Symbol, LNode>(); foreach (Pair<VList<LNode>, VList<LNode>> pair in cases) { var patterns = pair.Key.IsEmpty ? new VList<LNode>((LNode)null) : pair.Key; foreach (var pattern in patterns) { captures.Clear(); VList<LNode> _; if (pattern == null || LNodeExt.MatchesPattern(expression, pattern, ref captures, out _)) { captures[_hash] = expression; // define $# captures.Remove(__); return ReplaceCaptures(pair.Value.AsLNode(S.Splice), captures); } } } return F.Call(S.Splice); // none of the cases matched }
private static LNode ValidateOnStmt(LNode node, IMacroContext context, out VList <LNode> restOfStmts, out LNode firstArg) { var a = node.Args; LNode on_handler; restOfStmts = LNode.List(); firstArg = null; if (a.Count == 2) { firstArg = a[0]; } else if (a.Count != 1) { return(null); } if (!(on_handler = a.Last).Calls(S.Braces)) { return(null); } if (context.RemainingNodes.Count == 0) { context.Write(Severity.Warning, node, "{0} should not be the final statement of a block.", node.Name); } restOfStmts = new VList <LNode>(context.RemainingNodes); context.DropRemainingNodes = true; return(on_handler); }
static string GetFnAndClassNameString(IMacroContext context) { LNode @class, fn; GetFnAndClassName(context, out @class, out fn); var ps = LNode.Printer; if (fn == null) { return(@class == null ? null : ps.Print(@class, MessageSink.Null, ParsingMode.Expressions)); } else if (@class == null) { return(ps.Print(fn, MessageSink.Null, ParsingMode.Expressions)); } else { while (fn.CallsMin(S.Dot, 2)) { fn = fn.Args.Last; } return(string.Format("{0}.{1}", ps.Print(@class, MessageSink.Null, ParsingMode.Expressions), ps.Print(fn, MessageSink.Null, ParsingMode.Expressions))); } }
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 _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) { node = context.PreProcessChildren(); 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]; 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 node.With(newTarget, LNode.Literal(key.Name, key), value); }); return F.Call(S.Splice, stmts); } return null; }
public static LNode NullDot(LNode node, IMacroContext context) { if (!node.Calls(S.NullDot, 2)) { return(null); } var a = node.Args; LNode leftSide = a[0], rightSide = a[1]; // So our input will be something like a.b?.c().d<x>, which is parsed // (a.b) ?. (c().d<x>) // in EC# we would transform this to // a.b::tmp != null ? tmp.c().d<x> : null // but there's no EC# compiler yet, so instead use code that plain C# // can support: // a.b != null ? (a.b).c().d<x> : null LNode condition, thenExpr; if (LeMP.ecs.StandardMacros.LooksLikeSimpleValue(leftSide)) { condition = F.Call(S.NotEq, leftSide, F.@null); thenExpr = ConvertToNormalDot(leftSide, rightSide); } else { LNode tempVar = F.Id(StandardMacros.NextTempName(context, leftSide)); condition = F.Call(S.NotEq, F.Var(F.Missing, tempVar, leftSide), F.@null); thenExpr = ConvertToNormalDot(tempVar, rightSide); } return(F.InParens(F.Call(S.QuestionMark, condition, thenExpr, F.Null))); }
public static LNode static_if(LNode @if, IMacroContext context) { if ([email protected](2, 3)) { return(null); } LNode cond = context.PreProcess(@if.Args[0]); cond = ReduceBooleanExpr(cond); object @bool; if ((@bool = cond.Value) is bool) { LNode output = (bool)@bool ? @if.Args[1] : @if.Args.TryGet(2, null) ?? F.Call(S.Splice); if (output.Calls(S.Braces)) { return(output.WithTarget(S.Splice)); } else { return(output); } } else { return(Reject(context, @if.Args[0], "Only boolean expressions can be evaluated.")); } }
private static void WarnAboutMissingDollarSigns(LNode argList, IMacroContext context, LNode pattern, LNode replacement) { // Warn if a name appears in both pattern and replacement but uses $ in only one of the two. Dictionary <Symbol, LNode> pVars = ScanForVariables(pattern), rVars = ScanForVariables(replacement); // Also warn if it looks like all `$`s were forgotten. bool allIds = argList.Args.Count > 0 && argList.Args.All(n => !n.IsCall); foreach (var pair in pVars) { LNode rVar = rVars.TryGetValue(pair.Key, null); if (pair.Value.IsId) // id without `$` in pattern list { if (rVar != null && (allIds || !rVar.IsId)) { context.Sink.Warning(pair.Value, "`{0}` is written without `$`, so it may not match as intended.", pair.Value.Name); } } else // $id in pattern list { if (rVar != null && rVar.IsId) { context.Sink.Warning(rVar, "`{0}` appears in the output without `$` so replacement will not occur.", pair.Key); } } } }
public static LNode ExpectAncestorStack(LNode node, IMacroContext context) { // Verify AncestorsAndPreviousSiblings Assert.AreEqual(node.ArgCount, context.AncestorsAndPreviousSiblings.Count); int index = 0; foreach (var expect in node.Args) { Assert.IsTrue(expect.Calls(S.Tuple)); var pair = context.AncestorsAndPreviousSiblings[index]; ExpectList(pair.Item1, expect.Args.WithoutLast(1)); if (!expect.Args.Last.IsIdNamed("#skip")) { Assert.AreEqual(expect.Args.Last, pair.Item2.Target); } index++; } // Verify PreviousSiblings var expectedPreviousSiblings = node.Args.Last.Args.WithoutLast(1); int i = 0; foreach (var expected in expectedPreviousSiblings) { if (!expected.IsIdNamed("#skip")) { Assert.AreEqual(expected, context.PreviousSiblings[i]); } i++; } return(LNode.Call(S.Splice)); // delete this node }
internal CodeContractRewriter(LNode returnType, LNode fullMethodName, IMacroContext context) { ReturnType = returnType; FullMethodName = fullMethodName; Context = context; PrependStmts = new VList <LNode>(); }
public static LNode replace(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); var args = args_body.A; var body = args_body.B; if (args.Count >= 1) { var patterns = new Pair<LNode, LNode>[args.Count]; for (int i = 0; i < patterns.Length; i++) { var pair = args[i]; if (pair.Calls(S.Lambda, 2)) { LNode pattern = pair[0], repl = pair[1]; if (pattern.Calls(S.Braces, 1) && repl.Calls(S.Braces)) { pattern = pattern.Args[0]; repl = repl.WithTarget(S.Splice); } patterns[i] = Pair.Create(pattern, repl); } else { string msg = "Expected 'pattern => replacement'."; if (pair.Descendants().Any(n => n.Calls(S.Lambda, 2))) msg += " " + "(Using '=>' already? Put the pattern on the left-hand side in parentheses.)"; return Reject(context, pair, msg); } } int replacementCount; var output = Replace(body, patterns, out replacementCount); if (replacementCount == 0) context.Write(Severity.Warning, node, "No patterns recognized; no replacements were made."); return output.AsLNode(S.Splice); } return null; }
public static LNode ForwardProperty(LNode prop, IMacroContext context) { LNode name, fwd, body; if (prop.ArgCount != 4) return null; LNode target = GetForwardingTarget(name = prop.Args[1], fwd = prop.Args[3]); if (target != null) { body = F.Braces(F.Call(S.get, F.Braces(F.Call(S.Return, target))).SetBaseStyle(NodeStyle.Special)); return prop.WithArgChanged(3, body); } else if ((body = fwd).Calls(S.Braces)) { var body2 = body.WithArgs(stmt => { if (stmt.Calls(S.get, 1) && (target = GetForwardingTarget(name, stmt.Args[0])) != null) return stmt.WithArgs(new VList<LNode>(F.Braces(F.Call(S.Return, target)))); if (stmt.Calls(S.set, 1) && (target = GetForwardingTarget(name, stmt.Args[0])) != null) return stmt.WithArgs(new VList<LNode>(F.Braces(F.Call(S.Assign, target, F.Id(S.value))))); return stmt; }); if (body2 != body) return prop.WithArgChanged(3, body2); } return null; }
public static LNode saveAndRestore(LNode node, IMacroContext context) { var tmp_0 = context.GetArgsAndBody(true); var args = tmp_0.Item1; var body = tmp_0.Item2; if (args.Count == 1) { LNode newValue = null; { var tmp_1 = args[0]; LNode property; if (tmp_1.Calls(CodeSymbols.Assign, 2) && (property = tmp_1.Args[0]) != null && (newValue = tmp_1.Args[1]) != null || (property = tmp_1) != null) { string mainProp = KeyNameComponentOf(property).Name; string varPrefix = "old" + mainProp + "_"; LNode varName, varDecl = TempVarDecl(property, out varName, varPrefix); LNode tryFinally = LNode.Call(CodeSymbols.Try, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(body)).SetStyle(NodeStyle.Statement), LNode.Call(CodeSymbols.Finally, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Assign, LNode.List(property, varName)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Statement))))); if (newValue != null) { return LNode.Call(CodeSymbols.Splice, LNode.List(varDecl, LNode.Call(CodeSymbols.Assign, LNode.List(property, newValue)).SetStyle(NodeStyle.Operator), tryFinally)); } else { return LNode.Call(CodeSymbols.Splice, LNode.List(varDecl, tryFinally)); } } } } return null; }
public static Symbol NextTempName(IMacroContext ctx, LNode value) { string prefix = value.Name.Name; prefix = LNode.IsSpecialName(prefix) ? "tmp_" : prefix + "_"; return(NextTempName(ctx, prefix)); }
public static LNode ContractsOnMethod(LNode fn, IMacroContext context) { LNode oldFn = fn; if (fn.ArgCount >= 4) { var rw = new CodeContractRewriter(fn.Args[0], fn.Args[1], context); fn = ProcessArgContractAttributes(fn, 2, rw); if (fn.Args[0].HasAttrs) { fn = fn.WithArgChanged(0, fn.Args[0].WithAttrs(rw.Process(fn.Args[0].Attrs, null))); } if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[3]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(3, body); return(fn); } } return(null); }
public static LNode ContractsOnLambda(LNode fn, IMacroContext context) { LNode oldFn = fn; if (fn.ArgCount == 2) { var rw = new CodeContractRewriter(LNode.Missing, Id_lambda_function, context); fn = ProcessArgContractAttributes(fn, 0, rw, isLambda: true); if (fn.HasAttrs) { fn = fn.WithAttrs(rw.Process(fn.Attrs, null)); } if (rw.PrependStmts.IsEmpty) { return(null); } else { var body = fn.Args[1]; if (!body.Calls(S.Braces)) { body = LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(body)))).SetStyle(NodeStyle.Statement); } body = body.WithArgs(body.Args.InsertRange(0, rw.PrependStmts)); fn = fn.WithArgChanged(1, body); return(fn); } } return(null); }
public static LNode scope(LNode node, IMacroContext context) { var a = node.Args; if (a.Count == 2 && a[1].Calls(S.Braces) && a[0].IsId) { Symbol name = a[0].Name; if (name == _exit || name == S.Finally) { return(F.Call(_on_finally, a[1])); } else if (name == _success || name == S.Return) { return(F.Call(_on_return, a[1])); } else if (name == _failure || name == S.Catch) { return(F.Call(_on_throw_catch, a[1])); } else { return(Reject(context, a[0], "Expected 'exit', 'success', or 'failure'")); } } return(null); }
/// <summary>This method helps do the stage-one transform from <c>LLLPG (config) {...}</c> /// to <c>run_LLLPG (helper literal) {...}</c> and also invokes the ANTLR-style /// parser if the second argument is a token literal. If <c>node[0]</c> /// calls <c>expectedConfigNode</c> then the delegate is called to /// construct a code generation helper object; otherwise, this method has /// no effect and returns null.</summary> public static LNode LllpgMacro(LNode node, IMacroContext context, Symbol expectedCodeGenMode, Func <LNode, IPGCodeGenHelper> makeCodeGenHelper, bool isDefault = false) { LNodeList args, body; LNode tokenTree = null, codeGenOptions = null; if (node.ArgCount >= 1 && (tokenTree = node.Args.Last).Value is TokenTree) { args = node.Args.WithoutLast(1); body = LNodeList.Empty; } else { tokenTree = null; var p = context.GetArgsAndBody(orRemainingNodes: true); args = p.A; body = p.B; } if ((args.Count == 1 && (codeGenOptions = args[0]).Name == expectedCodeGenMode) || (args.Count == 0 && isDefault)) { if (tokenTree != null) { body = AntlrStyleParser.ParseTokenTree(tokenTree.Value as TokenTree, context.Sink); } IPGCodeGenHelper helper = makeCodeGenHelper(codeGenOptions); return(node.WithTarget(_run_LLLPG).WithArgs(F.Literal(helper), F.Braces(body))); } return(null); }
public static LNode @defaultCase(LNode node, IMacroContext context) { if (node.ArgCount == 0) { return(node.With(S.Label, LNode.Id(S.Default, node)).SetBaseStyle(NodeStyle.Default)); } else if (node.ArgCount == 1) { var arg = node.Args[0]; if (arg.Calls(S.Colon, 1) // .default: {...} ) { arg = arg.Args[0]; } else if (!arg.Calls(S.Braces) // expecting .default {...} ) { return(null); } return(F.Call(S.Splice, node.With(S.Label, LNode.Id(S.Default, node)).SetBaseStyle(NodeStyle.Default), arg)); } return(null); }
public static LNode LLLPG_lexer(LNode node, IMacroContext context) { return(LllpgMacro(node, context, _lexer, lexerCfg => { var helper = new IntStreamCodeGenHelper(); foreach (var option in MacroContext.GetOptions(lexerCfg.Args)) { LNode value = option.Value ?? LNode.Missing; string key = option.Key.Name.Name; switch (key.ToLowerInvariant()) { case "inputsource": helper.InputSource = value; break; case "inputclass": helper.InputClass = value; break; case "terminaltype": helper.TerminalType = value; break; case "settype": helper.SetType = value; break; case "listinitializer": helper.SetListInitializer(value); break; case "nocheckbydefault": SetOption <bool>(context, option.Key, value.Value, b => helper.NoCheckByDefault = b); break; default: context.Sink.Error(option.Key, "Unrecognized option '{0}'. Available options: " + "InputSource: var, InputClass: type, TerminalType: type, SetType: type, " + "ListInitializer: var _ = new List<T>(), NoCheckByDefault: true", key); break; } } return helper; })); }
public static LNode RangeExcl(LNode node, IMacroContext context) { LNode lo = null; { LNode hi; if (node.Args.Count == 2 && (lo = node.Args[0]) != null && (hi = node.Args[1]) != null || node.Args.Count == 1 && (hi = node.Args[0]) != null) { if (lo == null || lo.IsIdNamed(__)) { if (hi.IsIdNamed(__)) { return(Range_Everything); } else { return(LNode.Call(Range_UntilExclusive, LNode.List(hi))); } } else if (hi.IsIdNamed(__)) { return(LNode.Call(Range_StartingAt, LNode.List(lo))); } else { return(LNode.Call(Range_ExcludeHi, LNode.List(lo, hi))); } } } return(null); }
public static LNode DollarSignVariable(LNode node, IMacroContext context) { LNode id; if (node.ArgCount == 1 && (id = node.Args[0]).IsId && !id.HasPAttrs()) { object value; if (context.ScopedProperties.TryGetValue("$" + id.Name.Name, out value)) { if (value is LNode) { return(((LNode)value).WithRange(id.Range)); } else { context.Sink.Warning(id, "The specified scoped property is not a syntax tree. " + "Use `#getScopedProperty({0})` to insert it as a literal.", EcsNodePrinter.PrintId(id.Name)); } } else { context.Sink.Error(id, "There is no macro property in scope named `{0}`", id.Name); } } return(null); }
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 = 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); }
static bool DetectMissingVoidReturn(IMacroContext context, LNode lastStmt) { if (!NextStatementMayBeReachable(lastStmt)) { return(false); } var anc = context.Ancestors; var parent = anc.TryGet(anc.Count - 2, LNode.Missing); var grandparent = anc.TryGet(anc.Count - 3, LNode.Missing); do { if (parent.Calls(S.Braces)) { if (grandparent.CallsMin(S.Fn, 4) && grandparent.Args[0].IsIdNamed(S.Void)) { return(true); } if (grandparent.Calls(S.Constructor)) { return(true); } if (grandparent.Calls(S.set, 1) || grandparent.Calls(S.add, 1) || grandparent.Calls(S.remove, 1)) { return(true); } if (grandparent.Calls(S.Lambda, 2)) { return(true); } } return(false); } while (false); }
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 on_throw(LNode node, IMacroContext context) { LNode firstArg, rest, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null) return null; on_handler = on_handler.PlusArg(F.Call(S.Throw)); return TransformOnCatch(node, firstArg, rest, on_handler); }
public static LNode useDefaultTupleTypes(LNode node, IMacroContext context) { if (node.ArgCount != 0) return null; context.ScopedProperties.Remove(TupleMakers); context.ScopedProperties.Remove(DefaultTupleMaker); return F.Call(S.Splice); }
public static LNode runSequence(LNode node, IMacroContext context) { if (context.Parent.Calls(S.Braces)) return node.With(S.Splice, MaybeRemoveNoOpFromRunSeq(node.Args)); if (!context.ScopedProperties.ContainsKey(_useSequenceExpressionsIsRunning)) Reject(context, node, "#useSequenceExpressions is required to make #runSequence work"); return null; }
public static LNode static_tryDeconstruct(LNode node, IMacroContext context) { if (node.AttrNamed(S.Static) == null && !node.HasSpecialName) return Reject(context, node, "Expected 'static' attribute"); foreach (var arg in node.Args) DoDeconstruct(arg, context, printErrorOnFailure: false); return F.Call(S.Splice); }
// 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; }
internal CodeGeneratorForMatchCase(IMacroContext context, LNode input, VList<LNode> handler) { _context = context; _input = input; _handler = handler; var @break = LNode.Call(CodeSymbols.Break); if (_handler.IsEmpty || !_handler.Last.Equals(@break)) _handler.Add(@break); }
public static LNode useSymbols(LNode input, IMacroContext context) { bool inType = context.Ancestors.Any(parent => { var kind = EcsValidators.SpaceDefinitionKind(parent); return kind != null && kind != S.Namespace; }); var args_body = context.GetArgsAndBody(true); args_body.B = context.PreProcess(args_body.B); return UseSymbolsCore(input.Attrs, args_body.A, args_body.B, context, inType); }
public static LNode on_finally(LNode node, IMacroContext context) { VList<LNode> rest; LNode firstArg, on_handler = ValidateOnStmt(node, context, out rest, out firstArg); if (on_handler == null || firstArg != null) return null; node.Style &= ~NodeStyle.OneLiner; // avoid collapsing output to one line return node.With(S.Try, F.Braces(rest), node.With(S.Finally, on_handler)); }
public static LNode AddCsLineDirectives(LNode node, IMacroContext context) { if (node.ArgCount != 0) return null; int sourceLine = -1; var list0 = new VList<LNode>(context.RemainingNodes); var list1 = context.PreProcess(list0); var list2 = AddLineDirectives(list1, true, ref sourceLine); context.DropRemainingNodes = true; return F.Call(S.Splice, list2); }
public static LNode replaceTarget(LNode outerNode, IMacroContext context1) { // Test the direct way to register a macro context1.RegisterMacro( new MacroInfo(null, outerNode[0].Name.Name, (node, context2) => { return node.WithTarget(outerNode[1]); })); return LNode.Call(S.Splice); }
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 static LNode @using1(LNode node, IMacroContext sink) { if (node.ArgCount == 1 && IsComplexId(node.Args[0])) { // Looks like an import statement sink.Write(Severity.Warning, node.Target, "The 'import' statement replaces the 'using' statement in LeMP."); return node.WithTarget(S.Import); } var result = TranslateSpaceDefinition(node, sink, S.Alias); if (result != null) return result.PlusAttr(F.Id(S.FilePrivate)); return null; }
public static LNode replace(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); var args = args_body.A; var body = args_body.B; if (args.Count == 1 && args[0].Calls(S.Tuple)) args = args[0].Args; // LESv2 if (args.Count >= 1) { bool preprocess = node.Calls("replacePP"); var patterns = new Pair<LNode, LNode>[args.Count]; for (int i = 0; i < patterns.Length; i++) { var pair = args[i]; if (pair.Calls(S.Lambda, 2)) { LNode pattern = pair[0], repl = pair[1]; if (preprocess) { pattern = context.PreProcess(pattern); repl = context.PreProcess(repl); } if (pattern.Calls(S.Braces)) { if (pattern.ArgCount == 1) pattern = pattern.Args[0]; else context.Write(Severity.Error, pattern, "The braces must contain only a single statement. To search for braces literally, use `{{ ... }}`"); } if (repl.Calls(S.Braces)) repl = repl.Args.AsLNode(S.Splice); // Avoid StackOverflowException when pattern is $Id (sadly, it // is uncatchable so it can crash LeMP.exe and even Visual Studio) if (LNodeExt.GetCaptureIdentifier(pattern) != null) return Reject(context, pattern, "The left side of `=>` cannot be a capture. Remove the `$`."); patterns[i] = Pair.Create(pattern, repl); } else { string msg = "Expected 'pattern => replacement'."; if (pair.Descendants().Any(n => n.Calls(S.Lambda, 2))) msg += " " + "(Using '=>' already? Put the pattern on the left-hand side in parentheses.)"; return Reject(context, pair, msg); } } int replacementCount; var output = Replace(body, patterns, out replacementCount); if (replacementCount == 0) context.Sink.Warning(node, "No patterns recognized; no replacements were made."); return output.AsLNode(S.Splice); } return null; }
public static LNode TupleType(LNode node, IMacroContext context) { var stem = node.Args[0, F.Missing]; if (stem.IsId && (stem.Name == S.AltList || stem.Name == S.Tuple)) { var tupleMakers = MaybeInitTupleMakers(context.ScopedProperties); var bareType = tupleMakers.TryGet(node.Args.Count - 1, new Pair<LNode, LNode>()).A; if (bareType == null) bareType = ((Pair<LNode, LNode>)context.ScopedProperties[DefaultTupleMaker]).A; if (bareType != null) return node.WithArgChanged(0, bareType); } return null; }
static string GetFnAndClassNameString(IMacroContext context) { LNode @class, fn; GetFnAndClassName(context, out @class, out fn); var ps = ParsingService.Current; if (fn == null) return @class == null ? null : ps.Print(@class, null, ParsingService.Exprs); else if (@class == null) return ps.Print(fn, null, ParsingService.Exprs); else { while (fn.CallsMin(S.Dot, 2)) fn = fn.Args.Last; return string.Format("{0}.{1}", ps.Print(@class, null, ParsingService.Exprs), ps.Print(fn, null, ParsingService.Exprs)); } }
public static LNode staticMatches(LNode node, IMacroContext context) { if (node.ArgCount != 2) return null; LNode candidate = context.PreProcess(AutoStripBraces(node[0])); LNode pattern = AutoStripBraces(node[1]); MMap<Symbol, LNode> captures = new MMap<Symbol, LNode>(); VList<LNode> _; if (LNodeExt.MatchesPattern(candidate, pattern, ref captures, out _)) { SetSyntaxVariables(captures, context); return F.True; } return F.False; }
public static LNode on_return(LNode node, IMacroContext context) { VList<LNode> rest; LNode varDecl, bracedHandler = ValidateOnStmt(node, context, out rest, out varDecl); if (bracedHandler == null) return null; rest = context.PreProcess(rest); bracedHandler = context.PreProcess(bracedHandler); LNode varName; if (varDecl == null) { varName = Id__result__; varDecl = F.Var(F.Missing, varName); } else { { LNode tmp_0; if (varDecl.Calls(CodeSymbols.Var, 2) && (tmp_0 = varDecl.Args[1]) != null && tmp_0.Calls(CodeSymbols.Assign, 2) && (varName = tmp_0.Args[0]) != null) context.Write(Severity.Error, varName, "The return value cannot be assigned here. The value of this variable must be placed on the return statement(s)."); else if (varDecl.Calls(CodeSymbols.Var, 2) && (varName = varDecl.Args[1]) != null) { } else if ((varName = varDecl).IsId) varDecl = varName.With(S.Var, F.Missing, varName); else return Reject(context, varDecl, "The first parameter to on_return must be a simple identifier (the name of a variable to return) or a variable declaration (for a variable to be returned)."); } } bool foundReturn = false; rest = rest.SmartSelect(arg => arg.ReplaceRecursive(rnode => { { LNode retVal; if (rnode.Calls(CodeSymbols.Lambda, 2)) return rnode; else if (rnode.Calls(CodeSymbols.Return, 0)) { foundReturn = true; return LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(bracedHandler.Args).Add(rnode)).SetStyle(NodeStyle.Statement); } else if (rnode.Calls(CodeSymbols.Return, 1) && (retVal = rnode.Args[0]) != null) { foundReturn = true; var retValDecl = varDecl.WithArgChanged(1, LNode.Call(CodeSymbols.Assign, LNode.List(varName, retVal)).SetStyle(NodeStyle.Operator)); rnode = rnode.WithArgs(varName); return LNode.Call(CodeSymbols.Braces, LNode.List().Add(retValDecl).AddRange(bracedHandler.Args).Add(rnode)).SetStyle(NodeStyle.Statement); } else return null; } })); if (DetectMissingVoidReturn(context, rest[rest.Count - 1, LNode.Missing])) rest.Add(bracedHandler.Args.AsLNode(S.Braces)); else if (!foundReturn) context.Write(Severity.Warning, node, "'on_return': no 'return' statements were found in this context, so this macro had no effect."); return LNode.Call((Symbol) "#noLexicalMacros", LNode.List(rest)); }
public static LNode UseSymbolsCore(VList<LNode> symbolAttrs, VList<LNode> options, VList<LNode> body, IMacroContext context, bool inType) { // Decode options (TODO: invent a simpler approach) string prefix = "sy_"; var inherited = new HashSet<Symbol>(); foreach (var pair in MacroContext.GetOptions(options)) { if (pair.Key.Name.Name == "prefix" && pair.Value.IsId) prefix = pair.Value.Name.Name; else if (pair.Key.Name.Name == "inherit" && pair.Value.Value is Symbol) inherited.Add((Symbol)pair.Value.Value); else if (pair.Key.Name.Name == "inherit" && (pair.Value.Calls(S.Braces) || pair.Value.Calls(S.Tuple)) && pair.Value.Args.All(n => n.Value is Symbol)) foreach (var arg in pair.Value.Args) inherited.Add((Symbol)arg.Value); else context.Sink.Warning(pair.Value, "Unrecognized parameter. Expected prefix:id or inherit:{@@A; @@B; ...})"); } // Replace all symbols while collecting a list of them var symbols = new Dictionary<Symbol, LNode>(); VList<LNode> output = body.SmartSelect(stmt => stmt.ReplaceRecursive(n => { if (!inType && n.ArgCount == 3) { // Since we're outside any type, we must avoid creating symbol // fields. When we cross into a type then we can start making // Symbols by calling ourself recursively with inType=true var kind = EcsValidators.SpaceDefinitionKind(n); if (kind == S.Class || kind == S.Struct || kind == S.Interface || kind == S.Alias || kind == S.Trait) { var body2 = n.Args[2]; return n.WithArgChanged(2, UseSymbolsCore(symbolAttrs, options, body2.Args, context, true).WithName(body2.Name)); } } var sym = n.Value as Symbol; if (n.IsLiteral && sym != null) return symbols[sym] = LNode.Id(prefix + sym.Name); return null; })); // Return updated code with variable declaration at the top for all non-inherit symbols used. var _Symbol = F.Id("Symbol"); var vars = (from sym in symbols where !inherited.Contains(sym.Key) select F.Call(S.Assign, sym.Value, F.Call(S.Cast, F.Literal(sym.Key.Name), _Symbol))).ToList(); if (vars.Count > 0) output.Insert(0, F.Call(S.Var, ListExt.Single(_Symbol).Concat(vars)) .WithAttrs(symbolAttrs.Add(F.Id(S.Static)).Add(F.Id(S.Readonly)))); return F.Call(S.Splice, output); }
public static LNode ForwardMethod(LNode fn, IMacroContext context) { LNode args, fwd, body; if (fn.ArgCount != 4 || !(fwd = fn.Args[3]).Calls(S.Forward, 1) || !(args = fn.Args[2]).Calls(S.AltList)) return null; VList<LNode> argList = GetArgNamesFromFormalArgList(args, formalArg => context.Write(Severity.Error, formalArg, "'==>': Expected a variable declaration here")); LNode target = GetForwardingTarget(fn.Args[1], fwd); LNode call = F.Call(target, argList); bool isVoidFn = fn.Args[0].IsIdNamed(S.Void); body = F.Braces(isVoidFn ? call : F.Call(S.Return, call)); return fn.WithArgChanged(3, body); }