クレム通訳を書く


11

Clemは、ファーストクラスの機能を備えた最小のスタックベースのプログラミング言語です。あなたの目的はクレム言語のインタプリタを書くことです。ここにあるリファレンス実装に含まれるすべての例を適切に実行する必要があります

  • いつものように、標準の抜け穴が適用されます。
  • バイトカウントによる最小のエントリが優先されます。

クレム語

Clemは、ファーストクラスの機能を備えたスタックベースのプログラミング言語です。クレムを学ぶ最良の方法はclem、引数なしでインタプリタを実行することです。インタラクティブモードで起動し、使用可能なコマンドで遊ぶことができます。サンプルプログラムを実行するには、「example」がプログラムclem example.clmの名前であると入力します。この簡単なチュートリアルは、始めるのに十分なはずです。

関数には2つの主要なクラスがあります。原子関数と複合関数。複合関数は、他の複合関数とアトミック関数で構成されるリストです。複合関数にそれ自体を含めることはできません。

原子関数

アトミック関数の最初のタイプは定数です。定数は、単に整数値です。たとえば、-10。インタープリターが定数を検出すると、それをスタックにプッシュします。clem今すぐ実行します。入力-10プロンプトで。君は見るべきだ

> -10
001: (-10)
>

001はスタック内の関数の位置を表し、入力し(-10)定数です。+11プロンプトで入力してください。君は見るべきだ

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

(-10)がスタックの2番目の位置に移動し(11)、最初の位置を占めていることに注意してください。これがスタックの性質です!これが-減分コマンドでもあることに気づくでしょう。たび-または+番号の前に、彼らはその数ではなく、対応するコマンドの兆候を示しています。他のすべてのアトミック関数はコマンドです。合計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.

プロンプトでコマンドを入力すると、コマンドが実行されます。入力#(重複コマンド)プロンプトで。君は見るべきだ

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

(11)が複製されていることに注意してください。今タイプ%(dropコマンド)プロンプトで。君は見るべきだ

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

コマンドをスタックにプッシュするには、単にそれを括弧で囲みます。入力(-)プロンプトで。これにより、デクリメント演算子がスタックにプッシュされます。君は見るべきだ

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

複合関数

複数のアトミック関数を括弧で囲んで、複合関数を形成することもできます。プロンプトで複合関数を入力すると、スタックにプッシュされます。入力($+$)プロンプトで。君は見るべきだ

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

技術的には、スタック上のすべてが複合関数です。ただし、スタック上の複合関数の一部は単一のアトミック関数で構成されています(この場合、便宜上、それらはアトミック関数と見なします)。スタック上の複合関数を操作する場合、.コマンド(連結)が役立つことがよくあります。.今すぐ入力してください。君は見るべきだ

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

スタックの最初の関数と2番目の関数が連結され、スタックの2番目の関数が結果のリストの最初に来ることに注意してください。スタック上にある関数(それがアトミックかコンパウンドか)を実行するには、wコマンド(while)を発行する必要があります。w命令は、スタック上の最初の機能をポップし、あれば、スタック上の第二の機能が非ゼロで一定であるとしてそれを繰り返し実行します。入力するとどうなるかを予測してみてくださいw。次に、と入力しwます。君は見るべきだ

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

それはあなたが期待したことですか?スタックの上にある2つの数値が追加され、それらの合計が残ります。もう一度やってみましょう。最初にゼロをドロップし、と入力して10をプッシュします%10。君は見るべきだ

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

次に、関数全体を一度に入力しますが%、最後にゼロを追加してゼロを取り除きます。入力(-$+$)w%プロンプトで。君は見るべきだ

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

(このアルゴリズムは、スタックの最初の定数が正の場合にのみ機能することに注意してください)。

文字列

文字列も存在します。それらは主に構文上の砂糖ですが、非常に便利です。インタプリタは文字列に遭遇すると、各文字を最後から最初にスタックにプッシュします。%前の例から11をドロップするために入力します。次に、0 10 "Hi!"プロンプトを入力します。0NULLターミネータを挿入しますと、10改行文字を挿入します。君は見るべきだ

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

(>)wNULLターミネータに遭遇するまで、スタックから文字を印刷するために入力します。君は見るべきだ

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

結論

うまくいけば、これでインタプリタを使い始めるのに十分なはずです。言語設計は比較的単純なものでなければなりません。何かがひどく不明確であるかどうかを知らせてください:)いくつかのことは意図的に曖昧なままにされました:値は署名さなければならず、少なくとも 16ビットであり、スタックはすべての参照プログラムを実行するのに十分な大きさでなければなりません。多くの詳細は刻まれていません本格的な言語仕様を投稿するには法外に大きいため(ここではまだ記述していません:P)。疑わしい場合は、リファレンス実装を模倣してください。

クレムのesolangs.orgページ

Cのリファレンス実装


あなたはまだ言語仕様を書いていないと言います。私はあなたが言語の創始者だと思いますか?
COTO 2014

@COTOそうです。言語を作成しました。
Orby

5
非常に重要な質問:「クレム」または「シーレム」と発音しますか?
マーティンエンダー2014

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

2
@コマンドが上位3つの関数を回転する方向を指定することができます。(
001-

回答:


1

ハスケル、931 921 875

これはまだ完全にはゴルフされていませんが、おそらく決してゴルフされません。それでも、それは他のすべてのソリューションよりもすでに短くなっています。 もうすぐこれをゴルフします。これ以上ゴルフをしたくありません。

Cのリファレンス実装を試していないため、おそらくいくつかの微妙なバグがあります。

このソリューションでは、タイプStateT [String] IO ()を使用して「実行可能な」クレムプログラムを格納します。ほとんどのプログラムは、「実行可能なプログラム」を解析するパーサーです。

この使用を実行するために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文字

すべての基本的なゴルフ用品を手に入れました。すべてのサンプルプログラムを実行し、文字ごとに出力を照合します。

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)

テスト

clemint.pyclemtest_data.pyclemtest.py、およびコンパイル済みclemバイナリをディレクトリに収集して実行しclemtest.pyます。

拡張

最もゴルフされていないバージョンはこれです。それに従ってください。

Sメインスタックです。スタックの各アイテムは3つのリストであり、次のいずれかです。

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

定数の場合、fは定数をスタックにプッシュする関数です。atmoicsの場合、fは操作の1つを実行する関数です(-など+)。化合物の場合fsは、アイテムのリストです。

xecアイテムを実行します。定数またはアトミックの場合は、関数を実行するだけです。複合の場合、再帰がまだない場合は、各関数を実行します。だから、実行(10 20 - 30)の各機能を実行します1020-、と30残して、10 19 30スタック上に。再帰があった場合は、複合関数をスタックにプッシュするだけです。たとえば、を実行する(10 20 (3 4) 30)と、結果はで10 20 (3 4) 30はなくになり10 20 3 4 30ます。

ネスティングは少しトリッキーでした。読書をしながら何をします(1 (2 (3 4)))か?解決策は、スタックのスタックを用意することです。各ネストレベルで、新しいスタックがスタックのスタックにプッシュされ、すべてのプッシュ操作はこのスタックに送られます。さらに、ネストがある場合、アトミック関数は実行される代わりにプッシュされます。あなたが見るのであれば10 20 (- 30) 4010プッシュされ、その後20、新しいスタックが作成され、-そして30新しいスタックにプッシュされ、そして)新しいスタックからポップは、スタック1レベルダウンに項目にそれを回すと、プッシュそれを。endnest()ハンドル)。アイテムが1つだけプッシュされ、メインスタックにプッシュバックする特別なケースがあるため、少しトリッキーでした。つまり(10)、定数をプッシュする必要があります10、1つの定数との複合では-あり+ません。これが原則であるかどうかはわかりませんが、それが機能する方法です...

私のインタプリタは、文字ごとのプロセッサです-トークンを作成しません-そのため、数値、文字列、コメントは、扱いが面倒です。N現在処理中の数値には別のスタックがあります。数値ではない文字が処理されるときはいつでも、endnum()最初にその数値を完成させてスタックに置く必要があるかどうかを確認するために呼び出す必要があります。文字列またはコメントのどちらにいても、ブール変数によって追跡されます。文字列を閉じると、すべての内部がスタックにプッシュされます。負の数には、特別な処理も必要です。

概要については以上です。残りはすべてビルトインを実装し、中に深いコピーを行うようにしてください作っていた+-#


賞賛!楽しかったですか?:)
Orby

@Orby:確かにやった!それは興味深い言語で、間違いなく奇妙な言語です。1k未満のインタープリターを入手できるといいのですが。他の提出物に何が期待できるかわからない。
Claudiu

4

C 837

はるかに優れた(そしてより短い)バージョンを見つけてくれた@ceilingcatに感謝

これはすべてを単純な文字列として扱います-すべてのスタック項目は文字列であり、定数も文字列です。

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

オンラインでお試しください!

私のオリジナルのあまりゴルフされていないバージョン(これが空でない場合、スタックが終了時にスタックを印刷し、-eパラメーターをとるので、ファイルから読み取る代わりにコマンドラインでスクリプトを指定できます):

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

いいね!CでPythonソリューションを打ち負かした印象的です。短いバージョンをアップロードしなければならなかったので、なんとか60バイトを削り落としました。1000文字未満になる別のアプローチがあるのか​​どうか疑問です
Claudiu

@Claudiu私もそう考えました-しかし、私はその方法を理解できませんでした。
ジェリージェレミア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.