Golf a Purple Interpreter


13

Golf a Purple Interpreter

Purpleเป็น esolang ที่ออกแบบมาโดยมีวัตถุประสงค์หลักสองประการ:

  • เพื่อเป็นการลดAubergineเนื่องจากมีเพียงภาษาตัวเองไม่เพียงพอในการปรับเปลี่ยนคำสั่งเดียว
  • เพื่อยอมรับความเป็นไปได้ของล่ามกอล์ฟขนาดเล็กที่น่ากลัว การผ่านครั้งแรกของฉันที่ล่าม Python 2 ที่มีคุณสมบัติครบถ้วนสมเหตุสมผลมีเพียง 702 ไบต์และฉันแน่ใจว่านักกอล์ฟที่มีประสบการณ์มากขึ้นสามารถโกนหนวดได้เล็กน้อย

เป้าหมายของคุณคือการเขียนล่ามสำหรับภาษานี้

ข้อมูลเกี่ยวกับสีม่วง:

โปรแกรมสีม่วงคือลำดับของอักขระที่วางในอาร์เรย์หน่วยความจำที่ไม่มีที่สิ้นสุดและแอดเดรสที่อักขระตัวแรกของโปรแกรมจะถูกวางที่แอดเดรสศูนย์ ส่วนที่เหลือของอาร์เรย์ (ทั้งก่อนและหลังที่เก็บโปรแกรมสีม่วง) จะเริ่มต้นเป็นศูนย์

Purple มี register สามตัวที่เรียกว่าaและbและiซึ่งแต่ละตัวสามารถเก็บค่าจำนวนเต็มที่ลงนามแล้วและถูกกำหนดค่าเริ่มต้นเป็นศูนย์ ฉันยังเป็นตัวชี้คำสั่งและชี้ไปที่คำสั่งสีม่วงที่กำลังดำเนินการอยู่ในปัจจุบัน

แต่ละรอบล่ามจะอ่านลำดับของอักขระสามตัวที่ต่อเนื่องกันเริ่มต้นจากตำแหน่งหน่วยความจำที่ระบุโดยตัวชี้คำสั่งและพยายามเรียกใช้ลำดับนี้ตามคำสั่งสีม่วง หลังจากนั้นตัวชี้คำสั่งจะเพิ่มขึ้น 3 เสมอ

วากยสัมพันธ์คำสั่งสีม่วงประกอบด้วยอักขระสามตัว (หรือการเข้ารหัส) ในหนึ่งแถวเช่น " xyz "

อักขระตัวแรก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.

อีกสองไบต์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 กำหนดไว้บนความแตกต่าง

หากลำดับของอักขระสามตัว (หรือการเข้ารหัส) นั้นไม่ใช่คำสั่งสีม่วงที่ถูกต้องล่ามจะหยุดทันทีโดยไม่มีข้อผิดพลาดใด ๆ

ล่ามของคุณจะต้อง:

  • เป็นโปรแกรมที่สมบูรณ์ไม่ใช่ฟังก์ชั่น
  • ไม่เคยส่งออกไปยัง stderr, เว้นแต่ EOF คืออ่าน
  • ทำตัวเหมือนกันกับการใช้งานอ้างอิงในอินพุตที่มีรูปแบบที่ดีทั้งหมดซึ่งไม่เกี่ยวข้องกับตัวเลขที่มีขนาดใหญ่มากรวมถึงโปรแกรมทดสอบที่ระบุด้านล่าง (ดีเหมือนกันถึงเวลา - มันสามารถทำงานช้าลง แต่ไม่มากเกินไป!)

คุณสามารถมอบโปรแกรมให้กับล่ามในรูปแบบใด ๆ ที่คุณต้องการ: อ่านจากไฟล์ฝังในโปรแกรมเป็นสตริงหรืออ่านจาก 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% ถ้าล่ามของคุณอ่านชื่อไฟล์จาก stdin หรือจากอาร์กิวเมนต์บรรทัดคำสั่งและโหลดโปรแกรมจากไฟล์

1
เซลล์หน่วยความจำมีขนาดเท่าใด? ไบต์ตัวอักษร (Unicode?), (โดยพลการ) จำนวนเต็มขนาดใหญ่? ดูเหมือนว่าคุณกำลังใช้ "ตัวละคร" และ "ไบต์" ที่มีความหมายเหมือนกัน
Paŭlo Ebermann

@ PaŭloEbermannการเดาของฉันคือการใช้งานเฉพาะ ตัวอย่างเช่นฉันต้องใช้uint32สำหรับตัวละครและ MAXINT สำหรับ ints
cat

2
@sysreq นั่นเป็นตัวบล็อกจริงๆเหรอ? การนำไปใช้ของคุณอาจมีสองเทปหนึ่งรายการสำหรับลบและอีกหนึ่งสำหรับดัชนีบวก (ใช่ฉันจะใช้รหัสอีกเล็กน้อย แต่ไม่มากขนาดนั้น)
Paŭlo Ebermann

1
@sysreq โดยพื้นฐานแล้วล่ามตัวเอง Purple จะเป็นโปรแกรมที่อ่านโปรแกรม Purple จาก stdin แล้วทำสิ่งที่โปรแกรมนั้นจะทำ โปรแกรมสีม่วงตัวแรก (ล่าม) สามารถทำงานในสิ่งที่คุณต้องการล่าม โปรแกรมที่เขียนทับที่อยู่หน่วยความจำต่ำสุดที่มีการป้อนข้อมูลอย่างสมบูรณ์จากนั้นลบตัวเองก่อนที่จะข้ามไปยังรหัสที่อ่านจะมีคุณสมบัติ (แม้ว่าฉันไม่คิดว่ามันเป็นไปได้จริง)
quintopia

2
ฉันเข้ามาใกล้ที่จะมีไทม์ที่สามารถตีความตัวเองได้ แต่ฉันก็สายเกินไป
แมว

คำตอบ:


7

Pyth, 148 128 121 ไบต์ (หรือ 124 * .9 = 111.6, ดูด้านล่าง)

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 ให้ป้อนโปรแกรม Purple ในส่วนที่เหลือของ STDIN หากต้องการใช้โค้ดที่มีบรรทัดใหม่ให้ใช้เวอร์ชันสำรองที่ด้านล่าง

พอสมควรกอล์ฟ นี่คือการแบ่งบรรทัดและการเยื้องเพื่อความชัดเจน:

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ถูกรวมเข้าเป็นตัวแปรเดียว, J. Zเป็นตัวชี้คำสั่ง kคืออินพุตไปยังโปรแกรมสีม่วง 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 มักจะแปลกเมื่อSTDINและSTDOUTจะต้อง ...

พรอมต์แรกคืออินพุตสำหรับสตริงโปรแกรม แต่ละพรอมต์ที่เป็นผลมาจาก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
ที่สองc="charCodeAt"สามารถถูกแทนที่ด้วยเพียงแค่c?
Dendrobium

การเข้าถึงอาร์เรย์ด้วยดัชนีลบทำงานใน JavaScript หรือไม่
nimi

@Drobrobium ว้าวฉันไม่รู้ว่าจะทำยังไงฮ่าฮ่า! ขอบคุณ
user81655

2
@nimi มันใช้งานได้ อาร์เรย์ไม่สนับสนุนดัชนีลบ แต่ใช้ประโยชน์จากความจริงที่ว่าพวกเขายังทำตัวเป็นวัตถุด้วย เป็นเช่นเดียวกับarray[-1] = 1 ทั้งสองสามารถเข้าถึงได้ด้วยarray = { "-1": 1 } array[-1]
user81655

@ user81655: อ่าดีไม่รู้ด้วยซ้ำ
nimi

3

Ceylon, 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 แทน ซ้ำ ๆ หากจำเป็น)

(เมื่อพยายามที่จะเขียน -1 ถึง stdout ล่ามจะจบด้วย OverflowError แม้ว่าจะคล้ายกันเกิดขึ้นถ้าจำนวนเต็มนอกช่วง Unicode ส่งออก)

ล่ามใช้โปรแกรมเป็นอาร์กิวเมนต์บรรทัดคำสั่งแรกของมัน (ให้แน่ใจว่าจะพูดมันสำหรับเปลือกของคุณเมื่อมันมีช่องว่างหรือสิ่งที่น่าสนใจอื่น ๆ )

ในศรีลังกาเราสามารถอ่านอินพุตบรรทัดได้อย่างง่ายดายเท่านั้น (ฉันเดาว่านี่จะเปลี่ยนเป็นหนึ่งในเวอร์ชันถัดไป) ดังนั้นเมื่อoใช้สำหรับการอ่านฉันอ่านทั้งบรรทัดและบัฟเฟอร์ส่วนสำหรับการใช้ในอนาคต ฉันเดาว่ามันทำงานคล้ายกันในการใช้งาน Python เมื่อเชื่อมต่อกับเทอร์มินัล


เมื่อพยายามประมวลผลคำสั่ง (ส่วนหนึ่ง) ซึ่งไม่ใช่หนึ่งในอักขระที่ถูกต้องnothingจะทำให้ AssertionError ถูกโยนทิ้งซึ่งเราจะจับในบล็อก catch รอบลูปหลัก

ฉันคิดว่าสิ่งนี้ควรเป็นประเภทยกเว้นแบบกำหนดเอง (เนื่องจาก AssertionError อาจเกิดขึ้นได้ในที่อื่น ๆ ถ้าฉันมีข้อบกพร่อง) แต่นั่นจะใช้พื้นที่มากขึ้นโดยกินการปรับปรุงส่วนใหญ่ที่ฉันทำไว้จากเวอร์ชันแรก

เทคนิคบางอย่างที่ใช้สำหรับการเล่นกอล์ฟ:

  • รุ่นก่อนหน้าใช้ ceylon.collection.HashMap - แทนตอนนี้เราใช้แผนที่ไม่เปลี่ยนรูปเป็นที่สร้างขึ้นโดยmapฟังก์ชั่นและสร้างใหม่ในแต่ละครั้งAหรือBจะใช้เป็นx
  • ฉันใช้นามแฝงนำเข้าสำหรับตัวระบุทั้งหมดจาก ceylon.language ซึ่งใช้มากกว่าหนึ่งครั้ง (รวมถึงvariableคำอธิบายประกอบซึ่งตอนนี้l)
  • ฉันกำจัดคลาสE(สำหรับสภาพแวดล้อม) และวิธีs(ขั้นตอน) - ตอนนี้ทุกอย่างเกิดขึ้นภายในrunฟังก์ชั่น
  • แทนที่จะใช้.integerเพื่อรับ codepoint ของตัวละคร.hashให้ผลเหมือนกัน ดังนั้นจึงstring*.hashเป็นเช่นเดียวกับstring.map(Character.integer)(ให้ iterable ของ codepoints จากสตริง)
  • เมื่อประเภทคือนามแฝง-นำเข้าจะสั้นกว่าis I ...exists ...
  • เมื่อแปลงบางสิ่ง (เช่นx) เป็นสตริง"``t``"จะสั้นกว่าt.string(หรือสิ่งที่ฉันใช้สำหรับอักขระString{t})
  • ฟังก์ชั่นที่ใช้เพียงครั้งเดียวมักจะสามารถ inline

นี่คือเวอร์ชันที่จัดรูปแบบ (และแสดงความคิดเห็น):

// 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
    }
}

ฉันใช้ส่วนหนึ่งของรหัสนั้นอีกครั้งสำหรับ"ตัวแปลแบบขนาน"พยายามค้นหาโปรแกรมที่หยุดทำงานทั้งหมด (มีหลายตัว) (ฉันใช้ Purple รุ่นที่ไม่ใช่ I / O เนื่องจาก I / O ใช้พื้นที่มากและไม่ได้ใช้ในงานนั้น)
Paŭlo Ebermann
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.