Example #1
0
 public void SanitizeIdentifierTests()
 {
     AreEqual("I_aposd", EcsValidators.SanitizeIdentifier("I'd"));
     AreEqual("_123", EcsValidators.SanitizeIdentifier("123"));
     AreEqual("_plus5", EcsValidators.SanitizeIdentifier("+5"));
     AreEqual("__empty__", EcsValidators.SanitizeIdentifier(""));
     AreEqual("_lt_gt", EcsValidators.SanitizeIdentifier("<>"));
 }
Example #2
0
        public static LNode useSymbols(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 + EcsValidators.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));
        }
Example #3
0
 static string LiteralToIdent(object literal)
 {
     return(EcsValidators.SanitizeIdentifier((literal ?? "null").ToString()));
 }
Example #4
0
        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));
        }