/// <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)); }