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:


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:

Throws:


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:

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:

Throws:


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:

Returns: Range. The data body range containing the column data.

Throws:


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:

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:

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:

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:

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:


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:

Throws:


Used in (16 file(s))