コード例 #1
0
        private static LNode RegisterSimpleMacro(LNodeList attrs, LNode pattern, LNode body, IMacroContext context)
        {
            if (DecodeSubstitutionExpr(pattern, out _, out _, out _) != null)
            {
                return(Reject(context, pattern, "Defining a macro that could match everything is not allowed."));
            }

            MacroMode modes = GetMacroMode(ref attrs, pattern);

            LNode macroName   = pattern.Target ?? pattern;
            LNode replacement = body.AsList(S.Braces).AsLNode(S.Splice);

            if (pattern.IsCall)
            {
                WarnAboutMissingDollarSigns(pattern.Args, context, pattern, replacement);
            }

            // Note: we could fill out the macro's Syntax and Description with the
            // pattern and replacement converted to strings, but it's generally a
            // waste of CPU time as those strings are usually not requested.
            // Compromise: provide syntax pattern only
            var syntax = pattern.ToString();
            var lma    = new LexicalMacroAttribute(syntax, "User-defined macro at {0}".Localized(pattern.Range.Start), macroName.Name.Name)
            {
                Mode = modes
            };

            if ((modes & (MacroMode.MatchEveryLiteral | MacroMode.MatchEveryCall | MacroMode.MatchEveryIdentifier)) != 0)
            {
                lma = new LexicalMacroAttribute(syntax, lma.Description)
                {
                    Mode = modes
                }
            }
            ;

            var macroInfo = new MacroInfo(null, lma, UserDefinedMacro);

            macroInfo.Mode |= MacroMode.UseLogicalNameInErrorMessages;

            context.RegisterMacro(macroInfo);

            return(F.Splice());            // delete the `define` node from the output

            LNode UserDefinedMacro(LNode candidate, IMacroContext context2)
            {
                MMap <Symbol, LNode> captures = new MMap <Symbol, LNode>();

                if (candidate.MatchesPattern(pattern, ref captures, out LNodeList unmatchedAttrs))
                {
                    LNode replacement2 = WithUniqueIdentifiers(replacement, context.IncrementTempCounter, out _);
                    return(ReplaceCaptures(replacement2, captures).PlusAttrsBefore(unmatchedAttrs));
                }
                return(null);
            }
        }
コード例 #2
0
            LNode ESEInForLoop(LNode stmt, VList <LNode> attrs, VList <LNode> init, LNode cond, VList <LNode> inc, LNode block)
            {
                // TODO: handle multi-int and multi-inc
                var preInit   = VList <LNode> .Empty;
                var init_apos = init.SmartSelect(init1 => {
                    init1 = EliminateSequenceExpressionsInExecStmt(init1);
                    if (init1.CallsMin(__numrunSequence, 1))
                    {
                        preInit.AddRange(init1.Args.WithoutLast(1));
                        return(init1.Args.Last);
                    }
                    return(init1);
                });
                var cond_apos = BubbleUpBlocks(cond);
                var inc_apos  = inc.SmartSelectMany(inc1 => {
                    inc1 = BubbleUpBlocks(inc1);
                    return(inc1.AsList(__numrunSequence));
                });

                block = EliminateSequenceExpressionsInChildStmt(block);
                if (init_apos != init || cond_apos != cond || inc_apos != inc)
                {
                    init = init_apos;
                    if (inc_apos != inc)
                    {
                        var blockStmts = block.AsList(S.Braces).AddRange(inc_apos);
                        block = blockStmts.AsLNode(S.Braces);
                        inc   = LNode.List();
                    }
                    if (cond_apos.CallsMin(__numrunSequence, 1))
                    {
                        var preCond = cond_apos.Args.WithoutLast(1);
                        cond = cond_apos.Args.Last;
                        stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement)));
                    }
                    else
                    {
                        stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block));
                    }
                    if (preInit.Count != 0)
                    {
                        stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement);
                    }
                    return(stmt);
                }
                else
                {
                    return(stmt.WithArgChanged(3, block));
                }
            }
コード例 #3
0
ファイル: BuiltinMacros.cs プロジェクト: dadhi/ecsharp
 private static LNode ChangePath(LNode call, LNode path, IMacroContext context, Func <LNode, IMacroContext, LNode> change)
 {
     foreach (var index in path.AsList(CodeSymbols.Tuple))
     {
         // Check if index is an integer, a negated integer, or an
         // identifier matching the name of an argument of `call`...
         if (index.Value is int iArg ||
             index.Calls(S.Sub, 1) && index[0].Value is int negArg && ((iArg = -negArg) & 0) == 0 ||
             index.IsId &&
             (iArg = EnumerableExt.FirstIndexWhere <LNode>(call, n => n.Name == index.Name) ?? -1) != -1 &&
             ((iArg += call.Min) & 0) == 0)
         {
             LNode arg = call.TryGet(iArg, null);
             if (arg == null)
             {
                 context.Warning(path, "Index is out of range ({1})", iArg, call.Min <= call.Max
                                                 ? "expected {0} to {1}".Localized(call.Min, call.Max)
                                                 : "'{0}' has no children".Localized(LNode.Printer.Print(call, null, ParsingMode.Expressions)));
             }
             else
             {
                 call = call.WithChildChanged(iArg, change(arg, context));
             }
         }
コード例 #4
0
        /// <summary>Converts an expression to a list. Similar to calling
        /// <c>AsList(block, CodeSymbols.Splice)</c>, if the expression calls #splice
        /// then the arguments of the splice are returned, and if not then the
        /// argument is converted to a list with one item. However, if the call to
        /// #splice has attached trivia/attributes, those attributes are attached to
        /// the output list using <see cref="IncludingAttributes(LNodeList, LNodeList)"/>.
        /// </summary>
        /// <param name="node">A node that may or may not be a call to #splice</param>
        /// <returns>A list of nodes that <c>block</c> is equivalent to.</returns>
        /// <remarks>
        /// Attributes attached to #splice are ordinarily attached to the first item
        /// in the output list, but any %trailing attribute is attached to the last
        /// item instead. If the #splice() call has no arguments, then (i) if it has
        /// no trivia attributes, an empty list is returned, but (ii) if it has
        /// trivia attributes, the attributes themselves are returned as the content
        /// of the list. This assumes that printers can print
        /// </remarks>
        public static LNodeList Unsplice(this LNode node)
        {
            var list = node.AsList(S.Splice);

            return(list.IncludingAttributes(node.Attrs));
        }
コード例 #5
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));
        }
コード例 #6
0
			LNode ESEInForLoop(LNode stmt, VList<LNode> attrs, VList<LNode> init, LNode cond, VList<LNode> inc, LNode block)
			{
				// TODO: handle multi-int and multi-inc
				var preInit = VList<LNode>.Empty;
				var init_apos = init.SmartSelect(init1 => {
					init1 = EliminateSequenceExpressionsInExecStmt(init1);
					if (init1.CallsMin(__numrunSequence, 1)) {
						preInit.AddRange(init1.Args.WithoutLast(1));
						return init1.Args.Last;
					}
					return init1;
				});
				var cond_apos = BubbleUpBlocks(cond);
				var inc_apos = inc.SmartSelectMany(inc1 => {
					inc1 = BubbleUpBlocks(inc1);
					return inc1.AsList(__numrunSequence);
				});
			
				block = EliminateSequenceExpressionsInChildStmt(block);
				if (init_apos != init || cond_apos != cond || inc_apos != inc) {
					init = init_apos;
					if (inc_apos != inc) {
						var blockStmts = block.AsList(S.Braces).AddRange(inc_apos);
						block = blockStmts.AsLNode(S.Braces);
						inc = LNode.List();
					}
					if (cond_apos.CallsMin(__numrunSequence, 1)) {
						var preCond = cond_apos.Args.WithoutLast(1);
						cond = cond_apos.Args.Last;
						stmt = LNode.Call(CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), LNode.Missing, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preCond).Add(LNode.Call(CodeSymbols.If, LNode.List(cond, block, LNode.Call(CodeSymbols.Break))))).SetStyle(NodeStyle.Statement)));
					} else {
						stmt = LNode.Call(LNode.List(attrs), CodeSymbols.For, LNode.List(LNode.Call(CodeSymbols.AltList, LNode.List(init)), cond, LNode.Call(CodeSymbols.AltList, LNode.List(inc)), block));
					}
					if (preInit.Count != 0) {
						stmt = LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(preInit).Add(stmt)).SetStyle(NodeStyle.Statement);
					}
					return stmt;
				} else {
					return stmt.WithArgChanged(3, block);
				}
			}