Example #1
0
 // Looks for contract attributes in a list and creates statements that
 // should be inserted at the beginning of the method that those attributes
 // are a part of. `variableName` is the name of the associated method
 // argument, or null if the attributes are attached to the return value or
 // the entire method. Returns the attribute list with contract attributes
 // removed.
 internal LNodeList Process(LNodeList attributes, LNode variableName, bool isPropSetter = false)
 {
     return(attributes.SmartWhere(attr => {
         LNode exceptionType = null;
         var mode = GetContractAttrMode(attr, out exceptionType);
         if (mode != null)
         {
             ProcessAttribute(attr, mode, exceptionType, variableName, isPropSetter);
             return false;                   // Remove contract attribute from method signature
         }
         return true;                        // No change
     }));
 }
Example #2
0
 LNode MaybeQuoteList(LNodeList 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")), Quote(arg));
             }
         }
         return(argList);
     }
     else
     {
         return(F.Call(LNode_List, list.SmartSelect(item => Quote(item))));
     }
 }
Example #3
0
        public static LNode ContractsOnProperty(LNode prop, IMacroContext context)
        {
            // Performance note: one should keep in mind that this macro usually
            // has no effect. It looks for contracts and usually finds none.
            LNode oldProp = prop;

            if (prop.ArgCount == 4)
            {
                LNode braces    = prop[3];
                var   oldBraces = braces;
                var   rw        = new CodeContractRewriter(prop.Args[0], prop.Args[1], context);

                // If this has an argument list (this[...]), process its contract attributes
                prop = ProcessArgContractAttributes(prop, 2, rw);

                // Remove contract attributes from the property and store in a list
                LNodeList cAttrs = LNode.List();
                prop = prop.WithArgChanged(0, GrabContractAttrs(prop.Args[0], ref cAttrs, ContractAppliesTo.Getter));
                prop = GrabContractAttrs(prop, ref cAttrs);

                // Find the getter and setter
                LNode     getter = null, setter = null;
                int       getterIndex = -1, setterIndex = -1;
                LNodeList getterAttrs = LNode.List(), setterAttrs = LNode.List();
                bool      isLambdaProperty = !braces.Calls(S.Braces);
                if (isLambdaProperty)
                {
                    if (cAttrs.Count == 0)
                    {
                        return(null);                           // lambda property has no contract attributes
                    }
                    // Transform into a normal property
                    getterAttrs = cAttrs;
                    getter      = LNode.Call(CodeSymbols.get, LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(braces)))).SetStyle(NodeStyle.StatementBlock))).SetStyle(NodeStyle.Special);
                    braces      = LNode.Call(CodeSymbols.Braces, LNode.List(getter)).SetStyle(NodeStyle.StatementBlock);
                    getterIndex = 0;
                }
                else
                {
                    for (int i = 0; i < braces.Args.Count; i++)
                    {
                        var part = braces.Args[i];
                        if (part.Calls(S.get))
                        {
                            getter = part; getterIndex = i;
                        }
                        if (part.Calls(S.set))
                        {
                            setter = part; setterIndex = i;
                        }
                    }

                    // Now create separate lists of contract attributes for the getter and the setter
                    if (cAttrs.Count != 0)
                    {
                        getterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Getter) != 0);
                        setterAttrs = cAttrs.SmartWhere(a => (PropertyContractInterpretation(a) & ContractAppliesTo.Setter) != 0);
                    }
                }

                // Process the discovered attributes to produce prepended statements
                var sharedPrependStmts = rw.PrependStmts;
                if (getter != null)
                {
                    getter = GrabContractAttrs(getter, ref getterAttrs);
                    rw.Process(getterAttrs, null);
                    rw.PrependStmtsToGetterOrSetter(ref braces, getterIndex, getter);
                }
                if (setter != null)
                {
                    rw.PrependStmts = sharedPrependStmts;
                    setter          = GrabContractAttrs(setter, ref setterAttrs);
                    rw.Process(setterAttrs, LNode.Id(CodeSymbols.value), true);
                    rw.PrependStmtsToGetterOrSetter(ref braces, setterIndex, setter);
                }

                // Update the property
                if (braces == oldBraces)
                {
                    return(null);                       // this is the common case
                }
                else
                {
                    return(prop.WithArgChanged(3, braces));
                }
            }
            return(null);
        }
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));
        }