Computer science 创建最短的图灵完全解释器

Computer science 创建最短的图灵完全解释器,computer-science,code-golf,turing-complete,Computer Science,Code Golf,Turing Complete,我刚刚尝试创建尽可能小的语言解释器。你想加入并尝试一下吗 游戏规则: 您应该指定要解释的编程语言。如果它是你发明的语言,它应该在注释中附带一个命令列表 您的代码应该以分配给代码和数据变量的示例程序和数据开始 代码应该以结果的输出结束。最好在每个中间步骤都有调试语句 您的代码应该是可运行的 您可以假设数据为0和1s(int、string或boolean,由您选择),输出为单个位 该语言应该是图灵完整的,因为对于在标准模型上编写的任何算法,如图灵机、马尔可夫链或您选择的类似算法,很明显(或解释)如

我刚刚尝试创建尽可能小的语言解释器。你想加入并尝试一下吗

游戏规则:

  • 您应该指定要解释的编程语言。如果它是你发明的语言,它应该在注释中附带一个命令列表
  • 您的代码应该以分配给代码和数据变量的示例程序和数据开始
  • 代码应该以结果的输出结束。最好在每个中间步骤都有调试语句
  • 您的代码应该是可运行的
  • 您可以假设数据为0和1s(int、string或boolean,由您选择),输出为单个位
  • 该语言应该是图灵完整的,因为对于在标准模型上编写的任何算法,如图灵机、马尔可夫链或您选择的类似算法,很明显(或解释)如何编写一个在解释器执行后执行该算法的程序
  • 代码长度定义为删除输入部分、输出部分、调试语句和不必要的空格后的代码长度。请将结果代码及其长度添加到帖子中
  • 您不能使用使编译器为您执行代码的函数,例如
    eval()
    exec()
    或类似函数

这是一个社区维基,意味着无论是问题还是答案都无法从投票中获得声誉分数。但还是投票吧

解释我刚刚创建的语言的Python程序:

from random import randint
z = [randint(0,1), None]  # example: input data is 0 or 1
x = '_b_ed_a_i____d_ai'   # this program will set p = data[1] = not data[0]

# input x[i], data z[k]
# jumper j
# p is +1 or -1 each step
# commands:
#    a   set data = 0 or 1
#    b   j += 0 or +-9 depending on data
#    d   move data left or right
#    e   perform jump left or right
#    j   set jumper to 0
#    i   end program
# output: p or some data[x], both possible

g = globals()
p = i = 1
a = b = d = j = e = k = 0
while i:
    h = x[i]
    g[h] = p = -p
    i += 1 + j*e
    if a: z[k] = i % 2
    if z[k]: j += 9*b
    k += d
    g[h] = 0        
#    print(a, b, d, e, i, j, k, h, z)

print('Input:', z, 'Output:', p > 0)
优化版本:

g=globals()
p=i=1
a=b=d=j=e=k=0
while i:
 h=x[i]
 g[h]=p=-p
 i+=1+j*e
 if a:z[k]=i%2
 if z[k]:j+=9*b
 k+=d
 g[h]=0
k=0
while k<c.length
 t=c[k]
 k+=1
 if t[0]=='z'
  r[t[1]]=0
 if t[0]=='s'
  if !r[t[1]]?
   r[t[1]]=0
  r[t[1]]+=1
 if t[0]=='j'&&r[t[1]]==r[t[2]]
  k=t[3]
alert r[0]
114字节

更新:我想补充一点,我的程序的目的不是创建任何实用的语言,甚至不是以某种方式拥有“最佳”(最短)的解释器,而是演示有趣的编程技巧。例如,我依赖于通过
globals()
直接访问全局变量,因此我甚至从未测试
j
命令,节省宝贵的字节:)

这一个比ilya n.的Python解决方案要长,但是语言更容易掌握。这种语言中的每个命令(我称之为Kaputt,德语中“break”的意思)都是一个字节。定义了以下6个命令:

  • 0
    :将零推到堆栈上
  • 1
    :将一个推到堆栈上
  • I
    :(如果)从堆栈中弹出一个值(必须为零或一)。运行以下代码块(直到“
    i
    ”),如果它是一个代码块;如果是零,则跳过该块
  • i
    :(endif)结束if块并在块未运行时推送一个1(因为“
    i
    ”弹出一个零)。关于后者的解释,请参见下文
  • D
    :(def)从堆栈中弹出待定义函数的名称(见下文),并将以下块绑定到该名称(直到“
    D
    ”)
  • d
    :(enddef)结束函数定义
  • 任何其他字节:检查此(一个字节;但请参见下面的编辑)名称是否有函数。如果是,运行此功能;如果没有,请将此字节推到堆栈上,以便
    D
    立即使用
嵌套的ifs是合法的;嵌套函数定义不可用。
i
(endif)在且仅在相应的if块未运行时推送一个one,这一事实允许使用类似于if/else/endif结构的习惯用法:

# [code that left a one or zero on the stack]
I    # abbreviated "(" below
# [code to be run if it was a one]
0iI  # abbreviated "/" below
# [code to be run if it was a zero]
1iIi # abbreviated ")" below
# [continuing...]
请注意,实际上不允许使用注释、换行符、空格等

下面是一些常见函数的示例。它们使用上述缩写
(/)

<D(/)d
定义交换堆栈上最上面两个值的函数
~
(swap)

RD(R/<)d
显然,没有空间进行错误检查,因此
i()
需要合法的卡普特代码。测试用例:

script = "<D(/)d"                 # < = pop
script += "&D((1/0)/<0)d"         # & = and
script += "~D((11/10)/(01/00))d"  # ~ = swap
script += "RD(R/<)d"              # R = remove
script += "|D(<1/(1/0))d"         # | = or
script=script.replace("(","I")   
script=script.replace("/","0iI")
script=script.replace(")","1iIi") # ( and / and ) are no real commands of the language.

S=[]
i(script+"1111010111111111R",S)
assert S == ["1","1","1","1","0"]

S=[]
i(script+"00&",S)
assert S == ["0"]

S=[]
i(script+"01~",S)
assert S == ["1","0"]

S=[]
i(script+"00|",S)
assert S == ["0"]

S=[]
i(script+"01|",S)
assert S == ["1"]
这定义了一个函数
inc
,该函数增加堆栈顶部的两位数字(顶部的LSB)

测试:


让我们将多字节函数名称为语言扩展名:-)

将下面的代码汇编成一个150字节的BF解释器

    add dh,10h
    push dx
    add dh,10h
    push dx
    mov bl,80h
    lea dx,[bx+2]
    add bl,byte ptr [bx]
    mov byte ptr [bx+1],al
    mov ah,3dh
    int 21h
    pop ds,es
    jc l14
    mov bx,ax
    mov ah,3fh  
    mov cx,di
    xor dx,dx
    int 21h
    jc l14
    mov bx,ax
    xor ax,ax
    rep stosw
    xor di,di
    xor si,si
    inc ch
l1:
    cmp si,bx
    jae l14
    lodsb
    mov bp,8
    push l1
l2: 
    dec bp
    js ret
    cmp al,[bp+l15]
    jne l2
l3:
    mov cl,[bp+8+l15]
    jmp cx
l4:
    inc di  
    ret
l5:
    dec di
    ret
l6:
    es inc byte ptr [di]
    ret
l7:
    es dec byte ptr [di]
    ret
l8:
    mov ah,2
    es mov dl,[di]
    int 21h
    ret
l9:
    mov ah,8
    int 21h
    es mov [di],al
    ret
l10:
    es cmp byte ptr [di],dh
    jne ret
l11:    
    cmp si,bx
    jae l14
    lodsb
    cmp al,']'
    jne l11
    ret
l12:
    es cmp byte ptr [di],dh
    je ret
l13:    
    dec si
    jz l14
    cmp byte ptr [si-1],'['
    jne l13
l14:
    ret
l15:
    db '>'
    db '<'
    db '+'
    db '-'
    db '.'
    db ','
    db '['
    db ']'
    db offset l4-100h
    db offset l5-100h
    db offset l6-100h
    db offset l7-100h
    db offset l8-100h
    db offset l9-100h
    db offset l10-100h
    db offset l12-100h
添加dh,10小时
推送dx
加dh,10小时
推送dx
mov bl,80小时
lea dx,[bx+2]
添加bl,字节ptr[bx]
mov字节ptr[bx+1],al
mov-ah,3dh
int 21h
流行音乐
jc l14
mov-bx,ax
mov-ah,3fh
mov cx,di
异或dx,dx
int 21h
jc l14
mov-bx,ax
xor ax,ax
代表stosw
异或di,di
异或硅,硅
公司
l1:
cmp-si,bx
jae l14
洛兹
莫夫英国石油公司,8
推动l1
l2:
十二月英国石油公司
js ret
cmp铝[bp+l15]
jne l2
l3:
mov cl,[bp+8+l15]
jmp-cx
l4:
有限公司
ret
l5:
德克迪
ret
l6:
es inc字节ptr[di]
ret
l7:
es dec字节ptr[di]
ret
l8:
mov啊,2
es mov dl[di]
int 21h
ret
l9:
mov啊,8
int 21h
es mov[di],al
ret
l10:
es cmp字节ptr[di],dh
jne ret
l11:
cmp-si,bx
jae l14
洛兹
cmp al,']'
jne l11
ret
l12:
es cmp字节ptr[di],dh
杰雷特
l13:
dec si
jz l14
cmp字节ptr[si-1],'['
jne l13
l14:
ret
l15:
db'>'
db'在此基础上,下面是一个(对于IO稍微通用的)模拟器

Fortran77 观察到且没有装载脚手架(655个字符)

带注释、装载脚手架等(2435个字符):

示例程序:

13
1025
0

14 
1025
0

14
15
0

0
0
0

-1
1
0
产生产出:

$ cat trivial.oisc | ./a.out 
           1
          -1
这是意料之中的


这确实是非常简单的代码。这里的重点不是我能把它编码得有多紧,而是图灵完整性需要一种多么简单的语言。

不是我的,而是看看210位的二进制lambda演算自解释器。

自定义语言:SLA
关键字:
i-解释SLB q-退出

自定义语言:SLB
关键字:
cp(“文本”)-将文本解释为C程序

Examp
for n in xrange(4):
    S=[]
    i(script + [str((n & 2)/2), str(n & 1), "inc"], S)
    assert S == [str(((n+1) & 2)/2), str((n+1) & 1)]
    add dh,10h
    push dx
    add dh,10h
    push dx
    mov bl,80h
    lea dx,[bx+2]
    add bl,byte ptr [bx]
    mov byte ptr [bx+1],al
    mov ah,3dh
    int 21h
    pop ds,es
    jc l14
    mov bx,ax
    mov ah,3fh  
    mov cx,di
    xor dx,dx
    int 21h
    jc l14
    mov bx,ax
    xor ax,ax
    rep stosw
    xor di,di
    xor si,si
    inc ch
l1:
    cmp si,bx
    jae l14
    lodsb
    mov bp,8
    push l1
l2: 
    dec bp
    js ret
    cmp al,[bp+l15]
    jne l2
l3:
    mov cl,[bp+8+l15]
    jmp cx
l4:
    inc di  
    ret
l5:
    dec di
    ret
l6:
    es inc byte ptr [di]
    ret
l7:
    es dec byte ptr [di]
    ret
l8:
    mov ah,2
    es mov dl,[di]
    int 21h
    ret
l9:
    mov ah,8
    int 21h
    es mov [di],al
    ret
l10:
    es cmp byte ptr [di],dh
    jne ret
l11:    
    cmp si,bx
    jae l14
    lodsb
    cmp al,']'
    jne l11
    ret
l12:
    es cmp byte ptr [di],dh
    je ret
l13:    
    dec si
    jz l14
    cmp byte ptr [si-1],'['
    jne l13
l14:
    ret
l15:
    db '>'
    db '<'
    db '+'
    db '-'
    db '.'
    db ','
    db '['
    db ']'
    db offset l4-100h
    db offset l5-100h
    db offset l6-100h
    db offset l7-100h
    db offset l8-100h
    db offset l9-100h
    db offset l10-100h
    db offset l12-100h
  subroutine o(n,m)
  integer m(n)              
  l=1;                      
  do while (l.ne.0)
     i=m(l)
     j=m(l+1)
     k=m(l+2)
     mi=mg(n,m,i)
     mj=mg(n,m,j)
     if(j.eq.(n+2)) then
        write(6,*)mj-mi
     else
        m(l+1)=mj-mi
     endif
     if (m(l+1).lt.0) then
        l=mg(n,m,k)
     else
        l=l+3
     endif
  end do
  return
  end
  function mg(n,m,i)
  integer m(n)
  if (i.eq.n+2) then
     read(5,*)mg
  elseif (i.eq.n+1) then
     mg=0
  else
     mg=m(i)
  endif
  return
  end
      program c
      parameter (n=1024)        ! The size to use for memeory
      integer m(n)              ! represent the memory
c     Load a program into memory
      i=1
 1    read(5,*,end=2)l
c     write (6,*) "Load ",l," into location ",i
      m(i)=l
      i=i+1
      goto 1
c     Run the computer
 2    call o(n,m)
      stop
      end

      subroutine o(n,m)
c     Simulate a simple computer that supports only a single
c     instruction. Memory is a fixed size array of integers.
c
c     The supported instruction is subtract-branch-negative which we
c     give the assembly syntax
c
c     sbn i j k
c
c     and acts by subtracting the value in memeory location i from that
c     in location j and storing the result in j. If the result is
c     negative, the PC is set to k. Because there is only one opcode, it
c     is not represented, so a program consists simply of a series of
c     triplet (i,j,k), and the PC is generally incremented by 3. The
c     program counter starts at 1
c
c     Povisions for IO and halting are by way of special memory
c     location:
c
c     * PC=0 means halt
c     * writes (opcode argument j) to memory location n+1 send
c       output, reads from n+1 evaluate as 0
c     * reads (opcode argument i, j, k) from memory location n+2 fetch
c       input, writes to n+2 are forbidden
c     * All others reads and writes outside of memeory are forbidden

c     n                         ! the size of the computers memory 
      integer m(n)              ! an array representing the computers memory
      l=1;                      ! program counter
      do while (l.ne.0)
         i=m(l)
         j=m(l+1)
         k=m(l+2)
c        write (6,*) "Executing from PC=",l,": (",i,", ",j,", ", k,")"
c        handle the write special case for output
         mi=mg(n,m,i)
         mj=mg(n,m,j)
         if(j.eq.(n+1)) then
            write(6,*)mj-mi
         else
c           write (6,*) "Setting location ",j," to ",mj-mi
            m(l+1)=mj-mi
         endif
         if (m(l+1).lt.0) then
            l=mg(n,m,k)
         else
            l=l+3
         endif
      end do
      return
      end

c     Handle the various read special cases
      function mg(n,m,i)
      integer m(n)
      if (i.eq.n+2) then
         read(5,*)mg
      elseif (i.eq.n+1) then
         mg=0
      else
         mg=m(i)
      endif
c     write (6,*) "Read ",mg," from location ",i
      return
      end
13
1025
0

14 
1025
0

14
15
0

0
0
0

-1
1
0
$ cat trivial.oisc | ./a.out 
           1
          -1
#include "/dev/tty"
perl -ne'push@a,split;if(eof){$i=0;while($i>=0){($a,$b,$c)=@a[$i..$i+2];$b>-1?$a[$b]-=$a[$a]:print chr$a[$a];$i=$b>-1&&$a[$b]<=0?$c:$i+3;}}'
#!/usr/bin/perl -n
push @prog, split /\s+/, $_;
if(eof) {
  $i = 0;
  while($i >= 0) {
    ($a, $b, $c) = @prog[$i .. $i + 2];
    if($b > -1) {
      $prog[$b] -= $prog[$a];
    } else {
      print chr $prog[$a];
    }
    if($b > -1 and $prog[$b] <= 0) {
      $i = $c;
    } else {
      $i + 3;
    }
  }
}
0 0 6 72 105 10 3 -1 9 4 -1 12 5 -1 15 0 0 -1
0    0     6
72   105   10
3   -1     9
4   -1     12
5   -1     15
0    0    -1
p,m=0,gets.chomp.split.map(:to_i)
while p>0
p=(m[m[b]]-=m[m[a]])>0?p+3:c
end
$><<m[0]
# Addition program, similar to http://planetmath.org/examplesofunlimitedregistermachines
c = [['j', 1, 2, 4], ['s', 0], ['s', 2], ['j', 1, 1, 0]]

# Input: 32, 13, thus the desired output is: 45
r = [32, 13]

k = 0
while k < c.length
    t = c[k]
    k += 1
    if t[0] == 'z'
        r[t[1]] = 0
    if t[0] == 's'
        if !r[t[1]]?
            r[t[1]] = 0
        r[t[1]] += 1
    if t[0] == 'j' && r[t[1]] == r[t[2]]
        k = t[3]
alert r[0]
k=0
while k<c.length
 t=c[k]
 k+=1
 if t[0]=='z'
  r[t[1]]=0
 if t[0]=='s'
  if !r[t[1]]?
   r[t[1]]=0
  r[t[1]]+=1
 if t[0]=='j'&&r[t[1]]==r[t[2]]
  k=t[3]
alert r[0]