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 exa
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.
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....
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)
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.