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.UnwrapBraces());
            input    = input.UnwrapBraces();
            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
        private static void ApplyRuleOptions(ref LNode node, Rule rule, IMacroContext context)
        {
            node = node.WithAttrs(node.Attrs.WhereSelect(attr => {
                if (attr.ArgCount > 1)
                {
                    return(attr);
                }
                LNode key    = attr.Target ?? attr;
                object value = null;
                if (attr.ArgCount == 1)
                {
                    value = attr.Args[0].Value;
                }
                switch (key.Name.Name)
                {
                case "fullLLk":
                case "FullLLk":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.FullLLk = v);
                    break;

                case "#private":
                case "private":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.IsPrivate = v);
                    return(attr);                            // keep attribute

                case "#public":
                case "#internal":
                case "#protected":
                case "public":
                case "internal":
                case "protected":                            // this is before macros run, and non-special names are used in LES
                    rule.IsPrivate = false;
                    return(attr);                            // keep attribute

                case "token":
                case "Token":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.IsToken = v);
                    break;

                case "start":
                case "Start":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.IsStartingRule = v);
                    break;

                case "#extern":
                case "extern":
                case "Extern":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.IsExternal = v);
                    break;

                case "#inline":
                case "inline":
                case "Inline":
                case "#fragment":
                case "fragment":
                    SetOption <bool>(context, key, value ?? G.BoxedTrue, v => rule.IsInline = v);
                    break;

                case "k":
                case "K":
                case "LL":
                    SetOption <int>(context, key, value, k => rule.K = k);
                    break;

                case "recognizer":
                case "Recognizer":
                    LNode sig = attr.Args[0, null];
                    if (sig != null)
                    {
                        // Invoke macros here so that LES code like "public fn Foo()::bool"
                        // is transformed into a method signature.
                        sig = context.PreProcess(sig.UnwrapBraces());
                    }
                    if (sig != null && sig.CallsMin(S.Fn, 3))
                    {
                        sig = sig.WithoutAttrNamed(S.TriviaAppendStatement);                                 // prevent weird-looking output
                        rule.MakeRecognizerVersion(sig).TryWrapperNeeded();
                    }
                    else
                    {
                        context.Sink.Error(sig, "'recognizer' expects one parameter, a method signature.");
                    }
                    break;

                default:
                    return(attr);
                }
                return(NoValue.Value);
            }).ToArray());
        }
Esempio n. 3
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));
        }