This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch openformula in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 2b0e2e7916b882c981c766b68cb8be75b353fbf6 Author: Pedro Giffuni <[email protected]> AuthorDate: Wed Nov 11 17:15:48 2026 -0500 docs: OpenFormula Small Group semantic conformance audit (Milestone F) Source-level audit of COUNTA/COUNTBLANK, INDEX row-vector, criteria syntax, and Basic Limits/syntax against ODF 1.4 Part 4. Creates semantic-audit.md, semantic-gaps.tsv (15 gaps), and milestone-g-sequence.md. No production source modified; no runtime verification performed. --- openformula-milestone-g-sequence.md | 120 ++++++++++++ openformula-small-group-semantic-audit.md | 310 ++++++++++++++++++++++++++++++ openformula-small-group-semantic-gaps.tsv | 16 ++ 3 files changed, 446 insertions(+) diff --git a/openformula-milestone-g-sequence.md b/openformula-milestone-g-sequence.md new file mode 100644 index 0000000000..f8797f675d --- /dev/null +++ b/openformula-milestone-g-sequence.md @@ -0,0 +1,120 @@ +# Milestone G — OpenFormula Small Group Semantic Conformance: Implementation Sequence + +**Branch:** `openformula` +**Baseline:** `9c42008d93` (post-E.1.1) +**Source:** `openformula-small-group-semantic-audit.md`, `openformula-small-group-semantic-gaps.tsv` +**Scope:** Implementation planning only. No code changes in this document. + +## Principles + +1. **Audit-first:** Implementation follows the F-audit findings; no speculative fixes. +2. **Dependency-ordered:** Changes with no dependencies first; dependent changes after. +3. **Risk-ordered:** Lower-risk, self-contained changes before higher-risk behavior changes. +4. **No LibreOffice code:** All fixes derived from AOO source inspection only. +5. **No production changes without runtime validation:** All changes deferred until a configured AOO build tree exists and calcval can run (currently macOS-only). + +## Phase G.1 — Zero-Risk Documentation & Test Harness (No Code Changes) + +These items require no production source modification and can be completed immediately. + +| Order | Gap ID | Area | Action | Rationale | +|---|---|---|---|---| +| G.1.1 | F-GAP-008 | Basic Limits — strings | Locate `STRING_MAXLEN` definition; confirm value >= 32767 or document actual limit | Static lookup; no code change | +| G.1.2 | F-GAP-012 | Syntax — named expressions | Document named expression test requirement (pre-defined `MYNAME` not available in calcval harness) | Test infrastructure note | +| G.1.3 | F-GAP-015 | COUNTA/COUNTBLANK | Document scalar `""` vs formula-cell `""` distinction as implementation-defined | Audit documentation | + +## Phase G.2 — Low-Risk: Criteria Syntax Empty-Cell Fix (ScDBCount) + +This is a targeted fix with a clear TODO in source and contained blast radius. + +| Order | Gap ID | Area | File | Change | Risk | +|---|---|---|---|---|---| +| G.2.1 | F-GAP-004 / F-GAP-005 | Criteria — DCOUNT empty-cell matching | `main/sc/source/core/tool/interpr1.cxx:6838-6845` | Address ScDBCount TODO: ensure empty cells matched by query are counted when field is missing; adapt ScQueryCellIterator callers as noted | Low — isolated to DCOUNT without field; documented TODO already exists | + +**Dependencies:** None. +**Runtime validation needed:** Yes — DCOUNT with empty result cells. + +## Phase G.3 — Medium-Risk: COUNTA/COUNTBLANK Behavior Review + +These are behavior decisions, not necessarily code changes. ODF 1.4 permits implementation-defined behavior for formula cells returning `""`. + +| Order | Gap ID | Area | Decision Point | Action If Change Needed | Risk | +|---|---|---|---|---|---| +| G.3.1 | F-GAP-001 | COUNTA matrix | Confirm matrix empty-string handling is acceptable | None needed (ODF 1.4 silent) | Low | +| G.3.2 | F-GAP-002 | COUNTBLANK formula-cell `""` | Decide whether to inspect formula result for `""` | Modify `ScCountEmptyCells()` cell-type check to also check formula result | Medium — behavior change; document as compatibility option | + +**Dependencies:** G.2.1 (empty-cell iteration patterns shared). +**Runtime validation needed:** Yes — COUNTBLANK with formula cells returning `""`. + +## Phase G.4 — Medium-Risk: INDEX Row-Vector Compatibility + +No code change needed for ODF 1.4 conformance (Milestone A already fixed). Compatibility option is optional. + +| Order | Gap ID | Area | Decision Point | Action If Needed | Risk | +|---|---|---|---|---|---| +| G.4.1 | F-GAP-003 | INDEX row-vector | Assess legacy document regression from Milestone A fix | Add document-compatibility option if corpus testing shows impact | Medium — affects existing documents | + +**Dependencies:** None (Milestone A already applied). +**Runtime validation needed:** Yes — corpus regression test against existing documents. + +## Phase G.5 — Low-Risk: Basic Limits Runtime Verification + +No code changes expected (no hard limits found). Verification only. + +| Order | Gap ID | Area | Test | Expected Result | +|---|---|---|---|---| +| G.5.1 | F-GAP-006 | Formula length | 1024+ char formula (1026-char `=1+1+...+1`) | Accepted, result 513 | +| G.5.2 | F-GAP-007 | Parameter count | 30-parameter MAX call | Accepted, result 30 | +| G.5.3 | F-GAP-008 | String length | 32767-char string (after STRING_MAXLEN confirmed) | Accepted | +| G.5.4 | F-GAP-009 | Nesting depth | 7 nested IFs | Accepted, result 1 | + +**Dependencies:** None. +**Runtime validation needed:** Yes — all four tests. + +## Phase G.6 — Low-Risk: Syntax Verification + +| Order | Gap ID | Area | Test | Expected Result | +|---|---|---|---|---| +| G.6.1 | F-GAP-010 | Operator precedence | `2+3*4=14`, `2^3^2=512` | Correct precedence/associativity | +| G.6.2 | F-GAP-011 | References | Cell and range resolution | Correct values | +| G.6.3 | F-GAP-013 | Error propagation | `=NA()` → #N/A; error args propagate | Correct | +| G.6.4 | F-GAP-014 | Whitespace | `=2 + 3 * 4` → 14 | Whitespace-transparent | + +**Dependencies:** None. +**Runtime validation needed:** Yes — all syntax tests. + +## Dependency Summary + +``` +G.1 (docs/tests, no code) ──┐ + ├── G.2 (ScDBCount empty-cell) ──┐ +G.5 (limits verification) ──┤ ├── G.3 (COUNTBLANK decision) +G.6 (syntax verification) ──┤ │ +G.4 (INDEX compatibility) ──┘ └── (runtime-validated) +``` + +## Estimated Implementation Complexity + +| Phase | Complexity | Rationale | +|---|---|---| +| G.1 (docs) | Trivial | No code changes | +| G.2 (ScDBCount) | Low | Isolated TODO; contained scope | +| G.3 (COUNTBLANK) | Medium | Behavior decision; may need compatibility option | +| G.4 (INDEX compat) | Medium | Corpus regression analysis needed | +| G.5/G.6 (verification) | Low | Test execution only; no code changes | + +## Definition of Done for Milestone G + +- All G.1 documentation items complete +- G.2 ScDBCount empty-cell fix implemented (if corpus testing confirms need) +- G.3 COUNTBLANK behavior documented (change or no-change decision recorded) +- G.4 INDEX compatibility impact assessed +- G.5/G.6 all runtime tests executed against built AOO +- No production changes without corresponding calcval test coverage +- All changes verified against ODF 1.4 normative expected results + +## Deferred Items (Post-G) + +- Full function-by-function semantic verification of all 110 Small Group functions (requires runtime) +- ODF save/reload round-trip testing +- Excel import/export function name alignment diff --git a/openformula-small-group-semantic-audit.md b/openformula-small-group-semantic-audit.md new file mode 100644 index 0000000000..017ee5fc7b --- /dev/null +++ b/openformula-small-group-semantic-audit.md @@ -0,0 +1,310 @@ +# OpenFormula 1.4 Small Group Semantic Conformance Audit + +**Branch:** `openformula` +**Baseline:** `9c42008d93` +**Scope:** Audit and report only. No production code modifications. +**Method:** Source-level static inspection only. No runtime verification possible (no AOO build tree; calcval harness macOS-only). + +## Normative Source + +OASIS Open Document Format for Office Applications (OpenDocument) Version 1.4, Part 4: Recalculated Formula (OpenFormula) Format, OASIS Standard, 6 October 2025. + +--- + +## F.1 COUNTA / COUNTBLANK (§6.13.7–6.13.8) + +### Normative Requirement + +**COUNTA (§6.13.7):** Counts non-empty cells. Empty string `""` is not blank and is counted. + +**COUNTBLANK (§6.13.8):** Counts empty cells. Whether a cell returning `""` is blank is implementation-defined. + +### AOO Implementation Locations + +- `ScCount2()` — `main/sc/source/core/tool/interpr1.cxx:3834` +- `ScCountEmptyCells()` — `main/sc/source/core/tool/interpr1.cxx:4714` +- `IterateParameters(ifCOUNT2)` — `main/sc/source/core/tool/interpr1.cxx:3487` (shared by COUNTA, COUNT, and others) +- Dispatch: `interpr4.cxx:3591` (ocCount2 → ScCount2), `interpr4.cxx:3640` (ocCountEmptyCells → ScCountEmptyCells) +- Declaration: `main/sc/source/core/inc/interpre.hxx:505` (ScCount2), `:526` (ScCountEmptyCells) + +### Execution Path — COUNTA + +`ScCount2()` calls `IterateParameters(ifCOUNT2)` which iterates over parameters via a `switch(GetStackType())`: + +| Stack Type | Code Path | Behavior | +|---|---|---| +| `svString` (literal string, incl. `""`) | interpr1.cxx:3506-3543 → `default` branch | `Pop(); nCount++;` — **counts string argument** (empty string counted) | +| `svDouble` (numeric literal) | interpr1.cxx:3546 | Counts | +| `svSingleRef` (single cell) | interpr1.cxx:3567-3624 | `eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE` → counts non-empty cell | +| `svDoubleRef` / `svRefList` (range) | interpr1.cxx:3626-3711 | `ScCellIterator` over range; `eType != CELLTYPE_NONE && eType != CELLTYPE_NOTE` → counts non-empty cells | +| `svMatrix` (array) | interpr1.cxx:3713-3760 | `nCount += nC * nR` — **counts ALL matrix elements** including strings (no `IsString` check for COUNT2) | +| `svError` | interpr1.cxx:3762-3774 | `nCount++` — counts error values | + +### Execution Path — COUNTBLANK + +`ScCountEmptyCells()`: + +| Stack Type | Code Path | Behavior | +|---|---|---| +| `svSingleRef` | interpr1.cxx:4723-4731 | `eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE` → nCount=1 (not blank), else blank | +| `svDoubleRef` / `svRefList` | interpr1.cxx:4733-4757 | `ScCellIterator`; same cell-type check; result = `nMaxCount - nCount` | +| `default` | interpr1.cxx:4760 | `SetError(errIllegalParameter)` — scalar args not supported | + +### Current Behavior vs ODF 1.4 + +| Scenario | ODF 1.4 Normative | AOO Behavior | Match? | +|---|---|---|---| +| Truly empty cell (CELLTYPE_NONE) | COUNTA: not counted; COUNTBLANK: blank | Cell type CELLTYPE_NONE → not counted by COUNTA, blank by COUNTBLANK | Yes | +| Cell with text | COUNTA: counted; COUNTBLANK: not blank | CELLTYPE_STRING → counted/not blank | Yes | +| Cell with number | COUNTA: counted; COUNTBLANK: not blank | CELLTYPE_VALUE → counted/not blank | Yes | +| Scalar `""` argument | COUNTA: counted (not blank) | svString → counted | Yes | +| Formula cell returning `""` | COUNTBLANK: implementation-defined whether blank | CELLTYPE_FORMULA ≠ CELLTYPE_NONE → counted by COUNTA, NOT blank by COUNTBLANK; formula result not inspected | Implementation-defined; AOO does not check formula result | +| Matrix with empty-string cells | COUNTA: not specified explicitly | `nCount += nC * nR` counts all elements including strings | Counts empty-string cells as non-empty | +| Scalar `""` to COUNTBLANK | Not applicable (COUNTBLANK takes ranges) | `SetError(errIllegalParameter)` — only reference types accepted | N/A | + +### Conformance Classification + +- **COUNTA scalar `""`**: Required by ODF 1.4 — matches (empty string counted as non-blank). +- **COUNTA empty cells**: Required by ODF 1.4 — matches (CELLTYPE_NONE not counted). +- **COUNTBLANK truly empty cells**: Required by ODF 1.4 — matches. +- **COUNTBLANK formula cell returning `""`**: Implementation-defined (§6.13.8) — AOO uses cell-type-only check; does not inspect formula result. +- **COUNTA matrix with strings**: ODF 1.4 does not explicitly address matrix empty-string handling — AOO counts all matrix elements including strings. + +### Known Compatibility Risks + +- A `ScDBCount` TODO comment (interpr1.cxx:6838-6845) notes that `ScQueryCellIterator` only returns pointers to existing cells, so empty cells matched by a query are not counted. This affects DCOUNT without a field, not COUNTA/COUNTBLANK directly. +- Changing COUNTBLANK to inspect formula results (treating `""`-returning formulas as blank) would be a behavior change affecting existing documents. + +### Recommended Implementation Changes + +None required for ODF 1.4 conformance (implementation-defined behavior is permitted). If a compatibility option is desired, document the current cell-type-only behavior and consider adding formula-result inspection as an option. + +### Suggested Regression Tests + +- `COUNTA` over range with truly empty cells, text, numbers, and `""` scalar argument +- `COUNTBLANK` over range with truly empty cells vs. cells containing text/numbers +- `COUNTBLANK` over formula cells returning `""` (document current behavior as implementation-defined) + +--- + +## F.2 INDEX Row-Vector Behavior (§6.14.6) + +### Normative Requirement + +ODF 1.4 §6.14.6: `INDEX` with a single row argument on a single-row range (row vector) returns an error. The ODF 1.3 rule allowing the Row argument to act as a column offset for row vectors was removed. + +### AOO Implementation Location + +- `ScIndex()` — `main/sc/source/core/tool/interpr1.cxx:7419` +- Dispatch: `interpr4.cxx:3650` (ocIndex → ScIndex) +- Declaration: `main/sc/source/core/inc/interpre.hxx:564` +- Parameter class: `main/sc/source/core/tool/parclass.cxx:123` (`ocIndex: {{ Reference, Value, Value, Value }, 0}`) + +### Execution Path — ScIndex + +``` +nParamCount = GetByte() // number of arguments +nArea = (nParamCount == 4) ? GetDouble() : 1 +nCol = (nParamCount >= 3) ? GetDouble() : 0 +nRow = (nParamCount >= 2) ? GetDouble() : 0 +``` + +Then `switch(GetStackType())`: + +| Stack Type | Behavior | +|---|---| +| `svMatrix` | Bounds check against matrix dimensions; vector access when nCol==0 or nRow==0 | +| `svSingleRef` | `if (nCol > 1 || nRow > 1) PushIllegalArgument();` — single cell, only row/col 0 or 1 valid | +| `svDoubleRef` / `svRefList` | Full range bounds checking; returns sub-reference or single cell | + +### Before and After Milestone A (commit bb58e28fc2) + +**Before Milestone A:** A `bRowArray` flag was set when `nParamCount == 2 && nRow1 == nRow2` (single-row range with row argument). This caused: +- Row bounds check to be skipped: `(nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray)` +- Alternative row bounds: `(nRow > nCol2 - nCol1 + 1 && bRowArray)` — checked against column width +- Row argument reinterpreted as column: `nCol = (SCCOL) nRow; nRow = 1;` + +Effect: `INDEX(H1:L1;3)` returned the 3rd column (value 30) instead of an error. + +**After Milestone A:** All `bRowArray` logic removed. `INDEX(H1:L1;3)` now: +- nCol=0, nRow=3 +- svDoubleRef path: `(nRow > 0 && nRow1+nRow-1 > nRow2)` → `(3 > 0 && 1+3-1 > 1)` → true → `PushIllegalArgument()` → error + +This matches ODF 1.4 §6.14.6. + +### Conformance Classification + +**Required by ODF 1.4** — Fixed in Milestone A. `INDEX(H1:L1;3)` returns error as required. + +### Known Compatibility Risks + +- Legacy ODF 1.3 documents relying on `INDEX(rowvector; n)` returning column n will now get an error. Regression risk for document compatibility. +- Compatibility handling can be isolated: the fix is localized to `ScIndex()`; no other code paths depend on `bRowArray`. + +### Recommended Implementation Changes + +None needed for ODF 1.4 conformance. Consider a document-compatibility option if legacy document regression is observed (out of scope for this milestone). + +### Suggested Regression Tests + +- `INDEX(H1:L1;3)` → error (single-arg row vector) +- `INDEX(H1:L1;;3)` → column value (column-only arg) +- `INDEX(D1:D5;6)` → error (row out of range) +- `INDEX(D1:F3;2;3)` → 2D result + +--- + +## F.3 Criteria Syntax (§4.11.11) — Database Functions + +### Normative Requirement + +ODF 1.4 §4.11.11: Criteria syntax supported in database functions (DCOUNT, DCOUNTA, DSUM, DAVERAGE, DMAX, DMIN, etc.). Criteria support comparison operators (`<`, `<=`, `>`, `>=`, `=`, `<>`), text criteria, wildcards, and criteria ranges. + +### AOO Implementation Locations + +- `GetDBParams()` — `main/sc/source/core/tool/interpr1.cxx:6621` (parses database + field + criteria range) +- `DBIterator()` — `main/sc/source/core/tool/interpr1.cxx:6760` (iterates matching records for DBSum, DBAverage, DBMax, DBMin, DBProduct) +- `ScDBCount()` — `main/sc/source/core/tool/interpr1.cxx:6828` (DCOUNT) +- `ScDBCount2()` — `main/sc/source/core/tool/interpr1.cxx:6882` (DCOUNTA) +- `ScDBSum()` — `main/sc/source/core/tool/interpr1.cxx:6821` (DSUM) +- `ScDBAverage()` — `main/sc/source/core/tool/interpr1.cxx:6909` (DAVERAGE) +- `ScDBMax()` — `main/sc/source/core/tool/interpr1.cxx:6916` (DMAX) +- `ScDBMin()` — `main/sc/source/core/tool/interpr1.cxx:6923` (DMIN) +- Operator prefix parsing — `main/sc/source/core/tool/queryparam.cxx:106-144` +- `ScQueryEntry` struct — `main/sc/inc/global.hxx:797` +- `ScQueryOp` enum — `main/sc/inc/global.hxx:733-740` (SC_EQUAL, SC_LESS, SC_GREATER, SC_LESS_EQUAL, SC_GREATER_EQUAL, SC_NOT_EQUAL) + +### Function-to-Opcode Mapping + +| Function | Opcode | Implementation | +|---|---|---| +| DCOUNT | ocDBCount → SC_OPCODE_DB_COUNT | ScDBCount() | +| DCOUNTA | ocDBCount2 → SC_OPCODE_DB_COUNT_2 | ScDBCount2() | +| DSUM | ocDBSum → SC_OPCODE_DB_SUM | ScDBSum() → DBIterator(ifSUM) | +| DAVERAGE | ocDBAverage → SC_OPCODE_DB_AVERAGE | ScDBAverage() → DBIterator(ifAVERAGE) | +| DMAX | ocDBMax → SC_OPCODE_DB_MAX | ScDBMax() → DBIterator(ifMAX) | +| DMIN | ocDBMin → SC_OPCODE_DB_MIN | ScDBMin() → DBIterator(ifMIN) | + +Name mapping: `main/formula/source/core/resource/core_resource.src:227-228` (`DCOUNT` → SC_OPCODE_DB_COUNT, `DCOUNTA` → SC_OPCODE_DB_COUNT_2) + +### Criteria Parsing Execution Path + +1. Formula compiler parses `DCOUNT(database, field, criteria)` into opcodes with three Reference parameters. +2. `GetDBParams()` pops: + - Criteria range (double ref) + - Field (double, string, single ref, double ref, or missing) + - Database range (double ref) +3. For each criteria entry, `queryparam.cxx:106-144` parses operator prefixes: + - `<>` → SC_NOT_EQUAL + - `<=` → SC_LESS_EQUAL + - `<` → SC_LESS + - `>=` → SC_GREATER_EQUAL + - `>` → SC_GREATER + - `=` or default → SC_EQUAL +4. `bQueryByString` set via `IsNumberFormat()` (GetDBParams :6745-6746) — determines if criteria is text or numeric +5. `bRegExp` set via `MayBeRegExp()` (GetDBParams :6748) — enables wildcard/RegExp matching +6. `DBIterator()` iterates matching records using `ScDBQueryDataIterator` + +### Current Behavior vs ODF 1.4 + +| Requirement | ODF 1.4 Normative | AOO Behavior | Match? | +|---|---|---|---| +| Comparison operators `<, <=, >, >=, =, <>` | Required | Parsed from criteria cell prefix (queryparam.cxx:106-144) | Yes | +| Text criteria | Required | `bQueryByString` via `IsNumberFormat` | Yes | +| Wildcards / RegExp | Required | `bRegExp` via `MayBeRegExp` | Yes | +| Empty criteria field | Field may be omitted (count all matching) | `bMissingField` path in ScDBCount | Yes | +| Criteria range with multiple rows | AND across rows | ScQueryParamBase entries | Yes | +| DCOUNT without field counts all matching records | Required | ScDBCount: bMissingField + INTERNAL type → counts all matching | Yes, with TODO limitation | +| DCOUNT with field counts values in field | Required | ScDBQueryDataIterator over field column | Yes | + +### Known Limitations + +**ScDBCount empty-cell limitation (interpr1.cxx:6838-6845):** +``` +// TODO: currently the QueryIterators only return cell pointers of +// existing cells, so if a query matches an empty cell there's +// nothing returned, and therefor not counted! +``` +This means DCOUNT without a field may undercount when matching records have empty cells in the result column. + +### Conformance Classification + +**Required by ODF 1.4** — Criteria syntax parsing and database function evaluation are implemented. The empty-cell matching limitation in ScDBCount is a known gap. + +### Recommended Implementation Changes + +Address the ScDBCount TODO (empty-cell matching) if DCOUNT without field is used with empty result cells. This is a targeted fix, not a broad change. + +### Suggested Regression Tests + +- `DCOUNT(R1:T6;;V1:V2)` with `Age>=30` → 4 (B,C,D,E) +- `DSUM` with criteria +- `DAVERAGE`, `DMAX`, `DMIN` with criteria +- Text criteria, wildcard criteria, empty criteria cell + +--- + +## F.4 Basic Limits and Syntax (§3.7, §5) + +### F.4.1 Basic Limits (§3.7) + +#### Formula Length — at least 1024 characters + +**Source finding:** No explicit formula length limit (MAXFORMULA or equivalent) found in `main/sc/source/core/tool/interpr1.cxx`, `main/sc/source/core/inc/interpre.hxx`, or `main/formula/source/core/api/FormulaCompiler.cxx`. The FormulaCompiler parses token arrays dynamically. + +**Classification:** Unverifiable by static inspection. Absence of a hard limit does not prove conformance. Runtime test required. + +#### Function Parameters — at least 30 + +**Source finding:** `MustHaveParamCount()` family (`interpre.hxx:897-927`) enforces per-function min/max parameter counts. No global 30-parameter cap found. Individual functions like MAX use `MustHaveParamCountMin` or exact counts. + +**Classification:** Unverifiable by static inspection. No cap found, but runtime test required. + +#### ASCII Strings — at least 32767 characters + +**Source finding:** `CheckStringResultLen()` (`interpre.hxx:930-936`) checks string concatenation against `STRING_MAXLEN`. The `STRING_MAXLEN` definition was not located in the sc module headers during this audit. + +**Classification:** Unverifiable by static inspection. STRING_MAXLEN value not confirmed. + +#### Nesting Depth — at least 7 levels + +**Source finding:** No nesting depth limit (MAXNEST or equivalent) found in FormulaCompiler or ScInterpreter source. No recursion depth enforcement found. + +**Classification:** Unverifiable by static inspection. Absence of limit does not prove conformance. + +### F.4.2 Remaining Syntax Requirements (§5) + +| Requirement | Section | AOO Implementation | Status | +|---|---|---|---| +| Parenthesized expressions | §5.2 | FormulaCompiler.cxx handles via RangeLine/IntersectionLine/UnaryLine | Present, runtime-unverified | +| Constant Numbers | §5.3 | FormulaCompiler.cxx tokenizes numeric literals | Present, runtime-unverified | +| Constant Strings with quote escaping | §5.4 | FormulaCompiler.cxx tokenizes string literals | Present, runtime-unverified | +| Operator precedence (Table 1) | §5.5 | Parser: UnionLine → IntersectionLine → RangeLine → UnaryLine → binary ops | Present, runtime-unverified | +| Functions and parameters | §5.6 | FormulaCompiler.cxx compiles function calls | Present, runtime-unverified | +| Nonstandard function names as errors | §5.7 | ocNoName opcode; xlformula.cxx stubs | Present, runtime-unverified | +| Cell and range references | §5.8 | compiler.cxx PopDoubleRef/PopSingleRef | Present, runtime-unverified | +| Simple Named Expressions | §5.11 | compiler.cxx:NameLine (ocName opcode) | Present; requires pre-defined name | +| Error constants and propagation | §5.12 | ocErrNull-ocErrNA in compiler.hrc; nGlobalError/SetError in interpre.hxx | Present, runtime-unverified | +| Whitespace handling | §5.14 | ocSpaces → ocIntersect at FormulaCompiler.cxx:830-831 | Present, runtime-unverified | +| Reference intersection `!` | §6.4.12 | interpr1.cxx:ScIntersect | Present, runtime-unverified | +| Reference range `:` | §6.4.11 | interpr1.cxx:ScRangeFunc | Present, runtime-unverified | + +### Cross-Cutting Finding + +**Static source tracing confirms implementation presence, not semantic equivalence.** All Small Group syntax and basic limits infrastructure exists in source, but none has been verified against ODF 1.4 normative expected results at runtime. This is consistent with the E.1/E.1.1 finding that all requirements are UNVERIFIED pending runtime execution. + +--- + +## Conformance Summary + +| Category | Count | Status | +|---|---|---| +| Basic Limits (§3.7) | 4 | Unverifiable statically; no hard limits found | +| Syntax (§5) | 11 | Implementation present; runtime-unverified | +| COUNTA/COUNTBLANK (§6.13.7-8) | 2 | Partially matches; formula-cell `""` is implementation-defined | +| INDEX (§6.14.6) | 1 | ODF 1.4 compliant after Milestone A; regression risk | +| Criteria Syntax (§4.11.11) | 1 | Implemented; ScDBCount empty-cell TODO limitation | +| Operators (§6.4) | 14 | Implementation present; runtime-unverified | +| Functions (§2.3.2.E) | 110 | Implementation present; runtime-unverified | + +**No production code changes are recommended at this audit stage.** Milestone G should implement targeted fixes based on this audit's findings, prioritized by dependency and risk. diff --git a/openformula-small-group-semantic-gaps.tsv b/openformula-small-group-semantic-gaps.tsv new file mode 100644 index 0000000000..acbb1a35af --- /dev/null +++ b/openformula-small-group-semantic-gaps.tsv @@ -0,0 +1,16 @@ +GapID ODF Clause Requirement AOO Implementation Location Current Behavior Classification Severity Proposed Next Action Test Coverage Status +F-GAP-001 §6.13.7 COUNTA counts non-empty cells; empty string "" is not blank main/sc/source/core/tool/interpr1.cxx:3834 (ScCount2 via IterateParameters(ifCOUNT2) at :3487) COUNTA counts scalar string args including ""; counts cells where eCellType != CELLTYPE_NONE && != CELLTYPE_NOTE; matrix case counts ALL elements including strings; formula cells returning "" counted as non-blank based on CELLTYPE_FORMULA (result not checked) Required by ODF 1.4; implementation-defined for formula cel [...] +F-GAP-002 §6.13.8 COUNTBLANK counts empty cells; cell returning "" blankness is implementation-defined main/sc/source/core/tool/interpr1.cxx:4714 (ScCountEmptyCells) COUNTBLANK uses cell-type-only check: eCellType != CELLTYPE_NONE && != CELLTYPE_NOTE → not blank; truly empty CELLTYPE_NONE cells are blank; formula cells returning "" treated as not blank (CELLTYPE_FORMULA != CELLTYPE_NONE); scalar "" argument not supported (only references) Implementation-defined (§6.13.8); differs from po [...] +F-GAP-003 §6.14.6 INDEX single-argument row vector returns error (no row-vector shortcut) main/sc/source/core/tool/interpr1.cxx:7419 (ScIndex); Milestone A commit bb58e28fc2 removed bRowArray shortcut INDEX(H1:L1;3) returns error (nRow > 1 on single row → PushIllegalArgument); INDEX(H1:L1;;3) works (column-only); INDEX on matrix/vector handled per ODF 1.4 Required by ODF 1.4; fixed in Milestone A; regression risk for legacy ODF 1.3 documents High Regression-test against existing document [...] +F-GAP-004 §4.11.11 Criteria syntax supported in database functions main/sc/source/core/tool/interpr1.cxx:6621 (GetDBParams), :6760 (DBIterator); queryparam.cxx:106-144 (operator prefix parsing) Criteria operators parsed: <>, <=, <, >=, >, =; bQueryByString via IsNumberFormat; bRegExp via MayBeRegExp; DBCount has TODO about empty-cell matching (interpr1.cxx:6838-6845) Required by ODF 1.4; parser and evaluation present; empty-cell matching limitation documented Medium Verify criteria match [...] +F-GAP-005 §4.11.11 Database criteria: text criteria, wildcards, empty criteria main/sc/source/core/tool/queryparam.cxx:106-144 (operator parsing); interpr1.cxx:6745-6748 (bQueryByString/bRegExp) Text criteria supported via bQueryByString; wildcard/RegExp via MayBeRegExp; empty criteria cell (svMissing) handled as missing field Required by ODF 1.4; partially verified Medium Verify wildcard and text criteria matching calcval: no dedicated wildcard/empty-criteria cases — not covered +F-GAP-006 §3.7 Formula length at least 1024 characters No explicit formula length limit found in main/sc or main/formula source No MAXFORMULA or formula-size enforcement found; FormulaCompiler parses token arrays dynamically Unverifiable (static); absence of hard limit doesn't prove conformance Low Runtime test with 1024+ char formula calcval: BL-001-1024chars (1026-char expression) — UNVERIFIED +F-GAP-007 §3.7 At least 30 parameters per function where prototype permits main/sc/source/core/inc/interpre.hxx:897-927 (MustHaveParamCount family) Per-function parameter count enforced via MustHaveParamCount/MustHaveParamCountMin; no global 30-param cap found Unverifiable (static); no 30-param cap found Low Runtime test with 30-parameter function call calcval: BL-002-30params (MAX with 30 params) — UNVERIFIED +F-GAP-008 §3.7 Strings up to 32767 ASCII characters main/sc/source/core/inc/interpre.hxx:930-936 (CheckStringResultLen vs STRING_MAXLEN); STRING_MAXLEN definition not located in sc headers String overflow checked via CheckStringResultLen against STRING_MAXLEN; exact STRING_MAXLEN value not confirmed in sc module Unverifiable (static); STRING_MAXLEN definition not located Low Locate STRING_MAXLEN definition; runtime test with 32767-char string calcval: BL-003-32767chars — UNVERIFIED +F-GAP-009 §3.7 At least 7 nesting levels of functions No nesting depth limit found in FormulaCompiler or ScInterpreter source No MAXNEST or recursion depth enforcement found Unverifiable (static); absence of limit doesn't prove conformance Low Runtime test with 7+ nested functions calcval: BL-004-7nesting (7 nested IFs) — UNVERIFIED +F-GAP-010 §5.5 Operator precedence per Table 1 main/formula/source/core/api/FormulaCompiler.cxx (parser grammar: UnionLine → IntersectionLine → RangeLine → UnaryLine → binary ops) Parser structure exists (RangeLine/IntersectionLine/UnionLine); precedence not verified at runtime Unverifiable (static); parser infrastructure present Medium Runtime precedence test (2+3*4=14, 2^3^2=512) calcval: SYN-005-precedence, SYN-005-power — UNVERIFIED +F-GAP-011 §5.8 Reference syntax supported main/sc/source/core/tool/compiler.cxx (PopDoubleRef/PopSingleRef) Cell and range reference handling present in compiler Unverifiable (static); implementation present Low Runtime reference test calcval: SYN-008-ref, SYN-008-range — UNVERIFIED +F-GAP-012 §5.11 Simple Named Expressions supported main/sc/source/core/tool/compiler.cxx:NameLine (ocName opcode) Named expression compilation present; requires pre-defined name not set up in calcval harness Unverifiable (static); harness limitation Medium Define named expression in test environment; verify calcval: SYN-009-named (=MYNAME) — not directly executable (no name setup) +F-GAP-013 §5.12 Error syntax and propagation main/sc/source/core/inc/interpre.hxx:196 (nGlobalError), :854 (SetError), :263 (Push substitutes FormulaErrorToken); error constants ocErrNull-ocErrNA in compiler.hrc Error constants and propagation present; not verified against ODF 1.4 normative results Unverifiable (static) Medium Runtime error propagation test calcval: SYN-010-error (=NA() → #N/A), CONV-005-error — UNVERIFIED +F-GAP-014 §5.14 Whitespace handling main/formula/source/core/api/FormulaCompiler.cxx:830-831 (ocSpaces → ocIntersect) Whitespace converted to intersection at compile time; not runtime-verified Unverifiable (static) Low Runtime whitespace test calcval: SYN-011-whitespace — UNVERIFIED +F-GAP-015 §6.13.7 / §6.13.8 COUNTA/COUNTBLANK distinction: scalar "" vs cell formula returning "" main/sc/source/core/tool/interpr1.cxx:3487 (svString case counts ""), :4714 (cell-type-only check for COUNTBLANK) AOO distinguishes scalar "" (counted by COUNTA) from cell formula returning "" (COUNTBLANK uses cell type, not result) Implementation-defined; cell-type-only approach may not match ODF intent for formula cells returning "" Medium Document distinction; consider formula-result insp [...]
