Пример #1
0
        protected override void Stmt(string result, LNode input, Action <EcsNodePrinter> configure = null, Mode mode = Mode.Both)
        {
            bool exprMode = (mode & Mode.Expression) != 0;

            if ((mode & Mode.PrinterTest) == 0)
            {
                return;
            }

            var sb      = new StringBuilder();
            var printer = EcsNodePrinter.New(input, sb, "  ");

            printer.AllowChangeParentheses = false;
            printer.NewlineOptions        &= ~(NewlineOpt.AfterOpenBraceInNewExpr | NewlineOpt.BeforeCloseBraceInNewExpr);
            if (configure != null)
            {
                configure(printer);
            }
            if (exprMode)
            {
                printer.PrintExpr();
            }
            else
            {
                printer.PrintStmt();
            }
            AreEqual(result, sb.ToString());
        }
Пример #2
0
        public string Print(LNode node, IMessageSink msgs, object mode = null, string indentString = "\t", string lineSeparator = "\n")
        {
            var sb = new StringBuilder();

            EcsNodePrinter.Print(node, sb, msgs, mode, indentString, lineSeparator);
            return(sb.ToString());
        }
Пример #3
0
 public void StaticMethods()
 {
     AreEqual("@this", EcsNodePrinter.PrintId(GSymbol.Get("this"), false));
     AreEqual("normal_id", EcsNodePrinter.PrintId(GSymbol.Get("normal_id"), false));
     AreEqual("operator+", EcsNodePrinter.PrintId(S.Add, true));
     AreEqual("operator`frack!`", EcsNodePrinter.PrintId(GSymbol.Get("frack!"), true));
     AreEqual(@"@@`frack!`", EcsNodePrinter.PrintSymbolLiteral(GSymbol.Get("frack!")));
     AreEqual(@"@@this", EcsNodePrinter.PrintSymbolLiteral(GSymbol.Get("this")));
 }
Пример #4
0
        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));
        }
Пример #5
0
		void CheckIsComplexIdentifier(bool? result, LNode expr)
		{
			var np = new EcsNodePrinter(expr, null);
			_testNum++;
			var isCI = EcsValidators.IsComplexIdentifier(expr);
			if (result == null && !isCI)
				return;
			else if (result == isCI)
				return;

			Assert.Fail(string.Format(
				"IsComplexIdentifier: fail on test #{0} '{1}'. Expected {2}, got {3}",
				_testNum, expr.ToString(), result, isCI));
		}
Пример #6
0
 static LNode GetForwardingTarget(LNode fwd, LNode methodName)
 {
     if (fwd.Calls(S.Forward, 1))
     {
         LNode target = fwd.Args[0];
         if (target.Calls(S.Dot, 2) && (target.Args[1].IsIdNamed(_hash) || target.Args[1].IsIdNamed(__)))
         {
             return(target.WithArgChanged(1, target.Args[1].WithName(
                                              EcsNodePrinter.KeyNameComponentOf(methodName))));
         }
         return(target);
     }
     else
     {
         return(null);
     }
 }
Пример #7
0
        void CheckIsComplexIdentifier(bool?result, LNode expr)
        {
            var np = new EcsNodePrinter(expr, null);

            _testNum++;
            var isCI = EcsValidators.IsComplexIdentifier(expr);

            if (result == null && !isCI)
            {
                return;
            }
            else if (result == isCI)
            {
                return;
            }

            Assert.Fail(string.Format(
                            "IsComplexIdentifier: fail on test #{0} '{1}'. Expected {2}, got {3}",
                            _testNum, expr.ToString(), result, isCI));
        }
Пример #8
0
        /// <summary>Expresses an EC# token as a string.</summary>
        /// <remarks>Note that some Tokens do not contain enough information to
        /// reconstruct a useful token string, e.g. comment tokens do not store the
        /// comment but merely contain the location of the comment in the source code.
        /// For performance reasons, a <see cref="Token"/> does not have a reference
        /// to its source file, so this method cannot return the original string.
        /// <para/>
        /// The results are undefined if the token was not produced by <see cref="EcsLexer"/>.
        /// </remarks>
        public static string ToString(Token t)
        {
            StringBuilder sb = new StringBuilder();

            if (t.Kind == TokenKind.Operator || t.Kind == TokenKind.Assignment || t.Kind == TokenKind.Dot)
            {
                if (t.Type() == TT.BQString)
                {
                    return(EcsNodePrinter.PrintString((t.Value ?? "").ToString(), '`', false));
                }
                string value = (t.Value ?? "(null)").ToString();
                return(value);
            }
            switch (t.Type())
            {
            case TT.EOF: return("/*EOF*/");

            case TT.Spaces: return(" ");

            case TT.Newline: return("\n");

            case TT.SLComment: return("//\n");

            case TT.MLComment: return("/**/");

            case TT.Shebang: return("#!" + (t.Value ?? "").ToString() + "\n");

            case TT.Id:
            case TT.ContextualKeyword:
                return(EcsNodePrinter.PrintId(t.Value as Symbol ?? GSymbol.Empty));

            case TT.@base: return("base");

            case TT.@this: return("this");

            case TT.Number:
            case TT.String:
            case TT.SQString:
            case TT.Symbol:
            case TT.OtherLit:
                return(EcsNodePrinter.PrintLiteral(t.Value, t.Style));

            case TT.Comma: return(",");

            case TT.Semicolon: return(";");

            case TT.LParen: return("(");

            case TT.RParen: return(")");

            case TT.LBrack: return("[");

            case TT.RBrack: return("]");

            case TT.LBrace: return("{");

            case TT.RBrace: return("}");

            case TT.AttrKeyword:
                string value = (t.Value ?? "(null)").ToString();
                return(value);

            case TT.TypeKeyword:
                Symbol valueSym = (t.Value as Symbol) ?? GSymbol.Empty;
                string result;
                if (EcsNodePrinter.TypeKeywords.TryGetValue(valueSym, out result))
                {
                    return(result);
                }
                else
                {
                    Debug.Fail("Unexpected value for " + t.Type());
                    return((t.Value ?? "(null)").ToString());
                }

            case TT.@break:     return("break");

            case TT.@case:          return("case");

            case TT.@checked:       return("checked");

            case TT.@class:         return("class");

            case TT.@continue:      return("continue");

            case TT.@default:       return("default");

            case TT.@delegate:      return("delegate");

            case TT.@do:            return("do");

            case TT.@enum:          return("enum");

            case TT.@event:         return("event");

            case TT.@fixed:         return("fixed");

            case TT.@for:           return("for");

            case TT.@foreach:       return("foreach");

            case TT.@goto:          return("goto");

            case TT.@if:            return("if");

            case TT.@interface:     return("interface");

            case TT.@lock:          return("lock");

            case TT.@namespace:     return("namespace");

            case TT.@return:        return("return");

            case TT.@struct:        return("struct");

            case TT.@switch:        return("switch");

            case TT.@throw:         return("throw");

            case TT.@try:           return("try");

            case TT.@unchecked:     return("unchecked");

            case TT.@using:         return("using");

            case TT.@while:         return("while");

            case TT.@operator:  return("operator");

            case TT.@sizeof:    return("sizeof");

            case TT.@typeof:    return("typeof");

            case TT.@else:      return("else");

            case TT.@catch:     return("catch");

            case TT.@finally:       return("finally");

            case TT.@in:            return("in");

            case TT.@as:            return("as");

            case TT.@is:            return("is");

            case TT.@new:           return("new");

            case TT.@out:           return("out");

            case TT.@stackalloc: return("stackalloc");

            case TT.PPif: return("#if");

            case TT.PPelse: return("#else");

            case TT.PPelif: return("#elif");

            case TT.PPendif: return("#endif");

            case TT.PPdefine: return("#define");

            case TT.PPundef: return("#undef");

            case TT.PPwarning: return("#warning" + t.Value);

            case TT.PPerror: return("#error" + t.Value);

            case TT.PPnote: return("#note" + t.Value);

            case TT.PPline: return("#line");

            case TT.PPregion: return("#region" + t.Value);

            case TT.PPendregion: return("#endregion");

            case TT.PPpragma: return("#pragma");

            case TT.PPignored: return((t.Value ?? "").ToString());

            default:
                return(string.Format("@`unknown token 0x{0:X4}`", t.TypeInt));
            }
        }
Пример #9
0
        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);
        }
Пример #10
0
        private static bool DetectSetOrCreateMember(LNode arg, out Symbol relevantAttribute, out Symbol fieldName, out Symbol paramName, out LNode newArg, out LNode propOrFieldDecl)
        {
            relevantAttribute = null;
            fieldName         = null;
            paramName         = null;
            newArg            = null;
            propOrFieldDecl   = null;
            LNode _, type, name, defaultValue, propArgs;

            if (EcsValidators.IsPropertyDefinition(arg, out type, out name, out propArgs, out _, out defaultValue) && propArgs.ArgCount == 0)
            {
                // #property(Type, Name<T>, {...})
                relevantAttribute = S.Property;
                fieldName         = EcsNodePrinter.KeyNameComponentOf(name);
                paramName         = ChooseArgName(fieldName);
                if (defaultValue != null)                   // initializer is Args[4]
                {
                    newArg          = LNode.Call(S.Var, LNode.List(type, F.Assign(paramName, defaultValue)), arg);
                    propOrFieldDecl = arg.WithArgs(arg.Args.Initial(4));
                }
                else
                {
                    newArg          = LNode.Call(S.Var, LNode.List(type, F.Id(paramName)), arg);
                    propOrFieldDecl = arg;
                }
                DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl);
                return(true);
            }
            else if (IsVar(arg, out type, out paramName, out defaultValue))
            {
                int a_i = 0;
                foreach (var attr in arg.Attrs)
                {
                    if (attr.IsId)
                    {
                        var a = attr.Name;
                        if (a == _set || FieldCreationAttributes.Contains(a))
                        {
                            relevantAttribute = a;
                            fieldName         = paramName;
                            paramName         = ChooseArgName(fieldName);
                            if (a == _set)
                            {
                                newArg = F.Var(type, paramName, defaultValue).WithAttrs(arg.Attrs.Without(attr));
                            }
                            else
                            {
                                // in case of something like "[A] public params T arg = value",
                                // assume that "= value" represents a default value, not a field
                                // initializer. Most attributes stay on the argument.
                                newArg = arg.WithArgChanged(1,
                                                            defaultValue != null ? F.Assign(paramName, defaultValue) : F.Id(paramName));
                                propOrFieldDecl = LNode.Call(S.Var, LNode.List(type, F.Id(fieldName)), arg);
                                DSOCM_DistributeAttributes(arg.Attrs, ref newArg, ref propOrFieldDecl);
                            }
                            break;
                        }
                    }
                    a_i++;
                }
                return(newArg != null);
            }
            return(false);
        }
Пример #11
0
        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);
        }
Пример #12
0
        /// <summary>Expresses an EC# token as a string.</summary>
        /// <remarks>Note that some Tokens do not contain enough information to
        /// reconstruct a useful token string, e.g. comment tokens do not store the
        /// comment but merely contain the location of the comment in the source code.
        /// For performance reasons, a <see cref="Token"/> does not have a reference
        /// to its source file, so this method cannot return the original string.
        /// <para/>
        /// The results are undefined if the token was not produced by <see cref="EcsLexer"/>.
        /// </remarks>
        public static string ToString(Token t, ICharSource sourceCode)
        {
            if (sourceCode != null && t.EndIndex <= sourceCode.Count)
            {
                return(sourceCode.Slice(t.StartIndex, t.Length).ToString());
            }

            StringBuilder sb = new StringBuilder();

            if (t.Kind == TokenKind.Operator || t.Kind == TokenKind.Assignment || t.Kind == TokenKind.Dot)
            {
                if (t.Type() == TT.BQString)
                {
                    return(EcsNodePrinter.PrintString((t.Value ?? "").ToString(), '`', false));
                }
                string value = (t.Value ?? "(null)").ToString();
                return(value);
            }
            switch (t.Type())
            {
            case TT.EOF: return("/*EOF*/");

            case TT.Spaces: return(" ");

            case TT.Newline: return("\n");

            case TT.SLComment: return("//\n");

            case TT.MLComment: return("/**/");

            case TT.Shebang: return("#!" + (t.Value ?? "").ToString() + "\n");

            case TT.Id:
            case TT.ContextualKeyword:
            case TT.LinqKeyword:
                var mode = (t.Style & NodeStyle.Operator) != 0 ? EcsNodePrinter.IdPrintMode.Operator :
                           (t.Style & NodeStyle.VerbatimId) != 0 ? EcsNodePrinter.IdPrintMode.Verbatim : EcsNodePrinter.IdPrintMode.Normal;
                return(EcsNodePrinter.PrintId(t.Value as Symbol ?? GSymbol.Empty, mode));

            case TT.Base: return("base");

            case TT.This: return("this");

            case TT.Literal:
                return(EcsNodePrinter.PrintLiteral(t.Value, t.Style));

            case TT.Comma: return(",");

            case TT.Semicolon: return(";");

            case TT.LParen: return("(");

            case TT.RParen: return(")");

            case TT.LBrack: return("[");

            case TT.RBrack: return("]");

            case TT.LBrace: return("{");

            case TT.RBrace: return("}");

            case TT.AttrKeyword:
                string value = (t.Value ?? "(null)").ToString();
                return(value);

            case TT.TypeKeyword:
                Symbol valueSym = (t.Value as Symbol) ?? GSymbol.Empty;
                string result;
                if (EcsFacts.TypeKeywords.TryGetValue(valueSym, out result))
                {
                    return(result);
                }
                else
                {
                    Debug.Fail("Unexpected value for " + t.Type());
                    return((t.Value ?? "(null)").ToString());
                }

            case TT.CheckedOrUnchecked:
                Debug.Assert(LNode.IsSpecialName((Symbol)t.Value));
                return(((Symbol)t.Value).Name.Substring(1));

            case TT.Break:       return("break");

            case TT.Case:        return("case");

            case TT.Class:       return("class");

            case TT.Continue:    return("continue");

            case TT.Default:     return("default");

            case TT.Delegate:    return("delegate");

            case TT.Do:          return("do");

            case TT.Enum:        return("enum");

            case TT.Event:       return("event");

            case TT.Fixed:       return("fixed");

            case TT.For:         return("for");

            case TT.Foreach:     return("foreach");

            case TT.Goto:        return("goto");

            case TT.If:          return("if");

            case TT.Interface:   return("interface");

            case TT.Lock:        return("lock");

            case TT.Namespace:   return("namespace");

            case TT.Return:      return("return");

            case TT.Struct:      return("struct");

            case TT.Switch:      return("switch");

            case TT.Throw:       return("throw");

            case TT.Try:         return("try");

            case TT.Using:       return("using");

            case TT.While:       return("while");

            case TT.Operator:    return("operator");

            case TT.Sizeof:      return("sizeof");

            case TT.Typeof:      return("typeof");

            case TT.Else:        return("else");

            case TT.Catch:       return("catch");

            case TT.Finally:     return("finally");

            case TT.In:          return("in");

            case TT.As:          return("as");

            case TT.Is:          return("is");

            case TT.New:         return("new");

            case TT.Out:         return("out");

            case TT.Stackalloc:  return("stackalloc");

            case TT.PPif: return("#if");

            case TT.PPelse: return("#else");

            case TT.PPelif: return("#elif");

            case TT.PPendif: return("#endif");

            case TT.PPdefine: return("#define");

            case TT.PPundef: return("#undef");

            case TT.PPwarning: return("#warning" + t.Value);

            case TT.PPerror: return("#error" + t.Value);

            case TT.PPnote: return("#note" + t.Value);

            case TT.PPline: return("#line");

            case TT.PPregion: return("#region" + t.Value);

            case TT.PPendregion: return("#endregion");

            case TT.PPpragma: return("#pragma");

            case TT.PPignored: return((t.Value ?? "").ToString());

            default:
                return(string.Format("@`unknown token 0x{0:X4}`", t.TypeInt));
            }
        }
Пример #13
0
        public static LNode SetOrCreateMember(LNode fn, IMessageSink sink)
        {
            // Expecting #fn(Type, Name, #(args), {body})
            if (fn.ArgCount < 3 || !fn.Args[2].Calls(S.AltList))
            {
                return(null);
            }
            var   args = fn.Args[2].Args;
            LNode body = null;

            VList <LNode> propOrFieldDecls = VList <LNode> .Empty;
            VList <LNode> setStmts         = VList <LNode> .Empty;

            for (int i = 0; i < args.Count; i++)
            {
                var    arg             = args[i];
                Symbol a               = S.Property;
                Symbol fieldName       = null;
                Symbol paramName       = null;
                LNode  plainArg        = null;
                LNode  propOrFieldDecl = null;
                if (arg.CallsMin(S.Property, 4))
                {
                    // #property(Type, Name<T>, {...})
                    var name = arg.Args[1];
                    fieldName = EcsNodePrinter.KeyNameComponentOf(name);
                    paramName = ChooseArgName(fieldName);
                    if (arg.ArgCount == 5)                       // initializer is Args[4]
                    {
                        plainArg        = F.Var(arg.Args[0], F.Call(S.Assign, F.Id(paramName), arg.Args[4]));
                        propOrFieldDecl = arg.WithArgs(arg.Args.First(4));
                    }
                    else
                    {
                        plainArg        = F.Var(arg.Args[0], paramName);
                        propOrFieldDecl = arg;
                    }
                }
                else
                {
                    LNode type, defaultValue;
                    if (IsVar(arg, out type, out paramName, out defaultValue))
                    {
                        int a_i = 0;
                        foreach (var attr in arg.Attrs)
                        {
                            if (attr.IsId)
                            {
                                a = attr.Name;
                                if (a == _set ||
                                    a == S.Public || a == S.Internal || a == S.Protected || a == S.Private ||
                                    a == S.ProtectedIn || a == S.Static || a == S.Partial)
                                {
                                    fieldName = paramName;
                                    paramName = ChooseArgName(fieldName);
                                    if (a == _set)
                                    {
                                        plainArg = F.Var(type, paramName, defaultValue).WithAttrs(arg.Attrs.RemoveAt(a_i));
                                    }
                                    else
                                    {
                                        // in case of something like "[A] public params T arg = value",
                                        // assume that "= value" represents a default value, not a field
                                        // initializer, that [A] belongs on the field, except `params`
                                        // which stays on the argument.
                                        plainArg        = F.Var(type, paramName, defaultValue);
                                        propOrFieldDecl = arg;
                                        if (arg.Args[1].Calls(S.Assign, 2))
                                        {
                                            propOrFieldDecl = arg.WithArgChanged(1,
                                                                                 arg.Args[1].Args[0]);
                                        }
                                        int i_params = arg.Attrs.IndexWithName(S.Params);
                                        if (i_params > -1)
                                        {
                                            plainArg        = plainArg.PlusAttr(arg.Attrs[i_params]);
                                            propOrFieldDecl = propOrFieldDecl.WithAttrs(propOrFieldDecl.Attrs.RemoveAt(i_params));
                                        }
                                    }
                                    break;
                                }
                            }
                            a_i++;
                        }
                    }
                }
                if (plainArg != null)
                {
                    if (body == null)
                    {
                        if (fn.ArgCount < 4 || !fn.Args[3].Calls(S.Braces))
                        {
                            return(Reject(sink, arg, Localize.Localized("'{0}': to set or create a field or property, the method must have a body in braces {{}}.", a)));
                        }
                        body = fn.Args[3];
                    }

                    args[i] = plainArg;
                    LNode assignment;
                    if (fieldName == paramName)
                    {
                        assignment = F.Call(S.Assign, F.Dot(F.@this, F.Id(fieldName)), F.Id(paramName));
                    }
                    else
                    {
                        assignment = F.Call(S.Assign, F.Id(fieldName), F.Id(paramName));
                    }
                    setStmts.Add(assignment);
                    if (propOrFieldDecl != null)
                    {
                        propOrFieldDecls.Add(propOrFieldDecl);
                    }
                }
            }
            if (body != null)             // if this macro has been used...
            {
                var parts = fn.Args;
                parts[2] = parts[2].WithArgs(args);
                parts[3] = body.WithArgs(body.Args.InsertRange(0, setStmts));
                fn       = fn.WithArgs(parts);
                if (propOrFieldDecls.IsEmpty)
                {
                    return(fn);
                }
                else
                {
                    propOrFieldDecls.Add(fn);
                    return(F.Call(S.Splice, propOrFieldDecls));
                }
            }
            return(null);
        }
Пример #14
0
        private static bool DetectSetOrCreateMember(LNode arg, out Symbol relevantAttribute, out Symbol fieldName, out Symbol paramName, out LNode plainArg, out LNode propOrFieldDecl)
        {
            relevantAttribute = null;
            fieldName         = null;
            paramName         = null;
            plainArg          = null;
            propOrFieldDecl   = null;
            LNode _, type, name, defaultValue, propArgs;

            if (EcsValidators.IsPropertyDefinition(arg, out type, out name, out propArgs, out _, out defaultValue) && propArgs.ArgCount == 0)
            {
                // #property(Type, Name<T>, {...})
                relevantAttribute = S.Property;
                fieldName         = EcsNodePrinter.KeyNameComponentOf(name);
                paramName         = ChooseArgName(fieldName);
                if (defaultValue != null)                   // initializer is Args[4]
                {
                    plainArg        = F.Var(type, paramName, defaultValue);
                    propOrFieldDecl = arg.WithArgs(arg.Args.First(4));
                }
                else
                {
                    plainArg        = F.Var(type, paramName);
                    propOrFieldDecl = arg;
                }
                return(true);
            }
            else if (IsVar(arg, out type, out paramName, out defaultValue))
            {
                int a_i = 0;
                foreach (var attr in arg.Attrs)
                {
                    if (attr.IsId)
                    {
                        var a = attr.Name;
                        if (a == _set ||
                            a == S.Public || a == S.Internal || a == S.Protected || a == S.Private ||
                            a == S.ProtectedIn || a == S.Static || a == S.Partial)
                        {
                            relevantAttribute = a;
                            fieldName         = paramName;
                            paramName         = ChooseArgName(fieldName);
                            if (a == _set)
                            {
                                plainArg = F.Var(type, paramName, defaultValue).WithAttrs(arg.Attrs.Without(attr));
                            }
                            else
                            {
                                // in case of something like "[A] public params T arg = value",
                                // assume that "= value" represents a default value, not a field
                                // initializer, that [A] belongs on the field, except `params`
                                // which stays on the argument.
                                plainArg        = F.Var(type, paramName, defaultValue);
                                propOrFieldDecl = arg;
                                if (arg.Args[1].Calls(S.Assign, 2))
                                {
                                    propOrFieldDecl = arg.WithArgChanged(1,
                                                                         arg.Args[1].Args[0]);
                                }
                                int i_params = arg.Attrs.IndexWithName(S.Params);
                                if (i_params > -1)
                                {
                                    plainArg        = plainArg.PlusAttr(arg.Attrs[i_params]);
                                    propOrFieldDecl = propOrFieldDecl.WithAttrs(propOrFieldDecl.Attrs.RemoveAt(i_params));
                                }
                            }
                            break;
                        }
                    }
                    a_i++;
                }
                return(plainArg != null);
            }
            return(false);
        }