Clem 통역사 작성


11

Clem은 일류 기능을 갖춘 최소 스택 기반 프로그래밍 언어입니다. 귀하의 목표는 Clem 언어에 대한 통역사를 작성하는 것입니다. 참조 구현에 포함 된 모든 예제를 올바르게 실행해야합니다 ( 여기 참조) .

  • 평소와 같이 표준 허점이 적용됩니다.
  • 바이트 수로 가장 작은 항목이 이깁니다.

클렘 언어

Clem은 일류 함수를 사용하는 스택 기반 프로그래밍 언어입니다. Clem을 배우는 가장 좋은 방법은 clem인수없이 통역사 를 운영하는 것 입니다. 대화식 모드에서 시작하여 사용 가능한 명령으로 재생할 수 있습니다. 예제 프로그램을 실행하려면 clem example.clm여기서 example은 프로그램 이름입니다.를 입력하십시오. 이 간단한 자습서는 시작하기에 충분해야합니다.

함수에는 두 가지 주요 클래스가 있습니다. 원자 함수와 복합 함수. 복합 함수는 다른 복합 함수와 원자 함수로 구성된 목록입니다. 복합 함수는 자체를 포함 할 수 없습니다.

원자 함수

원자 함수의 첫 번째 유형은 상수 입니다. 상수는 단순히 정수 값이다. 예를 들어 -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: (- $ + $)
> 

스택의 첫 번째 및 두 번째 기능이 연결되었으며 스택의 두 번째 기능이 결과 목록에서 먼저 나타납니다. 스택에있는 함수를 실행하려면 (원자이든 복합이든) w명령을 실행 해야합니다 (그 동안). 이 w명령은 스택의 첫 번째 함수를 팝하고 스택의 두 번째 함수가 0이 아닌 상수 인 한 반복해서 실행합니다. 입력하면 어떤 일이 일어날 지 예측해보십시오 w. 이제을 입력하십시오 w. 넌 봐야 해

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

그게 당신이 예상 한 것입니까? 스택 위에 앉아있는 두 숫자가 추가되었고 그 합은 남아 있습니다. 다시 해보자. 먼저을 입력하여 0을 삭제하고 10을 입력 %10합니다. 넌 봐야 해

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

이제 전체 기능을 한 번에 입력하지만 %끝에 0을 제거하기 위해 추가 기능을 추가 합니다. (-$+$)w%프롬프트에서 입력 하십시오. 넌 봐야 해

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

이 알고리즘은 스택의 첫 번째 상수가 양수인 경우에만 작동합니다.

문자열도 있습니다. 그것들은 주로 구문 설탕이지만 꽤 유용 할 수 있습니다. 인터프리터가 문자열을 만나면 각 문자를 마지막에서 맨 처음으로 스택으로 푸시합니다. %이전 예제에서 11을 삭제하려면 입력 하십시오. 이제 0 10 "Hi!"프롬프트에 입력 하십시오. 는 0널 (NULL) 터미네이터를 삽입되고는 10개행 문자를 삽입합니다. 넌 봐야 해

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

(>)wNULL 터미네이터가 나올 때까지 스택에서 문자를 인쇄하려면 입력 하십시오. 넌 봐야 해

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

결론

잘하면 이것이 통역사를 시작하기에 충분해야합니다. 언어 디자인은 비교적 간단해야합니다. 어떤 것이 몹시 불분명한지 알려주세요 :) 의도적으로 모호한 것이 몇 가지 있습니다. 값은 부호가 있어야하고 16 비트 이상 이어야하며, 스택은 모든 참조 프로그램을 실행할 수있을 정도로 커야합니다. 완전한 언어 사양이 게시하기에는 엄청나게 클 것이므로 (아직 작성하지 않았습니다 : P). 의심스러운 경우 참조 구현을 모방하십시오.

Clem의 esolangs.org 페이지

C의 참조 구현


아직 언어 사양을 작성하지 않았다고 말합니다. 나는 당신이 언어의 창시자라고 생각합니까?
COTO

@COTO 맞습니다. 언어를 만들었습니다.
Orby

5
매우 중요한 질문 : "klem"또는 "see-lem"이라고 발음합니까?
Martin Ender

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

2
@ 명령이 3 가지 최상위 기능을 회전하는 방향을 지정할 수 있습니다. (001->
002-

답변:


1

하스켈, 931 921 875

이것은 아직 완전히 골프되지는 않았지만 아마도 결코 그렇지 않을 것입니다. 여전히 다른 모든 솔루션보다 짧습니다. 나는 이것을 더 빨리 골프 할 것이다. 나는 이것보다 더 골프를 느끼고 싶지 않습니다.

C 참조 구현으로 연주하지 않았기 때문에 아마도 약간의 미묘한 버그가 있습니다.

이 솔루션은 유형 StateT [String] IO ()을 사용하여 "실행 가능한"clem 프로그램을 저장합니다. 대부분의 프로그램은 "실행 가능한 프로그램"을 구문 분석하는 파서입니다.

이 사용을 실행하려면 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.py , clemtest_data.py , clemtest.py 및 컴파일 된 clem바이너리를 디렉토리에 모아서 실행하십시오 clemtest.py.

확장 :

가장 골치 아픈 버전은 이것 입니다. 그와 함께 따르십시오.

S메인 스택입니다. 스택의 각 항목은 다음 중 하나 인 3 개의 목록입니다.

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

상수의 f경우 상수를 스택으로 푸시하는 기능입니다. atmoics 들어, f동작들 중 하나를 실행하는 기능 (예이고 -, +). 화합물 fs의 경우 항목 목록입니다.

xec항목을 실행합니다. 상수이거나 원자이면 함수를 실행하기 만합니다. 복합 인 경우 아직 재귀가 없으면 각 기능을 실행합니다. 그래서 실행 (10 20 - 30)각 기능을 수행한다 10, 20, -, 및 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) 40, 10누르면, 다음 20, 새로운 스택이 만들어지고, -그리고 30새로운 스택으로 푸시하고, )새로운 스택에서 팝, 스택 한 레벨 아래에 항목으로 바뀝니다 및 푸시를. endnest()핸들 ). 하나의 아이템 만 밀고 메인 스택으로 다시 밀고 갈 때 특별한 경우가 있기 때문에 약간 까다 롭습니다. 즉 (10), 상수를 밀어야합니다10, 하나 개의 상수가 아닌 복합, 다음 때문 -+작업을하지 않습니다. 이것이 원칙인지 여부는 확실하지 않지만 작동 방식입니다 ...

통역사는 문자 별 프로세서로 토큰을 만들지 않으므로 숫자, 문자열 및 주석이 처리하기가 다소 성가신 것입니다. 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에서 파이썬 솔루션을 이겼는데 인상적입니다. 더 짧은 버전을 업로드해야하고 60 바이트 정도를 줄였습니다. 1000 자 미만의 다른 방법이 있는지 궁금합니다.
Claudiu

@ Claudiu 나도 그렇게 생각했지만 어떻게 알아낼 수 없었습니다.
Jerry Jeremiah
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.