公式 Token 與使用者函式
HotXLS 在兩個活頁簿引擎中都公開呼叫端擁有的公式 Token 串流,以進行無損檢查與受防護的回寫,並提供階層式使用者函式登錄以進行自訂計算
Token 模型
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, and ArrayValues |
| TXLSFormulaTokenStream | DecodeBiff, Clone, CloneSyntaxTree, BindContext、 CanCompileFor、 CanWriteBiffAt、 ReplaceReference、 Count、 Items、 Source、 FormulaText、 FullyDecoded、 Rgce、 RgbExtra、 BiffWritable與 BiffVersion |
| TXLSCompiledFormula | CreateTokenStream 傳回編譯後公式 Token 串流的呼叫端自有複本 |
| 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 家族則把 token 位元組寫回以便儲存。 MaxRow 與 MaxCol 限定了編譯器接受的參照格線——XLSX 引擎把它們從 BIFF8 預設值提高到 XLSX 格線,而放不進 BIFF8 的參照沒有對應的 ptg 形式,因此 BIFF8 寫入器會回報它們無法編碼
ArrayFormulaMode 會在寫入 ARRAY 記錄(陣列公式)的 rgce 時設立:之後 value class 的參照與陣列常數會以 array class(tAreaA / tArrayA)序列化,正如 Excel 自己的寫法。若運算元是 value class,Excel 會用隱含交集來計算陣列公式,而不把它當成真正的陣列公式,這正是引擎要為 ARRAY 記錄自己的 rgce 設起這個旗標的原因
簡單已儲存葉節點的有界檢查
TXLSCompiledFormula.ReadSimpleLeafInfo(ExpectedOwner, MaxBytes, MaxTextCharacters, AllowUnboundBiff) 就地檢查主語法樹與任何保留的 token 串流,不複製、不反編譯、不評估,也不具現化 token 或陣列; AllowUnboundBiff 預設 False
受支援的形狀是一個絕對 REF3D 或 RANGE3D 葉節點與一個字串常值,串流被保留時恰好一個連貫的 token;運算式、相對或位移參照、陣列、外部陣列資料、未知 token、主葉與保留葉不相符,以及外部繫結擁有者都回傳 fslkUnsupported ,不改變快取值或計算狀態
| TXLSFormulaSimpleLeafKind | fslkUnsupported、 fslkRange或 fslkLiteral;使用其他結果欄位前先檢查 Kind |
| TXLSFormulaLeafOwnerState | flosNoRetainedStream 回報沒有 token 串流來源, flosUnboundStream 回報匯入的未繫結 BIFF, flosSameOwner 回報確切的繫結擁有者相符, flosForeignOwner 拒絕不同的繫結擁有者 |
| TXLSFormulaSimpleLeafInfo | 不含字串的值記錄,包含 Kind、 OwnerState、 RangeType、 ExternId、 FirstRow、 FirstCol、 LastRow、 LastCol、 TextCharacters、 StoredTextCharacters、 ReadBytes、 SyntaxNodes與 TokenCount |
| Range coordinates and identity | 座標以 0 為基數; ExternId 是以 1 為基數的邏輯語法樹 ID,而非 BIFF 線路索引;呼叫端必須透過釘選的活頁簿解析它,並拒絕外部活頁簿、工作表跨度、缺漏工作表或超出受支援擷取範圍的座標 |
| Source costs | TextCharacters 是以 UTF-16 code unit 計的常值輸出長度,範圍則為零; StoredTextCharacters 加總保留的來源文字、token 文字與受檢語法文字; ReadBytes 加總其兩位元組文字成本與保留的原始位元組長度,包括另外保留的表示;這些計數器描述的是有界檢查的輸入,而非總堆積用量 |
| Syntax and token counts | SyntaxNodes 對單純的已編譯葉節點為一,當主葉與保留葉都受檢時為二; TokenCount 無串流時為零,有受支援且連貫的保留串流時為一 |
| TXLSFormulaTokenStream.ReadSimpleLeafInfo | 較低層接受相同的擁有者與配額、選用的 ComparedLeaf 與選用的 ReferenceContext;它直接檢查串流自己的私有語法、token 與原始位元組; ComparedLeaf 追加主葉連貫性檢查, ReferenceContext 則不複製地對映序列化文字公式的 extern 索引 |
| TXLSCompiledFormula.ReadSimpleLiteral | 接受相同的引數,重複葉節點檢查,並在其餘位元組與字元配額也涵蓋輸出後,才回傳呼叫端自有的 WideString;不受支援的葉節點或剩餘容量不足會引發 EInvalidOperation |
ExpectedOwner 必須是附加到消費來源的確切計算器,並在檢查與常值擷取期間保持存活且穩定;缺漏的擁有者引發 EArgumentException ,負配額引發 EArgumentOutOfRangeException ,過大的保留來源輸入則在內容比較或輸出複製前引發 EInvalidOperation
沒有保留串流的單純葉節點無法確立其原始擁有者;未繫結的匯入串流也不帶活頁簿身分,因此只有當呼叫端已釘選來源計算器並會在讀取儲存格前驗證本機工作表身分時,才適合明確使用 AllowUnboundBiff=True;匯入參照檢查目前接受 BIFF8 參照並拒絕不同的 BIFF5 extern 表示
名為 FFirstRowRel、 FFirstColRel、 FLastRowRel與 FLastColRel 的舊版語法欄位對絕對座標為 true;此協助程式也檢查獨立的 token 相對旗標與位移表示,因此消費端不必從這些歷史名稱推斷絕對座標
語法樹
CloneSyntaxTree 會傳回一份呼叫端自有的 TXLSSyntaxItem 節點剖析樹,供結構檢查使用:每個節點會回報自己的 ItemType、literal 承載(Value、 IntValue、 DecValue)、前導 Spaces、樹狀連結(Parent、 Child、 ChildCount)、透過 TXLSRangeItem 取得的 Range參照葉節點,以及陣列常數資料(ArrayRows、 ArrayCols、 ArrayValueCount,以及 TXLSArraySerVal 裡的 ArrayValues項目)。這棵樹是分離的副本,走訪它不會碰觸活頁簿已編譯的公式;而變更用成員(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與 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與 TXLSXWorkbook 公開 RegisterUserFunction、 UnregisterUserFunction、 ClearUserFunctions、 RegisteredUserFunctionCount、 RegisterGlobalUserFunction、 UnregisterGlobalUserFunction、 ClearGlobalUserFunctions與 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 則控制垂直或水平穩定排序與公式移動