Schreiben Sie einen Clem-Dolmetscher


11

Clem ist eine minimale stapelbasierte Programmiersprache mit erstklassigen Funktionen. Ihr Ziel ist es, einen Dolmetscher für die Clem-Sprache zu schreiben. Es sollte richtig alle Beispiele auszuführen , die in der Referenzimplementierung enthalten, die verfügbar ist hier .

  • Wie üblich gelten Standardlücken .
  • Der kleinste Eintrag nach Byte-Anzahl gewinnt.

Die Clem-Sprache

Clem ist eine stapelbasierte Programmiersprache mit erstklassigen Funktionen. Der beste Weg, um Clem zu lernen, besteht darin, den clemInterpreter ohne Argumente auszuführen . Es wird im interaktiven Modus gestartet, sodass Sie mit den verfügbaren Befehlen spielen können. Geben Sie zum Ausführen der Beispielprogramme Folgendes ein: clem example.clmBeispiel ist der Name des Programms. Dieses kurze Tutorial sollte ausreichen, um Ihnen den Einstieg zu erleichtern.

Es gibt zwei Hauptfunktionsklassen. Atomfunktionen und zusammengesetzte Funktionen. Zusammengesetzte Funktionen sind Listen, die aus anderen zusammengesetzten Funktionen und atomaren Funktionen bestehen. Beachten Sie, dass eine zusammengesetzte Funktion sich nicht selbst enthalten kann.

Atomfunktionen

Die erste Art der Atomfunktion ist die Konstante . Eine Konstante ist einfach ein ganzzahliger Wert. Zum Beispiel -10. Wenn der Interpreter auf eine Konstante stößt, schiebt er sie auf den Stapel. Lauf clemjetzt. Geben Sie -10an der Eingabeaufforderung ein. Das solltest du sehen

> -10
001: (-10)
>

Der Wert 001beschreibt die Position der Funktion im Stapel und (-10) ist die gerade eingegebene Konstante . Geben Sie nun +11an der Eingabeaufforderung ein. Das solltest du sehen

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

Beachten Sie, dass (-10)die zweite Position im Stapel erreicht wurde und (11)jetzt die erste belegt. Das ist die Natur eines Stapels! Sie werden feststellen, dass dies -auch der Dekrementierungsbefehl ist. Wann immer -oder +vor einer Zahl, bezeichnen sie das Vorzeichen dieser Zahl und nicht den entsprechenden Befehl. Alle anderen atomaren Funktionen sind Befehle . Insgesamt gibt es 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.

Wenn Sie an der Eingabeaufforderung einen Befehl eingeben, wird der Befehl ausgeführt. Geben Sie #an der Eingabeaufforderung (den Befehl duplicate) ein. Das solltest du sehen

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

Beachten Sie, dass (11) dupliziert wurde. Geben Sie nun %an der Eingabeaufforderung den Befehl drop ein. Das solltest du sehen

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

Um einen Befehl in den Stapel zu verschieben, schließen Sie ihn einfach in Klammern ein. Geben Sie (-)an der Eingabeaufforderung ein. Dadurch wird der Dekrementierungsoperator auf den Stapel verschoben. Das solltest du sehen

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

Zusammengesetzte Funktionen

Sie können auch mehrere atomare Funktionen in Klammern setzen, um eine zusammengesetzte Funktion zu bilden. Wenn Sie an der Eingabeaufforderung eine zusammengesetzte Funktion eingeben, wird diese auf den Stapel verschoben. Geben Sie ($+$)an der Eingabeaufforderung ein. Das solltest du sehen

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

Technisch gesehen ist alles auf dem Stapel eine zusammengesetzte Funktion. Einige der zusammengesetzten Funktionen auf dem Stapel bestehen jedoch aus einer einzelnen Atomfunktion (in diesem Fall werden wir sie der Einfachheit halber als Atomfunktionen betrachten). Bei der Bearbeitung zusammengesetzter Funktionen auf dem Stapel ist der .Befehl (Verkettung) häufig hilfreich. Geben Sie .jetzt ein. Das solltest du sehen

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

Beachten Sie, dass die erste und die zweite Funktion auf dem Stapel verkettet wurden und dass die zweite Funktion auf dem Stapel in der resultierenden Liste an erster Stelle steht. Um eine Funktion auszuführen, die sich auf dem Stapel befindet (egal ob atomar oder zusammengesetzt), müssen wir den wBefehl (while) ausgeben . Der wBefehl öffnet die erste Funktion auf dem Stapel und führt sie wiederholt aus, solange die zweite Funktion auf dem Stapel eine Konstante ungleich Null ist. Versuchen Sie vorherzusagen, was passieren wird, wenn wir tippen w. Geben Sie nun ein w. Das solltest du sehen

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

Hast du das erwartet? Die zwei Zahlen, die oben auf dem Stapel sitzen, wurden addiert und ihre Summe bleibt erhalten. Versuchen wir es nochmal. Zuerst lassen wir die Null fallen und drücken eine 10 durch Eingabe %10. Das solltest du sehen

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

Jetzt geben wir die gesamte Funktion in einem Schuss ein, aber %am Ende fügen wir ein zusätzliches hinzu , um die Null loszuwerden. Geben Sie (-$+$)w%an der Eingabeaufforderung ein. Das solltest du sehen

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

(Beachten Sie, dass dieser Algorithmus nur funktioniert, wenn die erste Konstante auf dem Stapel positiv ist.)

Saiten

Saiten sind ebenfalls vorhanden. Sie sind meistens syntaktischer Zucker, können aber sehr nützlich sein. Wenn der Interpreter auf eine Zeichenfolge stößt, schiebt er jedes Zeichen vom letzten zum ersten auf den Stapel. Geben Sie ein %, um die 11 aus dem vorherigen Beispiel zu löschen. Geben Sie nun 0 10 "Hi!"an der Eingabeaufforderung ein. Das 0fügt einen NULL-Terminator ein und das 10fügt ein neues Zeilenzeichen ein. Das solltest du sehen

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

Geben Sie ein (>)w, um Zeichen vom Stapel zu drucken, bis der NULL-Terminator angezeigt wird. Das solltest du sehen

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

Schlussfolgerungen

Hoffentlich sollte dies ausreichen, um Ihnen den Einstieg in den Dolmetscher zu erleichtern. Das Sprachdesign sollte relativ einfach sein. Lassen Sie mich wissen, wenn etwas furchtbar unklar ist :) Einige Dinge wurden absichtlich vage gelassen: Werte müssen signiert sein und mindestens 16 Bit, der Stapel muss groß genug sein, um alle Referenzprogramme usw. auszuführen. Viele Details wurden nicht geschnitzt hier draußen, weil eine vollständige Sprachspezifikation unerschwinglich groß wäre (und ich habe noch keine geschrieben: P). Im Zweifelsfall ahmen Sie die Referenzimplementierung nach.

Die esolangs.org Seite für Clem

Die Referenzimplementierung in C.


Sie sagen, Sie haben die Sprachspezifikation noch nicht geschrieben. Ich nehme dann an, dass Sie der Urheber der Sprache sind?
COTO

@COTO Das ist richtig. Ich habe die Sprache erstellt.
Orby

5
Sehr wichtige Frage: Sprechen Sie es "klem" oder "see-lem" aus?
Martin Ender

4
@ MartinBüttner: "klem" :)
Orby

2
Möglicherweise möchten Sie die Richtung angeben, in die der Befehl @ die drei wichtigsten Funktionen dreht. (001 -> 002 -> 003 -> 001 oder 003 -> 002 -> 001 -> 003)
Kwokkie

Antworten:


1

Haskell, 931 921 875

Dies ist noch nicht vollständig Golf, aber es wird wahrscheinlich nie sein. Dennoch ist es bereits kürzer als alle anderen Lösungen. Ich werde das bald mehr Golf spielen. Ich habe keine Lust mehr darauf zu spielen.

hat wahrscheinlich ein paar subtile Fehler, weil ich nicht mit der C-Referenzimplementierung gespielt habe.

Diese Lösung verwendet den Typ StateT [String] IO ()zum Speichern eines "ausführbaren" Clem-Programms. Das meiste Programm ist ein Parser, der das "ausführbare Programm" analysiert.

um diese Verwendung auszuführen 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

5

Python, 1684 1281 Zeichen

Habe alle grundlegenden Golf-Sachen erledigt. Es führt alle Beispielprogramme aus und stimmt die Ausgabe Zeichen für Zeichen ab.

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)

Testen :

Sammeln Sie clemint.py , clemtest_data.py , clemtest.py und eine kompilierte clemBinärdatei in einem Verzeichnis und führen Sie sie aus clemtest.py.

Erweiterung :

Die ungolfedeste Version ist diese . Folgen Sie diesem.

Sist der Hauptstapel. Jedes Element des Stapels ist eine 3-Liste, eine von:

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

Für die Konstanten fist eine Funktion, die die Konstante auf den Stapel schiebt. Für die atmoics, fist eine Funktion , die eine der Operationen ausführt (z -, +). Für die Verbindungen fsist eine Liste von Elementen.

xecführt ein Element aus. Wenn es sich um eine Konstante oder ein Atom handelt, wird nur die Funktion ausgeführt. Wenn es sich um eine Verbindung handelt und noch keine Rekursion stattgefunden hat, führt sie jede Funktion aus. So Ausführung (10 20 - 30)wird jede der Funktionen ausführen 10, 20, -, und 30, so dass 10 19 30auf dem Stapel. Wenn es eine Rekursion gegeben hat, wird die zusammengesetzte Funktion einfach auf den Stapel verschoben. Bei der Ausführung (10 20 (3 4) 30)sollte das Ergebnis beispielsweise 10 20 (3 4) 30nicht sein 10 20 3 4 30.

Das Verschachteln war etwas schwierig. Was machst du beim Ablesen (1 (2 (3 4)))? Die Lösung besteht darin, einen Stapel Stapel zu haben. Auf jeder Verschachtelungsebene wird ein neuer Stapel auf den Stapelstapel geschoben, und alle Push-Vorgänge werden auf diesen Stapel übertragen. Wenn eine Verschachtelung stattgefunden hat, werden atomare Funktionen verschoben anstatt ausgeführt. Wenn Sie also sehen 10 20 (- 30) 40, 10wird geschoben, dann 20wird ein neuer Stapel erstellt -und 30auf den neuen Stapel geschoben. Er )springt vom neuen Stapel ab, verwandelt ihn in einen Gegenstand und schiebt ihn eine Ebene tiefer auf den Stapel. endnest()Griffe ). Es war etwas knifflig, da es einen Sonderfall gibt, in dem nur ein Gegenstand geschoben wurde und wir zurück auf den Hauptstapel schieben. Das heißt, (10)sollte die Konstante drücken10, kein Verbund mit der einen Konstante, weil dann -und +nicht funktionieren. Ich bin mir nicht sicher, ob dies ein Prinzip ist, aber so funktioniert es ...

Mein Interpreter ist ein zeichenweiser Prozessor - er erstellt keine Token -, daher war es etwas ärgerlich, mit Zahlen, Zeichenfolgen und Kommentaren umzugehen. Es gibt einen separaten Stapel Nfür eine aktuell verarbeitete Nummer, und jedes Mal, wenn ein Zeichen verarbeitet wird, das keine Nummer ist, muss ich anrufen, um endnum()zu sehen, ob ich diese Nummer zuerst vervollständigen und auf den Stapel legen soll. Ob wir uns in einer Zeichenfolge oder einem Kommentar befinden, wird durch boolesche Variablen verfolgt. Wenn eine Zeichenfolge geschlossen ist, werden alle Innereien auf dem Stapel verschoben. Negative Zahlen erforderten ebenfalls eine spezielle Behandlung.

Das war's für die Übersicht. Der Rest wurde die Umsetzung aller Einbauten, und sicherstellen , dass tiefe Kopien in tun +, -und #.


Ein großes Lob! Hattest du Spaß? :)
Orby

@ Orby: Ich habe es wirklich getan! Es ist eine interessante Sprache, definitiv eine seltsame. Ich hoffe, ich kann einen Dolmetscher bekommen, der <1k ist. Ich bin mir nicht sicher, was ich von anderen Einsendungen erwarten soll.
Claudiu

4

C 837

Vielen Dank an @ceilingcat für die Suche nach einer viel besseren (und kürzeren) Version

Dies behandelt alles als einfache Zeichenfolgen - alle Stapelelemente sind Zeichenfolgen, sogar Konstanten sind Zeichenfolgen.

#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);}}}}

Probieren Sie es online aus!

Eine weniger Golfversion meines Originals (im Gegensatz zur Golfversion druckt diese den Stapel, wenn er endet, wenn er nicht leer ist und einen -e-Parameter verwendet, sodass Sie das Skript in der Befehlszeile angeben können, anstatt aus einer Datei zu lesen):

#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;
}

Nett! Beeindruckend, dass Sie die Python-Lösung in C geschlagen haben. Ich muss meine kürzere Version hochladen. Ich habe es geschafft, etwa 60 Bytes zu sparen. Ich frage mich immer noch, ob es einen anderen Ansatz gibt, der weit weniger als 1000 Zeichen ergibt
Claudiu,

@Claudiu Das dachte ich auch - aber ich konnte nicht herausfinden wie.
Jerry Jeremiah
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.