小さな言語は小さな通訳に値する


21

これは非常に簡単な言語定義です。

A Variable is any string that does not contain ^, <, >, !, or ?
The empty string is a valid variable identifier
The value of every variable starts at 0.
A Statement is one of (var is a Variable, P is a Program):
    var^   -> changes var to be equal to 1 more than itself
    var<P> -> while var > 0, changes var to be equal to 1 less than itself, then runs P
    var! -> output value of var
    var? -> ask for non-negative integer as input, increase var by that value
A Program is a concatenation of Statements, running a Program means running each Statement in order

サンプルプログラム(空の文字列は変数ですが、明確にするために控えめに使用します。デフォルトでは通常0である場合、一部の変数はプログラム内でゼロにリセットされます):

<>: sets the value of the empty string variable to 0
b<>b?b<a^>: asks for b, then adds the value stored in b to a, zeroing b in the process
b<>b?a<>b<a^>: asks for b, then sets a to the value of b, zeroing b in the process
a<>c<>b<a^c^>c<b^> : copies the value in b into a without zeroing it
b<>c<>a<c^c^c<b^>>b! : outputs a multiplied by 2
b^b<a<>a?a!b^> : outputs what you input, forever

あなたの目標は、この言語の最小のインタプリタを書くことです。

  1. 変数の値は任意に大きくすることができ、理論上は言語がアクセスできる合計メモリによってのみ制限されるべきですが、最大2 ^ 256までの値を処理する必要があります。

  2. 理論上、プログラムは任意の長さのプログラムを処理できる必要がありますが、2 ^ 32文字以下のプログラムで作業する必要があります。最大2 ^ 32までの深さのネストされたループも処理する必要があります。

  3. プログラムは有効なプログラムであり、入力を求めたときに負でない整数しか取得できないと想定できます。また、入力文字列にはASCII印刷可能文字のみが含まれると想定することもできます。

  4. 解釈するプログラムの速度は重要ではありません。5桁の乗算のような単純な処理では、最適化なしですでに非常に遅くなります。

  5. 言語で記述された方法で入力を合理的に受け入れたり出力を生成したりできない言語を使用する場合は、それを可能にする任意の解釈を使用します。これは、言語が必要な動作を実装できない理由に適用されます。すべての言語が競争できるようにしたい。

  6. 最短のプログラムが勝ちます。標準の抜け穴が適用されます。


サイドチャレンジとして、2016年の数値を出力するプログラムをどれだけ短く作成できるかを確認したいのですが、まず、コードをテストできるようにインタープリターが書き込まれるのを待つ必要があります。
ニール

1
ここにはPython 2.7のインタープリターがあります
摩擦摩擦メロン

2
この言語は何と呼ばれていますか?それはesolangs.orgの
wizzwizz4

@ニール私は72文字でそれを行うことができました
メロン

@FricativeMelon 72?43でできる!
ニール

回答:


4

Ruby、182バイト

$h=Hash.new 0
def r(c)c.scan(/(([^!?^<>]*)(<(\g<1>*)>|[!?^]))/){$4?($1=~/(.*?)<(.*)>/
($h[$1]-=1;r$2)while$h[$1]>0):$3<?"?p($h[$2]):$h[$2]+=$3<?@?STDIN.gets.to_i:
1}end
r IO.read *$*

このようにしてみてください:

$ cat code
a?b<>c<>a<c^c^c<b^>>b!

$ ruby lynn.rb code
3                           <-- input
6                           <-- output

使い方

このr関数は、入力文字列をトークン化し、各トークンを実行します。

def r(c)
    c.scan(/(([^!?^<>]*)(<(\g<1>*)>|[!?^]))/){
        ...
    }
end

$2一致する変数名を探し[^!?^<>]*、次に

  • <...>whereは...0個以上のプログラムに一致します(\g再帰です)。その場合$4nil
  • !?、または^によってキャプチャ文字、$3ケースする、$4ですnil

トークンを実行するためのロジックは、少しインデントすると非常に簡単です。

$4 ? (                                    # If it's a loop:
    $1 =~ /(.*?)<(.*)>/                   #   Re-match token*
    ($h[$1]-=1; r $2) while $h[$1] > 0    #   Recurse to run loop
) :                                       # Else:
    $3 < ?"                               #   If it's an !:
      ? p($h[$2])                         #     Print the var
      : $h[$2] +=                         #   Else, increment it by:
          $3 < ?@                         #     If it's a ?:
              ? STDIN.gets.to_i           #       User input
              : 1                         #     Else: 1

* There's an oniguruma bug, I think, that keeps me from simply using $3 here.

私はこれがどのように機能するか本当に興味があります。
ジェリージェレミア

1

JavaScriptの(ES6)184 194 209

Edit Simplified(入力および出力に関数パラメーターを使用するのは良い考えのようですが、そうではありませんでした)、さらに1バイトが節約されましたthx @ӍѲꝆΛҐӍΛПҒЦꝆ

編集2変更された解析。インクリメント/入力のロジックは、@ Lynnの答えから借用しています

F=(p,i=0,v={},n='')=>eval("for(;c='>?^!<'.indexOf(q=p[i++]||'');n=~c?'':n+q)if(c>3){for(;v[n]--;)F(p,i,v);i=F(p,i,v[n]=0)}else~c&&v?c>2?alert(v[n]|0):v[n]=~~v[n]+(--c||+prompt()):0;i")

少ないゴルフ

F=(p,      // program 
   i = 0,  // initial instruction pointer  
   v = {}, // variables (default to empty) or if 0, flag of dummy execution
   n = ''    // name of current variable (has to be local for recursive calls)
{
  for(; c='>?^!<'.indexOf(q=p[i++]||''); )
  // q = current character
  // c = current command (int 0..4 or -1 id not recognized)
  //     note 0 end of subprogram or end of program
  {
    if(c>3) // 4='<' call subprogram - recursive
    {
      for(;v[n]--;)
        F(p,i,v); // conditional call, repeated - using real environment
      v[n] = 0; // Reset variable at loop end
      i=F(p,i,0) // one more unconditional dummy call, just to advance i
    }
    else
      ~c&&v? // if valid command (1..3) and not dummy
      c>2?
        alert(v[n]|0) // output, undefined becomes 0
        :v[n]=~~v[n]+(--c||+prompt()) // inc with 1 or user input
      :0     // not valid command or dummy, do nothing
    n=~c?'':n+q // reset or update current variable name
  }
  return i // return current istruction pointer (for recursive calls)
}

テストスニペットは、@ Neilが投稿したプログラムを使用して2016の評価を開始します。我慢して...

F=(p,i=0,v={},n='')=>eval("for(;c='>?^!<'.indexOf(q=p[i++]||'');n=~c?'':n+q)if(c>3){for(;v[n]--;)F(p,i,v);i=F(p,i,v[n]=0)}else~c&&v?c>2?alert(v[n]|0):v[n]=~~v[n]+(--c||+prompt()):0;i")

// TEST
function definput(){  I.disabled = KI.checked; }
function defoutput(){  O.disabled = KO.checked; }

function run()
{
  var prog=P.value, irows = I.value.split('\n'), pi=0;
  var fout=x=>O.value+=x+'\n';
  var fin=x=>irows[pi++];
  var saveAlert=alert, savePrompt=prompt
  if (!KO.checked) alert=fout,O.value=''
  if (!KI.checked) prompt=fin
  
  F(prog);
  
  alert=saveAlert
  prompt=savePrompt
}

P.value="^^^^<a^a^>a<^^^^><a^b^>a<c<b^^>b<c^^>>!"

run()
Program <button onclick="run()">RUN</button><br>
<textarea id=P></textarea><br>
Input (or <input type=checkbox id=KI onclick="definput()"> interactive prompt)<br>
<textarea id=I>5</textarea><br>
Output (or <input type=checkbox id=KO onclick="defoutput()"> popup)<br>
<textarea id=O readonly></textarea><br>


オプションevalを避けるために使用してreturnいますか?
ママファンロール

@ӍѲꝆΛҐӍΛПҒЦꝆはい、evalは1バイトを節約します。まだもっと充実した何かを探しています
-edc65

0

Perl、251バイト

@p=split/([<>!?^])/,<>;for$c(0..$#p){$_=$p[$c];/</&&push@j,$c;if(/>/){$a=pop@j;$p[$c]=">$a";$p[$a]="<$c";}}while($c<$#p){$_=$p[$c];/\^/&&$v{$l}++;/!/&&print$v{$l};/\?/&&($v{$l}=<>);/<(\d+)/&&($v{$l}?$v{$l}--:($c=$1));/>(\d+)/&&($c=$1-2);$l=$_;$c++;} 

読みやすいバージョン:

# treat the first line of input as a program

# split on punctuation keywords; @p will contain the program as a list
# of tokens (including whitespace between adjacent punctuation)
@p = split /([<>!?^])/, <>;

# rewrite jump addresses

# the interpreter could scan backwards to avoid this, but that idea
# makes me feel dirty
for $c (0..$#p) {
    $_ = $p[$c];
    # save loop-start address on stack
    /</ && push @j, $c;
    if (/>/) {
        # if we encounter a loop-end instruction, rewrite it and the
        # corresponding loop-start to include the address (of the
        # instruction---jumps have to offset from this)
        $a = pop @j;
        $p[$c] = ">$a";
        $p[$a] = "<$c";
    }
}

# execute the program

# our program is already in @p

# $c will contain our program counter

# $l will contain the name of the last-referenced variable

while ($c < $#p) {
    # move current instruction into $_ for shorter matching
    $_ = $p[$c];

    # increment instruction
    /\^/ && $v{$l}++;

    # output instruction
    /!/ && print $v{$l};

    # input instruction
    /\?/ && ($v{$l} = <>);

    # loop start, including address
    /<(\d+)/ && ($v{$l} ? $v{$l}-- : ($c = $1));

    # loop end, including address
    />(\d+)/ && ($c = $1-2);

    # copy current instruction into "last variable name"---this will
    # sometimes contain operators, but we have null-string
    # instructions between adjacent operators, so it'll be fine
    $l = $_;

    # advance the program counter
    $c++;
}

これは、ループを直接ジャンプに修正するために大量のバイトを浪費しますが、ループの開始を逆方向にスキャンすると、私の美意識が損なわれます。


0

標準C ++、400バイト

これは g++ -g test.cpp -Wall -Wextra -pedantic -std=gnu++11

#include<map>
#include<cstring>
#define b ;break;case
#define u unsigned long long
std::map<std::string,u>V;void r(char*s){char*p,*q,*e;for(u c;*s;s=p){p=strpbrk(s,"^<?!");c=*p;*p++=0;switch(c){b'^':V[s]++b'<':for(e=p,c=0;*e!='>'||c;e++)c+=(*e=='<')-(*e=='>');*e++=0;while(V[s]>0){V[s]--;r(q=strdup(p));free(q);}p=e;b'?':scanf("%llu",&V[s])b'!':printf("%llu",V[s]);}}}int main(int,char*v[]){r(v[1]);}

もう少し短くできるかもしれません。提案があればコメントしてください。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.