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?

AIとつくる日本語プログラミング言語Aoi (あおい)(ステップ9)~ 中間表現(IR)の最小実装~

1
Last updated at Posted at 2026-09-05

はじめに

生成AI(Microsoft CopilotとGitHub Copilot)に日本語プログラミング言語Aoi(あおい)の実装言語仕様を読み込ませて設計タスクから実装を展開しています。本記事ではIRの最小版を実装します。

日本語プログラミング言語 Aoi (あおい) という自作言語の企画趣旨はこちらの記事をご参照ください。言語仕様はこちらをご参照ください。開発コード名を「Re:Mind」としており、AIとのやりとりではこの名称が使われています。

生成AI

Microsoft Copilot

Windows11アプリケーションのMicrosoft Copilot。昨年末から日本語プログラミング言語 Aoi(Re:Mind)の原型を作成してもらった生成AI123。モードは「無料プラン」。サインインした状態で使用します。GitHub Copilotの使い方アドバイザーとして利用。

GitHub Copilot

GitHub Copilotのフリー版を導入。こちらの記事でAmazonのkiro-cliのskillsとかなり近いことができることが判明して結果オーライでした。スーパバイザとしてMicrosoft Copilotを使いGitHub CopilotのCodeAgent推論量を最小化するプロンプトをCopilotで生成しGitHub CopilotのCodeAgentで実行という工法?を採用しています。

本記事のステップ

こちらの記事で生成された「Re:Mind コンパイラ全体設計」を細分化したこちらの回の記事の「1. 最小セット(MVP)」の具体的な実装の途中です。今回は「中間表現(IR)」の最小版を実装します。

1. 最小セット(MVP)

まずは「1つの小さなプログラムを通せる」レベルに絞る構成。
Phase 1. Lexer タスク
前々回記事以前で完了
Phase 2. Parser タスク
前々回記事以前で完了
Phase 3. AST タスク
前々回記事で完了
Phase 4. 意味解析と名前解決
前回記事で完了

Phase 5. 中間表現(IR) タスク**

  1. AST から後続ターゲットへ落とし込むための IR を追加する。
  2. IR の設計例は以下の通り。
    • ProgramIR: namespace/import/class/method の集合
    • ClassIR: field と method の集合
    • MethodIR: parameter / local variable / body block
    • BlockIR: 文の列
    • StatementIR: assignment / call / if / while / for / return / break / continue
    • ExpressionIR: identifier / literal / binary / call / member access / array access / new
  3. AST → IR の変換は「構文情報を保持したまま、バックエンド非依存の形に近づける」設計にする。
  4. 生成器は IR を受け取って言語別に出力する構造に切り替える。

Microsoft Copilotとの事前準備

プロンプト1

Phase 5:中間表現(IR)の最小タスク化プロンプトを作成してください。

生成レスポンス1

🧩 タスク 2:StatementIR の派生ノードを追加
IR.cs に以下の StatementIR 派生ノードを差分として追加してください。

  • AssignmentIR
  • CallIR
  • IfIR
  • WhileIR
  • ReturnIR
  • BreakIR
  • ContinueIR

プロパティの枠だけ追加し、ロジックは書かないでください。
既存コードは保持してください。
🧩 タスク 3:ExpressionIR の派生ノードを追加
IR.cs に以下の ExpressionIR 派生ノードを差分として追加してください。

  • IdentifierIR
  • LiteralIR
  • BinaryIR
  • CallExpressionIR
  • MemberAccessIR
  • ArrayAccessIR
  • NewExpressionIR

プロパティの枠だけ追加し、ロジックは書かないでください。
既存コードは保持してください。
🧩 タスク 4:AST → IR 変換器(IRBuilder)の枠を作る
IRBuilder.cs を新規作成し、
AST → IR の変換を行うクラスの「枠(骨格)」だけを追加してください。

必要な構造:

  • IRBuilder クラス
  • Build(CompilationUnit ast) メソッド
  • BuildStatement(Statement stmt) の枠
  • BuildExpression(Expression expr) の枠

中身のロジックは書かず、枠だけ作成してください。
既存コードは保持してください。
🧩 タスク 5:AST → IR の最小変換(Identifier / Literal)
IRBuilder.cs に、
IdentifierExpression → IdentifierIR
LiteralExpression → LiteralIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧩 タスク 6:MemberAccess の最小変換
IRBuilder.cs に、
MemberAccessExpression → MemberAccessIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧩 タスク 7:Invocation の最小変換
IRBuilder.cs に、
InvocationExpression → CallExpressionIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧩 タスク 8:Assignment の最小変換
IRBuilder.cs に、
AssignmentStatement → AssignmentIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧩 タスク 9:If(◇)の最小変換
IRBuilder.cs に、
IfStatement → IfIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧩 タスク 10:Loop(〇) の最小変換
IRBuilder.cs に、
WhileStatement → WhileIR
への最小変換ロジックだけを差分として追加してください。
既存コードは保持し、必要な部分だけ生成してください。
🧭 mylifewithviloinさんのプロジェクトに最適な実行順序(Phase 5)
1.IR の枠(骨格)を作る
2.StatementIR の派生ノード
3.ExpressionIR の派生ノード
4.IRBuilder の枠
5.Identifier / Literal の変換
6.MemberAccess の変換
7.Invocation の変換
8.Assignment の変換
9.If の変換
10.Loop の変換
この順番は AST → IR の依存関係に完全準拠しています。

GitHub Copilot

Microsoft Copilotが生成したGitHub CopilotへのPhase 5の各タスクの実行プロンプトを逐次実行した結果、下記のソースコードが生成されました。IR.csのひな形は前回の記事で作成したものです。

IR.cs
using System.Collections.Generic;

namespace ReMindAst
{
    public class ProgramIR
    {
        public List<string> Namespaces { get; } = new();
        public List<string> Imports { get; } = new();
        public List<ClassIR> Classes { get; } = new();
    }

    public class ClassIR
    {
        public string Name { get; set; } = "";
        public List<string> Fields { get; } = new();
        public List<MethodIR> Methods { get; } = new();
    }

    public class MethodIR
    {
        public string Name { get; set; } = "";
        public string ReturnType { get; set; } = "void";
        public List<string> Parameters { get; } = new();
        public List<string> LocalVariables { get; } = new();
        public BlockIR Body { get; } = new();
    }

    public class BlockIR
    {
        public List<StatementIR> Statements { get; } = new();
    }

    public abstract class StatementIR
    {
    }

    public class AssignmentIR : StatementIR
    {
        public string Target { get; set; } = "";
        public ExpressionIR Value { get; set; } = null!;
    }

    public class CallIR : StatementIR
    {
        public string MethodName { get; set; } = "";
        public List<ExpressionIR> Arguments { get; } = new();
    }

    public class IfIR : StatementIR
    {
        public ExpressionIR Condition { get; set; } = null!;
        public BlockIR ThenBlock { get; } = new();
        public BlockIR? ElseBlock { get; set; }
    }

    public class WhileIR : StatementIR
    {
        public ExpressionIR Condition { get; set; } = null!;
        public BlockIR Body { get; } = new();
    }

    public class ReturnIR : StatementIR
    {
        public ExpressionIR? Value { get; set; }
    }

    public class BreakIR : StatementIR
    {
    }

    public class ContinueIR : StatementIR
    {
    }

    public abstract class ExpressionIR
    {
    }

    public class IdentifierIR : ExpressionIR
    {
        public string Name { get; set; } = "";
    }

    public class LiteralIR : ExpressionIR
    {
        public string Value { get; set; } = "";
    }

    public class BinaryIR : ExpressionIR
    {
        public ExpressionIR Left { get; set; } = null!;
        public string Operator { get; set; } = "";
        public ExpressionIR Right { get; set; } = null!;
    }

    public class CallExpressionIR : ExpressionIR
    {
        public string MethodName { get; set; } = "";
        public List<ExpressionIR> Arguments { get; } = new();
    }

    public class MemberAccessIR : ExpressionIR
    {
        public ExpressionIR Target { get; set; } = null!;
        public string MemberName { get; set; } = "";
    }

    public class ArrayAccessIR : ExpressionIR
    {
        public ExpressionIR Array { get; set; } = null!;
        public ExpressionIR Index { get; set; } = null!;
    }

    public class NewExpressionIR : ExpressionIR
    {
        public string TypeName { get; set; } = "";
        public List<ExpressionIR> Arguments { get; } = new();
    }
}

IRBuilder.csは前記の「タスク 4:AST → IR 変換器(IRBuilder)の枠を作る」以降のタスクで生成されました。

IRBuilder.cs
namespace ReMindAst
{
    public class IRBuilder
    {
        public ProgramIR Build(CompilationUnit ast)
        {
            throw new System.NotImplementedException();
        }

        public StatementIR BuildStatement(Statement stmt)
        {
            return stmt switch
            {
                AssignmentStatement assignment => new AssignmentIR
                {
                    Target = assignment.Left switch
                    {
                        IdentifierExpression identifier => identifier.NameEn,
                        MemberAccessExpression memberAccess => memberAccess.MemberName,
                        _ => ""
                    },
                    Value = BuildExpression(assignment.Right)
                },
                IfStatement ifStatement => BuildIfStatement(ifStatement),
                WhileStatement whileStatement => BuildWhileStatement(whileStatement),
                _ => throw new System.NotSupportedException()
            };
        }

        private IfIR BuildIfStatement(IfStatement ifStatement)
        {
            var result = new IfIR
            {
                Condition = BuildExpression(ifStatement.Condition),
                ElseBlock = new BlockIR()
            };

            foreach (var statement in ifStatement.ThenBody)
            {
                result.ThenBlock.Statements.Add(BuildStatement(statement));
            }

            foreach (var statement in ifStatement.ElseBody)
            {
                result.ElseBlock.Statements.Add(BuildStatement(statement));
            }

            return result;
        }

        private WhileIR BuildWhileStatement(WhileStatement whileStatement)
        {
            var result = new WhileIR
            {
                Condition = BuildExpression(whileStatement.Condition)
            };

            foreach (var statement in whileStatement.Body)
            {
                result.Body.Statements.Add(BuildStatement(statement));
            }

            return result;
        }

        public ExpressionIR BuildExpression(Expression expr)
        {
            return expr switch
            {
                IdentifierExpression identifier => new IdentifierIR
                {
                    Name = identifier.NameEn
                },
                LiteralExpression literal => new LiteralIR
                {
                    Value = literal.Value?.ToString() ?? ""
                },
                MemberAccessExpression memberAccess => new MemberAccessIR
                {
                    Target = BuildExpression(memberAccess.Expression),
                    MemberName = memberAccess.MemberName
                },
                InvocationExpression invocation => BuildInvocationExpression(invocation),
                _ => throw new System.NotSupportedException()
            };
        }

        private CallExpressionIR BuildInvocationExpression(InvocationExpression invocation)
        {
            var call = new CallExpressionIR
            {
                MethodName = invocation.Target switch
                {
                    IdentifierExpression identifier => identifier.NameEn,
                    MemberAccessExpression memberAccess => memberAccess.MemberName,
                    _ => ""
                }
            };

            foreach (var argument in invocation.Arguments)
            {
                call.Arguments.Add(BuildExpression(argument));
            }

            return call;
        }
    }
}

本ステップの評価

今回も基本的コメントがついていませんが、それは後で追記可能と踏んでいて、かなりよいと評価しています。先を急ぎます。サンプルソースに対してトランスコンパイルが実行できる状況づくりを目指します。

おわりに

以上です。次回の方向性はPhase 6: コード生成パイプラインの実装の実行からとなります。「最小セット(MVP)」の完成条件にだいぶ近づいてきました。

  1. Microsoft Copilotで日本語トランスコンパイラ言語 Re:Mindをいじりたおす - Qiita
    「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の認知度を確認

  2. Copilotが生成した日本語トランスコンパイラ言語 Re:MindのASTパーサが動作するまで - Qiita
    「Re:MindのASTパーサが動作するまで」の記事リストです。(ステップ降順)
    ステップ12 いったん生成
    ステップ11
    ステップ10 生成済コードの伝達完了
    ステップ9
    ステップ8
    ステップ7
    ステップ6 生成済コードの伝達開始
    ステップ5
    ステップ4 Program.csが動作
    ステップ3
    ステップ2
    ステップ1 検証環境構築

  3. Copilotが生成した日本語トランスコンパイラ言語 Re:MindのC#コード生成が動作するまで - Qiita
    「Re:MindのC#コード生成が動作するまで」の記事リストです。(ステップ降順)
    ステップ4
    ステップ3
    ステップ2
    ステップ1

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?