FormulaData
Answers what the formula parser is allowed to accept. Create reads two ListObjects from a worksheet, T_XlsFonctions for the Excel function names and T_ascii for the separator characters, and holds each of them as one delimited string. ExcelFormulasIncludes says whether a token is a known Excel function, and SpecialCharacterIncludes whether a character is a known separator.
GROUPED FORMULAS
Seven tokens stand for an aggregation over rows that match a set of criteria, SUMIFS and MEANIFS among them. IsGroupFunction says whether a token is one of the seven, GroupAggregator names the Excel function it stands for, and GroupUsesNativeFunction says whether the output uses the matching Excel *IFS function directly.
Version: 1.0 (2026-02-09)
Factory
Create #
create
Instantiate a FormulaData helper bound to a worksheet source
Signature:
Public Function Create(ByVal formWksh As Worksheet) As FormulaData
Validates that the supplied worksheet contains the required ListObjects (T_XlsFonctions and T_ascii) with expected columns, then reads their data into memory. Each table is resolved once: the old code validated the two tables and then resolved both again to read them.
Parameters:
formWksh: Worksheet. The worksheet hosting the lookup tables.
Returns: FormulaData. A fully initialised instance ready for use by the Formulas parser.
Throws:
- ProjectError.ObjectNotInitialized When formWksh is Nothing.
- ProjectError.ElementNotFound When a required table or column is missing.
- ProjectError.ErrorUnexpectedState When a required column contains no data.
Lookups
SpecialCharacterIncludes #
special-character-includes
Determine whether a special character is allowed
Signature:
Public Function SpecialCharacterIncludes(ByVal specialChar As String) As Boolean
Membership tests against the cached function and character lists.
Checks the character index for the provided token. Returns False when the input is empty. The match is exact, including case: the T_ascii entries are stored as they are written on the worksheet.
Parameters:
specialChar: String. Single-character token to inspect.
Returns: Boolean. True when the character is listed in the lookup table.
ExcelFormulasIncludes #
excel-formulas-includes
Determine whether an Excel function is allowed
Signature:
Public Function ExcelFormulasIncludes(ByVal ExcelFormula As String) As Boolean
Normalises the input to uppercase and checks the function index. Returns False when the input is empty. Function names are stored uppercase, so the lookup is case-insensitive.
Parameters:
ExcelFormula: String. Function name to inspect.
Returns: Boolean. True when the function is listed in the lookup table.
GroupFunctions
IsGroupFunction #
group-function-includes
Determine whether a grouped function token is registered
Signature:
Public Function IsGroupFunction(ByVal functionName As String) As Boolean
The grouped formulas and the canonical Excel aggregation function each token maps to.
Normalises the input and looks it up in the mapping. Returns False for an empty string and for a token that is not one of the seven.
Parameters:
functionName: String. Grouped token to look up.
Returns: Boolean. True when the token is known.
GroupAggregator #
group-aggregator
Retrieve the Excel aggregator linked to a grouped formula token
Signature:
Public Function GroupAggregator(ByVal functionName As String) As String
Returns the canonical Excel aggregator function name for the token. Returns vbNullString when the token is not one of the seven.
Parameters:
functionName: String. Grouped token to translate.
Returns: String. Aggregator name or vbNullString when unknown.
GroupUsesNativeFunction #
group-uses-native
*Determine whether the grouped function should emit a native Excel IFS aggregator
Signature:
Public Function GroupUsesNativeFunction(ByVal functionName As String) As Boolean
When True, the Formulas class emits the native *IFS function (e.g. SUMIFS) instead of wrapping the aggregator in an IF block. Returns False when the token is not one of the seven.
Parameters:
functionName: String. Grouped token to inspect.
Returns: Boolean. True when the output uses the corresponding *IFS function directly.
Internal members (not exported)
Factory
Seal #
seal
Prevent further changes to setup-only properties
Signature:
Public Sub Seal()
Marks the instance as sealed so guarded setters raise when invoked after construction. Called by the factory before returning. It also closes the back door the class used to carry: a direct New followed by Initialise skipped nothing, but a later Wksh write rebuilt every lookup underneath a parser that was already reading them.
GuardNotSealed #
guard-not-sealed
Reject writes to setup-only properties after sealing
Signature:
Private Sub GuardNotSealed(ByVal propName As String)
Raises an error when a guarded setter is invoked on a sealed instance.
Parameters:
propName: String. Name of the property being guarded.
PublicAccessors
Wksh #
wksh
Worksheet hosting the lookup tables
Signature:
Public Property Get Wksh() As Worksheet
Properties that expose the internal state.
Returns the worksheet reference stored during creation. All cached lookups originate from ListObjects on this worksheet.
Returns: Worksheet. The worksheet reference stored at creation.
Wksh #
wksh-set
Assign the worksheet hosting the lookup tables
Signature:
Public Property Set Wksh(ByVal formWksh As Worksheet)
Replaces the worksheet reference and rereads both lookups from it. The factory seals the instance, so this raises after construction.
Parameters:
formWksh: Worksheet. The worksheet providing formula metadata.
Throws:
- ProjectError.SomethingWentWrong When the instance is sealed.
GroupFunctions
GroupMappingFound #
group-mapping-found
Resolve one grouped token to its aggregator and native flag
Signature:
Private Function GroupMappingFound(ByVal functionName As String, _
ByRef aggregator As String, _
ByRef usesNative As Boolean) As Boolean
The seven mappings are compiled in. The old class held them in a BetterArray registry with routines to add and remove entries, and nothing ever added one: the seven literals were the whole set. The three public answers above each walked that registry with the same twenty lines.
Parameters:
functionName: String. Grouped token to look up.aggregator: ByRef String. Filled with the Excel aggregator.usesNative: ByRef Boolean. Filled with the native-function flag.
Returns: Boolean. True when the token is one of the seven.
RegisterGroupTokens #
register-group-tokens
Put the grouped tokens and their aggregators in the function index
Signature:
Private Sub RegisterGroupTokens()
The seven tokens and the four aggregators they name are accepted by the tokeniser whether or not the worksheet table lists them. Formulas.AppendToken depends on this and a test asserts it.
PrivateHelpers
Initialise #
initialise
Perform one-time initialisation of the instance
Signature:
Public Sub Initialise(ByVal formWksh As Worksheet)
Initialisation, validation, and index building.
Stores the worksheet reference and reads both lookup columns into memory. It stays Public because the factory calls it on a second instance, and it is guarded: after the factory seals that instance, this raises.
Parameters:
formWksh: Worksheet. The worksheet providing lookup sources.
Throws:
- ProjectError.ElementNotFound When a required table or column is missing.
- ProjectError.ErrorUnexpectedState When a required column contains no data.
- ProjectError.SomethingWentWrong When the instance is sealed.
ColumnRange #
column-range
Resolve the data body range of one lookup column
Signature:
Private Function ColumnRange(ByVal tableName As String, _
ByVal columnName As String) As Range
Locates the named ListObject and column on the stored worksheet and returns the DataBodyRange, raising the error that says which part is missing. This is the only place either table is resolved.
Parameters:
tableName: String. ListObject name.columnName: String. ListColumn header.
Returns: Range. The data body range containing the column data.
Throws:
- ProjectError.ElementNotFound When the table or the column is absent.
- ProjectError.ErrorUnexpectedState When the column holds no data.
BuildIndex #
build-index
Read one column and turn it into a delimited lookup string
Signature:
Private Function BuildIndex(ByVal sourceRange As Range, _
ByVal normaliseCase As Boolean) As String
Reads the whole column in one crossing, then walks the values in memory. The old code read one cell at a time and called BetterArray.Includes once per row against the very list it was filling, which copies the whole internal array on every call. Duplicates are skipped with one InStr over the string being built.
Parameters:
sourceRange: Range. The cells containing the values to index.normaliseCase: Boolean. True to uppercase entries before storing.
Returns: String. The distinct values, each wrapped in INDEX_SEPARATOR.
ColumnValues #
column-values
Read a one-column range into a string array
Signature:
Private Function ColumnValues(ByVal sourceRange As Range) As Variant
A range of one cell answers with a scalar rather than an array, so that case is handled on its own.
Parameters:
sourceRange: Range. The one-column range to read.
Returns: Variant. A one-based array of strings.
CellText #
cell-text
Read one cell value as text
Signature:
Private Function CellText(ByVal cellValue As Variant) As String
A cell holding an error value raises a type mismatch on CStr, and a table the user is editing can hold one.
Parameters:
cellValue: Variant. The value read from the worksheet.
Returns: String. The text, or an empty string.
IndexHolds #
index-holds
Test one entry against a delimited lookup string
Signature:
Private Function IndexHolds(ByVal lookupIndex As String, ByVal entry As String) As Boolean
Parameters:
lookupIndex: String. The delimited index to search.entry: String. The entry to look for.
Returns: Boolean. True when the index holds the entry.
AppendFunctionToken #
append-function-token
Add one token to the function index when it is not already there
Signature:
Private Sub AppendFunctionToken(ByVal functionToken As String)
Parameters:
functionToken: String. Token to guarantee in the lookup.
ErrorHandling
ThrowError #
throw-error
Raise a consistent error for this class
Signature:
Private Sub ThrowError(ByVal errNumb As Long, ByVal errorMessage As String)
Centralised error-raising helper for consistent ProjectError usage.
Wrapper around Err.Raise that standardises the source to "FormulaData", providing a consistent stack trace across all methods in this class. The message goes through as written, the same as the other five classes in this folder.
Parameters:
errNumb: Long. ProjectError code describing the failure.errorMessage: String. Human-readable explanation.
Throws:
- ProjectError.
Always raises the specified error.
Used in (16 file(s))
- AnalysisOutput.cls
- CrossTableFormula.cls
- Formulas.cls
- LinelistSpecs.cls
- VarWriter.cls
- SetupErrors.cls
- TestAnalysisOutput.bas
- TestCrossTableFormula.bas
- TestFormulaBuilder.bas
- TestSpatialTables.bas
- TestFormulaData.bas
- TestFormulas.bas
- TestEventLinelistSheets.bas
- TestLLDataEntry.bas
- TestSectionBuilder.bas
- TestVarWriter.bas