🧠

業務中に 難解プログラミング言語をC#から呼びたいときに使うライブラリを作ったよ!(そんな業務あってたまるか!)

に公開2

皆さん、難解プログラミング言語(Esolang) って知っておりますでしょうか?
有名なのだと、 Brainfuck の様な 意図的に読解が困難なように設計されたプログラミング言語 のことです。

https://ja.wikipedia.org/wiki/難解プログラミング言語

で本題

で、 まず、 C# には source generator を使って 未実装な partial な関数を実装するアプローチがよくあります。

標準の ILogger を使った ゼロアロケートロギングとかも

// LogInformation() の第二引数以降は params object[] args なので 構造体を入れると boxing する
log.LogInformation("{value}", value);
var log_ = new Log(log);
log_.LogValue(value);

partial class Log(ILogger log) {
   [LoggerMessage(LogLevel.Infomration, "{value}")]
   public partial void LogValue(int value);
}

とかすることで Log.LogValue() の実装が追加されます。

https://learn.microsoft.com/ja-jp/dotnet/core/extensions/logging/high-performance-logging#define-logger-messages-with-source-generation

なので、私は「難解プログラミング言語」を 関数として実装しようと思います。

え、なんて?

そうです、 ソースジェネレータで 難解プログラミング言語を関数化します。

で、ここに作成したライブラリがあります。

https://www.nuget.org/packages/Esolang.Brainfuck.Generator/1.1.2

https://www.nuget.org/packages/Esolang.Piet.Generator/1.1.1

https://www.nuget.org/packages/Esolang.Funge.Generator/1.1.0

使い方

とりあえず プロジェクトを作ります

dotnet new console -n EsolangSample
cd EsolangSample
dotnet add package Esolang.Brainfuck.Generator
dotnet add package Esolang.Piet.Generator
dotnet add package Esolang.Funge.Generator

して これでいけますね。

using Esolang.Brainfuck;
using Esolang.Funge;
using Esolang.Piet;

Console.WriteLine(Programs.BrainfuckHelloWorld());
Console.WriteLine(Programs.FungeHelloWorld());
Console.WriteLine(Programs.PietHelloWorld());

partial class Programs
{
    [GenerateBrainfuckMethod("1+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.")]
    public static partial string BrainfuckHelloWorld();

    [GeneratePietMethod("data:text/ascii-piet,nqiaecfbknmeRtakcsqimemerTcnjlvutqiavtFsvubntqcslnqBbjemu   udjiNqifu  r  tlaFvldq rrr vneVujbm  k  nmsRsnadv a vfkqSkdceumbmuqcrVqesqfnbsrvtjUnfcltltdljceMkcltuemnbfnkB")]
    public static partial string PietHelloWorld();

    [GenerateFungeMethod( InlineSource ="""
    >               v
    v  ,,,,,"Hello" <
    >48*,           v
    v,,,,,,"World!" <
    >25*,@
    """)]
    public static partial string FungeHelloWorld();

}

とりあえず Program.cs 単体で完結できる方法なので他の方法についてはあとで解説しますが

brainfuck は 見ての通り、

piet は 仮に ascii-piet 構文で、

funge は raw string リテラルで

記述しています。

別の指定方法

プロジェクトに sample というフォルダを作って

sample/hello.b98

64+"!dlroW ,olleH">:#,_@

sample/hw1-11.gif

EsolangSample.csproj に次の ItemGroup を追加します。

<ItemGroup>
  <FungeSource Include="sample\*.b98" />
  <PietImage Include="sample\hw1-11.gif" CodelSize="11" PietLogicalPath="hw1-11.gif" />
</ItemGroup>

そして先ほどの Programs クラスに次のメソッドを追加します。

[GeneratePietMethod("hw1-11.gif")]
public static partial string PietHelloWorld2();

[GenerateFungeMethod("sample/hello.b98")]
public static partial string FugneHelloWorld2();

これにより画像やファイルからメソッドが作れます。

以上!

欄外:技術的な話: ソースジェネレータは text ファイルしか読み込めないのでは……?

そうです。だめです。ソースジェネレータのフェイズではだめです。なのでこっちではビルドフェイズに画像をテキストに変換しています。

https://github.com/Esolang-NET/Piet/blob/v1.1.1/Generator/buildTransitive/Esolang.Piet.Generator.targets#L11-L31

なのででっかい画像ファイルとかだと凄く不利です。

以上!

作成物

https://github.com/Esolang-NET/Brainfuck/tree/v1.1.2

https://github.com/Esolang-NET/Piet/tree/v1.1.1

https://github.com/Esolang-NET/Funge/tree/v1.1.0

Discussion

藤田望藤田望

Brainfuckは言語の仕様として

  • メモリ1語のサイズ
  • ポインタが上限下限を超えた場合の挙動
  • 改行やEOFの入出力での解釈

この辺厳密に決まっていないところがあったりするので処理系や既存コードで問題になることがありますがその辺自由に設定できるようなれば紹介されてるライブラリも業務で利用される機会が増える気がしますがどうでしょうか。

1
junerjuner

特に設定項目は無いですが、生成時に使っている 属性にそういう項目を設けることで挙動を切り替えたりするのも手ですね。

1