Skip to content

fix(dncl): treat / and ÷ as integer division#666

Merged
takaokouji merged 2 commits into
developfrom
fix/dncl-integer-division
May 10, 2026
Merged

fix(dncl): treat / and ÷ as integer division#666
takaokouji merged 2 commits into
developfrom
fix/dncl-integer-division

Conversation

@takaokouji
Copy link
Copy Markdown

Summary

DNCL の /÷ を「整数同士の割り算は整数を返す (Ruby/Python の慣習)」に修正します。共通テスト DNCL 例題のような aida = (hidari + migi) / 24.5 ではなく 4 になります。

scratch-vm の / は常に float を返す仕様 (変更不可) なので、変換層で対応します。

実装方針 (Issue 議論の E 案)

  1. D: .to_i のブロックマッピングを operator_mathop(floor) に変更 (旧: operator_add(x, 0) で truncate せず実質 no-op)。@ruby:method:to_i コメントマーカーで round-trip 時に .floor ではなく .to_i として復元。
  2. B + C: DNCL の /÷ を Ruby (a / b).to_i にラップ。新ヘルパー wrapIntegerDivisions がトップレベル / を検出して括弧バランスを取りながら左右オペランドを特定。
  3. Ruby → DNCL の round-trip で (EXPR / EXPR).to_iEXPR / EXPR に戻す (stripIntegerDivisionToI)。
  4. 後方互換: 旧形式で保存された .sb3 (operator_add(x, 0) + @ruby:method:to_i) を読み込み → ブロック→Ruby .to_i (既存 generator で対応)。Ruby .to_i → 新ブロック (operator_mathop(floor))。一方向 migration。

DNCL 入力 Ruby 出力 DNCL round-trip
aida = (hidari + migi) / 2 @aida = ((@hidari + @migi) / 2).to_i aida = (hidari + migi) / 2
a = 10 / 3 @a = (10 / 3).to_i a = 10 / 3
a = b / c * d @a = (@b / @c).to_i * @d a = b / c * d
a = b + c / d @a = @b + (@c / @d).to_i a = b + c / d
a = Data[i + 1] / 2 @a = (@_array_Data_[@i + 1] / 2).to_i a = Data[i + 1] / 2

Test plan

  • DNCL 関連 unit tests 348 件 pass
  • Ruby ↔ Blocks operators / round-trip tests 498 件 pass
  • 実機検証: ユーザの binary search プログラムで operator_divide の親が operator_mathop(floor) + @ruby:method:to_i
  • DNCL /, ÷, // がすべて (a / b).to_i にラップされる
  • Round-trip で .to_i ラッパーが除去される
  • CI pass

🤖 Generated with Claude Code

…/int)

Background: DNCL is taught alongside Python in 共通テスト curricula where
\`/\` is integer division for integer operands. Previously DNCL \`/\` mapped
to Ruby's float-returning \`/\`, so \`(hidari + migi) / 2\` produced 4.5
instead of 4 — breaking the canonical binary-search example.

scratch-vm cannot be modified; the runtime \`/\` always returns float. Fix
this entirely on the conversion layer:

- \`.to_i\` now compiles to \`operator_mathop(floor, x)\` (was a no-op
  \`operator_add(x, 0)\`). The \`@ruby:method:to_i\` comment marker preserves
  the source method name on round-trip. Legacy projects saved with the old
  \`operator_add(x, 0) + @ruby:method:to_i\` shape still emit \`.to_i\` via
  the existing compat path in operators-math-gen.js (one-way migration).
- DNCL \`/\` and \`÷\` (and existing \`//\`) all wrap their division as
  \`(a / b).to_i\` so the runtime truncates via the floor mathop. A new
  \`wrapIntegerDivisions\` helper walks the converted Ruby line and wraps
  each top-level \`/\` while skipping operands inside strings, balanced
  parens, and already-wrapped \`(... / ...).to_i\` shapes.
- Ruby → DNCL strips \`(EXPR / EXPR).to_i\` back to \`EXPR / EXPR\` so the
  \`.to_i\` doesn't surface as redundant \`整数(...)\` in the round-trip.
- Drop hardcoded (200, 0) on say/puts comments — the natural-anchor
  capture from PR #665 places them correctly now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

The two occurrences of '+ 6' computed offsets past the literal strings
').to_i' (in wrapIntegerDivisions, where the cursor was at ')') and
'.to_i' (in stripIntegerDivisionToI, where the cursor was past ')').
Replace both with .length references to the explicit string literals
so the intent is obvious.
@takaokouji takaokouji merged commit 16e18a5 into develop May 10, 2026
9 checks passed
@takaokouji takaokouji deleted the fix/dncl-integer-division branch May 10, 2026 04:07
github-actions Bot pushed a commit that referenced this pull request May 10, 2026
…ger-division

fix(dncl): treat / and ÷ as integer division
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