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."); } }
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()); }
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)); }