Eine kleine Sprache verdient einen kleinen Dolmetscher

21

Hier ist eine sehr einfache Sprachdefinition:

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

Beispielprogramme (Beachten Sie, dass der leere String eine Variable ist, ich ihn jedoch aus Gründen der Übersichtlichkeit sparsam verwenden werde und einige Variablen im Programm auf Null gesetzt werden, wenn sie normalerweise standardmäßig 0 sind):

<>: 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

Ihr Ziel ist es, den kleinsten Dolmetscher für diese Sprache zu schreiben.

  1. Der Wert einer Variablen kann beliebig groß sein und sollte theoretisch nur durch den Gesamtspeicher begrenzt werden, auf den Ihre Sprache Zugriff hat. Sie müssen jedoch nur Werte bis zu 2 ^ 256 verarbeiten.

  2. Ihr Programm sollte theoretisch in der Lage sein, beliebig lange Programme zu verarbeiten, Sie müssen jedoch nur Programme mit einer Länge von weniger als 2 ^ 32 Zeichen bearbeiten. Sie müssen auch verschachtelte Schleifen mit einer Tiefe von bis zu 2 ^ 32 verarbeiten.

  3. Sie können davon ausgehen, dass es sich bei dem Programm um ein gültiges Programm handelt und dass Sie nur dann nicht negative Ganzzahlen erhalten, wenn Sie nach Eingaben fragen. Sie können auch davon ausgehen, dass nur ASCII-druckbare Zeichen in der Eingabezeichenfolge enthalten sind.

  4. Die Geschwindigkeit des Programms, das Sie interpretieren, spielt keine Rolle, sie wird bereits bei einfachen Dingen wie der 5-stelligen Multiplikation ohne Optimierung schmerzhaft langsam sein.

  5. Wenn Sie eine Sprache verwenden möchten, die keine Eingaben oder Ausgaben in der von der Sprache beschriebenen Weise akzeptiert, verwenden Sie eine Interpretation, die Sie ermöglichen möchten. Dies gilt aus irgendeinem Grund, aus dem Ihre Sprache ein erforderliches Verhalten nicht implementieren kann. Ich möchte, dass alle Sprachen miteinander konkurrieren können.

  6. Kürzeste Sendung gewinnt. Es gelten Standardlücken.

Frikative Melone
quelle
Als Nebenherausforderung möchte ich sehen, wie kurz ein Programm sein kann, das die Zahl 2016 ausgibt, aber zuerst muss ich warten, bis ein Interpreter geschrieben ist, damit ich meinen Code testen kann.
Neil
1
Ich habe einen Dolmetscher in Python 2.7 hier .
Fricative Melone
2
Wie heißt diese Sprache? Es verdient einen Platz bei esolangs.org
wizzwizz4
@Neil Ich habe es geschafft, in 72 Zeichen zu tun
Fricative Melon
@FricativeMelon 72? Ich kann es in 43 tun!
Neil

Antworten:

4

Rubin, 182 Bytes

$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 *$*

Versuchen Sie es wie folgt:

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

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

Wie es funktioniert

Die rFunktion tokenisiert eine Eingabezeichenfolge und führt jedes Token aus:

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

Wir suchen nach $2passenden Variablennamen [^!?^<>]*, gefolgt von beiden

  • <...>wo ...kein oder mehr Programme ( \gist Rekursion), wobei in diesem Fall $4ist nichtnil
  • A !, ?oder ^Zeichen, erfaßt durch $3, wobei in diesem Fall $4ist nil.

Dann ist die Logik zum Ausführen eines Tokens recht einfach, wenn Sie es ein wenig einrücken:

$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.
Lynn
quelle
Ich bin sehr gespannt, wie das funktioniert.
Jerry Jeremiah
1

JavaScript (ES6) 184 194 209

Edit Simplified (Verwendung von Funktionsparametern für Ein- und Ausgabe schien eine gute Idee zu sein, war es aber nicht), 1 weiteres Byte gespeichert dank @ thПӍѲꝆΛҐӍΛ

Bearbeiten 2 Geändertes Parsen. Die Logik für Inkrement / Eingabe ist aus der Antwort von @ Lynn entlehnt

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")

Weniger golfen

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

TEST Das Snippet beginnt mit der Auswertung von 2016 mit dem von @Neil veröffentlichten Programm. Sei geduldig...

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>

edc65
quelle
Ist evaldas Vermeiden mit returnkeine Option?
Mama Fun Roll
@ ӍѲꝆΛҐӍΛПӍѲꝆΛҐӍΛ ja, eval speichert 1 Byte. Ich bin immer noch auf der Suche nach etwas Wesentlicherem
edc65
0

Perl, 251 Bytes

@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++;} 

Leichter zu lesende Version:

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

Dies verschwendet eine Menge Bytes, um Schleifen als direkte Sprünge zu reparieren, aber das Zurücksuchen nach dem Schleifenstart hat meinen Sinn für Ästhetik verletzt.

David Morris
quelle
0

Standard C ++, 400 Bytes

Dies kompiliert mit 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]);}

Ich könnte es vielleicht noch etwas kürzen. Wenn Sie einige Vorschläge haben, kommentieren Sie bitte.

Jerry Jeremiah
quelle
367 Bytes
Ceilingcat