From b5460e6b5d55cd8b9fb8f8b43a63180ecc0a1f6d Mon Sep 17 00:00:00 2001 From: Kouji Takao Date: Sat, 11 Apr 2026 20:32:22 +0900 Subject: [PATCH] docs: add DNCL mode language specification (Japanese) Add comprehensive language specification for smalruby3-editor's DNCL (Japanese) mode, documenting syntax, operators, control flow, built-in functions, block palette restrictions, and differences from standard DNCLv2. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/smalruby-dncl-spec.ja.md | 561 ++++++++++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 docs/smalruby-dncl-spec.ja.md diff --git a/docs/smalruby-dncl-spec.ja.md b/docs/smalruby-dncl-spec.ja.md new file mode 100644 index 00000000000..acb61366a16 --- /dev/null +++ b/docs/smalruby-dncl-spec.ja.md @@ -0,0 +1,561 @@ +# Smalruby 日本語モード(DNCL)言語仕様 + +このドキュメントは、smalruby3-editor の **日本語モード(DNCLモード)** で対応している構文と機能を定義します。 +ソースコードの `packages/scratch-gui/src/lib/dncl/`(DNCL ↔ Ruby トランスパイラ)と `packages/scratch-gui/src/containers/ruby-tab/dncl-mode.js`(Monaco エディタ言語定義)に基づいています。 + +> **注意**: 本ドキュメントは **smalruby3-editor における実装**を正として記載しています。大学入学共通テスト手順記述標準言語(DNCLv2)とは異なる点があります。差分は [DNCLv2 との違い](#7-dnclv2-との違い) を参照してください。 + +## 1. 概要 + +smalruby の日本語モード(DNCLモード)は、大学入学共通テスト手順記述標準言語(DNCL)を参考にした日本語ベースのプログラミング言語です。日本語で記述したコードは内部的に Ruby コードにトランスパイルされ、さらに Scratch ブロックに変換されて実行されます。 + +### 動作の仕組み + +``` +DNCL コード → (dncl-to-ruby.js) → Ruby コード → (ruby-to-blocks-converter) → Scratch ブロック → 実行 +``` + +ブロック → Ruby → DNCL の逆方向の変換も可能です(`ruby-to-dncl.js`)。 + +### 標準 DNCL(DNCLv2)との主な違い + +- **ブロック終端が日本語キーワード**(`を実行する`、`を繰り返す`、`と定義する`)であり、コロン `:` は使わない +- **条件分岐の末尾にコロンがない**(`もし 条件 ならば` であり、`もし 条件 ならば:` ではない) +- 内部的に Scratch ブロックに変換されるため、**使用できる機能が Scratch の範囲に限定される** +- 変数に `@` や `$` プレフィックスは使えない(Ruby の変数記法は自動的に処理される) + +## 2. プログラム構造 + +### トップレベル構造 + +DNCL モードのプログラムは、文(Statement)の連続で構成されます。class 定義や module 定義は使用しません。 + +``` +# 変数の初期化 +a = 0 + +# メインロジック +もし a > 0 ならば + 表示する(a) +を実行する +``` + +### インデント + +ブロック構造はインデントで表現します。制御構造の内部は 2 スペースのインデントが推奨されます。 + +``` +もし a > 0 ならば + 表示する("正の数") +そうでなければ + 表示する("正でない") +を実行する +``` + +### コメント + +`#` 以降が行コメントになります。 + +``` +# これはコメントです +a = 1 # 行末コメント +``` + +## 3. データ型 + +### リテラル + +| 型 | 構文 | 例 | 備考 | +|---|---|---|---| +| 整数 | `数字の並び` | `42`, `0`, `-5` | | +| 浮動小数点数 | `数字.数字` | `3.14`, `1.0` | | +| 文字列 | `"..."` または `「...」` | `"hello"`, `「テスト」` | `「」` は `""` に自動変換 | +| 真偽値 | `真` / `偽` | `真`, `偽` | Ruby の `true` / `false` に変換 | +| 配列 | `[値1, 値2, ...]` | `[1, 5, 10]` | | + +### 文字列リテラル + +ダブルクォート `"..."` と日本語カギカッコ `「...」` の両方が使えます。カギカッコはトランスパイル時にダブルクォートに変換されます。 + +``` +a = "hello" +b = 「こんにちは」 # → "こんにちは" に変換 +``` + +## 4. 変数 + +### 命名規則 + +| 先頭文字 | 内部変換 | 例 | Ruby 変換後 | +|---|---|---|---| +| 小文字 (`a-z`) | インスタンス変数 | `a`, `score` | `@a`, `@score` | +| 大文字(スカラー値) | 名前付き変数 | `A`, `Max` | `@_var_A_`, `@_var_Max_` | +| 大文字(配列値) | 名前付き配列 | `Kouka`, `Data` | `@_array_Kouka_`, `@_array_Data_` | + +大文字で始まる変数名が配列かスカラーかは、ソースコード内で配列リテラルの代入(`X = [...]`)、配列アクセス(`X[i]`)、または `要素数(X)` の使用があるかどうかで自動判定されます。 + +### 識別子に使える文字 + +- ASCII 英字(`a-z`, `A-Z`)、数字(`0-9`)、アンダースコア(`_`) +- ひらがな(`\u3040-\u309F`)、カタカナ(`\u30A0-\u30FF`)、漢字(`\u4E00-\u9FFF`) + +### 禁止文字 + +変数名に `@` や `$` を使うことはできません。使用した場合、バリデーションエラーが表示されます。 + +``` +# ❌ エラー: 日本語モードでは変数に「@」は使えません +@a = 10 + +# ✅ 正しい書き方 +a = 10 +``` + +### 代入 + +| 構文 | 例 | Ruby 変換後 | +|---|---|---| +| `変数 = 式` | `a = 10` | `@a = 10` | +| `変数 ← 式` | `a ← 10` | `@a = 10` | + +矢印 `←` による代入も使用できます(`=` と同等)。 + +``` +a = 10 # 通常の代入 +A ← 3 # 矢印による代入 +Kouka = [1, 5, 10] # 配列の初期化 +``` + +## 5. 演算子 + +### 算術演算子 + +| 演算子 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `+` | 加算 / 文字列結合 | `a + b` | `@a + @b` | +| `-` | 減算 | `a - 1` | `@a - 1` | +| `*` | 乗算 | `a * 2` | `@a * 2` | +| `/` | 除算 | `a / 3` | `@a / 3` | +| `÷` | 除算(全角) | `a ÷ 3` | `@a / 3` | +| `//` | 整数除算 | `10 // 3` | `(10 / 3).to_i` | +| `%` | 剰余 | `a % 2` | `@a % 2` | + +### 比較演算子 + +| 演算子 | 説明 | 例 | +|---|---|---| +| `=` | 等しい(条件式内) | `a = 10`(※文脈依存) | +| `==` | 等しい | `a == 10` | +| `!=` | 等しくない | `a != 0` | +| `<` | 小さい | `a < 10` | +| `>` | 大きい | `a > 0` | +| `<=` | 以下 | `a <= 10` | +| `>=` | 以上 | `a >= 0` | +| `≦` | 以下(全角) | `a ≦ 10` | +| `≧` | 以上(全角) | `a ≧ 0` | + +> **注意**: `=` の代入と比較の区別は文脈(文の先頭 = 代入、条件式内 = 比較)で判断されますが、曖昧さを避けるため条件式では `==` の使用を推奨します。 + +### 論理演算子 + +| 演算子 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `かつ` | 論理積 | `a > 0 かつ b < 10` | `@a > 0 && @b < 10` | +| `または` | 論理和 | `a > 0 または b > 0` | `@a > 0 \|\| @b > 0` | +| `でない` | 論理否定(後置) | `a でない` | `!@a` | + +> **注意**: `かつ`、`または`、`でない` はスペースで区切る必要があります。 + +## 6. 制御構造 + +### 条件分岐(もし〜ならば) + +``` +もし 条件 ならば + 文... +を実行する +``` + +`なら` と `ならば` の両方が使えます。ラウンドトリップ変換では `ならば` に正規化されます。 + +#### if-else + +``` +もし 条件 ならば + 文... +そうでなければ + 文... +を実行する +``` + +#### if-elsif-else + +``` +もし 条件1 ならば + 文1... +そうでなくもし 条件2 ならば + 文2... +そうでなければ + 文3... +を実行する +``` + +### 繰り返し(条件ループ) + +#### 条件の間繰り返す(while ループ) + +``` +条件 の間 + 文... +を繰り返す +``` + +例: + +``` +a > 0 の間 + a = a - 1 +を繰り返す +``` + +#### 範囲繰り返し(for ループ) + +**昇順(増やしながら):** + +``` +変数 を 開始値 から 終了値 まで 増分 ずつ増やしながら + 文... +を繰り返す +``` + +例: + +``` +i を 1 から 10 まで 1 ずつ増やしながら + 表示する(i) +を繰り返す +``` + +**降順(減らしながら):** + +``` +変数 を 開始値 から 終了値 まで 減分 ずつ減らしながら + 文... +を繰り返す +``` + +例: + +``` +i を 10 から 0 まで 1 ずつ減らしながら + 表示する(i) +を繰り返す +``` + +> **注意**: 終了値は**含まれます**(inclusive)。`1 から 10 まで` は 1, 2, 3, ..., 10 を繰り返します。 + +### ブロック終端キーワード + +制御構造の種類に応じて、異なる終端キーワードを使います。 + +| 終端キーワード | 対象 | +|---|---| +| `を実行する` | `もし〜ならば`(条件分岐) | +| `を繰り返す` | `の間`(while ループ)、`ずつ増やしながら/減らしながら`(for ループ) | +| `と定義する` | `関数〜`(関数定義) | + +## 7. 関数 + +### 関数定義 + +``` +関数 関数名(引数1, 引数2) + 文... +と定義する +``` + +例: + +``` +関数 add(a, b) + 返す a + b +と定義する +``` + +### 戻り値 + +``` +返す 式 +``` + +例: + +``` +関数 f(x) + 返す x * 2 +と定義する +``` + +## 8. 組み込み関数 + +### 入出力 + +| 関数 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `表示する(式)` | 値を表示する | `表示する(a)` | `say(@a, 1)` | +| `【外部からの入力】` | 外部からの入力を受け取る | `a = 【外部からの入力】` | `ask_and_wait("")`
`@a = answer` | + +`表示する` は複数の引数を受け取れます: + +``` +表示する(a, b, c) # → say(@a, @b, @c, 1) +``` + +`【外部からの入力】` は代入文の右辺で使用し、Ruby では 2 行に展開されます: + +``` +a = 【外部からの入力】 +# ↓ Ruby 変換後 +# ask_and_wait("") +# @a = answer +``` + +### 型変換 + +| 関数 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `整数(式)` | 整数に変換(小数点以下切り捨て) | `整数(x)` | `@x.to_i` | +| `文字列(式)` | 文字列に変換 | `文字列(x)` | `@x.to_s` | + +### 数値 + +| 関数 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `乱数(n)` | 乱数を生成 | `乱数(10)` | `rand(10)` | + +### 配列操作 + +| 関数 | 説明 | 例 | Ruby 変換後 | +|---|---|---|---| +| `要素数(配列)` | 配列の要素数を取得 | `要素数(Kouka)` | `@_array_Kouka_.length` | + +### 配列アクセス + +配列の要素にはインデックス(0 起点)でアクセスします。 + +``` +Kouka = [1, 5, 10] +a = Kouka[0] # → 1 +Kouka[0] = 100 # 要素の代入 +``` + +## 9. ブロックパレット(使用可能なブロック) + +DNCL モードでは、Scratch のブロックパレットから使用できるブロックが制限されます。 + +### 完全に非表示のカテゴリ + +- **動き(Motion)** — 非表示 +- **音(Sound)** — 非表示 + +### 使用可能なブロック + +| カテゴリ | 使用可能なブロック | +|---|---| +| **イベント** | `旗が押されたとき` のみ | +| **制御** | `〜秒待つ`, `〜回繰り返す`, `もし〜なら`, `もし〜でなければ`, `〜まで待つ`, `〜まで繰り返す`, `〜を止める` | +| **演算** | すべて | +| **見た目** | `〜と言う`(`say`)のみ | +| **調べる** | `〜と聞いて待つ`(`ask`), `答え`(`answer`)のみ | +| **変数** | すべて(変数の作成、代入、変化、表示/非表示) | +| **リスト** | すべて(追加、削除、挿入、置換、取得、検索、長さ、含む、空判定) | +| **ブロック定義** | すべて(関数定義、関数呼び出し、引数) | + +## 10. エディタ機能 + +### シンタックスハイライト + +Monaco エディタで以下の要素がハイライトされます: + +| 要素 | トークン種別 | 色の例 | +|---|---|---| +| 制御キーワード | `keyword` | `もし`, `ならば`, `を実行する` 等 | +| 組み込み関数 | `type.identifier` | `表示する`, `要素数` 等 | +| 論理演算子 | `keyword.operator` | `かつ`, `または`, `でない` | +| 真偽値 | `constant.language` | `真`, `偽` | +| 入力プレースホルダ | `keyword` | `【外部からの入力】` | +| 文字列 | `string` | `"..."`, `「...」` | +| 数値 | `number` | `42`, `3.14` | +| コメント | `comment` | `# ...` | +| 演算子 | `operator` | `÷`, `//`, `≦`, `≧`, `←` | + +### オートインデント + +以下のキーワードの後で自動的にインデントが増加します: + +- `なら` / `ならば`(条件分岐の開始) +- `そうでなければ`(else 節) +- `そうでなくもし`(elsif 節) +- `の間`(while ループ) +- `ずつ増やしながら` / `ずつ減らしながら`(for ループ) +- `関数〜`(関数定義) + +以下のキーワードでインデントが自動的に減少します: + +- `を実行する` +- `を繰り返す` +- `と定義する` +- `そうでなければ` +- `そうでなくもし` + +### 自動補完スニペット + +以下のスニペットが入力候補として表示されます: + +| トリガー | 展開 | +|---|---| +| `もし...ならば` | `もし 条件 ならば`
` `
`を実行する` | +| `もし...そうでなければ` | `もし 条件 ならば`
` `
`そうでなければ`
` `
`を実行する` | +| `繰り返し(増やす)` | `i を 1 から 10 まで 1 ずつ増やしながら`
` `
`を繰り返す` | +| `繰り返し(減らす)` | `i を 10 から 0 まで 1 ずつ減らしながら`
` `
`を繰り返す` | +| `条件の間繰り返す` | `条件 の間`
` `
`を繰り返す` | +| `関数定義` | `関数 名前(引数)`
` `
`と定義する` | +| `表示する` | `表示する()` | +| `【外部からの入力】` | `変数 = 【外部からの入力】` | +| `要素数` | `要素数(配列名)` | +| `整数` | `整数(値)` | +| `文字列` | `文字列(値)` | +| `乱数` | `乱数(範囲)` | + +### 括弧の自動閉じ + +以下のペアが自動で閉じられます: + +- `()`, `[]`, `{}` +- `""`, `''` +- `「」`, `【】` + +## 11. DNCLv2 との違い + +smalruby3-editor の日本語モードは DNCLv2 を参考にしていますが、以下の点で異なります。 + +### 構文の違い + +| 項目 | DNCLv2 | smalruby3-editor | +|---|---|---| +| 条件分岐の開始 | `もし 条件 ならば:` | `もし 条件 ならば`(コロンなし) | +| 繰り返しの開始 | `〜の間繰り返す:` | `条件 の間`(末尾の「繰り返す」とコロンなし) | +| for ループの開始 | `〜ずつ増やしながら繰り返す:` | `〜ずつ増やしながら`(末尾の「繰り返す」とコロンなし) | +| 関数定義の開始 | `関数名(引数) を定義する:` | `関数 関数名(引数)`(`関数` キーワードが先頭、コロンなし) | +| ブロック終端 | インデント戻りで暗黙終了 | `を実行する` / `を繰り返す` / `と定義する` で明示終了 | +| 代入(自然言語風) | `変数 に 式 を入れる` | 未対応(`=` または `←` のみ) | +| 配列宣言 | `要素数 n の配列 A` | 未対応(配列リテラル `A = [...]` で初期化) | +| else 節 | `そうでない` | `そうでなければ` | +| 等価比較 | `=`(条件式内で比較) | `==`(推奨)、`=` も使用可能だが曖昧 | + +### 機能の違い + +| 項目 | DNCLv2 | smalruby3-editor | +|---|---|---| +| 実行環境 | コンソール入出力 | Scratch ステージ上で実行 | +| 入出力 | `表示する(式)` で標準出力 | `表示する(式)` → `say(式, 1)` で吹き出し表示 | +| 入力 | `外部からの入力()` | `【外部からの入力】`(隅付きカッコ記法) | +| 戻り値 | `式 を返す` | `返す 式`(語順が逆) | +| 整数除算 | `//` | `//`(同じ、`.to_i` に変換) | +| 全角演算子 | 未定義 | `÷`(除算), `≦`(以下), `≧`(以上), `←`(代入)に対応 | +| スコープ | グローバル/ローカル | すべて Scratch のスプライト変数に変換 | +| 動き/音/見た目 | 対象外 | Scratch の一部ブロック(`say` のみ)が使用可能 | +| 配列操作 | `要素数(A)` 等 | `要素数(A)` 対応、Scratch のリストブロックも使用可能 | +| イベント駆動 | なし | `旗が押されたとき` ブロックを使用可能 | + +### 内部実装の特徴 + +smalruby3-editor の DNCL モードは、以下のアーキテクチャで動作します: + +1. **双方向トランスパイル**: DNCL ↔ Ruby の相互変換が可能(ラウンドトリップ対応) +2. **ソースマップ**: `【外部からの入力】` が 2 行に展開される際の行番号マッピングを管理 +3. **バリデーション**: `@` や `$` の禁止文字チェックをトランスパイル前に実行 +4. **配列名の自動検出**: ソース全体をスキャンして大文字変数の配列/スカラー判定を行う + +## 12. サポートされていない構文 + +以下の構文は日本語モード(DNCL モード)では**使用できません**: + +- class 定義 / module 定義 / include +- イベントハンドラ(`旗が押されたとき` 以外) +- 動き関連メソッド(`move`, `turn_right` 等) +- 音関連メソッド(`play`, `stop_all_sounds` 等) +- 見た目の大部分(`say` 以外の `think`, `show`, `hide` 等) +- 調べるの大部分(`ask`/`answer` 以外の `touching?`, `mouse.x` 等) +- `self.属性 = 値` 形式のプロパティ設定 +- 正規表現 +- `for` / `each` ループ +- `loop do...end`(無限ループ) +- `unless` / `case-when` +- 修飾子 if / unless +- `super` +- 複合代入演算子(`+=`, `-=` 等) +- `「変数 に 式 を入れる」` 形式の自然言語代入(DNCLv2 にはあるが未対応) + +## 13. 完全なサンプルプログラム + +### FizzBuzz + +``` +i を 1 から 30 まで 1 ずつ増やしながら + もし i % 15 == 0 ならば + 表示する("FizzBuzz") + そうでなくもし i % 3 == 0 ならば + 表示する("Fizz") + そうでなくもし i % 5 == 0 ならば + 表示する("Buzz") + そうでなければ + 表示する(i) + を実行する +を繰り返す +``` + +### 配列の合計 + +``` +Data = [3, 1, 4, 1, 5, 9, 2, 6] +合計 = 0 +i を 0 から 要素数(Data) - 1 まで 1 ずつ増やしながら + 合計 = 合計 + Data[i] +を繰り返す +表示する(合計) +``` + +### 関数定義と呼び出し + +``` +関数 最大値(a, b) + もし a > b ならば + 返す a + そうでなければ + 返す b + を実行する +と定義する + +x = 【外部からの入力】 +y = 【外部からの入力】 +表示する(最大値(整数(x), 整数(y))) +``` + +### 線形探索 + +``` +Data = [5, 3, 8, 1, 9, 2, 7] +key = 【外部からの入力】 +key = 整数(key) +found = 偽 +i を 0 から 要素数(Data) - 1 まで 1 ずつ増やしながら + もし Data[i] == key ならば + 表示する(文字列(i) + "番目に見つかりました") + found = 真 + を実行する +を繰り返す +もし found でない ならば + 表示する("見つかりませんでした") +を実行する +```