static LNode MaybeQuoteList(VList <LNode> list, bool substitutions) { if (list.IsEmpty) { return(null); } else if (substitutions && list.Any(a => VarArgExpr(a) != null)) { if (list.Count == 1) { return(F.Call(LNode_List, VarArgExpr(list[0]))); } // If you write something like quote(Foo($x, $(...y), $z)), a special // output style is used to accommodate the variable argument list. LNode argList = F.Call(LNode_List); foreach (LNode arg in list) { var vae = VarArgExpr(arg); if (vae != null) { argList = F.Call(F.Dot(argList, F.Id("AddRange")), vae); } else { argList = F.Call(F.Dot(argList, F.Id("Add")), QuoteOne(arg, substitutions)); } } return(argList); } else { return(F.Call(LNode_List, list.SmartSelect(item => QuoteOne(item, substitutions)))); } }
/// <summary>Searches a list of expressions/statements for one or more /// patterns, and performs replacements.</summary> /// <param name="stmts">A list of expressions/statements in which to search.</param> /// <param name="patterns">Each pair consists of (A) something to search /// for and (B) a replacement expression. Part A can use the substitution /// operator with an identifier inside (e.g. $Foo) to "capture" any /// subexpression, and part B can use the same substitution (e.g. $Foo) /// to insert the captured subexpression(s) into the output.</param> /// <param name="replacementCount">Number of replacements that occurred.</param> /// <returns>The result of applying the replacements.</returns> /// <remarks><see cref="LNodeExt.MatchesPattern"/> is used for matching.</remarks> public static VList <LNode> Replace(VList <LNode> stmts, Pair <LNode, LNode>[] patterns, out int replacementCount) { // This list is used to support simple token replacement in TokenTrees _tokenTreeRepls = InternalList <Triplet <Symbol, LNode, int> > .Empty; foreach (var pair in patterns) // Look for Id => Id or Id => Literal { if (pair.A.IsId && (pair.B.IsId || pair.B.IsLiteral)) { _tokenTreeRepls.Add(new Triplet <Symbol, LNode, int>(pair.A.Name, pair.B, 0)); } } // Scan the syntax tree for things to replace... int count = 0; var temp = new MMap <Symbol, LNode>(); var output = stmts.SmartSelect(stmt => stmt.ReplaceRecursive(n => { LNode r = TryReplaceHere(n, patterns, temp); if (r != null) { count++; } return(r); })); replacementCount = count; return(output); }
LNode ESEInForLoop(LNode stmt, VList <LNode> attrs, VList <LNode> init, LNode cond, VList <LNode> inc, LNode block) { // TODO: handle multi-int and multi-inc var preInit = VList <LNode> .Empty; var init_apos = init.SmartSelect(init1 => { init1 = EliminateSequenceExpressionsInExecStmt(init1); if (init1.CallsMin(__numrunSequence, 1)) { preInit.AddRange(init1.Args.WithoutLast(1)); return(init1.Args.Last); } return(init1); }); var cond_apos = BubbleUpBlocks(cond); var inc_apos = inc.SmartSelectMany(inc1 => { inc1 = BubbleUpBlocks(inc1); return(inc1.AsList(__numrunSequence)); }); block = EliminateSequenceExpressionsInChildStmt(block); if (init_apos != init || cond_apos != cond || inc_apos != inc) { init = init_apos; if (inc_apos != inc) { var blockStmts = block.AsList(S.Braces).AddRange(inc_apos); block = blockStmts.AsLNode(S.Braces); inc = LNode.List(); } if (cond_apos.CallsMin(__numrunSequence, 1)) { var preCond = cond_apos.Args.WithoutLast(1); cond = cond_apos.Args.Last; stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement))); } else { stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block)); } if (preInit.Count != 0) { stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement); } return(stmt); } else { return(stmt.WithArgChanged(3, block)); } }
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); }
LNode MaybeQuoteList(VList <LNode> list, bool isAttributes = false) { if (isAttributes && _ignoreTrivia) { list = list.SmartWhere(n => !n.IsTrivia || n.IsIdNamed(S.TriviaInParens)); } if (list.IsEmpty) { return(null); } else if (_doSubstitutions && list.Any(a => VarArgExpr(a) != null)) { if (list.Count == 1) { return(F.Call(LNode_List, VarArgExpr(list[0]))); } // If you write something like quote(Foo($x, $(...y), $z)), a special // output style is used to accommodate the variable argument list. LNode argList = F.Call(LNode_List); foreach (LNode arg in list) { var vae = VarArgExpr(arg); if (vae != null) { argList = F.Call(F.Dot(argList, F.Id("AddRange")), vae); } else { argList = F.Call(F.Dot(argList, F.Id("Add")), QuoteOne(arg)); } } return(argList); } else { return(F.Call(LNode_List, list.SmartSelect(item => QuoteOne(item)))); } }
static VList <LNode> AddLineDirectives(VList <LNode> nodes, bool stmtContext, ref int sourceLine_) { int sourceLine = sourceLine_; nodes = nodes.SmartSelect(node => { if (stmtContext && sourceLine > 0 && node.AttrNamed(S.TriviaAppendStatement) == null) { sourceLine++; // printer will print a newline by default } int explicitNewlines = node.Attrs.Count(n => n.IsIdNamed(S.TriviaNewline)); sourceLine += explicitNewlines; // Generate line directive if necessary; to avoid excess // clutter, don't consider emit #line directives within an expression. string lineDirective = null; if (stmtContext || explicitNewlines != 0) { if (node.Range.Source is EmptySourceFile || string.IsNullOrEmpty(node.Range.Source.FileName)) { // synthetic code: no source location if (sourceLine != -1) { sourceLine = -1; lineDirective = "#line default"; } } else { var start = node.Range.Start; if (sourceLine != start.Line) { sourceLine = start.Line; lineDirective = "#line " + start.Line + " " + EcsNodePrinter.PrintString(start.FileName, '"'); } } } int sourceLineWas = sourceLine; if (node.Name.Name.StartsWith("#") && node.ArgCount > 1) { // For some special calls like #if, #while, and #doWhile, // printer might print newlines in places we don't know about, // so erase our knowledge of what the current line is. if (sourceLine > 0) { sourceLine = int.MinValue; } } // Process children node = node.WithAttrs(AddLineDirectives(node.Attrs, false, ref sourceLine)); if (node.IsCall) { node = node.WithArgs(AddLineDirectives(node.Args, node.Calls(S.Braces), ref sourceLine)); } if (sourceLine > 0) { sourceLine += node.GetTrailingTrivia().Count(n => n.IsIdNamed(S.TriviaNewline)); } // Finally, add a line directive if requested. if (lineDirective != null) { var trivia = F.Trivia(S.TriviaCsPPRawText, lineDirective); if (!node.Attrs.Contains(trivia)) { // Trivia tends not to be included in the source range so adding #line // before trivia is generally wrong, while adding #line after attributes // tends to be wrong too. Sigh. Search for a good location to insert... // unless inserting #line default which we can just put at the beginning int insertIndex = 0; if (sourceLineWas > 0) { insertIndex = node.Attrs.IndexWhere(n => n.Range.Start.Line == sourceLineWas && n.Range.Source == node.Range.Source); if (insertIndex == -1) { insertIndex = node.Attrs.Count; } } node = node.WithAttrs(node.Attrs.Insert(insertIndex, trivia)); } } return(node); }); sourceLine_ = sourceLine; return(nodes); }
/// <summary>Searches a list of expressions/statements for one or more /// patterns, and performs replacements.</summary> /// <param name="stmts">A list of expressions/statements in which to search.</param> /// <param name="patterns">Each pair consists of (A) something to search /// for and (B) a replacement expression. Part A can use the substitution /// operator with an identifier inside (e.g. $Foo) to "capture" any /// subexpression, and part B can use the same substitution (e.g. $Foo) /// to insert the captured subexpression(s) into the output.</param> /// <param name="replacementCount">Number of replacements that occurred.</param> /// <returns>The result of applying the replacements.</returns> /// <remarks><see cref="LNodeExt.MatchesPattern"/> is used for matching.</remarks> public static VList<LNode> Replace(VList<LNode> stmts, Pair<LNode, LNode>[] patterns, out int replacementCount) { // This list is used to support simple token replacement in TokenTrees _tokenTreeRepls = InternalList<Triplet<Symbol, LNode, int>>.Empty; foreach (var pair in patterns) // Look for Id => Id or Id => Literal if (pair.A.IsId && (pair.B.IsId || pair.B.IsLiteral)) _tokenTreeRepls.Add(new Triplet<Symbol,LNode,int>(pair.A.Name, pair.B, 0)); // Scan the syntax tree for things to replace... int count = 0; var temp = new MMap<Symbol, LNode>(); var output = stmts.SmartSelect(stmt => stmt.ReplaceRecursive(n => { LNode r = TryReplaceHere(n, patterns, temp); if (r != null) count++; return r; })); replacementCount = count; return output; }
public VList <LNode> EliminateBlockExprs(VList <LNode> stmts, bool isDeclContext) { return(stmts.SmartSelect(stmt => { return EliminateBlockExprs(stmt, isDeclContext); })); }
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)); }
static VList<LNode> AddLineDirectives(VList<LNode> nodes, bool stmtContext, ref int sourceLine_) { int sourceLine = sourceLine_; nodes = nodes.SmartSelect(node => { if (stmtContext && sourceLine > 0 && node.AttrNamed(S.TriviaAppendStatement) == null) sourceLine++; // printer will print a newline by default int explicitNewlines = node.Attrs.Count(n => n.IsIdNamed(S.TriviaNewline)); sourceLine += explicitNewlines; // Generate line directive if necessary; to avoid excess // clutter, don't consider emit #line directives within an expression. string lineDirective = null; if (stmtContext || explicitNewlines != 0) { if (node.Range.Source is EmptySourceFile || string.IsNullOrEmpty(node.Range.Source.FileName)) { // synthetic code: no source location if (sourceLine != -1) { sourceLine = -1; lineDirective = "#line default"; } } else { var start = node.Range.Start; if (sourceLine != start.Line) { sourceLine = start.Line; lineDirective = "#line "+start.Line+" "+EcsNodePrinter.PrintString(start.FileName, '"'); } } } int sourceLineWas = sourceLine; if (node.Name.Name.StartsWith("#") && node.ArgCount > 1) { // For some special calls like #if, #while, and #doWhile, // printer might print newlines in places we don't know about, // so erase our knowledge of what the current line is. if (sourceLine > 0) sourceLine = int.MinValue; } // Process children node = node.WithAttrs(AddLineDirectives(node.Attrs, false, ref sourceLine)); if (node.IsCall) node = node.WithArgs(AddLineDirectives(node.Args, node.Calls(S.Braces), ref sourceLine)); if (sourceLine > 0) sourceLine += node.GetTrailingTrivia().Count(n => n.IsIdNamed(S.TriviaNewline)); // Finally, add a line directive if requested. if (lineDirective != null) { var trivia = F.Trivia(S.TriviaCsPPRawText, lineDirective); if (!node.Attrs.Contains(trivia)) { // Trivia tends not to be included in the source range so adding #line // before trivia is generally wrong, while adding #line after attributes // tends to be wrong too. Sigh. Search for a good location to insert... // unless inserting #line default which we can just put at the beginning int insertIndex = 0; if (sourceLineWas > 0) { insertIndex = node.Attrs.IndexWhere(n => n.Range.Start.Line == sourceLineWas && n.Range.Source == node.Range.Source); if (insertIndex == -1) insertIndex = node.Attrs.Count; } node = node.WithAttrs(node.Attrs.Insert(insertIndex, trivia)); } } return node; }); sourceLine_ = sourceLine; return nodes; }
LNode ESEInForLoop(LNode stmt, VList<LNode> attrs, VList<LNode> init, LNode cond, VList<LNode> inc, LNode block) { // TODO: handle multi-int and multi-inc var preInit = VList<LNode>.Empty; var init_apos = init.SmartSelect(init1 => { init1 = EliminateSequenceExpressionsInExecStmt(init1); if (init1.CallsMin(__numrunSequence, 1)) { preInit.AddRange(init1.Args.WithoutLast(1)); return init1.Args.Last; } return init1; }); var cond_apos = BubbleUpBlocks(cond); var inc_apos = inc.SmartSelectMany(inc1 => { inc1 = BubbleUpBlocks(inc1); return inc1.AsList(__numrunSequence); }); block = EliminateSequenceExpressionsInChildStmt(block); if (init_apos != init || cond_apos != cond || inc_apos != inc) { init = init_apos; if (inc_apos != inc) { var blockStmts = block.AsList(S.Braces).AddRange(inc_apos); block = blockStmts.AsLNode(S.Braces); inc = LNode.List(); } if (cond_apos.CallsMin(__numrunSequence, 1)) { var preCond = cond_apos.Args.WithoutLast(1); cond = cond_apos.Args.Last; stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement))); } else { stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block)); } if (preInit.Count != 0) { stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement); } return stmt; } else { return stmt.WithArgChanged(3, block); } }