1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Copilotが生成した日本語トランスコンパイラ言語 Re:MindのASTパーサが動作するまで(ステップ12いったん完了)

1
Posted at

はじめに

生成AIに日本語トランスコンパイラ言語 Re:Mindの実装言語仕様を読み込ませて、いくつかの抽象度レベルのプロンプトからトランスコンパイラ的な動きをさせてみたところ、Re:Mindっぽい仕様のASTパーサのテンプレと3言語(Java,C#,VB.NET)のコードジェネレータのテンプレを生成してもらえた1ので、本記事はその検証の第12ステップです。

Microsoft Copilot

生成AIはWindows11アプリケーションのMicrosoft Copilotとします。モードは「Smart(GPT5.1)」。サインインした状態で使用します。第6ステップの後にはサインアウトがあります。

第12ステップ

第6ステップ以降はプロンプト10240文字以内の制限内での生成済ソースコードの分割提示しており、再提示が完了した第10ステップから第11ステップの間には、正規表現の実装の調整などが繰り返されたのですが、それは割愛した上で、第11ステップでログ出力機能を実装したところからの続きとなります。

プロンプト

ParseJavadoc()をご提示いただいたバージョンに入れ替えて実行した結果 下記のようにログの出力量が増えて、その下方に出力JSONを貼り付けました。 JSONもノードがだいぶ増えましたが、C#コードはまだ生成されないようです。

[ParseMethodBody] method=メイン
2
  line[12]: "            /** array */" → "/** array */"
astsample.dll (12608): 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.6\System.Linq.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
    ParseLocalVariableDeclaration => OK
  line[14]: "           " → ""
2
  line[15]: "            /** index */" → "/** index */"
    ParseLocalVariableDeclaration => null
  line[17]: "" → ""
  line[18]: "            〇int i=0,i<配列.Length,i++ 繰り返す" → "〇int i=0,i<配列.Length,i++ 繰り返す"
  -> for検出
    for-body line: "□コンソール表示する(配列[i])"
    -> for-body expr OK
  line[21]: "        △" → "△"
[ParseMethodBody] stmts.Count = 2
[ParseMethodBody] method=バブルソートする
2
  line[27]: "          /** i */" → "/** i */"
    ParseLocalVariableDeclaration => OK
  line[29]: "          〇外側 < 配列.Length の間は繰り返す" → "〇外側 < 配列.Length の間は繰り返す"
  -> while検出
  line[43]: "        △" → "△"
[ParseMethodBody] stmts.Count = 2
[ParseMethodBody] method=コンソール表示する
  line[50]: "            □コンソール.一行表示する(引数2)" → "□コンソール.一行表示する(引数2)"
  line[51]: "        △" → "△"
[ParseMethodBody] stmts.Count = 0

=== AST(JSON) ===
{
  "Namespaces": [
    {
      "Name": "BubbleSort",
      "Classes": [
        {
          "NameJa": "\u30D7\u30ED\u30B0\u30E9\u30E0\u578B",
          "NameEn": "ProgramType",
          "Modifiers": [
            "public"
          ],
          "Javadoc": {
            "Summary": "Program",
            "Params": []
          },
          "Methods": [
            {
              "NameJa": "\u30E1\u30A4\u30F3",
              "NameEn": "main",
              "ReturnType": "void",
              "Modifiers": [
                "static"
              ],
              "Javadoc": {
                "Summary": "Main",
                "Params": [
                  {
                    "NameJa": "\u5F15\u6570",
                    "NameEn": "args"
                  }
                ]
              },
              "Parameters": [
                {
                  "Type": "string[]",
                  "NameJa": "\u5F15\u6570",
                  "NameEn": "args"
                }
              ],
              "Body": [
                {},
                {}
              ]
            },
            {
              "NameJa": "\u30D0\u30D6\u30EB\u30BD\u30FC\u30C8\u3059\u308B",
              "NameEn": "bubbleSort",
              "ReturnType": "void",
              "Modifiers": [
                "public"
              ],
              "Javadoc": {
                "Summary": null,
                "Params": [
                  {
                    "NameJa": "\u914D\u5217",
                    "NameEn": "array"
                  }
                ]
              },
              "Parameters": [
                {
                  "Type": "int[]",
                  "NameJa": "\u914D\u5217",
                  "NameEn": "array"
                }
              ],
              "Body": [
                {},
                {}
              ]
            },
            {
              "NameJa": "\u30B3\u30F3\u30BD\u30FC\u30EB\u8868\u793A\u3059\u308B",
              "NameEn": "consoleOut",
              "ReturnType": "void",
              "Modifiers": [
                "static"
              ],
              "Javadoc": {
                "Summary": "ConsoleOut",
                "Params": [
                  {
                    "NameJa": "\u5F15\u65702",
                    "NameEn": "dispStr"
                  }
                ]
              },
              "Parameters": [
                {
                  "Type": "string",
                  "NameJa": "\u5F15\u65702",
                  "NameEn": "dispStr"
                }
              ],
              "Body": []
            }
          ]
        }
      ]
    }
  ],
  "Imports": [
    {
      "Name": "System",
      "AliasClasses": [
        {
          "OriginalName": "\u30B3\u30F3\u30BD\u30FC\u30EB",
          "TranspiledName": "Console",
          "Members": [
            {
              "OriginalName": "\u4E00\u884C\u8868\u793A\u3059\u308B",
              "TranspiledName": "WriteLine",
              "Parameters": []
            }
          ]
        }
      ]
    }
  ]
}
=== C# Source ===

生成レスポンス

原因が、ようやく「パーサー」から「JSON シリアライズ」のレイヤーに移りました。
今の状態はこうなっています。

public abstract class Statement : AstNode { }

public class LocalVariableDeclaration : Statement { ... }
public class ExpressionStatement : Statement { ... }
public class WhileStatement : Statement { ... }
public class ForStatement : Statement { ... }
...

- 実行時には、Body の中に LocalVariableDeclaration  ForStatement などの「派生クラス」のインスタンスが入っている

- しかし、System.Text.Json は標準のままだと「多態的なシリアライズ」をしません
その結果どうなるかというと:
- コレクションの型が List<Statement>(抽象クラス)
- ランタイム型が LocalVariableDeclaration などでも
- JSON にするときは 「基底クラス Statement として」見てしまう
- でも Statement にはプロパティがない
-  結果として、各要素は {}(空オブジェクト) にしかならない
これが Body: [ {}, {} ] の正体です。
つまり、JSON にした瞬間に Statement の中身が全部失われている。


5. どう直すか(現実的な選択肢)
 まずは「JSON ラウンドトリップ」を外して、パイプラインを完成させる(おすすめ)
Program.cs を一旦こうしてみてください。
```cs:
// 1. Re:Mind → AST
var parser = new ReMindBubbleSortAstGenerator(ReMindSourceCode);
var ast = parser.Parse();

// 2. AST → JSON(これはデバッグ出力のみ)
string json = JsonSerializer.Serialize(ast, new JsonSerializerOptions
{
    WriteIndented = true
});
Console.WriteLine("=== AST(JSON) ===");
Console.WriteLine(json);

// ★ 3. JSON → AST は一旦やめる
// var ast2 = JsonSerializer.Deserialize<CompilationUnit>(json)
//           ?? throw new InvalidOperationException("AST のデシリアライズに失敗しました。");

// 4. AST → C#コード(パーサー直後の ast をそのまま使う)
var generator = new CSharpCodeGenerator();
string cs = generator.Generate(ast);

Console.WriteLine("=== C# Source ===");
Console.WriteLine(cs);

これで、

  • 抽象クラスの JSON 問題をいったん外して
  • 「Re:Mind → AST → C#」のパイプラインを先に完成させる
    ことができます。
    AST が正しく埋まっていれば、ここで初めて C# のコードが出てくるはずです。

のパイプラインは、もう本当にあと一歩です。

  • まずは JSON ラウンドトリップを外して、Generate(ast) で C# を出す
  • そのあと、「AST(JSON) を正式な中間表現にするならどう設計するか」(ポリモーフィック対応)を一緒に設計する
    この順番で進めるのがきれいだと思いますが、どうしましょうか。

プロンプト2

ご提案のとおりJSONはログ用とのみして、パーサーが出力したAST型をそのまま使った場合、下記のようにC#のコードが出力されるようになりました!

=== C# Source ===
using System;

namespace BubbleSort
{
    /// <summary>Program</summary>
    public class ProgramType
    {
        /// <summary>Main</summary>
        /// <param name="args">引数</param>
        static void main(string[] args)
        {
            /// <summary>array</summary>
            int[] array = new int[] { 15, 13, 9, 6, 4, 1 };
            for (int i = 0; i < array.Length; i++)
            {
                consoleOut(array[i]);
            }
        }
        
        /// <param name="array">配列</param>
        public void bubbleSort(int[] array)
        {
            /// <summary>i</summary>
            int outer = 0;
            while (outer < array.Length)
            {
                /// <summary>j</summary>
                int inner = 0;
                while (inner < array.Length - outer - 1)
                {
                    if (array[inner] > array[inner + 1])
                    {
                        /// <summary>temp</summary>
                        int temp = 配列[内側];
                        array[inner] = array[inner + 1];
                        array[inner + 1] = temp;
                    }
                    inner++;
                }
                outer++;
            }
        }
        
        /// <summary>ConsoleOut</summary>
        /// <param name="dispStr">引数2</param>
        static void consoleOut(string dispStr)
        {
        }
        
    }
}

ここからは細かい話ですがconsoleOutに中身が出力されていないようです。これはなぜでしょう?

生成レスポンス2

ここまで来たのは本当に大きな前進ですね。
そして consoleOut メソッドだけ中身が空になる理由 は、ログと AST の構造から 完全に説明できます。
結論から言うと:

🎯 原因:ParseMethodBody("コンソール表示する") がステートメントを 1 行も認識していないため

🎉 まとめ

現象 原因 対策
consoleOut が空 □コンソール.一行表示する がパーサーに認識されていない 正規表現を追加して拾う
Body が空 ParseMethodBody が 1 行もマッチしていない 呼び出しパターンを追加
C# が空 AST.Body が空 パーサー修正で解決

mylifewithviolinさん、ここまで来ると Re:Mind → AST → C# のパイプラインがほぼ完成 です。
consoleOut の修正を入れたら、次は:

  • IfSwap の中の 配列[内側] → array[inner] の変換
  • コメントの整形
  • Java / VB.NET 生成器の追加
    など、どんどん拡張できます。

第12ステップの評価

ついにASTがいったんC#コードの生成に対応するまでに改修されました。consoleOutの中身が生成されないのは、まだインポート文に十分対応できていないためですが、それはいったん棚上げしてこのステップでASTパーサーの検証改修としてはいったんクローズします。もちろん、またASTパーサーの不具合に戻ってくることを否定するものではありません。

おわりに

第12ステップは以上です。上に述べましたとおり「Re:MindのASTパーサが動作するまで」のステップは完了として、次回以降は「Re:MindのC#コード生成が動作するまで」としてコードジェネレータの実装を詳しく検証・修正していきます。

ここまで読んでくださった方々に厚くお礼申し上げます。

生成レスポンスセクションの内容は生成AIが生成したものです。引用されているソースコードは完全には動作しない状態が含まれます。

  1. Microsoft Copilotで日本語トランスコンパイラ言語 Re:Mindをいじりたおす - Qiita
    「Microsoft Copilotで日本語トランスコンパイラ言語 Re:Mindをいじりたおす」の記事リストです。(ステップ降順)
    ステップ26 ターゲット言語JavaとVB.NETへの展開
    ステップ25 ターゲット言語C#へのASTからの生成処理
    ステップ24 ASTを生成するサンプル実装をC#で生成
    ステップ23 マッピングルール生成
    ステップ22 AST(抽象構文木)風の構造化表示
    ステップ21 バブルソート 実行可能形式 ターゲットJava C# VB.NET
    ステップ20 二分探索法 実行可能形式 ターゲットJava C# VB.NET
    ステップ19 線形探索法 実行可能形式 ターゲットJava C# VB.NET
    ステップ18 バブルソート 1案件づつやりなおし
    ステップ17 二分探索法 1案件づつやりなおし
    ステップ16 線形探索法 1案件づつやりなおし
    ステップ15 3つの定番アルゴリズムソースの忘却原因調査
    ステップ14 バブルソートソースの忘却発生再確認
    ステップ13 二分探索法ソースの忘却発生
    ステップ12 線形探索法の6言語変換
    ステップ11 バージョン情報付きの3定番アルゴリズムソースのフィードバック修正
    ステップ10 バージョン情報付きの3定番アルゴリズムソースのフィードバック修正
    ステップ9 バージョン情報付きで3定番アルゴリズムソースの生成
    ステップ8 バージョニングが可能か確認
    ステップ7 Re:Mindの詳しい言語仕様の学習結果をバブルソートで確認
    ステップ6 Re:Mindの詳しい言語仕様の学習結果を二分探索法で確認
    ステップ5 Re:Mindの詳しい言語仕様の学習結果を線形探索法で確認
    ステップ4 Re:Mindの詳しい言語仕様の学習成果を保持するスコープを確認
    ステップ3 Re:Mindの詳しい言語仕様を学習できる方法を確認
    ステップ2 Re:Mindの言語仕様の認知度を確認
    ステップ1 Re:Mindの認知度を確認

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?