高尔夫紫色口译员


13

高尔夫紫色口译员

Purple是一种esolang,其设计目的主要有两个:

  • 尽量减少茄子,因为周围没有足够的自修改单指令语言。
  • 承认有可能会出现可怕的小型高尔夫口译员。我在功能相当合理的Python 2解释器上的第一次访问只有702个字节,而且我相信经验丰富的高尔夫球手可以从中获得很多好处。

您的目标是为此语言编写翻译。

紫色信息:

紫色程序是一系列字符,它们放置在一个无限的可寻址存储器阵列中,从而程序的第一个字符位于地址零。数组的其余部分(存储Purple程序的前后)都初始化为零。

Purple中有三个寄存器,分别称为abi,每个寄存器可以保存一个有符号整数,并初始化为零。i还是指令指针,并且始终指向当前正在执行的Purple指令。

每个周期,解释器将从指令指针指示的存储位置开始读取三个连续字符的序列,并尝试将该序列作为Purple指令执行。之后,指令指针总是增加3。

在语法上,Purple指令连续包含三个字符(或其编码),例如“ 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.

其他两个字节yz可以是以下任意一个:

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.

提取指令后,Purple解释器将先对y进行评估,然后对z进行评估,然后从y的结果中减去z的结果,然后对差异执行x指示的操作。

如果三个字符的序列(或其编码)不是有效的Purple指令,则解释器将立即停止而不会出现任何错误。

您的口译员必须:

  • 是一个完整的程序,而不是一个功能。
  • 除非读取EOF否则切勿输出到stderr 。
  • 在所有格式不大的输入中,其行为与参考实现相同,包括下面给出的测试程序。(嗯,同样取决于时间-它可以运行得较慢,但不会太大!)

您可以按照希望的任何形式将程序提供给解释器:从文件中读取程序,将其作为字符串嵌入程序中,或从标准输入中读取。

测试用例:

该程序

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!

得分:

这是,因此以字节为单位的最短源(可能由以下奖励修改)获胜。

奖金:

  • 如果您的解释器从stdin或命令行参数读取文件名并从文件中加载程序,则为-10%。

1
存储单元的大小是多少?字节,字符(unicode的?),(任意)大整数?看起来您正在使用具有相同含义的“字符”和“字节”。
圣保罗Ebermann

@PaŭloEbermann我的猜测是特定于实现的;比如我需要使用uint32的字符,MAXINT为整数

2
@sysreq这真的是一个障碍吗?您的实现可能只包含两个磁带,一个用于负数,一个用于正数索引。(是的,这将需要更多代码,但我认为不是很多。)
PaŭloEbermann

1
@sysreq基本上,Purple自我解释器将是一个从stdin读取Purple程序,然后执行该程序将执行的操作的程序。第一个Purple程序(解释器)可以在您喜欢的任何解释器中运行。一个程序会用输入完全覆盖最低的内存地址,然后在以某种方式跳转到读取的代码之前就删除自己的程序(尽管我认为这实际上是不可能的)。
quintopia,2015年

2
我几乎可以拥有一个能够自我解释的运行时,但是我来晚了。

Answers:


7

Pyth,148个 128 121字节(或124 * 0.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第一行给出的代码输入到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

基本上,#循环通过错误中断来执行和暂停。

ab合并为一个变量JZ是指令指针。k被输入到Purple程序中。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用于替换节省几个字节的常用短语。取消加载并且没有该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"用just代替第二个c吗?
2015年

带有负索引的数组访问是否可以在JavaScript中使用?
nimi 2015年

@Dendrobium哇,我不知道我如何错过那个哈哈!谢谢。
user81655 2015年

2
@nimi有效。数组本身不支持负索引,但这利用了它们也可以作为对象的事实。array[-1] = 1与相同array = { "-1": 1 }。都可以使用访问array[-1]
user81655 2015年

@ 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(重复(如果需要)。

(但是,当尝试将此-1写入stdout时,解释器将以OverflowError结尾。如果输出Unicode范围以外的Integer,也会发生类似情况。)

解释器将程序作为其第一个命令行参数(在包含空格或其他有趣内容的情况下,请为外壳引用该程序)。

在Ceylon中,我们只能轻松地逐行读取输入(我想这会在下一版本中改变),因此当o用于读取时,我正在读取整行并缓冲部分以备将来使用。我想当它连接到终端时,它在Python实现中的工作原理类似。


当试图执行一个不是有效字符之一的命令(部分)时,nothing它将导致抛出AssertionError,然后我们将其捕获到主循环中的catch块中。

我认为这最好是自定义的Exception类型(因为如果我有bug,AssertionError也可能在其他地方发生),但这将占用更多的空间,并且吃掉了我从第一个版本所做的大部分改进。

打高尔夫球的一些技巧:

  • 以前的版本使用ceylon.collection.HashMap –相反,我们现在使用该map函数创建的不可变映射,并每次创建一个新映射AB用作x
  • 我为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版本的Purple,因为I / O占用大量空间,并且未在该任务中使用。)
PaŭloEbermann
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.