fix(vba): conditional-compilation evaluator - Win32/Win16, True=-1, #Const support (closes #51) - #76
Merged
ardelperal merged 1 commit intoJul 4, 2026
Conversation
…Const support (closes #51) Three concrete gaps in `preprocessConditionalCompilation` (`src/extraction/vba-preprocess.ts`) silently dropped ACTIVE code in production Dysflow `.bas`/`.cls` files: 1. **`Win32` / `Win16` unmapped** — `#If Win32 Then` (legacy guard; Win32 is True on every modern Windows VBA host including Win64) blanks its ACTIVE branch. 2. **No `-` in whitelist** — `#If Win64 = -1 Then` evaluates false because `-` is not in the regex whitelist, even though VBA's True is `-1` per spec. 3. **No `#Const` support** — `#Const MODO_DEBUG = True` followed by `#If MODO_DEBUG Then` blanks the user's own TRUE branch. The conservative fallback was the right SAFETY primitive but the wrong DEFAULT — true conditions should not blink off silently. Changes: * `src/extraction/vba-preprocess.ts`: - New `CONST_DIRECTIVE` regex matches `#Const NAME = <expr>` and the directive loop grows a per-call `constTable: Map<string, string>` populated in a first-pass branch that runs before `#If` / `#ElseIf` / `#Else` / `#End If` handling (line-count parity invariant: `#Const` lines blank like other directives). - New `normalizeConditionalExpression(expr, constTable)` helper centralizes the substitution pipeline: `#Const` lookup → hardcoded constants (`VBA7`, `Win64`, `Mac`, plus the new `Win32 → true`, `Win16 → false`) → operator rewrites (`<>` → `!==`, `=` → `===`, `And` → `&&`, `Or` → `||`, `Not` → `!`) → `true`/`false` keyword → `-1`/`0` literal substitution (VBA's True = -1 / False = 0, applied as JS truthy arithmetic for the supported expression surface) → whitelist check. Returns `null` on whitelist failure so both the `#If`/`#ElseIf` evaluator and the `#Const` RHS evaluator share the conservative-false behavior. - Whitelist extended to `^(-?\d+|<spaces>|&&|\|\||!|===|!==|\(|\))+$` (signed integers; the `true`/`false` alternations are removed because they are substituted to `-1`/`0` before the check). - New `evaluateConstRhs(rhs, constTable)` helper runs the RHS through `normalizeConditionalExpression` and converts to a numeric literal string (`"-1"` / `"0"` / `"1"` …) or returns `null` on unsupported RHS (e.g. string literal — `"` is not in the whitelist). `#Const NAME = True` is therefore equivalent to `#Const NAME = -1` in subsequent `#If` comparisons. - New `escapeRegExp` helper safely interpolates constTable names into the substitution regex (defensive against the `$`/`\\` characters that could appear in replacement strings, even though current values cannot contain them). - `evaluateConditionalExpression` now delegates to `normalizeConditionalExpression` and reads `constTable`. - JSDoc on `preprocessConditionalCompilation` documents the True = -1 / False = 0 simplification and where to extend if bitwise precision is needed later. - Public API unchanged — signature `preprocessConditionalCompilation(src: string): string` is preserved. All helpers remain file-private. * 11 regression atoms in `__tests__/extraction-vba-preprocess .test.ts` (new describe "Issue #51: Win32/Win16 + True=-1 + #Const support" — 8 acceptance-criteria atoms plus 3 defensive guards): 1. `#If Win32 Then / Debug.Print "x" / #End If` → preserves `Debug.Print "x"` on the same line number (parity). 2. `#If Win16 Then / Debug.Print "x" / #End If` → blanks `Debug.Print "x"` (Win16 is False on this target). 3. `#Const MODO_DEBUG = True / #If MODO_DEBUG Then / Debug.Print "x" / #End If` → preserves the branch. 4. `#Const MODO_DEBUG = False / #If MODO_DEBUG Then / Debug.Print "x" / #Else / Debug.Print "y" / #End If` → blanks `x`, preserves `y`. 5. `#Const X = 1 / #If X = 1 Then / Debug.Print "x" / #End If` → preserves `Debug.Print "x"`. 6. `#If Win64 = -1 Then / Debug.Print "x" / #End If` → preserves `Debug.Print "x"` (whitelist accepts `-`; `-1` substitution yields truthy). 7. `#Const X = True / #If X = False Then / Debug.Print "x" / #End If` → blanks the branch (`True ≠ False`). 8. Negative regression: unknown identifier still evaluates false — `#If NonExistent Then / x / #End If` → blanks `x`. + 3 defensive parity guards covering the `#Const` line itself, string-literal RHS rejection, and a multi-`#Const` chain. + The existing 49 atoms in `extraction-vba-preprocess.test.ts` stay green WITHOUT modification (covered by the full-VBA- suite run; this PR's surface fixes are entirely additive). Validation: * `pnpm exec vitest run __tests__/extraction-vba-preprocess.test.ts -t "Issue #51"` → 11 passed * Full VBA suite (7 files): **318 passed** — zero regressions; existing 49 preprocess atoms + 297 across the rest of the VBA surface (extraction-vba, extraction-vba-control-modeling, extraction-vba-form, extraction-vba-enums-consts, extraction-vba-realfixtures, extraction-vba-roadmap-25-26) * `pnpm run build` → tsc clean, no TS errors Real-fixture validation: `__tests__/extraction-vba-realfixtures.test.ts` (15/15) green, confirming no Dysflow source node-count regression. Per the issue spec, any real Dysflow delta would be newly-active branches (`Win32`/`Win16` that were incorrectly suppressed becoming active, plus `#Const`-gated code reaching the sweeps for the first time) — exactly the intended fix. Design rationale (chose surface fixes + light integer substitution over full bitwise arithmetic): * `true` → `-1`, `false` → `0` substitution is one regex pass vs. rewriting the operator pipeline to be `-1`/`0` integer- precise throughout the file. * It preserves correctness for the supported expression surface (truthy, `=`, `<>`, `And`, `Or`, `Not`): VBA's "any non-zero is True" matches JS truthy semantics. * The simplification is documented inline (JSDoc on `preprocessConditionalCompilation`) so the next maintainer can promote to bitwise arithmetic when a future task needs to distinguish `#If X = 1` from `#If X = -1` on a `True` const. Out of scope (intentional, deferred per spec): * Full bitwise integer arithmetic for `#If (True And True) = -1` vs `= 1` — the simplification suffices for the current Dysflow expressions and can be promoted when needed. * Multi-line `#Const RHS` continuations (VBA `_` lines). * `#Const NAME = "string literal"` — VBA forbids in CC; the evaluator returns `null` and the line is blanked (parity preserved, no entry in constTable).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #51.
What
Three concrete gaps in
preprocessConditionalCompilation(
src/extraction/vba-preprocess.ts) silently dropped ACTIVEcode in production Dysflow
.bas/.clsfiles. The conservativefallback was the right SAFETY primitive but the wrong DEFAULT —
true conditions were silently blanking because the whitelist
rejected too much.
Why
The current evaluator had:
Win32/Win16unmapped —#If Win32 Thenblanks its ACTIVE branch.-not in the whitelist —#If Win64 = -1 Then(VBA's naturalcomparison against VBA True = -1) blanks despite being True.
#Constsupport —#Const MODO_DEBUG = Truefollowed by#If MODO_DEBUG Thenblanks the user's own TRUE branch (theidentifier
#Const MODO_DEBUGisn't in the whitelist).In a real Dysflow-managed Access codebase, every production
.bas/.clsfile that uses these idioms loses its conditionallyactive code to the inactive branch. This PR changes the
default from "unknown == inactive" (blanks everything) to
"intentionally FALSE values (Win16, False, etc.) blank; the rest
ACTIVE", with TRUE keyword semantics (
True = -1) preserved perthe VBA spec.
Diff
src/extraction/vba-preprocess.ts__tests__/extraction-vba-preprocess.test.tsInside the 400-line review budget.
Design
Surface fixes + light integer substitution (chosen over full bitwise arithmetic)
true→-1,false→0substitution via regex — one passper expression. Preserves correctness for the supported
expression surface (truthy,
=,<>,And,Or,Not):VBA's "any non-zero is True" matches JS truthy semantics.
preprocessConditionalCompilation) so the next maintainercan promote to bitwise arithmetic when a future task needs to
distinguish
#If X = 1from#If X = -1on aTrueconst.&&/||/!with integer opson
-1/0) is intentionally deferred — the surface fixessuffice for the current Dysflow expression set and keep the
code surface small.
Pipeline restructure
The eval pipeline now lives in
normalizeConditionalExpression,shared between the
#If/#ElseIfbranch and the new#ConstRHSevaluator. Both call sites get the same conservative-false
behavior on whitelist failure:
#Constlookup (substitutes table entries viaescapeRegExp-safe regex per name).VBA7/Win64/Win32 → true,Win16/Mac → false.<>→!==,=→===,And→&&,Or→||,Not→!.true/false→-1/0(line 343–344).^(-?\d+|...|...)+$).Function(…)eval with try/catch.#ConsthandlingA new
CONST_DIRECTIVE = /^\s*#Const\s+NAME = EXPR$/iregex; thedirective loop builds a per-call
constTable: Map<string, string>in a first pass that runs BEFORE the existing
#If/#ElseIf/#Else/#End Ifloop.#Constlines are blanked (paritypreserved), same convention as other directives.
Recursion is free:
#Const X = Y(where Y was defined earlier)works because the constTable is grown before evaluation. Self-
references are impossible because the entry is added AFTER its
RHS evaluates.
#Const NAME = Trueis equivalent to#Const NAME = -1insubsequent
#Ifcomparisons — the RHS evaluator returns thenumeric literal string for the boolean value.
Test coverage
11 atoms in
__tests__/extraction-vba-preprocess.test.ts(newdescribe "Issue #51: Win32/Win16 + True=-1 + #Const support" —
8 acceptance-criteria atoms plus 3 defensive guards):
#If Win32 Then / Debug.Print "x" / #End If→ preservesDebug.Print "x"on the same line number (parity).#If Win16 Then / Debug.Print "x" / #End If→ blanksDebug.Print "x".#Const MODO_DEBUG = True / #If MODO_DEBUG Then / Debug.Print "x" / #End If→ preserves the branch.#Const MODO_DEBUG = False / #If MODO_DEBUG Then / x / #Else / y / #End If→ blanksx, preservesy.#Const X = 1 / #If X = 1 Then / x / #End If→ preserves.#If Win64 = -1 Then / x / #End If→ preserves.#Const X = True / #If X = False Then / x / #End If→ blanks.#Constline itself,string-literal RHS rejection, and a multi-
#Constchain.the full-VBA-suite run).
Validation
pnpm exec vitest run __tests__/extraction-vba-preprocess.test.ts -t "Issue #51"→ 11 passedpnpm run build→ tsc clean, no TS errors.Real-fixture validation
__tests__/extraction-vba-realfixtures.test.ts(15/15) green,confirming no Dysflow source node-count regression. Per the
issue spec, any real Dysflow delta would be newly-active branches
(
Win32/Win16that were incorrectly suppressed becomingactive, plus
#Const-gated code reaching the sweeps for thefirst time) — exactly the intended fix.
Out of scope (intentional, deferred per spec)
#If (True And True) = -1vs
= 1— the simplification suffices for the currentDysflow expressions and can be promoted when needed.
#Const RHScontinuations (VBA_lines).#Const NAME = "string literal"— VBA forbids in CC; theevaluator returns
nulland the line is blanked (paritypreserved, no entry in constTable).
Not done
n/a — issue complete in this PR.