Escreva um intérprete para minha nova linguagem esotérica PointerLang

8

Eu projetei uma linguagem na qual a aritmética de ponteiros é a principal ferramenta de programação.

Aqui estão alguns exemplos.

(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]

Especificação de idioma

A definição da linguagem é muito simples. Você entenderá com muita facilidade se tiver experiência com C, mas não assumirei isso.

Todo programa no PointerLang possui o ponteiro , em resumo P,. Você pode pensar nisso como uma variável global única oculta, que você pode controlar usando comandos . Pinicialmente aponta para o início da matriz . Cada elemento da matriz tem o tipo intque é um número inteiro assinado de 32 bits.

Para programadores C

int32_t *P = malloc(1000);

No PointerLang, existem comandos e argumentos . Um argumento é aquele intque deve vir após um comando. Todos os comandos são executados da esquerda para a direita, a menos que seja especificado o contrário. A seguir está a lista de comandos. Asignifica argumento. Um comando sem Ameios não leva a um argumento. Um comando com Adeve receber um argumento. Dentro dos parênteses está a expressão C equivalente.

  • =A: atribua A em P ( *P = A)
  • +A: adicione A em P ( *P += A)
  • -A: subtraia A em P ( *P -= A)
  • *A: multiplicar por A em P ( *P *= A)
  • /A: divida por A em P ( *P /= A)
  • >A: mover Ppor A( P += A)
  • .: imprime o número inteiro em P ( printf("%d", *P))
  • !: imprime o número inteiro em P como ASCII ( printf("%c", (char)*P))
  • [: se o valor em P for 0, vá para o comando após o próximo ]( while (*P) {)
  • ]: vá para o anterior [que é o par correspondente ( })
  • ;A: se Afor positivo, vá para o comando após o próximo Ath ]; se Afor negativo, vá para o Ath [vindo antes; se A for 0, não faça nada.

Um literal inteiro é um argumento.

Os dois a seguir são argumentos especiais que aceitam um argumento.

  • -A: avaliado como um argumento com o mesmo valor absoluto Ae o sinal oposto de A; menos unário
  • *A: Movimento Ppor A, avaliar o valor em P, movimento Ppor -A( P[A])

Todos os comentários no PointerLang estão entre parênteses (comment).

Programa de exemplo

Este programa que conta de 1 a 10 é um bom exemplo para concluir sua compreensão.

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

Tenha cuidado ao interpretar -*-1. -é o comando e *-1é o argumento. Um literal inteiro indica efetivamente o final de um par de argumentos de comando.

Pode ser traduzido para C com correspondência 1 para 1, conforme

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

Extensões podem ser aplicadas a essa linguagem, como literais de caracteres, matrizes, literais de cadeias etc., mas você não precisa implementá-las, para simplificar.

O desafio

Você precisa implementar os principais recursos detalhados na seção Especificação de Idioma e nas NOTAS abaixo. Experimente a sua linguagem de programação favorita, escrevendo o programa mais curto possível.

NOTA1: O tamanho da matriz é indefinido. Mas deve ser grande o suficiente para resolver a maioria dos problemas.

NOTA2: Estouro de número inteiro é indefinido.

NOTA3: A especificação define apenas o resultado ou o efeito de determinadas construções de linguagem. Por exemplo, você não precisa seguir exatamente as etapas na definição do argumento *.

NOTA4: Quaisquer caracteres que não sejam comandos, argumentos ou comentários, espaços em branco são ignorados. =104!=105!é o mesmo que = 1 0 4! = 1 05 !por exemplo.

NOTA5: Comentários não são aninhados. ((comment))é um erro de sintaxe.

NOTA6: Fiz uma alteração de última hora para corrigir um buraco no meu idioma. O ~comando agora não é utilizado e ;sempre recebe um argumento.

NOTA7: Todo literal inteiro é decimal.

xiver77
fonte
13
Sou só eu ou isso é basicamente foda cerebral?
precisa
podemos assumir que todas as declarações passadas são válidas? ou seja, todas as chaves estão fechados
Levi
Tem certeza de que o primeiro exemplo =9[>1=9-*-1.>-1-1]imprime de 0 a 9? Depois de pontuar 8 porque P [0] = 1, subtrai 1 imediatamente antes do final do loop, o que faz com que P [0] = 0 e, quando ele inicia o loop novamente, deve sair porque P [0] = 0, para que exemplo deve imprimir apenas de 0 a 8. Ou estou realmente confuso?
Jerry Jeremiah
@JerryJeremiah hmm .. claro que parece que foi meu erro, vai corrigir agora.
X165
É como o BrainFuck queria ser.
SS Anne

Respostas:

4

C 413

Graças a @ceilingcat por algumas peças muito boas de golfe - agora ainda mais curtas

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

Experimente online!

e a versão um pouco menos complicada da minha resposta 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
fonte
@ceilingcat Ok, agora não é mais possível jogar golfe, por isso não faço ideia de como funciona. Como você continua diminuindo?
Jerry Jeremiah
Apenas substituídas switch(...){case'(':...case'.':...por j=='('?...:j=='.'?...chamadas de função e fatoradas para caber no operador ternário.
roofcat 9/03