Skip to content

feat(parser/ast): opener-embedded [[#if]] / [[#expr]] / [[#ifexpr]] と [[iftags]] bare prefix 対応#50

Merged
r74tech merged 5 commits into
developfrom
feat/opener-embedded-expr-eval
Jun 10, 2026
Merged

feat(parser/ast): opener-embedded [[#if]] / [[#expr]] / [[#ifexpr]] と [[iftags]] bare prefix 対応#50
r74tech merged 5 commits into
developfrom
feat/opener-embedded-expr-eval

Conversation

@r74tech

@r74tech r74tech commented Jun 10, 2026

Copy link
Copy Markdown
Owner

背景

[[div class="[[#if 1 | folded | unfolded ]]"]] のように block opener の属性内に [[#if]] / [[#expr]] / [[#ifexpr]] を埋め込んだ書式は、トークナイザが well-formed な opener を復元できず [[div]] 行ごと生テキストとして残ってしまっていた。[[iftags]]bare + / bare - の prefix を Wikidot 本家挙動と異なる扱いをしていた。

合わせて [[#expr]] / [[#ifexpr]] の evaluator が ! 演算子と true / false リテラルをサポートしておらず、これらを使う式 ([[#ifexpr !(false) | NOT | else ]] 等) が run-time error: Unknown character: ! で落ちていた。

変更点

1. preprocess 共通 utility の抽出

computeBracketDepths / maskRawRegions / makeUniqueSentinels / restorePlaceholderspackages/parser/src/parser/preprocess/utils.ts に切り出し、iftags 側と新規 expr 側で共有。

2. expression evaluator を @wdprlib/ast に移動

evaluateExpression / isTruthy / formatExprValue@wdprlib/render から @wdprlib/ast に集約。@wdprlib/parser から render に依存させないようにするため、評価器は ast 経由で参照する。

evaluator の機能追加:

  • ! (BANG) を unary not 演算子として認識 (!= は従来どおり)
  • true / false をリテラルとしてサポート (1 / 0 として評価)
  • parseUnary でも BANG を扱い、a != !(b) のような比較 RHS の !() を解釈
  • formatExprValueString(n) でフル精度 (以前の 6 桁固定丸めは廃止)

3. opener-embedded [[#if]] / [[#expr]] / [[#ifexpr]] の text-level 評価

packages/parser/src/parser/preprocess/expr.ts を追加し、[[# の出現位置の未閉ブラケット深度が > 0 のときに限り text-level に評価して then / else / 数値文字列に置換する。top-level の [[#if]] 等は既存の inline rule が処理するため AST に if / expr / ifexpr ノードが残る形は変わらない。

挙動の補足:

  • [[#expr(1+1)]] のようにキーワード直後に空白を持たない書式も受理する
  • empty [[#expr ]] は inline render と同じく空文字を返す
  • [[#ifexpr | ... ]] の empty expression は ERROR placeholder を残し silently 潰さない
  • ネストした [[#if [[#if ...]] | ... ]] は innermost-first で順次畳む

4. [[iftags]] の bare +/- prefix 対応

TagConditionhasEmptyRequired? / hasEmptyForbidden? を追加し、本家挙動を再現する。

  • [[iftags - ]] → 「forbid nothing」= Show Always
  • [[iftags + ]] → 「require unnamed tag」= 他にトークンがあっても常に false (Hide Always)
  • [[iftags]] (空) → supercommentout (常に false、従来どおり)

5. bun condition と src を package files に追加

bun runtime の development resolution が dist/index.js を読まずワークスペース内の src/index.ts を直接読むよう exports."." .bun を追加。publish tarball で src が引けるよう files にも追加した。通常の Node ESM / CJS consumer は従来どおり dist を読む。

検証

期待挙動の 11 ケース ([[iftags - ]] Show Always から [[#ifexpr !(true)!=!(false) | XOR ]] まで) がすべて期待値どおり出力されることを実機で確認:

Show Always
OR expr
AND expr
1 return "1"
0 return "0"
NOT expr
AND expr
OR expr
AND expr
OR expr
XOR expr

Test plan

  • bun run lint (10 warnings, 0 errors — 既存箇所のみ)
  • bun run format
  • bun run typecheck
  • bun test (1318 pass / 0 fail)
  • 新規ユニットテスト 18 ケース (tests/unit/parser/preprocess-if.test.ts):
    • opener-embedded [[#if]] の collapse / depth gating
    • top-level の [[#if]] を touch しないこと
    • 入れ子・malformed・raw region 内不可侵

r74tech added 5 commits June 10, 2026 22:12
iftags/preprocess.ts に閉じていた `computeBracketDepths` / `maskRawRegions` /
`makeUniqueSentinels` / `restorePlaceholders` を共通モジュール
`packages/parser/src/parser/preprocess/utils.ts` に移し、iftags 側は import に
切り替えた。次コミットで追加する opener-embedded [[#if]] 等の preprocess も
同じ utility を共有する。
…から共有

これまで @wdprlib/render の utils 配下にあった [[#expr]] / [[#ifexpr]] 用の
evaluator (`evaluateExpression`, `isTruthy`) と数値整形 (`formatExprValue`) を
@wdprlib/ast に集約し、render の expr 要素も新しい場所から import するように
変更した。

合わせて以下を追加・修正:

- `!` (BANG) を unary not 演算子として認識 (`!=` は従来どおり)
- `true` / `false` をリテラルとしてサポート (それぞれ 1 / 0 として評価)
- 比較演算子の RHS で `!()` が来る場合のため `parseUnary` でも BANG を扱う
  (例: `!(true)!=!(false)`)
- `formatExprValue` は `String(n)` でフル精度を保持 (以前の 6 桁丸めを廃止)

これにより render と preprocess の両方が同じ式評価ロジックを使い、出力が
場所によってずれない。
…t-level に評価

`[[div class="[[#if 1 | folded | unfolded ]]"]]` のように block opener の
属性内に埋め込まれた [[#if]] / [[#expr]] / [[#ifexpr]] は、トークナイザが
well-formed な opener を復元できず、これまで [[div]] 行ごと生テキスト化
していた。

`packages/parser/src/parser/preprocess/expr.ts` を追加し、`[[#` の出現位置の
未閉ブラケット深度 (`computeBracketDepths`) が > 0 のときに限り text-level に
評価して then / else / 数値文字列に置換する。top-level の [[#if]] 等は既存の
inline rule が処理するため AST に if/expr/ifexpr ノードが残る形は変わらない。

挙動の補足:

- `[[#expr(1+1)]]` のようにキーワード直後に空白を持たない書式も受理する
- empty `[[#expr ]]` は inline render に合わせて空文字を返す
  ([[#ifexpr | ... ]] の empty は ERROR placeholder のままにする)
- ネストした [[#if [[#if ...]] | ... ]] は innermost-first で順次畳む

`parse()` で `preprocessExpr(source)` を常時実行する。top-level 直接の
[[#if]] 等を touch しないので既存呼び出しには影響しない。
`[[iftags]]` の condition string が `+`/`-` のみ (タグ名なしのプレフィックス)
の場合、本家挙動は次のようになる:

- `[[iftags - ]]` → 「forbid nothing」= Show Always
- `[[iftags + ]]` → 「require unnamed tag」= 他にトークンがあっても常に false
  (Hide Always)
- `[[iftags]]` (空)  → supercommentout (= 常に false、従来どおり)

`TagCondition` に `hasEmptyRequired?` / `hasEmptyForbidden?` を追加して
bare prefix の有無を保持し、`evaluateTagCondition` の分岐を更新した。
`+` は他トークンと組み合わさっても常に false、`-` 単独のみ無条件 true。
bun runtime での development resolution が `dist/index.js` を読まず、
ワークスペース内の `src/index.ts` を直接読むよう `exports."." .bun` 条件を
追加した。あわせて publish tarball に src を含めるよう `files` にも src を
追加し、bun consumer が tarball install 後に bun 条件で src を解決しても
ファイルが存在する状態を保つ。

通常の Node ESM / CJS consumer は従来どおり `dist` を読むため挙動は不変。
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
wdpr-demo-v1 dafd205 Commit Preview URL

Branch Preview URL
Jun 10 2026, 01:15 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
wdpr-demo-v1-files dafd205 Commit Preview URL

Branch Preview URL
Jun 10 2026, 01:14 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
decompiler-preview dafd205 Commit Preview URL

Branch Preview URL
Jun 10 2026, 01:14 PM

@r74tech
r74tech merged commit cb4eb1c into develop Jun 10, 2026
8 checks passed
@r74tech
r74tech deleted the feat/opener-embedded-expr-eval branch June 10, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant