Esempio n. 1
0
        private static void DoDeconstruct(LNode arg, IMacroContext context, bool printErrorOnFailure)
        {
            LNode patternSpec = arg.Args[0, LNode.Missing], input = arg.Args[1, LNode.Missing];

            if (!arg.Calls(S.Assign, 2))
            {
                if (arg.Calls(S.Lambda, 2))
                {
                    G.Swap(ref patternSpec, ref input);
                }
                else
                {
                    context.Sink.Error(arg, "expected an assignment (`patterns = input`)");
                    return;
                }
            }

            // Build list of patterns out of the binary operator series p1 | p2 | p3
            var patterns = new FVList <LNode>();

            while (patternSpec.Calls(S.OrBits, 2) && !patternSpec.IsParenthesizedExpr())
            {
                patterns.Add(patternSpec[1]);
                patternSpec = patternSpec[0];
            }
            patterns.Add(patternSpec);

            // Remove outer braces, then run macros
            patterns = patterns.SmartSelect(p => p.Calls(S.Braces, 1) ? p[0] : p);
            input    = input.Calls(S.Braces, 1) ? input[0] : input;
            input    = context.PreProcess(input);

            // Perform matching & capturing
            foreach (var pattern in patterns)
            {
                IDictionary <Symbol, LNode> captures;
                if (LNodeExt.MatchesPattern(input, pattern, out captures))
                {
                    if (captures.Count == 0)
                    {
                        context.Write(printErrorOnFailure ? Severity.Warning : Severity.Error, pattern,
                                      "This pattern has no effect, since it does not use `$` to capture any variables.");
                    }
                    SetSyntaxVariables(captures, context);
                    return;
                }
            }

            if (printErrorOnFailure)
            {
                context.Sink.Error(arg, "Deconstruction failed.");
            }
        }
Esempio n. 2
0
        public static bool IsComplexIdentifier(LNode n, ICI f = ICI.Default, Pedantics p = Pedantics.Lax)
        {
            // Returns true if 'n' is printable as a complex identifier.
            //
            // To be printable, a complex identifier in EC# must not contain
            // attributes ((p & Pedantics.DropNonDeclAttrs) != 0 to override) and must be
            // 1. A simple symbol
            // 2. A substitution expression
            // 3. A dotted expr (a.b), where 'a' is a complex identifier and 'b'
            //    is (1) or (2); structures like #.(a, b, c) and #.(a, b<c>) do
            //    not count as complex identifiers. Note that a.b<c> is
            //    structured #of(#.(a, b), c), not #.(a, #of(b, c)). A dotted
            //    expression that starts with a dot, such as .a.b, is structured
            //    (.a).b rather than .(a.b); unary . has high precedence.
            // 4. An #of expr a<b,...>, where
            //    - 'a' is a complex identifier and not itself an #of expr
            //    - each arg 'b' is a complex identifier (if printing in C# style)
            //
            // Type names have the same structure, with the following patterns for
            // arrays, pointers, nullables and typeof<>:
            //
            // Foo*      <=> #of(@*, Foo)
            // Foo[]     <=> #of(@`[]`, Foo)
            // Foo[,]    <=> #of(#`[,]`, Foo)
            // Foo?      <=> #of(@?, Foo)
            // typeof<X> <=> #of(#typeof, X)
            //
            // Note that we can't just use #of(Nullable, Foo) for Foo? because it
            // doesn't work if System is not imported. It's reasonable to allow #?
            // as a synonym for global::System.Nullable, since we have special
            // symbols for types like #int32 anyway.
            //
            // (a.b<c>.d<e>.f is structured ((((a.b)<c>).d)<e>).f or #.(#of(#.(#of(#.(a,b), c), d), e), f)
            if ((f & ICI.AllowAttrs) == 0 && ((f & ICI.AllowParensAround) != 0 ? HasPAttrs(n, p) : HasPAttrsOrParens(n, p)))
            {
                // Attribute(s) are illegal, except 'in', 'out' and 'where' when
                // TypeParamDefinition inside <...>
                return((f & (ICI.NameDefinition | ICI.InOf)) == (ICI.NameDefinition | ICI.InOf) && IsPrintableTypeParam(n));
            }

            if (n.IsId)
            {
                return(true);
            }
            if (CallsWPAIH(n, S.Substitute, 1, p))
            {
                return(true);
            }

            if (CallsMinWPAIH(n, S.Of, 1, p) && (f & ICI.DisallowOf) == 0)
            {
                var baseName = n.Args[0];
                if (!IsComplexIdentifier(baseName, (f & (ICI.DisallowDotted)) | ICI.DisallowOf, p))
                {
                    return(false);
                }
                if ((f & ICI.AllowAnyExprInOf) != 0)
                {
                    return(true);
                }
                return(OfHasNormalArgs(n, (f & ICI.NameDefinition) != 0, p));
            }
            if (CallsWPAIH(n, S.Dot, p) && (f & ICI.DisallowDotted) == 0 && Range.IsInRange(n.ArgCount, 1, 2))
            {
                var   args = n.Args;
                LNode lhs = args[0], rhs = args.Last;
                // right-hand argument must be simple
                var rhsFlags = (f & ICI.ExprMode) | ICI.DisallowOf | ICI.DisallowDotted;
                if ((f & ICI.ExprMode) != 0)
                {
                    rhsFlags |= ICI.AllowParensAround;
                }
                if (!IsComplexIdentifier(args.Last, rhsFlags, p))
                {
                    return(false);
                }
                if ((f & ICI.ExprMode) != 0 && lhs.IsParenthesizedExpr() || (lhs.IsCall && !lhs.Calls(S.Dot) && !lhs.Calls(S.Of)))
                {
                    return(true);
                }
                return(IsComplexIdentifier(args[0], (f & ICI.ExprMode), p));
            }
            return(false);
        }
Esempio n. 3
0
        public bool AutoPrintComplexIdentOperator(Precedence precedence, Precedence context, Ambiguity flags)
        {
            // Handles #of and @`.`, including array types
            int    argCount = _n.ArgCount;
            Symbol name     = _n.Name;

            Debug.Assert((name == S.Of || name == S.Dot) && _n.IsCall);

            var first = _n.Args[0, null];

            if (first == null)
            {
                return(false);                // no args
            }
            bool needParens, needSpecialOfNotation = false;

            if (!CanAppearIn(precedence, context, out needParens) || needParens)
            {
                return(false);                // this only happens inside $ operator, e.g. $(a.b)
            }
            if (name == S.Dot)
            {
                // The trouble with the dot is its high precedence; because of
                // this, arguments after a dot cannot use prefix notation as a
                // fallback. For example "@.(a, b(c))" cannot be printed "a.b(c)"
                // since that means @.(a, b)(c)". The first argument to non-
                // unary "." can use prefix notation safely though, e.g.
                // "@.(b(c), a)" can (and must) be printed "b(c).a".
                if (argCount > 2)
                {
                    return(false);
                }
                if (HasPAttrs(first))
                {
                    return(false);
                }
                LNode afterDot = _n.Args.Last;
                // Unary dot is no problem: .(a) is parsed the same as .a, i.e.
                // the parenthesis are ignored, so we can print an expr like
                // @`.`(a+b) as .(a+b), but the parser counts parens on binary
                // dot, so in that case the argument after the dot must not be any
                // kind of call (except substitution) and must not have attributes,
                // unless it is in parens.
                if (argCount == 2 && !afterDot.IsParenthesizedExpr())
                {
                    if (HasPAttrs(afterDot))
                    {
                        return(false);
                    }
                    if (afterDot.IsCall && afterDot.Name != S.Substitute)
                    {
                        return(false);
                    }
                }
                else if ((flags & Ambiguity.CastRhs) != 0)
                {
                    return(false);                    // cannot print (Foo) @`.`(x) as (Foo) .x
                }
            }
            else if (name == S.Of)
            {
                var ici = ICI.Default | ICI.AllowAttrs;
                if ((flags & Ambiguity.InDefinitionName) != 0)
                {
                    ici |= ICI.NameDefinition;
                }
                if (!IsComplexIdentifier(_n, ici))
                {
                    if (IsComplexIdentifier(_n, ici | ICI.AllowAnyExprInOf))
                    {
                        needSpecialOfNotation = true;
                    }
                    else
                    {
                        return(false);
                    }
                }
            }

            if (name == S.Dot)
            {
                if (argCount == 1)
                {
                    _out.Write('.', true);
                    PrintExpr(first, EP.Substitute);
                }
                else
                {
                    PrintExpr(first, precedence.LeftContext(context), flags & Ambiguity.TypeContext);
                    _out.Write('.', true);
                    PrintExpr(_n.Args[1], precedence.RightContext(context));
                }
            }
            else if (_n.Name == S.Of)
            {
                // Check for special type names such as Foo? or Foo[]
                Symbol stk = SpecialTypeKind(_n, flags, context);
                if (stk != null)
                {
                    if (S.IsArrayKeyword(stk))
                    {
                        // We do something very strange in case of arrays of arrays:
                        // the order of the square brackets must be reversed when
                        // arrays are nested. For example, an array of two-dimensional
                        // arrays of int is written int[][,], rather than int[,][]
                        // which would be much easier to handle.
                        var stack     = InternalList <Symbol> .Empty;
                        var innerType = _n;
                        do
                        {
                            stack.Add(stk);
                            innerType = innerType.Args[1];
                        } while (S.IsArrayKeyword(stk = SpecialTypeKind(innerType, flags, context) ?? GSymbol.Empty));

                        PrintType(innerType, EP.Primary.LeftContext(context), (flags & Ambiguity.AllowPointer));

                        for (int i = 0; i < stack.Count; i++)
                        {
                            _out.Write(stack[i].Name, true);                             // e.g. [] or [,]
                        }
                    }
                    else
                    {
                        PrintType(_n.Args[1], EP.Primary.LeftContext(context), (flags & Ambiguity.AllowPointer));
                        _out.Write(stk == S._Pointer ? '*' : '?', true);
                    }
                    return(true);
                }

                PrintExpr(first, precedence.LeftContext(context));

                _out.Write(needSpecialOfNotation ? "!(" : "<", true);
                for (int i = 1; i < argCount; i++)
                {
                    if (i > 1)
                    {
                        WriteThenSpace(',', SpaceOpt.AfterCommaInOf);
                    }
                    PrintType(_n.Args[i], StartExpr, Ambiguity.InOf | Ambiguity.AllowPointer | (flags & Ambiguity.InDefinitionName));
                }
                _out.Write(needSpecialOfNotation ? ')' : '>', true);
            }
            else
            {
                Debug.Assert(_n.Name == S.Substitute);
                G.Verify(AutoPrintOperator(ContinueExpr, 0));
            }
            return(true);
        }