Escribir un intérprete para mi nuevo lenguaje esotérico PointerLang

8

Diseñé un lenguaje en el que la aritmética de punteros es la herramienta principal de programación.

Aquí hay unos ejemplos.

(print 0 to 8)
=9[>1=9-*-1.>-1-1]

(print 1 to 10 with spaces in between, character literal extension used)
=1[.>1=10-*-1[>1=' '!>-2+1;-2];1]='\n'!

(compute the factorial of 10)
=10>1=*-1-1[>-1**1>1-1]>-1.

(print "hi")
=104!=105!

(print "hi" with extension for arrays)
={104,105,0}[!>1]

(print "Hello, world!" with extension for C-style string literals)
="Hello, world!"[!>1]

Especificación de idioma

La definición del lenguaje es muy simple. Comprenderá muy fácilmente si tiene experiencia con C, pero no supondré que sí.

Cada programa tiene en PointerLang el puntero , en definitiva, P. Puede pensarlo como una variable global única oculta, que puede controlar mediante el uso de comandos . Pinicialmente apunta al comienzo de la matriz . Cada elemento de la matriz tiene el tipo intque es un entero con signo de 32 bits.

Para programadores en C

int32_t *P = malloc(1000);

En PointerLang, hay comandos y argumentos . Un argumento es el intque debe venir después de un comando. Todos los comandos se ejecutan de izquierda a derecha a menos que se especifique lo contrario. La siguiente es la lista de comandos. Asignifica argumento. Un comando sin Amedios no toma un argumento. Un comando con Adebe tomar una discusión. Dentro de los paréntesis está la expresión C equivalente.

  • =A: asigne A en P ( *P = A)
  • +A: agregue A en P ( *P += A)
  • -A: restar A en P ( *P -= A)
  • *A: multiplicar por A en P ( *P *= A)
  • /A: dividir por A en P ( *P /= A)
  • >A: moverse Ppor A( P += A)
  • .: imprime el entero en P ( printf("%d", *P))
  • !: imprime el entero en P como ASCII ( printf("%c", (char)*P))
  • [: si el valor en P es 0, vaya al comando después del siguiente ]( while (*P) {)
  • ]: vaya al anterior [que es el par coincidente ( })
  • ;A: Si Aes positivo, ir a la orden después de la AXX ]viene a continuación; si Aes negativo, ve al Ath que [viene antes; Si A es 0, no haga nada.

Un literal entero es un argumento.

Los siguientes dos son argumentos especiales que toman un argumento.

  • -A: evaluado como un argumento que tiene el mismo valor absoluto que Ay el signo opuesto de A; unario menos
  • *A: Mover Ppor A, evaluar el valor a P, mover Ppor -A( P[A])

Todos los comentarios en PointerLang están entre paréntesis (comment).

Programa de ejemplo

Este programa que cuenta del 1 al 10 es un buen ejemplo para completar su comprensión.

(print 1 to 10 with spaces in between)
=1[.>1=10-*-1[>1=32!>-2+1;-2];1]=10!

Ten cuidado cuando interpretes -*-1. -es el comando y *-1es el argumento. Un literal entero indica efectivamente el final de un par comando-argumento.

Se puede traducir a C con correspondencia 1 a 1 como

int main(void) {
    int32_t *P = malloc(1000);
    *P = 1; // =1
l:
    while (*P) { // [
        printf("%d", *P); // .
        P += 1; // > 1
        *P = 10; // =10
        *P -= P[-1]; // -*-1
        while (*P) { // [
            P += 1; // >1
            *P = 32; // =32
            printf("%c", (char)*P); // !
            P += -2; // >-2
            *P += 1; // +1
            goto l; // ;-2
        } // ]
        break; // ;1
    } // ]
    *P = 10; // =10
    printf("%c", (char)*P); // !
    return 0;
}

Las extensiones se pueden aplicar a este lenguaje, como literales de caracteres, matrices, literales de cadena, etc., pero no tiene que implementarlas, por simplicidad.

El reto

Debe implementar las funciones principales detalladas en la sección de Especificación de idioma y las NOTAS a continuación. Pruébalo con tu lenguaje de programación favorito, escribiendo el programa más corto posible.

NOTA1: el tamaño de la matriz no está definido. Pero debería ser lo suficientemente grande como para resolver la mayoría de los problemas.

NOTA2: El desbordamiento de enteros no está definido.

NOTA3: La especificación solo define el resultado o efecto de ciertas construcciones de lenguaje. Por ejemplo, no tiene que seguir exactamente los pasos en la definición del argumento *.

NOTA 4: se ignoran los caracteres que no sean comandos, argumentos o comentarios . =104!=105!es igual que = 1 0 4! = 1 05 !por ejemplo.

NOTA5: Los comentarios no están anidados. ((comment))es un error de sintaxis

NOTA 6: He realizado un cambio importante para arreglar un agujero en mi idioma. El ~comando ahora no se usa y ;siempre toma un argumento.

NOTA 7: Cada literal entero es decimal.

xiver77
fuente
13
¿Soy solo yo o es básicamente una mierda mental?
Claudiu
¿podemos suponer que todas las declaraciones aprobadas son válidas? es decir, todas las llaves están cerradas
Levi
¿Estás seguro de que el primer ejemplo =9[>1=9-*-1.>-1-1]imprime de 0 a 9? Después de que pone 8 porque P [0] = 1, luego resta 1 justo antes del final del bucle, lo que hace que P [0] = 0 y luego, cuando comienza el bucle nuevamente, debería salir porque P [0] = 0, entonces ejemplo solo debería imprimir de 0 a 8. ¿O estoy realmente confundido?
Jerry Jeremiah
@JerryJeremiah hmm .. seguro que parece mi error, lo arreglará ahora.
xiver77
Esto es como lo que BrainFuck quería ser.
SS Anne

Respuestas:

4

C 413

Gracias a @ceilingcat por algunas bonitas piezas de golf, ahora incluso más cortas

#define L strtol(I++,&I,10)
#define A*I==42?I++,P[L]:L
#define F(a)-91-a|i;i-=*I-93+a?*I==91+a:-1);}
i;char*I;X[999],*P=X;Y(){for(;*I++F(2)B(){for(i=!--I;*--I F(0)Z(c){for(c=A;c;c+=c<0?1:-1)if(i=c<1)B();else for(;*I++F(2)main(j,a)char**a;{for(I=a[1];j=*I++;printf(j-4?j+9?j==19?*P=A:j==1?*P+=A:j==3?*P-=A:!j?*P*=A:j==5?*P/=A:j==20?P+=A:j-17?j-51?j-49?I=j+2?I:index(I,41)+1:*P||Y(i=1):B():Z(),"":P:"%d",*P))j-=42;}

Pruébalo en línea!

y la versión un poco menos golfizada de mi respuesta original:

#include <stdio.h>
#include <stdlib.h>
#define L strtol(I++,&I,10)
#define A *I=='*'?I++,P[L]:L
int X[999],*P=X;
int main(int i, char *a[])
{
  char* I=a[1];
  while(*I)
  {
    switch(*I++)
    {
      case '(': while(*I++!=')'); break;
      case '.': printf("%d",*P); break;
      case '!': printf("%c",(char*)*P); break;
      case '[': if(!*P)
                  for(i=1;*I++!=']'||i;)
                  {
                    if(*I=='[')
                      i+=1;
                    if(*I==']')
                      i-=1;
                  }
                break;
      case ']': for(--I,i=0;*--I !='['||i;)
                {
                  if(*I==']')
                    i+=1;
                  if(*I=='[')
                    i-=1;
                }
                break;
      case '=': *P=A; break;
      case '+': *P+=A; break;
      case '-': *P-=A; break;
      case '*': *P*=A; break;
      case '/': *P/=A; break;
      case '>': P+=A; break;
      case ';': for(int c=A; c; c+=c<0?1:-1)
                {
                  if(c>0)
                    for(i=0;*I++!=']'||i;)
                    {
                      if(*I=='[')
                        i+=1;
                      if(*I==']')
                        i-=1;
                    }
                  else
                    for(--I,i=0;*--I !='['||i;)
                    {
                      if(*I==']')
                        i+=1;
                      if(*I=='[')
                        i-=1;
                    }
                }
    }
  }
  return 0;
}
Jerry Jeremiah
fuente
@ceilingcat Ok, ahora ya no es posible ungolfing, así que no tengo idea de cómo funciona. ¿Cómo sigues haciéndolo más corto?
Jerry Jeremiah
Sólo reemplazado switch(...){case'(':...case'.':...con j=='('?...:j=='.'?...y refactorizado llamadas a funciones para encajar dentro del operador ternario.
ceilingcat