private static Dictionary <Symbol, LNode> ScanForVariables(LNode code) { var nameTable = new Dictionary <Symbol, LNode>(); code.ReplaceRecursive(n => { if (n.IsId) { nameTable[n.Name] = n; } else { LNode id = LNodeExt.GetCaptureIdentifier(n); if (id != null) { if (!nameTable.ContainsKey(n.Name)) { nameTable[id.Name] = n; } return(n); } } return(null); }); return(nameTable); }
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."); } }
public static LNode staticMatches(LNode node, IMacroContext context) { if (node.ArgCount != 2) { return(null); } LNode candidate = context.PreProcess(UnwrapBraces(node[0])); LNode pattern = UnwrapBraces(node[1]); MMap <Symbol, LNode> captures = new MMap <Symbol, LNode>(); if (LNodeExt.MatchesPattern(candidate, pattern, ref captures, out LNodeList _)) { SetSyntaxVariables(captures, context); return(F.True); } return(F.False); }
public static LNode static_matchCode(LNode node, IMacroContext context) { if (node.AttrNamed(S.Static) == null && !node.HasSpecialName) { return(null); // handled by normal matchCode macro } var args_body = context.GetArgsAndBody(false); VList <LNode> args = args_body.Item1, body = args_body.Item2; if (args.Count != 1) { return(Reject(context, args[1], "Expected only one expression to match")); } var expression = context.PreProcess(AutoStripBraces(args[0])); var cases = GetCases(body, context.Sink); // The `default:` case is represented by an empty list of patterns. if (cases.WithoutLast(1).Any(pair => pair.Key.IsEmpty)) { context.Write(Severity.Error, node, "The `default:` case must be the last one, because the cases are tested in the order they appear, so no case after `default:` can be matched."); } MMap <Symbol, LNode> captures = new MMap <Symbol, LNode>(); foreach (Pair <VList <LNode>, VList <LNode> > pair in cases) { var patterns = pair.Key.IsEmpty ? new VList <LNode>((LNode)null) : pair.Key; foreach (var pattern in patterns) { captures.Clear(); VList <LNode> _; if (pattern == null || LNodeExt.MatchesPattern(expression, pattern, ref captures, out _)) { captures[_hash] = expression; // define $# captures.Remove(__); return(ReplaceCaptures(pair.Value.AsLNode(S.Splice), captures)); } } } return(F.Call(S.Splice)); // none of the cases matched }
static LNode TryReplaceHere(LNode node, LNode pattern, LNode replacement, MMap <Symbol, LNode> captures, Pair <LNode, LNode>[] allPatterns) { if (LNodeExt.MatchesPattern(node, pattern, ref captures, out LNodeList attrs)) { foreach (var pair in captures) { var input = pair.Value.AsList(S.Splice); int c; var output = Replace(input, allPatterns, out c); if (output != input) { captures[pair.Key] = output.AsLNode(S.Splice); } } return(ReplaceCaptures(replacement, captures).PlusAttrs(attrs)); } return(null); }
/// <summary>Finds capture variables like <c>$x</c> and replaces them with values /// from <c>captures</c> (e.g. <c>captures[(Symbol)"x"]</c> for <c>$x</c>)</summary> public static LNode ReplaceCaptures(LNode outputSpec, MMap <Symbol, LNode> captures) { if (captures.Count != 0) { // TODO: EXPAND SPLICES! Generally it works anyway though because // the macro processor has built-in support for #splice. return(outputSpec.ReplaceRecursive(n => { LNode id, cap; if ((id = LNodeExt.GetCaptureIdentifier(n)) != null) { if (captures.TryGetValue(id.Name, out cap)) { return cap; } } return null; })); } return(outputSpec); }
public static LNode replace(LNode node, IMacroContext context) { var args_body = context.GetArgsAndBody(true); var args = args_body.A; var body = args_body.B; if (args.Count == 1 && args[0].Calls(S.Tuple)) { args = args[0].Args; // LESv2 } if (args.Count >= 1) { bool preprocess = node.Calls("replacePP"); var patterns = new Pair <LNode, LNode> [args.Count]; for (int i = 0; i < patterns.Length; i++) { var pair = args[i]; if (pair.Calls(S.Lambda, 2)) { LNode pattern = pair[0], repl = pair[1]; if (preprocess) { pattern = context.PreProcess(pattern); repl = context.PreProcess(repl); } if (pattern.Calls(S.Braces)) { if (pattern.ArgCount == 1) { pattern = pattern.Args[0]; } else { context.Write(Severity.Error, pattern, "The braces must contain only a single statement. To search for braces literally, use `{{ ... }}`"); } } if (repl.Calls(S.Braces)) { repl = repl.Args.AsLNode(S.Splice); } // Avoid StackOverflowException when pattern is $Id (sadly, it // is uncatchable so it can crash LeMP.exe and even Visual Studio) if (LNodeExt.GetCaptureIdentifier(pattern) != null) { return(Reject(context, pattern, "The left side of `=>` cannot be a capture. Remove the `$`.")); } patterns[i] = Pair.Create(pattern, repl); } else { string msg = "Expected 'pattern => replacement'."; if (pair.Descendants().Any(n => n.Calls(S.Lambda, 2))) { msg += " " + "(Using '=>' already? Put the pattern on the left-hand side in parentheses.)"; } return(Reject(context, pair, msg)); } } int replacementCount; var output = Replace(body, patterns, out replacementCount); if (replacementCount == 0) { context.Sink.Warning(node, "No patterns recognized; no replacements were made."); } return(output.AsLNode(S.Splice)); } return(null); }