Una piccola lingua merita un piccolo interprete


21

Ecco una definizione linguistica molto semplice:

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

Programmi di esempio (nota che la stringa vuota è una variabile, ma la userò con parsimonia per motivi di chiarezza e alcune variabili vengono azzerate nel programma quando sono normalmente 0 per impostazione predefinita):

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

Il tuo obiettivo è scrivere l'interprete più piccolo per questa lingua.

  1. Il valore di una variabile può essere arbitrariamente grande e dovrebbe essere limitato solo dalla memoria totale a cui la tua lingua ha accesso, in teoria, ma ti viene richiesto solo di gestire valori fino a 2 ^ 256.

  2. Il tuo programma dovrebbe essere in grado di gestire programmi arbitrariamente lunghi, in teoria, ma ti verrà richiesto di lavorare solo su programmi di lunghezza inferiore a 2 ^ 32 caratteri. È necessario gestire anche cicli annidati di profondità fino a 2 ^ 32.

  3. Puoi presumere che il programma sia un programma valido e che otterrai sempre numeri non negativi solo quando chiedi un input. Puoi anche supporre che nella stringa di input siano inclusi solo caratteri stampabili ASCII.

  4. La velocità del programma che interpreti non ha importanza, sarà già dolorosamente lenta per cose semplici come la moltiplicazione a 5 cifre, senza ottimizzazione.

  5. Se si desidera utilizzare una lingua che non può ragionevolmente accettare input o produrre output nel modo descritto dalla lingua, utilizzare qualsiasi interpretazione che si desidera renderlo possibile. Questo vale per qualsiasi motivo per cui la tua lingua non è in grado di attuare alcuni comportamenti richiesti. Voglio che tutte le lingue possano competere.

  6. Vince il programma più breve. Si applicano scappatoie standard.


Come sfida secondaria voglio vedere quanto è breve un programma che posso scrivere che genera il numero 2016, ma prima devo aspettare che venga scritto un interprete in modo da poter testare il mio codice.
Neil,

1
Ho un interprete in Python 2.7 qui .
Fricative Melon,

2
Come si chiama questa lingua? Merita un posto a esolangs.org
wizzwizz4 l'

@Neil sono riuscito a farlo in 72 caratteri
Fricative Melon,

@FricativeMelon 72? Posso farlo in 43!
Neil,

Risposte:


4

Rubino, 182 byte

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

Provalo in questo modo:

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

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

Come funziona

La rfunzione tokenizza una stringa di input ed esegue ogni token:

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

Cerchiamo una $2corrispondenza dei nomi delle variabili [^!?^<>]*, seguita da entrambi

  • <...>dove ...corrisponde a zero o più programmi ( \gè ricorsione), nel qual caso $4non lo ènil
  • A !, ?o ^carattere, catturato da $3, nel qual caso $4è nil.

Quindi la logica per l'esecuzione di un token è abbastanza semplice quando rientri un po ':

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

Sono davvero curioso di come funzioni.
Jerry Jeremiah,

1

JavaScript (ES6) 184 194 209

Modifica semplificata (l'utilizzo di parametri di funzione per input e output sembrava una buona idea, ma non lo era), 1 byte in più salvato grazie a @ ӍѲꝆΛҐӍΛПҒЦꝆ

Modifica 2 Analisi modificata. La logica di incremento / input è presa in prestito dalla risposta di @ 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")

Meno golf

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 Lo snippet inizia a valutare il 2016 utilizzando il programma pubblicato da @Neil. Essere pazientare...

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>


Usare evalper evitare returnnon è un'opzione?
Mama Fun Roll,

@ ӍѲꝆΛҐӍΛПҒЦꝆ sì, eval salva 1 byte. Sto ancora cercando qualcosa di più sostanziale
edc65,

0

Perl, 251 byte

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

Versione più facile da leggere:

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

Questo spreca un mucchio di byte che fissano i loop per essere salti diretti, ma la scansione all'indietro per l'inizio del loop ha offeso il mio senso dell'estetica.


0

C ++ standard, 400 byte

Questo si compila con 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]);}

Potrei essere in grado di accorciarlo ancora un po '. Se hai qualche suggerimento, commenta.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.