private static ParseTreeNode MoveTo(ParseTreeNode pt, Context old, Context _new) { if (pt.Is(GrammarNames.Reference)) { return(_new.QualifyMinimal(old.Qualify(pt))); } return(pt.ChildNodes.Count == 0 ? pt : CustomParseTreeNode.From(pt).SetChildNodes(pt.ChildNodes.Select(x => MoveTo(x, old, _new)))); }
/// <summary> /// Add a workbook and sheet reference to a prefix if they aren't provided yet /// </summary> /// <remarks>Qualify because (book,sheet,name) is a fully qualified name</remarks> public ParseTreeNode Qualify(ParseTreeNode reference) { // Check if this reference can be qualified if (!isPrefixableReference(reference)) { return(reference); } var referenced = reference.ChildNodes.First(node => !node.Is(GrammarNames.Prefix)); bool hasPrefix = reference.ChildNodes.Any(node => node.Is(GrammarNames.Prefix)); var prefix = reference.FirstOrNewChild(CustomParseTreeNode.NonTerminal(GrammarNames.Prefix)); PrefixInfo prefixinfo = null; if (hasPrefix) { prefixinfo = prefix.GetPrefixInfo(); } var file = prefix.FirstOrNewChild(CustomParseTreeNode.NonTerminal(GrammarNames.File, GrammarNames.TokenEnclosedInBrackets, $"[{DefinedIn.FileName}]")); var sheet = prefix.FirstOrNewChild(CustomParseTreeNode.Terminal(GrammarNames.TokenSheet, DefinedIn.Worksheet)); // Named ranges can be both workbook-level and sheet-level and need additional logic if (referenced.ChildNodes.First().Is(GrammarNames.NamedRange)) { var name = referenced.ChildNodes.First().ChildNodes.First().Token.ValueString; // If a sheet was already provided, either the file was provided or will correctly be filled by definition above if (!(prefixinfo != null && prefixinfo.HasSheet)) { bool isDefinedOnSheetLevel = NamedRanges.Contains(new NamedRangeDef(DefinedIn, name)); // If a file was provided but no sheet, it's a workbook-level definition // If a sheet-level name is not defined, either a workbook-level name is defined, // or it's not defined and we assume the Excel default of workbook-level if ((prefixinfo != null && prefixinfo.HasFile) || !isDefinedOnSheetLevel) { sheet = CustomParseTreeNode.Terminal(GrammarNames.TokenSheet, ""); } } } prefix = prefix.SetChildNodes(file, sheet); return(CustomParseTreeNode.From(reference).SetChildNodes(prefix, referenced)); }
private static ParseTreeNode Replace(ParseTreeNode subject, ParseTreeNode search, ParseTreeNode replace, Context csub, Context csearch, Context crepl) { // Match, return the replacement if (Equals(subject, search, csub, csearch)) { return(MoveTo(replace, crepl, csub)); } // No match and no children to replace if (subject.ChildNodes.Count == 0) { return(subject); } var newChilds = subject.ChildNodes.Select(pt => Replace(pt, search, replace, csub, csearch, crepl)); var replacement = CustomParseTreeNode.From(subject); replacement.SetChildNodes(newChilds); return(replacement); }
/// <summary> /// Remove superfluous qualification from a node /// </summary> public ParseTreeNode QualifyMinimal(ParseTreeNode reference) { // Check if this reference can be qualified if (!isPrefixableReference(reference)) { return(reference); } var referenced = reference.ChildNodes.First(node => !node.Is(GrammarNames.Prefix)); var prefix = reference.FirstChild(GrammarNames.Prefix); // No prefix, it's already minimal if (prefix == null) { return(reference); } var prefixinfo = prefix.GetPrefixInfo(); var childs = new ParseTreeNodeList(); if (prefixinfo.HasFileName && prefixinfo.FileName != DefinedIn.FileName) { childs.Add(CustomParseTreeNode.NonTerminal(GrammarNames.File, GrammarNames.TokenEnclosedInBrackets, $"[{prefixinfo.FileName}]")); childs.Add(CustomParseTreeNode.Terminal(GrammarNames.TokenSheet, prefixinfo.Sheet)); } else if (prefixinfo.HasSheet && prefixinfo.Sheet != DefinedIn.WorksheetClean) { childs.Add(CustomParseTreeNode.Terminal(GrammarNames.TokenSheet, DefinedIn.Worksheet)); } if (childs.Count > 0) { prefix = CustomParseTreeNode.From(prefix).SetChildNodes(childs); return(CustomParseTreeNode.From(reference).SetChildNodes(prefix, referenced)); } else { return(CustomParseTreeNode.From(reference).SetChildNodes(referenced)); } }