Interprete autointerpretante


25

Sulla base di un commento di George Edison a questa domanda , scrivi il più piccolo interprete autointerpretato.

  • Puoi usare la lingua che preferisci.
  • Le lingue vuote non contano. Il programma deve contenere almeno due caratteri.
  • Non è necessario che il programma interpreti l' intera lingua, ma solo un sottoinsieme completo di funzionalità linguistiche di Turing (che contiene l'interprete).
  • Le quines non contano.
  • Non utilizzare la evalfunzione integrata della tua lingua o equivalente. Lo stesso vale per apply, ecc.

1
(Hmm .. dovrei fare qualcosa con /usr/bin/cat) per quanto riguarda la completezza di Turing?
Ming-Tang,

@ SHiNKiROU: Grazie, non ci ho pensato come un test. Aggiornato.
Hoa Long Tam,

Correlati: Lingua con l'interprete più piccolo scritto in sé su Stack Overflow, anche se sono poche (solo una?) Le risposte che rispettano effettivamente le regole fornite qui.
dmckee,

1
Dobbiamo riscrivere quel parser sexp Scheme o possiamo considerare ok la lingua host?
JB

@JB: le utilità di elaborazione delle stringhe del linguaggio vanno bene, incluso il sexpparser.
Hoa Long Tam,

Risposte:


19

CI - 260

,(1p0(2d())(41(2d())('#((1p0()(10()(1d,1p$)=)<)$2d,1p$)(40(1d,1c$^)(''(1d,^)('0
'9('0-(,'0'9('0-2p10*+1p$)(!1d)~)$^)(0($)'$(^)'^(&)'&(c)'c(p)'p(d)'d(=)'=(<)'<(
>)'>(~)'~(.)'.(,)',(!)'!(+)'+(-)'-(*)'*(/)'/(%)'%()38p(1p3p0(3d)((2)(3)=p1d1p$)
=)$)~)=)=,2p$&)=)=)<)$$

320 → 260: spingere semplici mappature da carattere a istruzione, quindi ripiegarle. Questo dimezza la dimensione del codice per caso (ci sono 18 casi), ma costa 30 caratteri per eseguire la piegatura.

Questo è un altro dei miei linguaggi costruiti (interprete di base ospitato su Gist ). È unico in quanto la lingua reifica i frammenti di codice. In altre parole, le stringhe di istruzioni in questo linguaggio basato su stack vengono utilizzate con lo stesso effetto delle strutture dati o delle chiusure in altre lingue:

1^      # Push a 1, and "lift" it to be a code literal.
(5 +)   # Define a code literal.
&       # Join the two code literals, forming (1 5 +)
$       # Execute the code literal.

L'interprete crea un frammento di codice dell'intero programma prima di eseguirlo, quindi può anche essere considerato un compilatore. Per questo motivo , impilare l'interprete non comporta un sovraccarico esponenziale del runtime.

L'interprete deriva tutti i suoi operatori direttamente dall'interprete host. Tuttavia, esegue l'analisi da solo, quindi la maggior parte del codice sono solo sequenze che traducono i caratteri nei rispettivi letterali di codice. Questo non è lo stesso dell'uso eval, ma rivela quanto dipende l'implementazione del linguaggio di programmazione dalla semantica della sua lingua / architettura host.


Lingua di riferimento:

Prendi l'interprete qui

blocchi

  • ( ... )

    Crea un "blocco", che è effettivamente un elenco di istruzioni senza contesto. Internamente, potrebbe anche essere un codice macchina.

  • bloccare $

    Chiama un blocco. Alla chiamata viene assegnato lo stack globale, che include il blocco chiamato.

  • valore ^

    Solleva un valore. Vale a dire, trasformalo in un blocco che spinge quel valore.

    Esempio :

       1 ^
    == (1)
    
  • blocco1 blocco2 &

    Unisci due blocchi, formandone uno che corre entrambi in sequenza.

    Esempio :

       (1) (2) &
    == (1 2)
    

Manipolazione dello stack

  • n c

    Copia l'ennesimo valore dello stack.

    Esempio :

       5 4 3 2 1 0 3c
    == 5 4 3 2 1 0 3
    
  • n p

    Prendi l'ennesimo valore della pila (rimuovilo e portalo in primo piano).

    Esempio :

       5 4 3 2 1 0 3p
    == 5 4 2 1 0 3
    
  • n d

    Elimina n valori dallo stack. 0dè una no-op.

    Esempio :

       5 4 3 2 1 0 3d
    == 5 4 3
    

Operatori relazionali

  • ab (on_true) (on_false) =

    Verifica se a è uguale a b. Consuma tutto tranne il primo argomento e chiama on_true o on_false. Se un argomento è zero e l'altro è qualsiasi altro tipo, il risultato sarà falso. Altrimenti, aeb devono essere numeri interi.

    Esempio :

       3 3 ('0+ .) (1d) =
    == 3 '0+ .
    
  • ab (on_true) (on_false) <

    Verifica se a è inferiore a b. aeb devono essere numeri interi.

    Esempio :

       3 5 (1d 5) () <
    == 3 1d 5
    == 5
    
  • ab (on_true) (on_false) >

    Verifica se a è maggiore di b. aeb devono essere numeri interi.

    Esempio :

       3 5 (1d 5) () >
    == 3
    
  • a lo hi (on_true) (on_false) ~

    Verifica se lo <= a <= hi. a, lo e hi devono essere numeri interi.

    Esempio :

       3 0 10 ('0+ .) (1d) ~
    == 3 '0+ .
    

I / O

  • c .

    Metti il ​​personaggio c (consumandolo dalla pila).

  • ,

    Prendi un personaggio e spingilo nello stack. Se è stata raggiunta la fine del file, viene premuto -1.

  • c !

    Annulla un personaggio. Proprio come ungetc in C, è consentito solo un pushback.

Letterali interi

  • 'c

    Spingere il personaggio c.

  • [0-9] +

    Inserire un numero intero decimale.

Aritmetica

  • ab +
  • ab -
  • ab *

    Aggiungi / sottrai / moltiplica due numeri.

    Esempio :

       3 5 + 7 3 + *
    == 80
    
  • ab /

  • ab %

    Divisione e modulo. A differenza di C, questi arrotondano all'infinito negativo.

miscellaneo

  • #commento in codice

    Il #personaggio commenta tutto fino alla fine della riga.

  • )

    Utilizzato per terminare i blocchi. Può essere utilizzato anche per terminare l'intero programma.

  • Tutti gli altri personaggi vengono ignorati.


24

Calcolo lambda binario, 232 bit (29 byte)

0101000110100000000101011000000000011110000101111110011110000101110011110000001111000010110110111001111100001111100001011110100111010010110011100001101100001011111000011111000011100110111101111100111101110110000110010001101000011010

Vedi http://en.wikipedia.org/wiki/Binary_lambda_calculus#Lambda_encoding per i dettagli


2
Perché non è questa la risposta accettata D: BLC è sorprendente!
cat

Puoi spiegarlo a tutti?
PyRulez,

1
Sfortunatamente, Wikipedia ha rimosso la pagina Binary Lambda Calculus. La mia pagina tromp.github.io/cl/cl.html si collega a una copia conservata e ad un documento che ho scritto che spiega il funzionamento dell'interprete.
John Tromp,

13

Non posso prendermi il merito per questo , ma ho pensato di condividere questo fantastico:

Brainf *** (423)

>>>+[[-]>>[-]++>+>+++++++[<++++>>++<-]++>>+>+>+++++[>++>++++++<<-]+>>>,<++[[>[
->>]<[>>]<<-]<[<]<+>>[>]>[<+>-[[<+>-]>]<[[[-]<]++<-[<+++++++++>[<->-]>>]>>]]<<
]<]<[[<]>[[>]>>[>>]+[<<]<[<]<+>>-]>[>]+[->>]<<<<[[<<]<[<]+<<[+>+<<-[>-->+<<-[>
+<[>>+<<-]]]>[<+>-]<]++>>-->[>]>>[>>]]<<[>>+<[[<]<]>[[<<]<[<]+[-<+>>-[<<+>++>-
[<->[<<+>>-]]]<[>+<-]>]>[>]>]>[>>]>>]<<[>>+>>+>>]<<[->>>>>>>>]<<[>.>>>>>>>]<<[
>->>>>>]<<[>,>>>]<<[>+>]<<[+<<]<]

10

BlockScript - 535

{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

BlockScript è un linguaggio banale basato su stack di spaghetti che ho creato appositamente per questa sfida. L'interprete di base è blockscript.c .

Programma di esempio (stampa i primi 15 numeri di Fibonacci):

{[B?B10/A!B10%d&:0}
{[B0<?'-.0B-A!:{B?Bh!{[B?B[A]A!B[B]'0+.:}!:'0.}!10.}
{[B?Dd!DC+B1-CecA!:}
0 1 15d!
;

L'interprete legge sia il codice sorgente che l'input del programma dall'input standard, in quell'ordine. Ciò significa che per eseguire un interprete all'interno di un interprete all'interno di un interprete, è sufficiente copiare e incollare:

# Level 1
{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

# Level 2
{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

# Level 3
{[B?B10/A!B10%d&:0}
{[B0<?'-.0B-A!:{B?Bh!{[B?B[A]A!B[B]'0+.:}!:'0.}!10.}
{[B?Dd!DC+B1-CecA!:}
0 1 15d!
;

Come nel film Inception , praticamente non puoi andare più in profondità di tre livelli. Non è una questione di tempo, ma di spazio. BlockScript perde abbondantemente la memoria, e questo ha a che fare con il modo in cui è progettato il linguaggio stesso.


Lingua di riferimento:

Prendi l'interprete qui

In BlockScript, lo "stack" non è un array che viene sovrascritto da operazioni successive come è possibile fare. In realtà è implementato come un elenco collegato immutabile e uno stack persiste per la durata del programma. Inoltre, nessun operatore (tranne @) rimuove i valori dallo stack. Tuttavia, le modifiche allo stack influiscono solo sul blocco in cui si verificano.

Selezione del valore

  • a attraverso z

    Prendi lo 0-25 ° oggetto dallo stack e spingilo nello stack. asi riferisce alla testa, o all'ultimo oggetto spinto, della pila.

  • A attraverso Z

    Prendi l'elemento 0-25 del fotogramma corrente e spingilo nello stack.

  • [

    Apri una "cornice" per selezionare gli elementi dal riferimento della pila (vedi sotto) sulla testa della pila. [non richiede una corrispondenza ], ma i frame hanno un ambito lessicale. In BlockScript, "scope" è determinato da parentesi graffe ( {... }) che formano blocchi. Pertanto, l'apertura di un frame all'interno di un blocco non avrà alcun effetto sul codice esterno al blocco.

  • ]

    Chiude il fotogramma corrente, tornando al fotogramma precedente (se presente).

blocchi

  • { ... }

    Crea un "blocco" e spingilo nello stack. All'interno di un blocco, lo stack inizierà da quello che era prima del blocco, tranne che lo stack del chiamante verrà spinto in alto. Le pile sono persistenti e immutabili in BlockScript, quindi i blocchi sono chiusure. Il linguaggio {[significa aprire un blocco, quindi aprire un frame per iniziare a selezionare gli argomenti (usando Athrough Z). Il valore di ritorno di un blocco è il capo dello stack quando }viene raggiunto.

    Esempio:

    '3 '2 '1 {[ b. d. f. B. C. D. A! } 'D 'C 'B d!;
    

    Questo stampa 123BCD123DCB123BCD123DCB…. Le lettere minuscole si riferiscono ai valori dello stack, mentre le lettere maiuscole si riferiscono agli argomenti (poiché il frame è impostato sullo stack del chiamante). A!prende il capo del chiamante (che è garantito essere il blocco chiamato) e lo chiama. Se ti stai chiedendo perché si inverte BCDogni altra volta, è perché inserisce B. C. D.quegli argomenti in ordine inverso proprio prima che il blocco si richiami.

  • !

    Chiama un blocco. Spingere il valore restituito nello stack.

Riferimenti dello stack

  • &

    Creare un riferimento di stack e trasferirlo nello stack. Pensa a questo come "super-contro", in quanto prende effettivamente ogni oggetto in pila e ne forma una "tupla". Il linguaggio &[mezzi che qualunque a, b, cdi cui prima ora possono essere raggiunti con A, B, C(per il resto del blocco o finché ]si incontra).

    In parte perché &acquisisce più valori di quanti ne richiedano normalmente, BlockScript perde memoria in base alla progettazione.

  • @

    Passa allo stack indicato dal riferimento dello stack a. Questo operatore è piuttosto strano, ma l'auto-interprete BlockScript lo usa un paio di volte per evitare di dover spingere due volte gli stessi argomenti. Gli effetti di @(o qualsiasi operazione di stack, per quella materia) sono limitati al blocco in cui viene invocato. Inoltre, il frame non è interessato da @, quindi il frame può essere usato per afferrare i valori necessari dopo il cambio di stack.

Espressione condizionale

  • ? <su vero> : <su falso>

    Espressione condizionale, proprio come l'operatore ternario in C. Cioè, se aè "vero" (cioè non è uguale allo zero intero), allora fai <su vero> , altrimenti fai <su falso> .

I / O

Nota: l'ingresso e l'output vengono eseguiti in UTF-8. Un "carattere" è un numero intero corrispondente a un indice Unicode.

  • ,

    Ottieni il prossimo carattere di input e spingilo nello stack. Se viene raggiunta la fine dell'input, premere invece -1.

  • .

    Emetti il ​​personaggio in testa alla pila.

Letterali interi / caratteri

Nota: numeri interi e caratteri sono la stessa cosa in BlockScript.

  • 'c

    Spingere il personaggio c.

  • [0-9] +

    Inserire un numero intero decimale.

Aritmetica

Questi operatori funzionano solo su valori interi.

  • +Calcola b+ a(spingendo il risultato, ma non scartando nessuno dei due valori).
  • -Calcola b- a.
  • *Calcola b* a.
  • /Calcola b/ a(divisione intera; arrotonda all'infinito negativo).
  • %Calcola b% a(modulo intero; arrotonda all'infinito negativo).

Operatori relazionali

Questi operatori funzionano solo su valori interi.

  • <Se bè inferiore a, premere 1, altrimenti premere 0.
  • >
  • =

miscellaneo

  • # Commenta alla fine della riga
  • Il programma deve terminare con ;
  • Tutti gli altri personaggi vengono ignorati.

2
Cristo. Vuoi condividere le specifiche come hai fatto per CI?
Casey,

@Casey: aggiunto un riferimento.
Joey Adams,

1
Ti interesserebbe sapere che stai sognando? ... Al livello 4.
Mateen Ulhaq,

3

Zozotez LISP : 414

gli avanzamenti di riga aggiunti per ottenere un bel blocco non sono necessari e non vengono conteggiati.

((\(E V A L)(E(r)'(())))(\(x e)(?(s x)(V x e)((\(b j)(?(= b ")(a j)(?(= b \)x
(?(= b ?)(?(E(a j)e)(E(a(d j))e)(E(a(d(d j)))e))(?(s b)(A b(E(a j)e)(E(a(d j)
)e))(E(a(d(d b)))(L(a(d b))j e)))))))(E(a x)e)(d x))))(\(x g)(? g(?(= x(a(a
g)))(d(a g))(V x(d g)))x))(\(f h v)(?(= f r)(r)(?(= f p)(p h)(?(= f s)(s h)(?
(= f a)(a h)(?(= f d)(d h)(?(= f =)(= h v)(c h v))))))))(\(k v i)(? k(L(d k)(
d v)(c(c(a k)(E(a v)i))i))i)))

In teoria dovrebbe essere in grado di funzionare da solo, ma poiché l'interprete originale è un binario BrainFuck e di per sé un interprete, sono stato in grado di testare solo ogni parte. Quando mi viene dato e un'espressione semplice, (p p)penso che abbia bisogno di più tempo dei 40 minuti che ho aspettato finora e sto usando il mio digiunojitbf per eseguirlo che (mis) usa Perl Inline-C per eseguire il codice C al volo.

È impossibile implementare l'intero Zozotez in Zozotez poiché non ha mezzi per mutare i contro e :(setq / define) ha bisogno di quello per aggiornare i binding. Inoltre non ho implementato argomenti espliciti start / progn o & rest, macro e argomenti di stampa speciali poiché non l'ho usato nell'interprete. Ho inclusop (stampa) anche se non lo uso, quindi i programmi devono stampare esplicitamente i loro calcoli proprio come l'interprete originale.

Lo stesso non golfato:

;; A stand alone Zozotez script need to be
;; contained in one expression, here with
;; all functions provided as arguments to
;; get them bound in the dynamic environment
((\ (E V A L)
  (E(r)'(())))
 ;; E (EVAL)
 (\ (x e)
   (? (s x)
      (V x e)
      ((\ (b j)
         (? (= b ") (a j)
         (? (= b \) x
         (? (= b ?) (? (E(a j)e) (E(a(d j))e) (E(a(d(d j)))e))
         (? (s b)
            (A b (E(a j)e) (E (a(d j))e))
            (E (a(d(d b))) (L(a(d b))j e)))))))
       (E (a x) e)(d x))))
 ;; V (VALUE / symbol->value)
 (\ (x g)
   (? g
      (? (= x (a (a g)))
         (d (a g))
         (V x (d g)))
      x))
 ;; A (APPLY) but just for primitives
 (\ (f h v)
   (? (= f r) (r)
   (? (= f p) (p h)
   (? (= f s) (s h)
   (? (= f a) (a h)
   (? (= f d) (d h)
   (? (= f =)
      (= h v)
      (c h v))))))))
 ;; L ( joint evLis / extend-env)
 (\ (k v i)
   (? k
      (L (d k) 
         (d v)
     (c (c (a k) 
           (E (a v) i)) 
        i))
      i)))

0

CHIQRSX9 + (probabilmente non concorrenziale), 2 byte

+I

Non c'è modo di scrivere un interprete auto-interpretante in questo linguaggio basato su HQ9 + senza usare I, che esegue un interprete integrato che elabora STDIN.


1
In nessun punto delle regole si dice che gli auto-interpreti integrati non sono consentiti. Dice per eval, che è per le espressioni, non per i programmi.
Erik the Outgolfer,

Come si calcolano i numeri primi in questa lingua?
pepery

@ppperry Xdovrebbe rendere completo il linguaggio Turing (quindi in grado di calcolare i numeri primi) in modo dipendente dall'implementazione.
user8397947

Secondo la pagina Esolang, il Xcomando dell'interprete Perl disturba casualmente il programma e lo esegue, il che significa che non si può usare il comando per calcolare in modo deterministico i numeri primi. Puoi darmi un esempio di interprete che ti consente di usare Xnel modo che hai specificato?
pepery

@ppperry Apparentemente l'interprete scritto in Perl è l'unico interprete, quindi no. Inoltre, cosa succede se esiste un programma che calcola i numeri primi quando "randomizzato" con un valore specifico?
user8397947

0

File system simultaneo Befunge 98 - 53 \ 18 byte (quasi certamente barare)

Interprete completo da 53 byte senza restrizioni (anche se non ho testato interazioni temporali complesse che coinvolgono la divisione e il wrapping IP):

v ;;;;;;;;
>]390'ai@
 t;;;;;;;;
;>zzzzz#;

Legge l'input da un file denominato ae lo esegue. Non è specificato nelle regole che non possiamo usare il codice di auto-modifica.

Interprete a 18 byte che non consente il wrapping (lo IP si sposta di un bordo del codice e inizia dal bordo opposto):

]210'ai@
t
><
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.