パープルインタープリターのゴルフ


13

パープルインタープリターのゴルフ

紫のは2つの主な目的で設計されたエソランです。

  • の最小化 Au子の、自己修正可能な1命令言語が十分にないためです。
  • の可能性を認める 恐ろしく小さなゴルフ通訳者の。適度にフル機能のPython 2インタープリターでの最初のパスはわずか702バイトであり、経験豊富なゴルファーはそこからかなりの量を削ることができると確信しています。

あなたの目標は、この言語の通訳を書くことです。

紫に関する情報:

紫色のプログラムは、プログラムの最初の文字がアドレス0に配置されるように、無限のアドレス可能なメモリアレイに配置される文字のシーケンスです。配列の残りの部分(紫色のプログラムが格納される場所の前後両方)はゼロに初期化されます。

Purpleには、aおよびbおよびiと呼ばれる3つのレジスタがあり、それぞれが符号付き整数を保持でき、ゼロに初期化されます。iは命令ポインターでもあり、常に現在実行中のパープル命令を指します。

各サイクルで、インタプリタは命令ポインタによって示されたメモリ位置から始まる3つの連続した文字のシーケンスを読み取り、このシーケンスをパープル命令として実行しようとします。その後、命令ポインタは常に3ずつ増加します。

構文的には、Purple命令は、「xyz」のような3つの文字(またはそのエンコード)が連続して構成されています。

最初の文字xは次のいずれかです。

abABio

これらの記号の意味は次のとおりです。

a - Place the result in register a.
b - Place the result in register b.
A - Place the result in the location in memory referred to by register a.
B - Place the result in the location in memory referred to by register b.
i - Set the instruction pointer to the result.
o - Output the result to stdout.

他の2バイトyおよびzは、次のいずれかです。

abABio1

これらの各シンボルの意味は次のとおりです。

a - Return the contents of register a.
b - Return the contents of register b.
A - Return the contents of the memory array at the address stored in register a.
B - Return the contents of the memory array at the address stored in register b.
i - Return the contents of register i (the instruction pointer).
o - Return the value of a single character read from stdin.
1 - Return the literal numeric value 1.

命令をフェッチした後、紫インタプリタは評価され、Y、次いで、Z、結果減算Zの結果から、Y、およびその後のアクションを実行することによって示されるX差に。

3文字のシーケンス(またはそのエンコード)が有効なパープル命令ではない場合、インタープリターはエラーを出さずに直ちに停止します。

通訳者は:

  • 関数ではなく、完全なプログラムであること。
  • EOFが読み取られない限り、stderrに出力しない
  • 以下に示すテストプログラムを含む、非常に大きな数を含まないすべての整形式入力で、参照実装と同じように動作します。(まあ、同じタイミングまでです-実行速度は遅くなりますが、大きすぎません!)

プログラムをインタプリタに任意の形式で提供できます。ファイルから読み取るか、プログラムに文字列として埋め込むか、stdinから読み取ります。

テストケース:

プログラム

ooo

入力で実行した場合

z!

もたらすべきです

Y

プログラム

bbboobiii

入力で実行した場合

It's a cat program.

(または他の入力)は

It's a cat program.

(または受け取った入力)からやり直し、同じことをもう一度行います。


プログラム

Aoab11bi1bABoAaiba

入力で実行した場合

0

もたらすべきです

0

その後停止しますが、入力で実行すると

1

出力を続ける必要があります

1

永遠に。


プログラム

b1bbb1oAbabaa1ab1Ab1Bi1b

もたらすべきです

b1bbb1oAbabaa1ab1Ab1Bi1b

プログラム

aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG

もたらすべきです

Hello, World!

得点:

これはなので、次のボーナスによって潜在的に変更される可能性があるため、バイト単位の最短ソースが勝ちます。

ボーナス:

  • インタープリターが標準入力またはコマンドライン引数からファイル名を読み取り、ファイルからプログラムをロードする場合、-10%。

1
メモリセルのサイズは?バイト、文字(ユニコードのもの?)、(任意の)大きな整数?同じ意味の「文字」と「バイト」を使用しているようです。
パエロエベルマン

@PaŭloEbermann私の推測では、それは実装固有のものです。例えば私が使用する必要がuint32int型のための文字とMAXINTのために

2
@sysreqそれは本当にブロッカーですか?実装では、2つのテープを使用できます。1つはネガティブインデックス用、もう1つはポジティブインデックス用です。(はい、これにはもう少しコードが必要になりますが、それほど多くはありません。)
PaŭloEbermann

1
@sysreqは基本的に、パープルセルフインタープリターは、stdinからパープルプログラムを読み取り、そのプログラムが実行するすべての処理を実行するプログラムです。最初のPurpleプログラム(インタープリター)は、好きなインタープリターで実行できます。最下位のメモリアドレスを入力で完全に上書きし、読み取りコードにジャンプする前に何らかの形で資格を得る前に自分自身を削除するプログラム(これは実際には不可能だと思いますが)。
キントピア

2
自己解釈が可能なランタイムを持つことに非常に近づきましたが、手遅れでした。

回答:


7

ピス、148 128 121バイト(または* 0.9 = 111.6 124、下を参照)

J,00=kjb.z .eXHkCbz#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

テストスイート

STDINの最初の行に指定されたコード、STDINの残りの部分のPurpleプログラムへの入力。改行でコードを使用するには、下部の代替バージョンを使用します。

合理的にゴルフ。わかりやすくするために、改行とインデントを使用しています。

J,00
=kjb.z
 .eXHkCbz
#
  =b-Fm
    ?=zx"oabABi1"C@H+Zd
      @
        s[0Jm.x@Hk0JZ1H)
        z
      Ch~tk
    S2
   ?hKx"abAB"=YC@HZ
    ?PK
      XH@JKb
      XJKb
  ?qY\i=Zb
  ?qY\opCb
  vN
  =+Z3

基本的に、 #ループはエラーブレークを介して実行と停止を行います。

aおよびbは、単一の変数に結合されますJZ命令ポインタです。kPurpleプログラムへの入力です。H辞書として表されるテープです。b現在の結果です。Y命令の現在の最初のバイトです。

ファイルからの読み取り:

J,00=kjb.z .eXHkCbjb'z#=b-Fm?q\o=zC@H+ZdCh~tk@s[Jm.x@Hk0JZ1H)x"abABi1"zS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

STDINの最初の行としてファイル名を指定します。テスト走行:

$ cat purple-final.pyth 
J,00=kjb.z .eXHkCbjb'z#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
$ cat purple-code.txt 
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG
$ pyth purple-final.pyth <<< 'purple-code.txt' 
Hello, World!

5

JavaScript(ES6)、292バイト

eval(`a=b=i=d=0;v=n=>(x=m[i+n])==97?a_98?b_65?m[a]_66?m[b]_105?i_111?p()[c]()_49?1:d=1;for(m=[...(p=prompt)()].map(b=>b[c="charCodeAt"]());!d;i+=3)(y=v(1),d)||(z=v(2),d)?1:(x=m[r=y-z,i])==97?a=r_98?b=r_65?m[a]=r_66?m[b]=r_105?i=r-3_111?alert(String.fromCharCode(r)):d=1`.replace(/_/g,":x=="))

説明

JavaScriptの回答はSTDINSTDOUT必要なときに常に奇妙です...

最初のプロンプトは、プログラム文字列の入力です。o命令から生じる各プロンプトは、最初の文字のみを読み取ります。

eval数バイトを節約する一般的なフレーズを置き換えるために使用されます。Ungolfedおよびevalプログラムなしでは、次のようになります。

// Initialisation
a=b=i=                            // initialise registers to 0
  d=0;                            // d is set to true when the program should die

// Gets the result of Y or Z
v=n=>                             // n = offset from i
  (x=m[i+n])==97?a:               // x = value of instruction
  x==98?b:
  x==65?m[a]:
  x==66?m[b]:
  x==105?i:
  x==111?p()[c]():
  x==49?1:
  d=1;                            // if it was none of the valid values, die

// Execution loop
for(
  m=                              // m = memory array
    [...(p=prompt)()]             // receive the program
    .map(b=>b[c="charCodeAt"]()); // initialise m to the ASCII values of the program
  !d;                             // finish if an error occured
  i+=3                            // increment i
)
  (y=v(1),d)||                    // get the value of Y and check for errors
  (z=v(2),d)?1:                   // get the value of Z and check for errors

    // Get the result of X
    (x=m[r=y-z,i])==97?a=r:       // r = result of y - z
    x==98?b=r:
    x==65?m[a]=r:
    x==66?m[b]=r:
    x==105?i=r-3:
    x==111?alert(String.fromCharCode(r)):
    d=1

2
2つ目c="charCodeAt"は単に置き換えられcますか?
デンドロビウム

JavaScriptで負のインデックスを持つ配列アクセスは機能しますか?
nimi

@Dendrobiumうわー、私はどのようにその母を逃したかわからない!ありがとう。
user81655

2
@nimi動作します。配列自体は負のインデックスをサポートしていませんが、これはオブジェクトとしても動作するという事実を利用しています。array[-1] = 1はと同じarray = { "-1": 1 }です。両方にアクセスできますarray[-1]
user81655

@ user81655:ああ、それは知らなかった。
nimi

3

セイロン、827の 792 671バイト

import ceylon.language{l=variable,I=Integer,x=nothing,p=process,m=map}shared void run(){try{if(exists d=p.arguments[0]){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;l{I*}c=[];I o{if(c==[]){if(exists e=p.readLine()){c=e*.hash.chain{10};}else{c={-1}.cycled;}}assert(is I r=c.first);c=c.rest;return r;}value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},111->{o},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v),111->((I v)=>p.write("``v.character``"))};I h(I v)=>f[v]?.first else x;while(0<1){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}}catch(AssertionError e){}}

プログラムがEOFで入力を読み取ろうとすると、リファレンス実装とは少し異なる動作をします。リファレンス実装はTypeErrorでクラッシュします。必要に応じて繰り返し)。

(ただし、この-1をstdoutに書き込もうとすると、インタープリターはOverflowErrorで終了します。Unicode範囲外の整数が出力される場合も同様に発生します。)

インタープリターは、プログラムを最初のコマンドライン引数として使用します(空白またはその他の興味深いものが含まれている場合は、シェルに引用符を付けてください)。

Ceylonでは、入力を行単位で簡単に読み取ることしかできないため(次のバージョンのいずれかで変更されると思います)、o読み取りに使用する場合、行全体を読み取り、将来の使用のためにパーツをバッファリングします。ターミナルに接続すると、Python実装でも同様に機能すると思います。


有効な文字の1つではないコマンド(パーツ)を実行しようとするnothingと、AssertionErrorがスローされ、メインループの周りのcatchブロックでキャッチされます。

私はこれがカスタム例外タイプであることが望ましいと思います(バグがある場合は他の場所でもAssertionErrorが発生する可能性があるため)が、それははるかに多くのスペースを必要とし、最初のバージョンから行ったほとんどの改善を食べます。

ゴルフに使用されるいくつかのトリック:

  • 以前のバージョンではceylon.collection.HashMapを使用していました。代わりに、map関数によって作成された不変マップを使用し、毎回新しいマップを作成するABxとして使用しますます。
  • 2回以上使用されるceylon.languageからのすべての識別子(variable現在はである注釈を含むl)にエイリアスインポートを使用します。
  • クラスE(環境用)とs(ステップ)メソッドを削除しましたrun。すべてが関数内で行われるようになりました。
  • .integer文字のコードポイントを取得するために使用する代わりに.hash、同じ結果が得られます。したがってstring*.hashstring.map(Character.integer)(文字列からコードポイントの反復可能を与える)と同じです。
  • タイプは別名、インポートされるとき、is I ...より短く、exists ...ます。
  • 何か(たとえばx)を文字列に変換するとき"``t``"t.string(または、文字に使用したものより短い)String{t}ます。
  • 多くの場合、一度だけ使用される関数はインライン化できます。

フォーマット済み(およびコメント付き)バージョンは次のとおりです。

// Purple – a self-modifying, "one-instruction" language.
//
// Question:  http://codegolf.stackexchange.com/q/65411/2338
// My answer: http://codegolf.stackexchange.com/a/65492/2338

import ceylon.language {
    l=variable,
    I=Integer,
    x=nothing,
    p=process,
    m=map
}

shared void run() {
    try {
        // Reading code from file certainly takes more than 73 characters,
        // this isn't worth the 10% bonus.
        if (exists d = p.arguments[0]) {

            // The memory tape, as a Map<Integer, Integer>.
            // We can't modify the map itself, but we
            // can replace it by a new map when update is needed.
            l value t = m {
                // It is initialized with the code converted to Integers.
                // We use `.hash` instead of `.integer` because it is shorter.
                *d*.hash.indexed };

            // three registers
            l I a = 0;
            l I b = 0;
            l I i = 0;

            // get value from memory
            I g(I j) =>
                    t[j] else 0;

            // cached input which is still to be read
            l {I*} c = [];

            // get value from stdin.
            // we can only comfortably access stdin by line, so we read a whole line
            // and cache the rest for later.
            I o {
                if (c == []) {
                    if (exists e = p.readLine()) {
                        c = e*.hash.chain { 10 }; // convert string into ints, append \n
                    } else {
                        // EOF – return just -1 from now on.
                        c = { -1 }.cycled;
                    }
                }
                assert (is I r = c.first);
                c = c.rest;
                return r;
            }


            // Map of "functions" for fetching values.
            // We wrap the values in iterable constructors for lazy evaluation
            //  – this is shorter than using (() => ...).
            // The keys are the (Unicode/ASCII) code points of the mapped
            // source code characters.
            value f = m {
                // a
                97 -> { a },
                // b
                98 -> { b },
                // A
                65 -> { g(a) },
                // B
                66 -> { g(b) },
                // i
                105 -> { i },
                // o
                111 -> { o },
                // 1
                49 -> { 1 }
            };

            // Map of functions for "storing" results.
            // The values are void functions taking an Integer,
            // the keys are the ASCII/Unicode code points of the corresponding
            // source code characters.
            value s = m {
                // a
                97 -> ((I v) => a = v),
                // b
                98 -> ((I v) => b = v),
                // Modification of the memory works by replacing the map with a new one.
                // This is certainly not runtime-efficient, but shorter than importing
                // ceylon.collections.HashMap.
                // A
                65 -> ((I v) => t = m { a->v, *t }),
                // B
                66 -> ((I v) => t = m { b->v, *t }),
                // i
                105 -> ((I v) => i = v),
                // o – output as a character.
                111 -> ((I v) => p.write("``v.character``"))
            };

            // accessor function for the f map
            I h(I v) =>
                    f[v]?.first else x;

            // the main loop, can only be left by exception
            while (0 < 1) {
                (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
                i += 3;
            }
        }
    } catch (AssertionError e) {
        // abort silently
    }
}

そのコードの一部を、すべての停止中のプログラムを見つけようとする「並列インタープリター」に再利用しました。(それらは多数あります。)(I / Oは多くのスペースを必要とし、そのタスクでは使用されないため、非I / OバージョンのPurpleを使用しました。)
PaŭloEbermann
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.