公式令牌与用户函数
HotXLS 在两个工作簿引擎中都暴露调用方拥有的公式令牌流,用于无损检查和受保护的回写,并提供了用于自定义计算的层级式用户函数注册表
令牌模型
TXLSFormulaTokenClass、TXLSFormulaTokenKind 和 TXLSFormulaTokenSource 对每个 TXLSFormulaToken 进行分类,并记录它来自解析的文本、经典 BIFF 字节还是其他受支持的来源
| TXLSFormulaToken | Kind、Opcode、BaseOpcode、TokenClass、RawBytes、TextValue、NumberValue、IntegerValue、ArgumentCount、FunctionIndex、ExternSheetIndex、Row1、Col1、Row2、Col2、Row1Relative、Col1Relative、Row2Relative、Col2Relative、ReferenceUsesOffsets、Supported、ArrayRows、ArrayCols、ArrayValueCount 和 ArrayValues |
| TXLSFormulaTokenStream | DecodeBiff、Clone、CloneSyntaxTree、BindContext、CanCompileFor、CanWriteBiffAt、ReplaceReference、Count、Items、Source、FormulaText、FullyDecoded、Rgce、RgbExtra、BiffWritable 和 BiffVersion |
| TXLSCompiledFormula | CreateTokenStream 返回编译公式令牌流的调用方拥有的克隆 |
| TXLSFormula | ArrayFormulaMode、FormulaLocale、MaxRow、MaxCol、FuncNamebyPtg、FuncArgsCntByPtg、GetCompiled、GetCompiledPreservingUnknownNames、GetCompiledR1C1、GetTokenStream、GetTranslated、GetStoreData、GetStoreDataExt、CanStoreData、BindUserDefinedFunctions、GetTExpData、SetMode、IsKnownFunction、GetFunctionNames 和 FunctionArgumentClass |
每个工作簿引擎都通过一个共享的 TXLSFormula 实例来编译和转换公式:GetCompiled 把 A1 风格的文本解析为语法树,GetCompiledR1C1 依据公式所在单元格的坐标解析 R1C1 引用,GetTranslated 从经典 BIFF 字节重建语法树,而 GetStoreData 系列则把令牌字节写回用于存储。MaxRow 和 MaxCol 限定编译器接受的引用网格——XLSX 引擎把它们从 BIFF8 默认值提升到 XLSX 网格;放不进 BIFF8 的引用没有对应的 ptg 形式,因此 BIFF8 写入器会把它们报告为不可编码
写入 ARRAY 记录(数组公式)的 rgce 时会设置 ArrayFormulaMode:值类引用和数组常量随后按数组类(tAreaA / tArrayA)序列化,与 Excel 自己的写法一致。若操作数是值类,Excel 会用隐式交集求值数组公式,而不是把它当作真正的数组公式——这正是引擎要为 ARRAY 记录自身的 rgce 设置该标志的原因
简单存储叶节点的有界检查
TXLSCompiledFormula.ReadSimpleLeafInfo(ExpectedOwner, MaxBytes, MaxTextCharacters, AllowUnboundBiff) 原位检查主语法树和任何保留的令牌流,不克隆、不反编译、不求值,也不具体化令牌或数组;默认的 AllowUnboundBiff 为 False
受支持的形状是一个绝对 REF3D 或 RANGE3D 叶节点和一个字符串字面量,当保留流时恰好对应一个连贯的令牌;表达式、相对或偏移引用、数组、外部数组数据、未知令牌、主叶与保留叶不匹配、外来绑定所有者都返回 fslkUnsupported,不改变缓存值或计算状态
| TXLSFormulaSimpleLeafKind | fslkUnsupported、fslkRange 或 fslkLiteral;使用其他结果字段前先检查 Kind |
| TXLSFormulaLeafOwnerState | flosNoRetainedStream 报告没有令牌流来源,flosUnboundStream 报告导入的未绑定 BIFF,flosSameOwner 报告精确的绑定所有者匹配,flosForeignOwner 拒绝不同的绑定所有者 |
| TXLSFormulaSimpleLeafInfo | 不含字符串的值记录,包含 Kind、OwnerState、RangeType、ExternId、FirstRow、FirstCol、LastRow、LastCol、TextCharacters、StoredTextCharacters、ReadBytes、SyntaxNodes 和 TokenCount |
| Range coordinates and identity | 坐标从零开始;ExternId 是逻辑上从 1 开始的语法树 ID,而不是 BIFF 线路索引;调用方必须通过钉定的工作簿解析它,并拒绝外部工作簿、跨工作表跨度、缺失的工作表或超出其受支持捕获范围的坐标 |
| Source costs | TextCharacters 是字面量输出的 UTF-16 code unit 长度,区域为 0;StoredTextCharacters 合计保留的源文本、令牌文本和被检查的语法文本;ReadBytes 合计它们的两字节文本开销和保留的原始字节长度(包括单独保留的表示);这些计数器描述的是有界检查的输入,而非总堆用量 |
| Syntax and token counts | SyntaxNodes 对普通编译叶为 1,同时检查主叶和保留叶时为 2;没有流时 TokenCount 为 0,有受支持的连贯保留流时为 1 |
| TXLSFormulaTokenStream.ReadSimpleLeafInfo | 下层接受同样的所有者和配额、可选的 ComparedLeaf 以及可选的 ReferenceContext;它直接检查流自己的私有语法、令牌和原始字节;ComparedLeaf 增加主叶连贯性检查,ReferenceContext 无复制地映射序列化的文本公式外部索引 |
| TXLSCompiledFormula.ReadSimpleLiteral | 接受相同参数,重复叶检查,仅在剩余字节和字符配额也覆盖其输出时才返回调用方拥有的 WideString;不受支持的叶或剩余容量不足引发 EInvalidOperation |
ExpectedOwner 必须是附着在消费源上的精确计算器,并且在整个检查和字面量提取期间保持存活且稳定;缺失所有者引发 EArgumentException,负配额引发 EArgumentOutOfRangeException,超大的保留源输入在内容比较或输出复制前引发 EInvalidOperation
没有保留流的普通叶无法确立其原始所有者;未绑定的导入流同样不携带工作簿标识,因此仅当调用方已经钉定源计算器并会在读取单元格前验证本地工作表标识时,才适合显式传入 AllowUnboundBiff=True;导入引用检查目前接受 BIFF8 引用,并拒绝不同的 BIFF5 外部表示
名为 FFirstRowRel、FFirstColRel、FLastRowRel 和 FLastColRel 的旧语法字段对绝对坐标为 true;辅助方法还会检查独立的令牌相对标志和偏移表示,因此使用者无需从这些历史名称推断绝对坐标
语法树
CloneSyntaxTree 返回一棵由调用方拥有的 TXLSSyntaxItem 节点解析树,用于结构性检查:每个节点都会报告自己的 ItemType、字面量负载(Value、IntValue、DecValue)、前导 Spaces、树链接(Parent、Child、ChildCount)、通过 Range 暴露为 TXLSRangeItem 的引用叶节点,以及数组常量数据(ArrayRows、ArrayCols、ArrayValueCount 和 ArrayValues 中的 TXLSArraySerVal 条目)。这棵树是脱离原工作簿的独立副本,遍历它不会触及工作簿已编译的公式;变更类成员(AddChild、PushChild、CopyObject、OffsetCopyReferences、AdjustForShift 以及 TXLSRangeItem.CopyObject / AdjustForShift)供在重新编译前改写克隆树的工具使用
工作簿入口点
Tokens := Worksheet.ParseFormulaTokens('SUM(A1:A4)', 1, 1);
try
if Tokens.ReplaceReference(OldRef, NewRef) then
Worksheet.SetFormulaTokens(1, 1, Tokens);
finally
Tokens.Free;
end;
ParseFormulaTokens, GetFormulaTokens, FormulaTokensToText, and SetFormulaTokens 让所有权保持明确,并在更改单元格之前校验上下文、BIFF 版本、token 支持、数组所有权和引用边界
用户函数注册表
TXLSUserFunctionResolver 和 TXLSUserFunctionRegistry 通过 RegisterHandler、UnregisterHandler、Clear、Contains、TryGetHandler 和 Count 提供按名称查找
Workbook.RegisterUserFunction('NETMARGIN', CalculateNetMargin);
Workbook.RegisterGlobalUserFunction('FISCALYEAR', CalculateFiscalYear);
try
Workbook.Calculate;
finally
Workbook.UnregisterUserFunction('NETMARGIN');
Workbook.UnregisterGlobalUserFunction('FISCALYEAR');
end;
IXLSWorkbook, TXLSWorkbook, and TXLSXWorkbook expose RegisterUserFunction, UnregisterUserFunction, ClearUserFunctions, RegisteredUserFunctionCount, RegisterGlobalUserFunction, UnregisterGlobalUserFunction, ClearGlobalUserFunctions, and RegisteredGlobalUserFunctionCount
等效的单元级入口点是 XLSRegisterGlobalUserFunction、XLSUnregisterGlobalUserFunction、XLSClearGlobalUserFunctions、XLSRegisteredGlobalUserFunctionCount 和 XLSGlobalUserFunctionRegistered
TXLSCalculator.OnUserFunctionResolve 在旧版事件回退之前参与,注册表处理程序在注册表锁之外被调用,因此处理程序可以安全地注销自身
除非为工作簿或单次上下文求值显式启用 AllowUnsafeFormulaCallbacks,否则危险的外部函数名和宏风格函数名会在参数求值与处理器查找之前被拦截;普通的用户函数名默认仍然可用
公式诊断
TXLSXFormulaIssueKind、TXLSXFormulaLocation 和 TXLSXFormulaIssue 由 TXLSXWorkbook.AnalyzeFormulaIssues 针对缺失函数、外部引用和循环依赖组件返回
宏表检查
TXLSSheetKind 通过 TXLSWorksheet.SheetKind 区分工作表、宏表、图表工作表、VBA 模块和未知工作表记录;TXLSMacroRisk、TXLSMacroCommandInfo 和 TXLSMacroCommandArray 描述 TXLSWorksheet.ScanMacroCommands 返回的类型化结果
合并自动调整与排序类型
TXLSMergedAutoFitStrategy 和 TXLSXMergedAutoFitStrategy 为合并区域选择首、末或平衡增长,而 TXLSSortOrientation、TXLSSortFormulaMode 和 TXLSSortCompareEvent 控制垂直或水平稳定排序以及公式移动