brainfuck 컴파일러 작성


13

brainfuck 프로그램을 가지고 실행 가능한 기계 코드로 컴파일하는 프로그램을 작성하십시오. x86, x86_64, jvm (java bytecode) 또는 armv6을 대상으로하고 ELF, a.out, 클래스 파일, exe, com 등의 실행 가능 형식 중 하나를 사용할 수 있습니다. 실행 파일은 Linux 또는 Windows (또는 둘 중 하나의 Java)에서 작동해야합니다.

프로그램이나 생성 된 실행 파일 모두 외부 프로그램 (예 : 다른 컴파일러, 어셈블러 또는 인터프리터)을 실행할 수 없습니다.

가장 짧은 코드가 승리합니다.


2
downvoting의 이유가 있습니까?
SE가 EVIL이기 때문에 aditsu가 종료했습니다.

머신 코드 소스가 있습니까? 예를 들어 사용할 수있는 리소스가 있다면 이것이 첫 번째 머신 코드 골프 연습이 될까요?
WallyWest

@ Eliseod'Annunzio 특정 리소스가 없지만 일반적으로 선택한 플랫폼에 맞는 어셈블리 언어 를 살펴보고 몇 가지 예를 어셈블 / 분해 할 수 있습니다. 구글은 당신의 친구입니다 :) 한참 전에 나는 두 개의 머신 코드 골프 대회에 참여했습니다. 잘하지 않았지만 추가 헤더와 물건이 없었기 때문에 DOS에 com 형식을 사용하고 있었음을 기억합니다. 암호. 다른 사람들이 더 많은 링크와 제안을 줄 수 있습니다.
SE가 EVIL이기 때문에 Aditsu 종료

답변:


5

C, 866 783 바이트

내 코드는 32 비트 ELF 실행 파일을 출력하므로 모든 사람의 설정에서 작동한다고 약속 할 수 없습니다. 실행 파일이 내 컴퓨터에서 segfaulting을 중지하도록하려면 약간의 조정이 필요했습니다.

이것을 실행하려는 사람은 :

$ uname --all
Linux 4.4.0-24-generic #43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Brainfuck 프로그램은 stdin에서 읽고 컴파일 된 ELF는 stdout에 작성됩니다.

#define P *(t++)
#define C case
#define B break
char a[30000],b[65535],f,*t=b;*c[100];**d=c;main(g){P=188;t+=4;while((f=getchar())!=-1)switch(f){C'>':P=68;B;C'<':P=76;B;C'+':P=254;P=4;P=36;B;C'-':P=254;P=12;P=36;B;C'.':P=187;t+=4;P=137;P=225;P=186;P=1;t+=3;P=184;P=4;t+=3;P=205;P=128;B;C',':P=187;P=1;t+=3;P=137;P=225;P=186;P=1;t+=3;P=184;P=3;t+=3;P=205;P=128;B;C'[':P=138;P=4;P=36;P=133;P=192;P=15;P=132;t+=4;*d=(int*)t-1;d++;B;C']':P=138;P=4;P=36;P=133;P=192;P=15;P=133;t+=4;d--;g=((char*)(*d+1))-t;*((int*)t-1)=g;**d=-g;B;}P=184;P=1;t+=3;P=187;t+=4;P=205;P=128;*(int*)(b+1)=0x8048054+t-b;long long z[]={282579962709375,0,4295163906,223472812116,0,4297064500,4294967296,577727389698621440,36412867248128,30064779550,140720308490240};write(1,&z,84);write(1,b,t-b);write(1,a,30000);}

언 골프

ungolfed 코드 버전에서 무슨 일이 일어나고 있는지 더 잘 알 수 있습니다. 골프 코드의 끝에있는 문자 배열은 ungolfed 코드의 ELF 및 프로그램 헤더 인코딩입니다. 이 코드는 또한 각 Brainfuck 명령이 바이트 코드로 변환되는 방법을 보여줍니다.

#include <linux/elf.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#define MAX_BIN_LEN 65535
#define MAX_JUMPS 100

unsigned int org = 0x08048000;



unsigned char move_right[] = {0x44};                              /*inc   esp         */

unsigned char move_left[]  = {0x4c};                              /*dec   esp         */

unsigned char inc_cell[]   = {0xfe,0x04,0x24};                    /*inc   [esp]       */

unsigned char dec_cell[]   = {0xfe,0x0c,0x24};                    /*dec   [esp]       */

unsigned char read_char[]  = {0xbb,0x00,0x00,0x00,0x00,           /*mov   ebx,  0     */
                              0x89,0xe1,                          /*mov   ecx,  esp   */
                              0xba,0x01,0x00,0x00,0x00,           /*mov   edx,  1     */
                              0xb8,0x03,0x00,0x00,0x00,           /*mov   eax,  3     */
                              0xcd,0x80};                         /*int   0x80        */

unsigned char print_char[] = {0xbb,0x01,0x00,0x00,0x00,           /*mov   ebx,  1     */
                              0x89,0xe1,                          /*mov   ecx,  esp   */
                              0xba,0x01,0x00,0x00,0x00,           /*mov   edx,  1     */
                              0xb8,0x04,0x00,0x00,0x00,           /*mov   eax,  4     */
                              0xcd,0x80};                         /*int   0x80        */


unsigned char loop_start[] = {0x8a,0x04,0x24,                     /*mov   eax,  [esp] */
                              0x85,0xc0,                          /*test  eax,  eax   */
                              0x0f,0x84,0x00,0x00,0x00,0x00};     /*je    int32_t     */

unsigned char loop_end[]   = {0x8a,0x04,0x24,                     /*mov   eax,  [esp] */
                              0x85,0xc0,                          /*test  eax,  eax   */
                              0x0f,0x85,0x00,0x00,0x00,0x00};     /*jne   int32_t     */

unsigned char call_exit[]  = {0xb8,0x01,0x00,0x00,0x00,           /*mov   eax,  1     */
                              0xbb,0x00,0x00,0x00,0x00,           /*mov   ebx,  0     */
                              0xcd,0x80};                         /*int   0x80        */
unsigned char prelude[]    = {0xbc,0x00,0x00,0x00,0x00};          /*mov   esp, int32_t*/

unsigned char tape[100];

int main(){
    unsigned char text[MAX_BIN_LEN];
    unsigned char *txt_ptr = text;

    int32_t *loop_jmps[MAX_JUMPS];
    int32_t **loop_jmps_ptr = loop_jmps;

    Elf32_Off entry;

    entry = org + sizeof(Elf32_Ehdr) + 1 * sizeof(Elf32_Phdr);

    memcpy(txt_ptr,prelude,sizeof(prelude));
    txt_ptr += sizeof(prelude);
    char input;
    while((input = getchar()) != -1){
        switch(input){
            case '>':
                memcpy(txt_ptr,move_right,sizeof(move_right));
                txt_ptr += sizeof(move_right);
                break;
            case '<':
                memcpy(txt_ptr,move_left,sizeof(move_left));
                txt_ptr += sizeof(move_left);
                break;
            case '+':
                memcpy(txt_ptr,inc_cell,sizeof(inc_cell));
                txt_ptr += sizeof(inc_cell);
                break;
            case '-':
                memcpy(txt_ptr,dec_cell,sizeof(dec_cell));
                txt_ptr += sizeof(dec_cell);
                break;
            case '.':
                memcpy(txt_ptr,print_char,sizeof(print_char));
                txt_ptr += sizeof(print_char);
                break;
            case ',':
                memcpy(txt_ptr,read_char,sizeof(read_char));
                txt_ptr += sizeof(read_char);
                break;
            case '[':
                memcpy(txt_ptr,loop_start,sizeof(loop_start));
                txt_ptr += sizeof(loop_start);
                *loop_jmps_ptr = (int32_t*) txt_ptr - 1;
                loop_jmps_ptr++;
                break;
            case ']':
                memcpy(txt_ptr,loop_end,sizeof(loop_end));
                txt_ptr += sizeof(loop_end);
                loop_jmps_ptr--;
                int32_t offset = ((unsigned char*) (*loop_jmps_ptr + 1)) - txt_ptr;
                *((int32_t*)txt_ptr - 1) = offset;
                **loop_jmps_ptr = -offset;
                break;
        }
    }

    memcpy(txt_ptr,call_exit,sizeof(call_exit));
    txt_ptr += sizeof(call_exit);

    *(int32_t*)(text + 1) = entry + (txt_ptr - text);


    Elf32_Ehdr ehdr = {
        {0x7F,'E','L','F',ELFCLASS32,ELFDATA2LSB,EV_CURRENT,0,0,0,0,0,0,0,0,0},
        ET_EXEC,
        EM_386,
        EV_CURRENT,
        entry,
        sizeof(Elf32_Ehdr),
        0,
        0,
        sizeof(Elf32_Ehdr),
        sizeof(Elf32_Phdr),
        1,
        0,
        0,
        SHN_UNDEF,
    };

    Elf32_Phdr phdr = {
        PT_LOAD,
        0,
        org,
        org,
        sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) + (txt_ptr - text),
        sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) + (txt_ptr - text),
        PF_R | PF_X | PF_W,
        0x1000,
    };

    int out = open("a.out",O_CREAT|O_TRUNC|O_WRONLY,S_IRWXU);
    write(out,&ehdr,sizeof(Elf32_Ehdr));
    write(out,&phdr,sizeof(Elf32_Phdr));

    write(out,text,txt_ptr-text);
    write(out,tape,sizeof(tape));
    close(out);
}

자기 수정 BrainFuck

바이트를 절약하기 위해 내 컴파일러의 테이프는 .bss섹션이나 그와 비슷한 멋진 부분에 할당되지 않습니다 . 대신, 테이프는 Brainfuck 프로그램의 컴파일 된 바이트 코드 바로 뒤에 작성된 30,000 개의 널 바이트입니다. 이것을 알고 내 컴파일러가 생성하는 바이트 코드를 알고 있으면 런타임에 바이트 코드를 생성하거나 수정할 수 있습니다. 이 '기능'에 대한 간단한 설명은 자체 종료 값을 설정하는 Brainfuck 프로그램입니다.

 <<<<<<+ 

프로그램은 종료 코드가 정상적으로 0이 설정되는 지점까지 테이프의 왼쪽 가장자리에서 바이트 코드로 이동합니다.이 바이트를 증가 시키면 프로그램이 종료 될 때 종료 코드가 0 대신 1로 설정됩니다. 지속성으로 Brainfuck에서 시스템 레벨 프로그래밍을 수행하는 데 사용할 수 있습니다.


좋은; 코드에는 실제로 865 바이트가 있습니다 (파일 끝에 줄 바꿈이 필요하지 않음). 또한 char 변수 선언을 병합 할 수 있습니다. 긴 배열도 0이 많기 때문에 압축 될 수 있는지 궁금합니다.
SE가 EVIL이기 때문에 aditsu가 종료 됨

@aditsu 헤더 배열에 대한 더 나은 인코딩을 찾기 위해 노력하고 있습니다. C에는 압축 라이브러리가 내장되어 있지 않으므로 압축하지 않아도됩니다. 내가 생각해 낸 최선의 방법은 long long int대신 배열로 인코딩하는 것입니다 char. 변수 선언 중 일부를 골라 낼 공간이 분명히 있습니다. 나는 거기에 얼마나 많이 갈 수 있는지 보며 대답을 업데이트 할 것이다.
ankh-morpork

나는 RLE의 형태를 생각하지만 .. :) 작동 무엇이든
SE 악이기 때문에 aditsu 종료

pow의 암시 적 선언을 사용하여 4 바이트에 대해 '577727389698621440'을 '4105 * pow (2,47)'로 '577727389698621440'을 대체 할 수 있습니다 (적어도 gcc와 함께 작동) ~
Maliafo

13

파이썬, 1974 자

import sys
s='\x11\x75\x30\xbc\x08\x4b\x03\x3c'
k=[]
for c in sys.stdin.read():
 if'>'==c:s+='\x84\x01\x01'
 if'<'==c:s+='\x84\x01\xff'
 if'+'==c:s+='\x2a\x1b\x5c\x33\x04\x60\x91\x54'
 if'-'==c:s+='\x2a\x1b\x5c\x33\x04\x64\x91\x54'
 if'['==c:k+=[len(s)];s+='\x2a\x1b\x33\x99\x00\x00'
 if']'==c:a=k[-1];k=k[:-1];d=len(s)-a;s=s[:a+4]+'%c%c'%(d>>8,d&255)+s[a+6:]+'\xa7%c%c'%(-d>>8&255,-d&255)
 if','==c:s+='\x2a\x1b\xb2\x00\x02\xb6\x00\x03\x91\x54'
 if'.'==c:s+='\xb2\x00\x04\x59\x2a\x1b\x33\xb6\x00\x05\xb6\x00\x06'
s+='\xb1'
n=len(s)
sys.stdout.write('\xca\xfe\xba\xbe\x00\x03\x00-\x00+\n\x00\x08\x00\x13\t\x00\x14\x00\x15\n\x00\x16\x00\x17\t\x00\x14\x00\x18\n\x00\x19\x00\x1a\n\x00\x19\x00\x1b\x07\x00\x1c\x07\x00\x1d\x01\x00\x06<init>\x01\x00\x03()V\x01\x00\x04Code\x01\x00\x0fLineNumberTable\x01\x00\x04main\x01\x00\x16([Ljava/lang/String;)V\x01\x00\nExceptions\x07\x00\x1e\x01\x00\nSourceFile\x01\x00\x06B.java\x0c\x00\t\x00\n\x07\x00\x1f\x0c\x00 \x00!\x07\x00"\x0c\x00#\x00$\x0c\x00%\x00&\x07\x00\'\x0c\x00(\x00)\x0c\x00*\x00\n\x01\x00\x01B\x01\x00\x10java/lang/Object\x01\x00\x13java/io/IOException\x01\x00\x10java/lang/System\x01\x00\x02in\x01\x00\x15Ljava/io/InputStream;\x01\x00\x13java/io/InputStream\x01\x00\x04read\x01\x00\x03()I\x01\x00\x03out\x01\x00\x15Ljava/io/PrintStream;\x01\x00\x13java/io/PrintStream\x01\x00\x05write\x01\x00\x04(I)V\x01\x00\x05flush\x00!\x00\x07\x00\x08\x00\x00\x00\x00\x00\x02\x00\x01\x00\t\x00\n\x00\x01\x00\x0b\x00\x00\x00\x1d\x00\x01\x00\x01\x00\x00\x00\x05*\xb7\x00\x01\xb1\x00\x00\x00\x01\x00\x0c\x00\x00\x00\x06\x00\x01\x00\x00\x00\x03\x00\t\x00\r\x00\x0e\x00\x02\x00\x0b\x00\x00'+'%c%c'%((n+60)>>8,(n+60)&255)+'\x00\x04\x00\x03\x00\x00'+'%c%c'%(n>>8,n&255)+s+'\x00\x00\x00\x01\x00\x0c\x00\x00\x00*\x00\n\x00\x00\x00\x05\x00\x06\x00\x06\x00\x08\x00\t\x00\x0b\x00\x0b\x00\x13\x00\r\x00\x1d\x00\x0f\x00&\x00\x11\x00,\x00\x12\x002\x00\x14\x008\x00\x15\x00\x0f\x00\x00\x00\x04\x00\x01\x00\x10\x00\x01\x00\x11\x00\x00\x00\x02\x00\x12')

아래는 자바 바이트 코드로의 번역입니다. local 0은 테이프를 나타내는 바이트 배열이고 local 1은 데이터 포인터입니다.

>  iinc 1,+1
<  iinc 1,-1
+  aload_0;iload_1;dup2;baload;iconst_1;iadd;i2b;bastore
-  aload_0;iload_1;dup2;baload;iconst_1;isub;i2b;bastore
[  aload_0;iload_1;baload;ifeq xx xx
]  goto xx xx
,  aload_0;iload_1;getstatic #2;invokevirtual #3;i2b;bastore
.  getstatic #4;dup;aload_0;iload_1;baload;invokevirtual #5;invokevirtual #6

xx xx일치 브래킷을 도달하는 오프셋입니다. # 2는 System.in, # 3은 read(), # 4는 System.out, # 5는 write(), # 6은 flush()입니다.

프리앰블은 30000 바이트 배열을 할당하고 테이프 위치를 0으로 초기화합니다.

끝에있는 거대한 래퍼 B.java는 각 opcode 중 하나에 대한 코드 로 더미 파일을 컴파일하고 (정확한 상수 테이블 및 기타 정크 생성을 유도하기 위해) 섬세한 수술을 수행하여 생성되었습니다.

다음과 같이 실행

python bfc.py < input.b > B.class
java B

분해

javap -c B

나는 그것이 더 골프를 칠 수 있다고 확신합니다. 나는 그것이 작동하는 것이 행복합니다 ...


1
sys import *에서 사용한 다음 두 sys를 모두 제거하여 2 개의 문자를 제거하십시오.
Timtech

2
base64를 사용하여 이진 데이터를 인코딩하고 일부 바이트를
줄일 수 있습니다

3

16 비트 x86 어셈블리 코드, 104 바이트

이 코드는 2014 년이지만, 방금 작업을 찾았습니다.

;compliant version, non-commands are ignored, but 104 bytes long

[bits 16]  
[org 0x100]  
; assume bp=091e used  
; assume di=fffe  
; assume si=0100  
; assume dx=cs (see here)  
; assume cx=00ff  
; assume bx=0000  
; assume ax=0000 used (ah)  
; assume sp=fffe  
start:
        mov al, code_nothing - start  
code_start:
        mov ch, 0x7f ; allow bigger programs  
        mov bx, cx  
        mov di, cx  
        rep stosb  
        mov bp, find_right + start - code_start ;cache loop head for smaller compiled programs  
        jmp code_start_end  
find_right:
        pop si  
        dec si  
        dec si ;point to loop head  
        cmp [bx], cl  
        jne loop_right_end  
loop_right:
        lodsb  
        cmp al, 0xD5 ; the "bp" part of "call bp" (because 0xFF is not unique, watch for additional '[')  
        jne loop_left  
        inc cx  
loop_left:
        cmp al, 0xC3 ; ret (watch for ']')  
        jne loop_right  
        loop loop_right ;all brackets matched when cx==0  
        db 0x3c ;cmp al, xx (mask push)  
loop_right_end:
        push si  
        lodsw ; skip "call" or dummy "dec" instruction, depending on context  
        push si  
code_sqright:
        ret  
code_dec:
        dec byte [bx]  
code_start_end:
        db '$' ;end DOS string, also "and al, xx"  
code_inc:
        inc byte [bx]  
        db '$'  
code_right:
        inc bx ;al -> 2  
code_nothing:
        db '$'  
code_left:
        dec bx  
        db '$'  
code_sqleft:
        call bp  
        db '$'  
; create lookup table  
real_start:
        inc byte [bx+'<'] ;point to code_left  
        dec byte [bx+'>'] ;point to code_right  
        mov byte [bx+'['], code_sqleft - start  
        mov byte [bx+']'], code_sqright - start  
        lea sp, [bx+45+2] ;'+' + 4 (2b='+', 2c=',', 2d='-', 2e='.')  
        push (code_dec - start) + (code_dot - start) * 256  
        push (code_inc - start) + (code_comma - start) * 256  
pre_write:
        mov ah, code_start >> 8  
        xchg dx, ax  
; write  
        mov ah, 9  
        int 0x21  
; read  
code_comma:
        mov dl, 0xff  
        db 0x3d ; cmp ax, xxxx (mask mov)  
code_dot:
        mov dl, [bx]  
        mov ah, 6  
        int 0x21  
        mov [bx], al  
        db '$'  
        db 0xff ; parameter for '$', doubles as test for zero  
; switch  
        xlatb  
        jne pre_write  
  ; next two lines can also be removed  
  ; if the program ends with extra ']'  
  ; and then we are at 100 bytes... :-)  
the_end:
        mov dl, 0xC3  
        int 0x21  
        int 0x20 

당신은 그것이 아니다 확신 통역 ?
SE가 EVIL이기 때문에 Aditsu 종료

1
아니요, 절대적으로 컴파일러입니다. "bf.com <hello.bf> out.com"을 실행하면 out.com이 실행됩니다.
피터 페리

1
좋아, 어떻게 컴파일하고 어떤 OS에서 작동하는지 설명해 주시겠습니까? 아직 실행할 수 없었습니다.
SE가 EVIL이기 때문에 aditsu가 종료 됨

YASM과 조립하고 MS-DOS에서 실행하십시오 (DOSBox를 통해 가능).
peter ferrie

1
'<'및 '>'는 이스케이프되어야합니다. 어쨌든 32 비트 Windows에는 실행할 DOS 콘솔이 있으며 "com"은 명시 적으로 허용되는 형식 중 하나입니다.
peter ferrie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.