6

For a given Excel formula in a cell, I'd like to be able to parse the formula in order to get a list of Excel Range references contained within the formula.

For example, if I have a cell with this formula:

= A + 25 + B  

....I would like to be able to get an array of excel ranges contained within the formula, so in this case, it would contain [A] and [B]

"Why do you even want to do this"?, I can hear you asking:
Just one example of why I want to do this is to look up "labels" for ranges in formulas.....so, as opposed to just doing a CTRL+~ to view the formulas in my sheet, I'd like the option of programatically accessing the range references within the formula in order to do a lookup of the label beside the target range.

So, in my above example, I could then write formulas something like:

=Offset(CellFormulaRanges('TheAddressMyFormulaIsIn',1),0,-1)
=Offset(CellFormulaRanges('TheAddressMyFormulaIsIn',2),0,-1)

...which would give me the the label to the left of the 1st and 2nd ranges within the formula.

Doing this would have to call upon some functionality already within Excel itself, as hand writing a formula parser is a complicated task:
http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html

Community
  • 1
  • 1
tbone
  • 5,715
  • 20
  • 87
  • 134
  • VBA has access to a formula's `Precedents`, but only those on the same sheet as the formula. There's this: http://www.ozgrid.com/forum/showthread.php?t=17028, but you're not going to be able to use that in a UDF, which seems like what you want. See also: http://stackoverflow.com/questions/5541342/when-called-from-an-excel-vba-udf-range-precedents-returns-the-range-and-not-it – Tim Williams Jul 04 '12 at 00:07
  • In addition to Tim's comment you can use the `NavigateArrows` method to identify off-sheet references. I used this approach [here](http://www.experts-exchange.com/Software/Office_Productivity/Office_Suites/MS_Office/Excel/A_2762-Using-a-recursive-NavigateArrows-Method-to-analyse-all-local-and-off-sheet-cell-dependencies.html), credit for the initial code belongs to Bill Manville. – brettdj Jul 04 '12 at 02:14

3 Answers3

5

Thanks to @TimWilliams and @brettdj for pointing me in the right direction to previous discussions on this topic, I can confidently say:

NO, EXCEL DOES NOT HAVE A METHOD FOR PARSING.

However, for my fairly minimal purposes, I've come up with something that works, works with cross worksheet references, and can be called from a UDF.

However, it is extremely brittle, and there are multitudes of perfectly legitimate formulas that I'm certain it wouldn't handle properly.

The code is a mess and could be greatly improved but I just wanted to throw it up on here as I'm moving onto to something else for the time being....

EDIT

Also found this, which looks very interesting:
http://www.dailydoseofexcel.com/archives/2009/12/05/formula-tokenizer/

Public Function CellPrecedents(cell As Range) As Variant()
    Dim resultRanges As New Collection
    If cell.Cells.count <> 1 Then GoTo exit_CellPrecedents
    If cell.HasFormula = False Then GoTo exit_CellPrecedents

    Dim formula As String
    formula = Mid(cell.formula, 2, Len(cell.formula) - 1)

    If IsRange(formula) Then
        resultRanges.Add Range(formula), 1
    Else
        Dim elements() As String
        'Debug.Print formula & " --> "
        formula = Replace(formula, "(", "")
        formula = Replace(formula, ")", "")
        'Debug.Print formula & " --> "
        elements() = SplitMultiDelims(formula, "+-*/\^")
        Dim n As Long, count As Integer
        For n = LBound(elements) To UBound(elements)
            If IsRange(elements(n)) Then
                'ACTUALLY JUST DO A REDIM PRESERVE HERE!!!!
                count = count + 1
                'resultRanges.Add Range(Trim(elements(n)))  '<---  Do **NOT** store as a range, as that gets automatically Eval()'d
                resultRanges.Add Trim(elements(n))
            End If
        Next
    End If

    Dim resultRangeArray() As Variant
    ReDim resultRangeArray(resultRanges.count)
    Dim i As Integer
    For i = 1 To resultRanges.count
        resultRangeArray(i) = CStr(resultRanges(i))  '// have to store as a string so Eval() doesn't get invoked (I think??)
    Next

    CellPrecedents = resultRangeArray

exit_CellPrecedents:
    Exit Function
End Function

Public Function IsRange(var As Variant) As Boolean
    On Error Resume Next
    Dim rng As Range: Set rng = Range(var)
    If err.Number = 0 Then IsRange = True
End Function

(just google SplitMultiDelims for that function)

tbone
  • 5,715
  • 20
  • 87
  • 134
  • 2
    https://github.com/spreadsheetlab/XLParser is an example of a more dedicated effort. – ivan_pozdeev Oct 08 '15 at 11:54
  • Thanks so much for this. Here is an updated version, compatibility if formula has error checking and has double qoutes: formula = Replace(formula, """", ""); formula = Replace(formula, "IFERROR", ""); ;; Fix if formula has only one range, i.e: =B98: If IsRange(formula) Then resultRanges.Add Trim(formula); Fix result range always has first index empty: ReDim resultRangeArray(1 To resultRanges.count); – PalFS May 10 '20 at 02:22
1

Tbone, One other option that is not directly what you asked for but could work as alternative solution.

Rather than using a formula to try and find the corrosponding label, Try adjusting your formulas to work for you. Here are a couple of options depending on what the formula you were trying to parse is. 1. If your formula is a lookup you could just offset to look to the left. 2. Alternatively you could use the "Indirect" function in both formulas to ensure they are referencing the correct location.

0

In short, I think you want to do subpart of : Use VBA to generate code to reproduce basic calculations on an Excel worksheet, and using a function to return n'th DirectPrecedents collection element address or name.

source: http://www.vb-helper.com/howto_vba_excel_formulas.html

This use case however has been deprecated. As of Excel 2007 tables allow a much better solution.

Margus
  • 19,694
  • 14
  • 55
  • 103
  • 1
    Bad news?: The DirectPrecedents property only works on the active sheet and can not trace remote references. (http://msdn.microsoft.com/en-us/library/aa175240(v=office.11).aspx) – tbone Jul 05 '12 at 03:46
  • Another limitation of this (of Excel) I'm fairly certain will be that it can't be called from a formula within a cell. – tbone Jul 05 '12 at 03:47