はじめに
生成AI(Microsoft CopilotとGitHub Copilot)に日本語プログラミング言語Aoi(あおい)の実装言語仕様を読み込ませて設計タスクから実装を展開しています。本記事ではコード生成パイプラインの最小版を実装します。
日本語プログラミング言語 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)」の具体的な実装の途中です。今回は「コード生成パイプライン」の最小版を実装します。
1. 最小セット(MVP)
まずは「1つの小さなプログラムを通せる」レベルに絞る構成。
Phase 1. Lexer タスク
前々回記事以前で完了
Phase 2. Parser タスク
前々回記事以前で完了
Phase 3. AST タスク
前々回記事以前で完了
Phase 4. 意味解析と名前解決
前々回記事で完了
Phase 5. 中間表現(IR) タスク
前回記事で完了
Phase 6. コード生成パイプライン
- 既存の CSharpCodeGenerator.cs / JavaCodeGenerator.cs / VbNetCodeGenerator.cs を、AST 直生成から IR ベース生成へ切り替える。
- 各バックエンドで共通に使える出力ユーティリティを整える。
- 実装対象は以下の順で進める。
- まず C# を安定化する
- 次に Java を追加する
- 最後に VB.NET を追加する
- 各生成器で実装する主な変換規則を整理する。
- 変数宣言: 型と初期化式の変換
- 配列: C# int[] / Java int[] / VB.NET Integer()
- 代入・演算: 中置演算子の変換
- 条件・ループ: if / while / for の変換
- 呼び出し: メソッド呼び出し・メンバー呼び出し・
Microsoft Copilotとの事前準備
プロンプト1
Phase 5: 中間表現(IR)が完了しました。
Phase 6: コード生成パイプラインのMVPタスクの最小プロンプトを作成してください。
生成レスポンス1
IR がすでに完成しているので、ここからは 既存の CSharp / Java / VB.NET のコード生成器を IR ベースに切り替える作業になります。
MVP の目的は:
まず C# の IR ベース生成を安定化
次に Java
最後に VB.NET
例外・switch は後続フェーズで扱う
という順番で進めることです。以下はすべて 既存の CSharpCodeGenerator.cs / JavaCodeGenerator.cs / VbNetCodeGenerator.cs に差分追加する形式です。
クレジット消費を最小化するために、1タスク=1機能だけを追加するように設計しています。🧩 タスク 1:CSharpCodeGenerator を IR ベースに切り替える(枠だけ)
既存の CSharpCodeGenerator.cs を、
AST 直生成から IR ベース生成へ切り替えるための「枠(骨格)」だけを差分追加してください。必要な構造:
- Generate(ProgramIR program) の枠
- GenerateClass(ClassIR classIR) の枠
- GenerateMethod(MethodIR methodIR) の枠
- GenerateStatement(StatementIR stmt) の枠
- GenerateExpression(ExpressionIR expr) の枠
中身のロジックは書かず、枠だけ追加してください。
既存コードは保持してください。
🧩 タスク 2:C# の変数宣言(IR → C#)の最小変換
CSharpCodeGenerator.cs に、
VariableDeclarationIR → "型 名 = 初期値;" の最小変換ロジックだけを差分追加してください。
既存コードは保持し、必要な部分だけ生成してください。
以下では「既存コードは保持し、必要な部分だけ生成してください。」を割愛しています。
🧩 タスク 3:C# の配列生成(IR → C#)の最小変換
CSharpCodeGenerator.cs に、
ArrayCreationExpressionIR → "型[] 名 = new 型[サイズ];" の最小変換ロジックだけを差分追加してください。
🧩 タスク 4:C# の代入(AssignmentIR → C#)の最小変換
CSharpCodeGenerator.cs に、
AssignmentIR → "左辺 = 右辺;" の最小変換ロジックだけを差分追加してください。
🧩 タスク 5:C# の条件(IfIR → C#)の最小変換
CSharpCodeGenerator.cs に、
IfIR → "if (条件) { ... }" の最小変換ロジックだけを差分追加してください。
🧩 タスク 6:C# のループ(WhileIR → C#)の最小変換
CSharpCodeGenerator.cs に、
WhileIR → "while (条件) { ... }" の最小変換ロジックだけを差分追加してください。
🧩 タスク 7:C# の呼び出し(CallExpressionIR → C#)の最小変換
CSharpCodeGenerator.cs に、
CallExpressionIR → "対象(引数...)" の最小変換ロジックだけを差分追加してください。
AliasClass の解決済み名をそのまま使う構造にしてください。
CSharpCodeGenerator.cs に、
MemberAccessIR → "対象.メンバー" の最小変換ロジックだけを差分追加してください。
🧩 タスク 9:JavaCodeGenerator の IR ベース切り替え(枠だけ)
JavaCodeGenerator.cs に、
IR ベース生成へ切り替えるための「枠(骨格)」だけを差分追加してください。
Generate(ProgramIR) / GenerateClass / GenerateMethod / GenerateStatement / GenerateExpression
の枠だけ追加し、中身は書かないでください。
🧩 タスク 10:VB.NET CodeGenerator の IR ベース切り替え(枠だけ)
VbNetCodeGenerator.cs に、
IR ベース生成へ切り替えるための「枠(骨格)」だけを差分追加してください。
Generate(ProgramIR) / GenerateClass / GenerateMethod / GenerateStatement / GenerateExpression
の枠だけ追加し、中身は書かないでください。
GitHub Copilot
Microsoft Copilotが生成したGitHub CopilotへのPhase 6の各タスクの実行プロンプトを逐次実行した結果、下記のソースコードが生成されました。
既存の CSharpCodeGenerator.cs / JavaCodeGenerator.cs / VbNetCodeGenerator.cs(の原型)は以前Microsoft Copilotが生成したものです。
CSharpCodeGenerator.cs
using System;
using System.Linq;
using ReMindAst;
namespace ReMindBackend
{
public class CSharpCodeGenerator
{
private readonly IndentWriter _w = new();
private Dictionary<string, AliasClass> _aliasMap;
public string Generate(CompilationUnit cu)
{
_aliasMap = BuildAliasMap(cu);
// using
foreach (var imp in cu.Imports)
{
_w.WriteLine($"using {imp.Name};");
}
_w.WriteLine();
// namespace
foreach (var ns in cu.Namespaces)
{
_w.WriteLine($"namespace {ns.Name}");
_w.WriteLine("{");
_w.Indent();
foreach (var cls in ns.Classes)
{
GenerateClass(cls);
}
_w.Unindent();
_w.WriteLine("}");
}
return _w.ToString();
}
public string Generate(ProgramIR program)
{
return string.Empty;
}
private void GenerateClass(ClassIR classIR)
{
}
private void GenerateMethod(MethodIR methodIR)
{
}
private void GenerateStatement(StatementIR stmt)
{
if (stmt is VariableDeclarationIR variable)
{
_w.WriteLine($"{variable.Type} {variable.Name} = {GenerateExpression(variable.Initializer)};");
}
if (stmt is AssignmentIR assignment)
{
_w.WriteLine($"{assignment.Target} = {GenerateExpression(assignment.Value)};");
}
if (stmt is IfIR ifStatement)
{
_w.WriteLine($"if ({GenerateExpression(ifStatement.Condition)})");
_w.WriteLine("{");
_w.Indent();
foreach (var statement in ifStatement.ThenBlock.Statements)
{
GenerateStatement(statement);
}
_w.Unindent();
_w.WriteLine("}");
}
if (stmt is WhileIR whileStatement)
{
_w.WriteLine($"while ({GenerateExpression(whileStatement.Condition)})");
_w.WriteLine("{");
_w.Indent();
foreach (var statement in whileStatement.Body.Statements)
{
GenerateStatement(statement);
}
_w.Unindent();
_w.WriteLine("}");
}
}
private string GenerateExpression(ExpressionIR expr)
{
return expr switch
{
NewExpressionIR arrayCreation => $"new {arrayCreation.TypeName}[{GenerateExpression(arrayCreation.Arguments[0])}]",
CallExpressionIR call => $"{call.MethodName}({string.Join(", ", call.Arguments.Select(GenerateExpression))})",
MemberAccessIR memberAccess => $"{GenerateExpression(memberAccess.Target)}.{memberAccess.MemberName}",
_ => string.Empty
};
}
private Dictionary<string, AliasClass> BuildAliasMap(CompilationUnit cu)
{
var map = new Dictionary<string, AliasClass>();
foreach (var import in cu.Imports)
{
foreach (var alias in import.AliasClasses)
{
map[alias.OriginalName] = alias;
}
}
return map;
}
private void GenerateClass(ClassDeclaration cls)
{
// XML コメント
if (cls.Javadoc != null)
WriteXmlComment(cls.Javadoc);
// public class ProgramType
var mods = string.Join(" ", cls.Modifiers);
_w.WriteLine($"{mods} class {cls.NameEn}");
_w.WriteLine("{");
_w.Indent();
foreach (var m in cls.Methods)
{
GenerateMethod(m);
_w.WriteLine();
}
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateMethod(MethodDeclaration m)
{
if (m.Javadoc != null)
WriteXmlComment(m.Javadoc);
var mods = string.Join(" ", m.Modifiers);
var paramList = string.Join(", ",
m.Parameters.Select(p => $"{MapType(p.Type)} {p.NameEn}"));
_w.WriteLine($"{mods} {MapType(m.ReturnType)} {m.NameEn}({paramList})");
_w.WriteLine("{");
_w.Indent();
foreach (var stmt in m.Body)
{
GenerateStatement(stmt);
}
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateStatement(Statement stmt)
{
switch (stmt)
{
case LocalVariableDeclaration v:
GenerateLocalVariable(v);
break;
case ExpressionStatement es:
_w.WriteLine($"{GenerateExpression(es.Expression)};");
break;
case ForStatement fs:
GenerateFor(fs);
break;
case WhileStatement ws:
GenerateWhile(ws);
break;
case IfStatement ifs:
GenerateIf(ifs);
break;
case AssignmentStatement ass:
_w.WriteLine($"{GenerateExpression(ass.Left)} = {GenerateExpression(ass.Right)};");
break;
default:
_w.WriteLine("// 未対応ステートメント");
break;
}
}
private void GenerateLocalVariable(LocalVariableDeclaration v)
{
if (v.Javadoc != null)
WriteXmlComment(v.Javadoc);
if (v.Initializer != null)
{
_w.WriteLine($"{MapType(v.Type)} {v.NameEn} = {GenerateExpression(v.Initializer)};");
}
else
{
_w.WriteLine($"{MapType(v.Type)} {v.NameEn};");
}
}
private void GenerateFor(ForStatement fs)
{
var init = $"{MapType(fs.Initializer.Type)} {fs.Initializer.NameEn} = {GenerateExpression(fs.Initializer.Initializer)}";
var cond = GenerateExpression(fs.Condition);
var iter = GenerateExpression(fs.Iterator);
_w.WriteLine($"for ({init}; {cond}; {iter})");
_w.WriteLine("{");
_w.Indent();
foreach (var s in fs.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateWhile(WhileStatement ws)
{
_w.WriteLine($"while ({GenerateExpression(ws.Condition)})");
_w.WriteLine("{");
_w.Indent();
foreach (var s in ws.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateIf(IfStatement ifs)
{
_w.WriteLine($"if ({GenerateExpression(ifs.Condition)})");
_w.WriteLine("{");
_w.Indent();
foreach (var s in ifs.ThenBody)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
if (ifs.ElseBody.Any())
{
_w.WriteLine("else");
_w.WriteLine("{");
_w.Indent();
foreach (var s in ifs.ElseBody)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
}
private string GenerateExpression(Expression expr)
{
return expr switch
{
IdentifierExpression id => id.NameEn,
LiteralExpression lit => lit.Value is string s ? $"\"{s}\"" : lit.Value!.ToString()!,
BinaryExpression bin => $"{GenerateExpression(bin.Left)} {bin.Operator} {GenerateExpression(bin.Right)}",
UnaryExpression un => $"{GenerateExpression(un.Operand)}{un.Operator}",
MemberAccessExpression ma => $"{GenerateExpression(ma.Expression)}.{ma.MemberName}",
ElementAccessExpression ea => $"{GenerateExpression(ea.ArrayExpression)}[{GenerateExpression(ea.IndexExpression)}]",
InvocationExpression inv => GenerateInvocation(inv, _aliasMap),
ArrayLiteralExpression arr => $"new {arr.ElementType}[] {{ {string.Join(", ", arr.Elements.Select(GenerateExpression))} }}",
_ => "/* 未対応式 */"
};
}
private string GenerateInvocation(InvocationExpression expr, Dictionary<string, AliasClass> aliasMap)
{
// ターゲットが MemberAccessExpression の場合(例:コンソール.一行表示する)
if (expr.Target is MemberAccessExpression ma)
{
// 左側(コンソール)
if (ma.Expression is IdentifierExpression id &&
aliasMap.TryGetValue(id.NameJa, out var alias))
{
// メソッド名(例:一行表示する)
var originalMember = ma.MemberName;
// マッピングを探す
var member = alias.Members
.FirstOrDefault(m => m.OriginalName == originalMember);
var memberName = member?.TranspiledName ?? originalMember;
return $"{alias.TranspiledName}.{memberName}({GenerateArgs(expr.Arguments)})";
}
}
// ターゲットが IdentifierExpression の場合(例:バブルソートする)
if (expr.Target is IdentifierExpression id2)
{
if (aliasMap.TryGetValue(id2.NameJa, out var alias))
{
// メソッド名は expr.Target.NameEn ではなく、expr.NameEn を使うべき
return $"{alias.TranspiledName}.{id2.NameEn}({GenerateArgs(expr.Arguments)})";
}
}
// fallback
return $"{GenerateExpression(expr.Target)}({GenerateArgs(expr.Arguments)})";
}
private string GenerateArgs(IEnumerable<Expression> args)
{
return string.Join(", ", args.Select(GenerateExpression));
}
private string GetInvocationMemberName(InvocationExpression expr)
{
return expr.Target switch
{
MemberAccessExpression ma => ma.MemberName,
IdentifierExpression id => id.NameEn, // fallback
_ => ""
};
}
private void WriteXmlComment(JavadocComment j)
{
if (!string.IsNullOrEmpty(j.Summary))
_w.WriteLine($"/// <summary>{j.Summary}</summary>");
foreach (var p in j.Params)
_w.WriteLine($"/// <param name=\"{p.NameEn}\">{p.NameJa}</param>");
}
private string MapType(string t)
{
return t switch
{
"string" => "string",
"string[]" => "string[]",
"int" => "int",
"int[]" => "int[]",
"void" => "void",
_ => t
};
}
}
}
JavaCodeGenerator.cs
using System;
using System.Linq;
using ReMindAst;
namespace ReMindBackend
{
public class JavaCodeGenerator
{
private readonly IndentWriter _w = new();
public string Generate(CompilationUnit cu)
{
// imports → import System; など
foreach (var imp in cu.Imports)
{
_w.WriteLine($"import {imp.Name};");
}
_w.WriteLine();
// namespace → package
foreach (var ns in cu.Namespaces)
{
_w.WriteLine($"package {ns.Name};");
_w.WriteLine();
foreach (var cls in ns.Classes)
{
GenerateClass(cls);
_w.WriteLine();
}
}
return _w.ToString();
}
public string Generate(ProgramIR program)
{
return string.Empty;
}
private void GenerateClass(ClassIR classIR)
{
}
private void GenerateMethod(MethodIR methodIR)
{
}
private void GenerateStatement(StatementIR stmt)
{
}
private string GenerateExpression(ExpressionIR expr)
{
return string.Empty;
}
private void GenerateClass(ClassDeclaration cls)
{
if (cls.Javadoc != null)
WriteJavadoc(cls.Javadoc);
var mods = string.Join(" ", cls.Modifiers);
if (!string.IsNullOrEmpty(mods))
mods += " ";
_w.WriteLine($"{mods}class {cls.NameEn} " + "{");
_w.Indent();
foreach (var m in cls.Methods)
{
GenerateMethod(m);
_w.WriteLine();
}
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateMethod(MethodDeclaration m)
{
if (m.Javadoc != null)
WriteJavadoc(m.Javadoc, m.NameJa);
var mods = string.Join(" ", m.Modifiers);
if (!string.IsNullOrEmpty(mods))
mods += " ";
var paramList = string.Join(", ",
m.Parameters.Select(p => $"{MapType(p.Type)} {p.NameEn}"));
_w.WriteLine($"{mods}{MapType(m.ReturnType)} {m.NameEn}({paramList}) " + "{");
_w.Indent();
foreach (var stmt in m.Body)
GenerateStatement(stmt);
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateStatement(Statement stmt)
{
switch (stmt)
{
case LocalVariableDeclaration v:
GenerateLocalVariable(v);
break;
case ExpressionStatement es:
_w.WriteLine($"{GenerateExpression(es.Expression)};");
break;
case ForStatement fs:
GenerateFor(fs);
break;
case WhileStatement ws:
GenerateWhile(ws);
break;
case IfStatement ifs:
GenerateIf(ifs);
break;
case AssignmentStatement ass:
_w.WriteLine($"{GenerateExpression(ass.Left)} = {GenerateExpression(ass.Right)};");
break;
default:
_w.WriteLine("// 未対応ステートメント");
break;
}
}
private void GenerateLocalVariable(LocalVariableDeclaration v)
{
if (v.Javadoc != null)
WriteJavadoc(v.Javadoc);
if (v.Initializer != null)
{
_w.WriteLine($"{MapType(v.Type)} {v.NameEn} = {GenerateExpression(v.Initializer)};");
}
else
{
_w.WriteLine($"{MapType(v.Type)} {v.NameEn};");
}
}
private void GenerateFor(ForStatement fs)
{
var init = $"{MapType(fs.Initializer.Type)} {fs.Initializer.NameEn} = {GenerateExpression(fs.Initializer.Initializer)}";
var cond = GenerateExpression(fs.Condition);
var iter = GenerateExpression(fs.Iterator);
_w.WriteLine($"for ({init}; {cond}; {iter}) " + "{");
_w.Indent();
foreach (var s in fs.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateWhile(WhileStatement ws)
{
_w.WriteLine($"while ({GenerateExpression(ws.Condition)}) " + "{");
_w.Indent();
foreach (var s in ws.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
private void GenerateIf(IfStatement ifs)
{
_w.WriteLine($"if ({GenerateExpression(ifs.Condition)}) " + "{");
_w.Indent();
foreach (var s in ifs.ThenBody)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
if (ifs.ElseBody.Any())
{
_w.WriteLine("else {");
_w.Indent();
foreach (var s in ifs.ElseBody)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("}");
}
}
private string GenerateExpression(Expression expr)
{
return expr switch
{
IdentifierExpression id => id.NameEn,
LiteralExpression lit => lit.Value is string s ? $"\"{s}\"" : lit.Value!.ToString()!,
BinaryExpression bin => $"{GenerateExpression(bin.Left)} {bin.Operator} {GenerateExpression(bin.Right)}",
UnaryExpression un => $"{GenerateExpression(un.Operand)}{un.Operator}",
MemberAccessExpression ma => GenerateMemberAccess(ma),
ElementAccessExpression ea => $"{GenerateExpression(ea.ArrayExpression)}[{GenerateExpression(ea.IndexExpression)}]",
InvocationExpression inv => $"{GenerateExpression(inv.Target)}({string.Join(", ", inv.Arguments.Select(GenerateExpression))})",
ArrayLiteralExpression arr => $"new {MapType(arr.ElementType)}[] {{ {string.Join(", ", arr.Elements.Select(GenerateExpression))} }}",
_ => "/* 未対応式 */"
};
}
private string GenerateMemberAccess(MemberAccessExpression ma)
{
// C# AST → Java では配列 length の扱いに差異あり
// array.length / list.size() 等だが、今回は配列 Length → length にだけ対応
if (ma.Expression is IdentifierExpression id &&
id.NameEn == "array" &&
ma.MemberName == "Length")
{
return $"{id.NameEn}.length";
}
return $"{GenerateExpression(ma.Expression)}.{ma.MemberName}";
}
private void WriteJavadoc(JavadocComment j, string? methodNameJa = null)
{
_w.WriteLine("/**");
if (!string.IsNullOrEmpty(methodNameJa))
{
// 関数名の注釈を日本語関数名にする
_w.WriteLine($" * {methodNameJa}");
}
else if (!string.IsNullOrEmpty(j.Summary))
{
_w.WriteLine($" * {j.Summary}");
}
foreach (var p in j.Params)
{
// @param 英語 日本語
_w.WriteLine($" * @param {p.NameEn} {p.NameJa}");
}
_w.WriteLine(" */");
}
private string MapType(string t)
{
return t switch
{
"string" => "String",
"string[]" => "String[]",
"int" => "int",
"int[]" => "int[]",
"void" => "void",
_ => t
};
}
}
}
VbNetCodeGenerator.cs
using System;
using System.Linq;
using ReMindAst;
namespace ReMindBackend
{
public class VbNetCodeGenerator
{
private readonly IndentWriter _w = new();
public string Generate(CompilationUnit cu)
{
// Imports
foreach (var imp in cu.Imports)
{
_w.WriteLine($"Imports {imp.Name}");
}
_w.WriteLine();
foreach (var ns in cu.Namespaces)
{
_w.WriteLine($"Namespace {ns.Name}");
_w.Indent();
foreach (var cls in ns.Classes)
{
GenerateClass(cls);
_w.WriteLine();
}
_w.Unindent();
_w.WriteLine("End Namespace");
}
return _w.ToString();
}
public string Generate(ProgramIR program)
{
return string.Empty;
}
private void GenerateClass(ClassIR classIR)
{
}
private void GenerateMethod(MethodIR methodIR)
{
}
private void GenerateStatement(StatementIR stmt)
{
}
private string GenerateExpression(ExpressionIR expr)
{
return string.Empty;
}
private void GenerateClass(ClassDeclaration cls)
{
if (cls.Javadoc != null)
WriteXmlComment(cls.Javadoc);
var mods = string.Join(" ", cls.Modifiers.Select(MapModifier));
if (!string.IsNullOrEmpty(mods))
mods += " ";
_w.WriteLine($"{mods}Class {cls.NameEn}");
_w.Indent();
foreach (var m in cls.Methods)
{
GenerateMethod(m);
_w.WriteLine();
}
_w.Unindent();
_w.WriteLine("End Class");
}
private void GenerateMethod(MethodDeclaration m)
{
if (m.Javadoc != null)
WriteXmlComment(m.Javadoc, m.NameJa);
var mods = string.Join(" ", m.Modifiers.Select(MapMethodModifier));
if (!string.IsNullOrEmpty(mods))
mods += " ";
var paramList = string.Join(", ",
m.Parameters.Select(p => $"{p.NameEn} As {MapType(p.Type)}"));
if (m.ReturnType == "void")
{
_w.WriteLine($"{mods}Sub {m.NameEn}({paramList})");
}
else
{
_w.WriteLine($"{mods}Function {m.NameEn}({paramList}) As {MapType(m.ReturnType)}");
}
_w.Indent();
foreach (var stmt in m.Body)
GenerateStatement(stmt);
_w.Unindent();
if (m.ReturnType == "void")
_w.WriteLine("End Sub");
else
_w.WriteLine("End Function");
}
private void GenerateStatement(Statement stmt)
{
switch (stmt)
{
case LocalVariableDeclaration v:
GenerateLocalVariable(v);
break;
case ExpressionStatement es:
_w.WriteLine(GenerateExpression(es.Expression));
break;
case ForStatement fs:
GenerateFor(fs);
break;
case WhileStatement ws:
GenerateWhile(ws);
break;
case IfStatement ifs:
GenerateIf(ifs);
break;
case AssignmentStatement ass:
_w.WriteLine($"{GenerateExpression(ass.Left)} = {GenerateExpression(ass.Right)}");
break;
default:
_w.WriteLine("' 未対応ステートメント");
break;
}
}
private void GenerateLocalVariable(LocalVariableDeclaration v)
{
if (v.Javadoc != null)
WriteXmlComment(v.Javadoc);
if (v.Initializer != null)
{
_w.WriteLine($"Dim {v.NameEn} As {MapType(v.Type)} = {GenerateExpression(v.Initializer)}");
}
else
{
_w.WriteLine($"Dim {v.NameEn} As {MapType(v.Type)}");
}
}
private void GenerateFor(ForStatement fs)
{
// Re:Mind の for (int i = 0; i < array.Length; i++) を
// VB.NET の For i As Integer = 0 To array.Length - 1 に変換したい場合もあるが、
// 今回は「そのまま For ... While 風」にせず、素直に For Next を生成することもできる。
// ここでは単純に 0 ? array.Length-1 を仮定した実装にしておく(バブルソート用)。
if (fs.Initializer == null ||
fs.Condition is not BinaryExpression bin ||
fs.Iterator is not UnaryExpression un ||
un.Operator != "++")
{
// 簡易: そのままコメントで逃がす
_w.WriteLine("' 未対応の For 形式");
return;
}
// int i = 0;
var varName = fs.Initializer.NameEn;
var startExpr = GenerateExpression(fs.Initializer.Initializer);
// i < array.Length
// → To array.Length - 1 を仮定
var endExpr = $"{GenerateExpression(bin.Right)} - 1";
_w.WriteLine($"For {varName} As {MapType(fs.Initializer.Type)} = {startExpr} To {endExpr}");
_w.Indent();
foreach (var s in fs.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("Next");
}
private void GenerateWhile(WhileStatement ws)
{
_w.WriteLine($"While {GenerateExpression(ws.Condition)}");
_w.Indent();
foreach (var s in ws.Body)
GenerateStatement(s);
_w.Unindent();
_w.WriteLine("End While");
}
private void GenerateIf(IfStatement ifs)
{
_w.WriteLine($"If {GenerateExpression(ifs.Condition)} Then");
_w.Indent();
foreach (var s in ifs.ThenBody)
GenerateStatement(s);
_w.Unindent();
if (ifs.ElseBody.Any())
{
_w.WriteLine("Else");
_w.Indent();
foreach (var s in ifs.ElseBody)
GenerateStatement(s);
_w.Unindent();
}
_w.WriteLine("End If");
}
private string GenerateExpression(Expression expr)
{
return expr switch
{
IdentifierExpression id => id.NameEn,
LiteralExpression lit => lit.Value is string s ? $"\"{s}\"" : lit.Value!.ToString()!,
BinaryExpression bin => $"{GenerateExpression(bin.Left)} {bin.Operator} {GenerateExpression(bin.Right)}",
UnaryExpression un => $"{GenerateExpression(un.Operand)} {MapUnaryOperator(un.Operator)}",
MemberAccessExpression ma => $"{GenerateExpression(ma.Expression)}.{ma.MemberName}",
ElementAccessExpression ea => $"{GenerateExpression(ea.ArrayExpression)}({GenerateExpression(ea.IndexExpression)})",
InvocationExpression inv => $"{GenerateExpression(inv.Target)}({string.Join(", ", inv.Arguments.Select(GenerateExpression))})",
ArrayLiteralExpression arr => $"{{ {string.Join(", ", arr.Elements.Select(GenerateExpression))} }}",
_ => "' 未対応式"
};
}
private void WriteXmlComment(JavadocComment j, string? methodNameJa = null)
{
if (!string.IsNullOrEmpty(methodNameJa))
{
_w.WriteLine("''' <summary>");
_w.WriteLine($"''' {methodNameJa}");
_w.WriteLine("''' </summary>");
}
else if (!string.IsNullOrEmpty(j.Summary))
{
_w.WriteLine("''' <summary>");
_w.WriteLine($"''' {j.Summary}");
_w.WriteLine("''' </summary>");
}
foreach (var p in j.Params)
{
_w.WriteLine($"''' <param name=\"{p.NameEn}\">{p.NameJa}</param>");
}
}
private string MapType(string t)
{
return t switch
{
"string" => "String",
"string[]" => "String()",
"int" => "Integer",
"int[]" => "Integer()",
"void" => "Void", // Function の場合は使わない想定
_ => t
};
}
private string MapModifier(string m)
{
return m switch
{
"public" => "Public",
"private" => "Private",
"internal" => "Friend",
_ => m
};
}
private string MapMethodModifier(string m)
{
return m switch
{
"static" => "Shared",
"public" => "Public",
"private" => "Private",
"internal" => "Friend",
_ => m
};
}
private string MapUnaryOperator(string op)
{
return op switch
{
"++" => "+= 1",
"--" => "-= 1",
_ => op
};
}
}
}
本ステップの評価
今回も基本的コメントがついていませんが、それは後で追記可能と踏んでいて、かなりよいと評価しています。先を急ぎます。サンプルソースに対してトランスコンパイルが実行できる状況づくりを目指します。
おわりに
以上です。次回の方向性はPhase 7: コア構文のトランスコンパイルの実装からとなります。「最小セット(MVP)」の完成条件にかなり近づいてきました。
-
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の認知度を確認 ↩ -
Copilotが生成した日本語トランスコンパイラ言語 Re:MindのASTパーサが動作するまで - Qiita
「Re:MindのASTパーサが動作するまで」の記事リストです。(ステップ降順)
ステップ12 いったん生成
ステップ11
ステップ10 生成済コードの伝達完了
ステップ9
ステップ8
ステップ7
ステップ6 生成済コードの伝達開始
ステップ5
ステップ4 Program.csが動作
ステップ3
ステップ2
ステップ1 検証環境構築 ↩ -
Copilotが生成した日本語トランスコンパイラ言語 Re:MindのC#コード生成が動作するまで - Qiita
「Re:MindのC#コード生成が動作するまで」の記事リストです。(ステップ降順)
ステップ4
ステップ3
ステップ2
ステップ1 ↩