public static LNode use_symbols(LNode input, IMacroContext context) { var args_body = context.GetArgsAndBody(true); // Decode options (TODO: invent a simpler approach) string prefix = "sy_"; var inherited = new HashSet <Symbol>(); foreach (var pair in MacroContext.GetOptions(args_body.A)) { if (pair.Key.Name == "prefix" && pair.Value.IsId) { prefix = pair.Value.Name.Name; } else if (pair.Key.Name == "inherit" && pair.Value.Value is Symbol) { inherited.Add((Symbol)pair.Value.Value); } else if (pair.Key.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.Write(Severity.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 = args_body.B.SmartSelect(stmt => stmt.ReplaceRecursive(n => { var sym = n.Value as Symbol; if (n.IsLiteral && sym != null) { return(symbols[sym] = LNode.Id(prefix + EcsNodePrinter.SanitizeIdentifier(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(input.Attrs.Add(F.Id(S.Static)).Add(F.Id(S.Readonly)))); } return(F.Call(S.Splice, output)); }
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)); }