Tulis juru bahasa Clem

11

Clem adalah bahasa pemrograman berbasis stack minimal yang menampilkan fungsi kelas satu. Tujuan Anda adalah menulis penerjemah untuk bahasa Clem. Itu harus benar menjalankan semua contoh termasuk dalam implementasi referensi, yang tersedia di sini .

  • Seperti biasa, celah standar berlaku.
  • Entri terkecil dengan jumlah byte menang.

Bahasa Clem

Clem adalah bahasa pemrograman berbasis stack dengan fungsi kelas satu. Cara terbaik untuk mempelajari Clem adalah dengan menjalankan cleminterpreter tanpa argumen. Ini akan mulai dalam mode interaktif, memungkinkan Anda untuk bermain dengan perintah yang tersedia. Untuk menjalankan program contoh, ketik di clem example.clmmana contoh adalah nama program. Tutorial singkat ini seharusnya sudah cukup untuk membantu Anda memulai.

Ada dua kelas fungsi utama. Fungsi atomik dan fungsi majemuk. Fungsi senyawa adalah daftar yang terdiri dari fungsi senyawa lain dan fungsi atom. Perhatikan bahwa fungsi gabungan tidak dapat mengandung dirinya sendiri.

Fungsi Atom

Tipe pertama dari fungsi atom adalah konstanta . Sebuah konstan hanyalah sebuah nilai integer. Misalnya, -10. Ketika penerjemah menemukan konstanta , itu mendorongnya ke tumpukan. Jalankan clemsekarang. Ketik -10pada prompt. Anda harus melihat

> -10
001: (-10)
>

Nilai tersebut 001menggambarkan posisi fungsi dalam tumpukan dan (-10) merupakan konstanta yang baru saja Anda masukkan. Sekarang masukkan +11saat diminta. Anda harus melihat

> +11
002: (-10)
001: (11)
>

Perhatikan bahwa (-10)telah pindah ke posisi kedua di tumpukan dan (11)sekarang menempati posisi pertama. Ini adalah sifat tumpukan! Anda akan melihat bahwa -itu juga perintah pengurangan. Setiap kali -atau +mendahului nomor, mereka menunjukkan tanda nomor itu dan bukan perintah yang sesuai. Semua fungsi atom lainnya adalah perintah . Total ada 14:

@  Rotate the top three functions on the stack
#  Pop the function on top of the stack and push it twice
$  Swap the top two functions on top of the stack
%  Pop the function on top of the stack and throw it away
/  Pop a compound function. Split off the first function, push what's left, 
   then push the first function.
.  Pop two functions, concatenate them and push the result
+  Pop a function. If its a constant then increment it. Push it
-  Pop a function. If its a constant then decrement it. Push it
<  Get a character from STDIN and push it to the stack. Pushes -1 on EOF.
>  Pop a function and print its ASCII character if its a constant
c  Pop a function and print its value if its a constant
w  Pop a function from the stack. Peek at the top of the stack. While it is
   a non-zero constant, execute the function.

Mengetik perintah saat diminta akan menjalankan perintah. Ketik #saat diminta (perintah rangkap). Anda harus melihat

> #
003: (-10)
002: (11)
001: (11)
> 

Perhatikan bahwa (11) telah digandakan. Sekarang ketikkan %pada prompt (perintah drop). Anda harus melihat

> %
002: (-10)
001: (11)
> 

Untuk mendorong perintah ke tumpukan, cukup lampirkan dalam tanda kurung. Ketik (-)pada prompt. Ini akan mendorong operator pengurangan ke tumpukan. Anda harus melihat

> (-)
003: (-10)
002: (11)
001: (-)
> 

Fungsi gabungan

Anda juga dapat menyertakan beberapa fungsi atom dalam tanda kurung untuk membentuk fungsi majemuk. Ketika Anda memasukkan fungsi gabungan saat diminta, itu didorong ke tumpukan. Ketik ($+$)pada prompt. Anda harus melihat

> ($+$)
004: (-10)
003: (11)
002: (-)
001: ($ + $)
>

Secara teknis, semua yang ada di stack adalah fungsi majemuk. Namun, beberapa fungsi senyawa pada stack terdiri dari fungsi atom tunggal (dalam hal ini, kami akan menganggapnya sebagai fungsi atom demi kenyamanan). Saat memanipulasi fungsi majemuk pada stack, .perintah (concatenation) seringkali berguna. Ketikkan .sekarang. Anda harus melihat

> . 
003: (-10)
002: (11)
001: (- $ + $)
> 

Perhatikan bahwa fungsi pertama dan kedua pada stack digabungkan, dan bahwa fungsi kedua pada stack datang pertama dalam daftar yang dihasilkan. Untuk menjalankan fungsi yang ada di stack (apakah itu atomik atau gabungan), kita harus mengeluarkan wperintah (while). The wperintah akan muncul fungsi pertama pada stack dan jalankan berulang kali selama fungsi kedua pada stack adalah non-nol konstan. Cobalah untuk memprediksi apa yang akan terjadi jika kita mengetik w. Sekarang, ketik w. Anda harus melihat

> w
002: (1)
001: (0)
> 

Itukah yang kamu harapkan? Dua angka yang duduk di atas tumpukan ditambahkan dan jumlahnya tetap. Ayo kita coba lagi. Pertama kita akan menjatuhkan nol dan mendorong 10 dengan mengetik %10. Anda harus melihat

> %10
002: (1)
001: (10)
> 

Sekarang kita akan mengetikkan seluruh fungsi dalam satu tembakan, tetapi kita akan menambahkan ekstra %di akhir untuk menghilangkan nol. Ketik (-$+$)w%pada prompt. Anda harus melihat

> (-$+$)w%
001: (11)
> 

(Perhatikan algoritma ini hanya berfungsi jika konstanta pertama pada stack positif).

String

String juga ada. Mereka sebagian besar gula sintaksis, tetapi bisa sangat berguna. Ketika penerjemah menemukan sebuah string, itu mendorong setiap karakter dari yang terakhir ke yang pertama ke tumpukan. Ketik %untuk menjatuhkan 11 dari contoh sebelumnya. Sekarang, ketik 0 10 "Hi!"prompt. The 0akan memasukkan terminator NULL dan 10akan menyisipkan karakter baru-line. Anda harus melihat

> 0 10 "Hi!"
005: (0)
004: (10)
003: (33)
002: (105)
001: (72)
> 

Ketik (>)wuntuk mencetak karakter dari tumpukan hingga kami menemukan terminator NULL. Anda harus melihat

> (>)w
Hi!
001: (0)
> 

Kesimpulan

Semoga ini cukup untuk membantu Anda memulai dengan juru bahasa. Desain bahasa harus relatif lurus ke depan. Beri tahu saya jika ada sesuatu yang sangat tidak jelas :) Beberapa hal sengaja dibiarkan kabur: nilai harus ditandatangani dan setidaknya 16 bit, tumpukan harus cukup besar untuk menjalankan semua program referensi, dll. Banyak detail belum diukir di sini karena spesifikasi bahasa lengkap akan sangat besar untuk dikirim (dan saya belum menulis satu pun: P). Jika ragu, meniru implementasi referensi.

Halaman esolangs.org untuk Clem

Implementasi referensi dalam C

Orby
sumber
Anda bilang belum menulis spesifikasi bahasa. Saya mengerti bahwa Anda adalah pencetus bahasa ini?
COTO
@COTO Itu benar. Saya menciptakan bahasa.
Orby
5
Pertanyaan yang sangat penting: apakah Anda mengucapkannya "klem" atau "see-lem"?
Martin Ender
4
@ MartinBüttner: "klem" :)
Orby
2
Anda mungkin ingin menentukan arah di mana perintah @ memutar 3 fungsi teratas. (001 -> 002 -> 003 -> 001, atau 003 -> 002 -> 001 -> 003)
kwokkie

Jawaban:

1

Haskell, 931 921 875

ini belum sepenuhnya golf tetapi mungkin tidak akan pernah. Meski begitu, ini sudah lebih pendek dari semua solusi lainnya. Saya akan golf ini lebih cepat. Saya tidak ingin bermain golf lebih dari ini.

mungkin memiliki beberapa bug halus karena saya tidak bermain dengan implementasi referensi C.

solusi ini menggunakan tipe StateT [String] IO ()untuk menyimpan program clem "runnable". kebanyakan program adalah parser yang mem-parsing "program runnable".

untuk menjalankan penggunaan ini r "<insert clem program here>".

import Text.Parsec
import Control.Monad.State
import Control.Monad.Trans.Class
import Data.Char
'#'%(x:y)=x:x:y
'%'%(x:y)=y
'@'%(x:y:z:w)=y:z:x:w
'$'%(x:y:z)=y:x:z
'/'%((a:b):s)=[a]:b:s
'+'%(a:b)=i a(show.succ)a:b
'.'%(a:b:c)=(a++b):c
_%x=x
b=concat&between(s"(")(s")")(many$many1(noneOf"()")<|>('(':)&((++")")&b))
e=choice[s"w">>c(do p<-t;let d=h>>= \x->if x=="0"then a else u p>>d in d),m&k,s"-">>(m&(' ':)&k<|>c(o(\(a:b)->i a(show.pred)a:b))),s"c">>c(do
 d<-t
 i d(j.putStr.show)a),o&(++)&map(show.ord)&between(s"\"")(s"\"")(many$noneOf"\""),(do
 s"<"
 c$j getChar>>=m.show.ord),(do
 s">"
 c$do
 g<-t
 i g(j.putChar.chr)a),m&b,o&(%)&anyChar]
k=many1 digit
i s f g|(reads s::[(Int,String)])>[]=f$(read s::Int)|0<1=g
t=h>>=(o tail>>).c
c n=return n
a=c()
h=head&get
(&)f=fmap f
m=o.(:)
o=modify
u=(\(Right r)->r).parse(sequence_&many e)""
r=(`runStateT`[]).u
s=string
j=lift
haskeller bangga
sumber
5

Python, 1684 1281 karakter

Selesaikan semua hal dasar golf. Ini menjalankan semua contoh program dan mencocokkan keluaran karakter-untuk-karakter.

import sys,os,copy as C
L=len
S=[]
n=[S]
Q=lambda:S and S.pop()or 0
def P(o):
 if o:n[0].append(o)
def X():x=Q();P(x);P(C.deepcopy(x))
def W():S[-2::]=S[-1:-3:-1]
def R():a,b,c=Q(),Q(),Q();P(a);P(c);P(b)
def A(d):
 a=Q()
 if a and a[0]:a=[1,a[1]+d,lambda:P(a)]
 P(a)
def V():
 a=Q();P(a)
 if a and a[0]-1and L(a[2])>1:r=a[2].pop(0);P(r)
def T():
 b,a=Q(),Q()
 if a!=b:P([0,0,(a[2],[a])[a[0]]+(b[2],[b])[b[0]]])
 else:P(a);P(b)
def r():a=os.read(0,1);F(ord(a)if a else-1)
def q(f):
 a=Q()
 if a and a[0]:os.write(1,(chr(a[1]%256),str(a[1]))[f])
def e(f,x=0):f[2]()if f[0]+f[1]else([e(z)for z in f[2]]if x else P(f))
def w():
 a=Q()
 while a and S and S[-1][0]and S[-1][1]:e(a,1)
def Y():n[:0]=[[]]
def Z():
 x=n.pop(0)
 if x:n[0]+=([[0,0,x]],x)[L(x)+L(n)==2]
D={'%':Q,'#':X,'$':W,'@':R,'+':lambda:A(1),'-':lambda:A(-1),'/':V,'.':T,'<':r,'>':lambda:q(0),'c':lambda:q(1),'w':w,'(':Y,')':Z}
def g(c):D[c]()if L(n)<2or c in'()'else P([0,1,D[c]])
N=['']
def F(x):a=[1,x,lambda:P(a)];a[2]()
def E():
 if'-'==N[0]:g('-')
 elif N[0]:F(int(N[0]))
 N[0]=''
s=j=""
for c in open(sys.argv[1]).read()+' ':
 if j:j=c!="\n"
 elif'"'==c:E();s and map(F,map(ord,s[:0:-1]));s=(c,'')[L(s)>0]
 elif s:s+=c
 elif';'==c:E();j=1
 else:
    if'-'==c:E()
    if c in'-0123456789':N[0]+=c
    else:E();c in D and g(c)

Pengujian :

Kumpulkan clemint.py , clemtest_data.py , clemtest.py , dan clembiner yang dikompilasi ke dalam direktori dan jalankan clemtest.py.

Perluasan :

Versi yang paling ungolfed adalah yang ini . Ikuti bersama dengan yang itu.

Sadalah tumpukan utama. Setiap item tumpukan adalah 3 daftar, salah satu dari:

Constant: [1, value, f]
Atomic: [0, 1, f]
Compound: [0, 0, fs]

Untuk konstanta, fadalah fungsi yang mendorong konstanta ke stack. Untuk atmosferik, fadalah fungsi yang mengeksekusi salah satu operasi (misalnya -, +). Untuk senyawa, fsadalah daftar barang.

xecmengeksekusi item. Jika itu adalah konstanta atau atom, itu hanya menjalankan fungsinya. Jika itu adalah senyawa, jika belum ada rekursi, ia menjalankan setiap fungsi. Jadi mengeksekusi (10 20 - 30)akan mengeksekusi masing-masing fungsi 10, 20, -, dan 30, meninggalkan 10 19 30di stack. Jika ada rekursi, maka itu hanya mendorong fungsi senyawa ke tumpukan. Misalnya, ketika mengeksekusi (10 20 (3 4) 30), hasilnya seharusnya 10 20 (3 4) 30, bukan 10 20 3 4 30.

Nesting agak sulit. Apa yang Anda lakukan saat membacanya (1 (2 (3 4)))? Solusinya adalah memiliki setumpuk tumpukan. Pada setiap tingkat bersarang, tumpukan baru didorong pada tumpukan tumpukan, dan semua operasi dorong masuk ke tumpukan ini. Lebih lanjut, jika telah ada sarang, maka fungsi atom didorong bukannya dieksekusi. Jadi jika Anda melihat 10 20 (- 30) 40, 10didorong, maka 20, maka tumpukan baru dibuat, -dan 30didorong ke tumpukan baru, dan )muncul dari tumpukan baru, mengubahnya menjadi item, dan mendorongnya ke tumpukan satu tingkat ke bawah. endnest()menangani ). Itu agak rumit karena ada kasus khusus ketika hanya satu item yang didorong dan kami mendorong kembali ke tumpukan utama. Artinya, (10)harus mendorong konstan10, bukan komposit dengan konstanta satu, karena itu -dan +tidak berfungsi. Saya tidak yakin apakah ini berprinsip tetapi cara kerjanya ...

Penerjemah saya adalah prosesor karakter per karakter - tidak membuat token - sehingga angka, string, dan komentar agak menjengkelkan untuk ditangani. Ada tumpukan terpisah N,, untuk nomor yang sedang diproses, dan kapan saja karakter yang bukan nomor diproses, saya harus menelepon endnum()untuk melihat apakah saya harus terlebih dahulu menyelesaikan nomor itu dan meletakkannya di tumpukan. Apakah kita dalam string atau komentar disimpan oleh variabel boolean; ketika sebuah string ditutup itu mendorong semua jeroan di stack. Angka negatif memerlukan penanganan khusus juga.

Itu saja untuk ikhtisar. Sisanya menerapkan semua built-in, dan pastikan untuk melakukan copy mendalam dalam +, -dan #.

Claudiu
sumber
Pujian! Apakah kamu bersenang-senang? :)
Orby
@Orby: Saya yakin begitu! Ini bahasa yang menarik, jelas yang aneh. Saya berharap saya bisa mendapatkan penerjemah yang <1k. Tidak yakin apa yang diharapkan dari kiriman lain.
Claudiu
4

C 837

Terima kasih kepada @ceilingcat untuk menemukan versi yang lebih baik (dan lebih pendek)

Ini memperlakukan semuanya sebagai string sederhana - semua item stack adalah string, bahkan konstanta adalah string.

#define Q strcpy
#define F(x)bcopy(b,f,p-b);f[p-b-x]=!Q(r,p);
#define C(x,y)Q(S[s-x],S[s-y]);
#define N[9999]
#define A Q(S[s++]
#define D sprintf(S[s++],"%d"
#define G(x)}if(*f==x){
#define H(x)G(x)s--;
#define R return
#define Z(x)T(t,u,v)-1||putchar(x);H(
char S N N;s;c;T(b,f,r)char*b,*f,*r;{char*p;strtol(b+=strspn(b," "),&p,0);if(p>b){F(0)R 1;}if(c=*b==40){for(p=++b;c;)c+=(*p==40)-(*p++==41);F(1)R-1;}p++;F(0)*r*=!!*b;R 0;}*P(char*p){if(*p==34)R++p;char*r=P(p+1);D,*p);R r;}E(char*x){char*p,c N,f N,r N,t N,u N,v N;for(Q(c,x);*c;Q(c,p)){Q(t,S[s-1]);if(T(c,f,p=r))A,f);else{{G(64)C(0,1)C(1,2)C(2,3)C(3,0)G(35)A,t);G(36)C(0,2)C(2,1)C(1,0)H(37)H(47)T(t,u,v);*v&&A,v);A,u);H(46)strcat(strcat(S[s-1]," "),t);H(43)D,atoi(t)+1);H(45)D,atoi(t)-1);G(60)D,getchar());H(62)Z(atoi(u))99)Z(*u)119)for(Q(u,t);atoi(S[s-1]);)E(u);G(34)p=P(p);}}}}

Cobalah online!

Versi kurang asli dari golf saya (tidak seperti versi golf yang satu ini mencetak tumpukan ketika berakhir jika tidak kosong dan mengambil parameter -e sehingga Anda dapat menentukan skrip pada baris perintah alih-alih membaca dari file):

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define FIRST_REST(x) memcpy(first, b, p - b); first[p - b - x] = '\0'; strcpy(rest, p);
#define COPY(dest,src) strcpy(stack[size + dest], stack[size + src]);
char stack[9999][9999]; int size = 0;
int token(char *b, char *first, char *rest)
{
    while (*b == 32) b++;
    char *p; int x = strtol(b, &p, 0);
    if (p > b) { FIRST_REST(0) return 1; }
    if (*b == '(') { int c = 1; for (p = ++b; c; ++p) c += (*p == '(') - (*p == ')'); FIRST_REST(1) return -1; }
    p++; FIRST_REST(0) if (!*b) *rest = '\0'; return 0;
}
char *push(char *pointer)
{
    if (*pointer == '\"') return pointer+1;
    char *result = push(pointer+1);
    sprintf(stack[size++], "%d", *pointer);
    return result;
}
void eval(char *x)
{
    char program[9999], first[9999], rest[9999], tos[9999], tmp1[9999], tmp2[9999];
    char *pointer;
    for (strcpy(program, x); *program; strcpy(program, pointer))
    {
        *stack[size] = '\0';
        strcpy(tos, stack[size-1]);
        if (token(program, first, rest))
        {
            pointer = rest;
            strcpy(stack[size++], first);
        }
        else
        {
            pointer = rest;
            if (*first == '@'){
                COPY(0, -1) COPY(-1, -2) COPY(-2, -3) COPY(-3, 0) }
            if (*first == '#')
                strcpy(stack[size++], tos);
            if (*first == '$'){
                COPY(0, -2) COPY(-2, -1) COPY(-1, 0) }
            if (*first == '%')
                size--;
            if (*first == '/'){
                size--; token(tos, tmp1, tmp2); if (*tmp2) strcpy(stack[size++], tmp2); strcpy(stack[size++], tmp1); }
            if (*first == '.'){
                size--; strcat(stack[size - 1], " "); strcat(stack[size - 1], tos); }
            if (*first == '+'){
                size--; sprintf(stack[size++], "%d", atoi(tos) + 1); }
            if (*first == '-'){
                size--; sprintf(stack[size++], "%d", atoi(tos) - 1); }
            if (*first == '<')
                sprintf(stack[size++], "%d", getchar());
            if (*first == '>'){
                size--; if (token(tos, tmp1, tmp2) == 1) putchar(atoi(tmp1)); }
            if (*first == 'c'){
                size--; if (token(tos, tmp1, tmp2) == 1) printf("%s", tmp1); }
            if (*first == 'w'){
                size--; strcpy(tmp1, tos); while (atoi(stack[size - 1])) eval(tmp1); }
            if (*first == '\"')
                pointer=push(pointer);
        }
    }
}
int main(int argc, char **argv)
{
    char program[9999] = "";
    int i = 0, comment = 0, quote = 0, space = 0;
    if (!strcmp(argv[1], "-e"))
        strcpy(program, argv[2]);
    else
    {
        FILE* f = fopen(argv[1], "r");
        for (;;) {
            char ch = fgetc(f);
            if (ch < 0) break;
            if (!quote) {
                if (ch == '\n') comment = 0;
                if (ch == ';') comment = 1;
                if (comment) continue;
                if (ch <= ' ') { ch = ' '; if (space++) continue; }
                else space = 0;
            }
            if (ch == '\"') quote = 1 - quote;
            program[i++] = ch;
        }
        fclose(f);
    }
    eval(program);
    for (int i = 0; i < size; i++) printf("%03d: (%s)\r\n",size-i,stack[i]);
    return 0;
}
Jerry Jeremiah
sumber
Bagus! Mengesankan Anda mengalahkan solusi Python dalam C. Saya harus mengunggah versi saya yang lebih pendek, saya berhasil memangkas sekitar 60 byte atau lebih .. Saya masih bertanya-tanya apakah ada pendekatan berbeda yang akan menghasilkan kurang dari 1000 karakter
Claudiu
@Cudiudiu saya juga berpikir begitu - tetapi saya tidak tahu caranya.
Jerry Jeremiah