Write an interpreter for my new esoteric language PointerLang

8

1

I designed a language in which pointer arithmetic is the main tool of programming.

Here are some examples.

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

Language Specification

The language definition is very simple. You will understand very easily if you have experience with C, but I will not assume so.

Every program in PointerLang has the pointer, in short, P. You can think it as a hidden single global variable, which you can control by using commands. P initially points to the beginning of the array. Every element in the array has the type int which is a 32-bit signed integer.

For C programmers

int32_t *P = malloc(1000);

In PointerLang, there are commands and arguments. An argument is an int which must come after a command. All commands are executed from left to right unless specified otherwise. The following is the list of commands. A stands for argument. A command without A means it does not take an argument. A command with A must take an argument. Inside the parentheses is the equivalent C expression.

  • =A : assign A at P (*P = A)
  • +A : add A at P (*P += A)
  • -A : subtract A at P (*P -= A)
  • *A : multiply by A at P (*P *= A)
  • /A : divide by A at P (*P /= A)
  • >A : move P by A (P += A)
  • . : print the integer at P (printf("%d", *P))
  • ! : print the integer at P as ASCII (printf("%c", (char)*P))
  • [ : if the value at P is 0 go to the command after the next ] (while (*P) {)
  • ] : go to the previous [ that is the matching pair (})
  • ;A : if A is positive, go to the command after the Ath ] coming next; if A is negative, go to the Ath [ coming before; if A is 0, do nothing.

An integer literal is an argument.

The following two are special arguments that take an argument.

  • -A : evaluated as an argument having the same absolute value as A and the opposite sign of A; unary minus
  • *A : move P by A, evaluate the value at P, move P by -A (P[A])

All comments in PointerLang are between parentheses (comment).

Example Program

This program which counts from 1 to 10 is a good example to complete your understanding.

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

Be careful when you interpret -*-1. - is the command and *-1 is the argument. An integer literal effectively indicates the end of a command-argument pair.

It can be translated to C with 1-to-1 correspondence as

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

Extensions can be applied to this language such as character literals, arrays, string literals etc, but you don't have to implement them, for simplicity.

The Challenge

You have to implement the core features detailed in the Language Specification section and the NOTEs below. Give it a try with your favourite programming language, writing the shortest program possible.

NOTE1: The size of the array is undefined. But it should be big enough to solve most problems.

NOTE2: Integer overflow is undefined.

NOTE3: The specification only defines the result or effect of certain language constructs. For example, you don't have to exactly follow the steps in the definition of the argument *.

NOTE4: Any characters that are not commands, arguments, or comments Whitespaces are ignored. =104!=105! is as same as = 1 0 4! = 1 05 ! for example.

NOTE5: Comments are not nested. ((comment)) is a syntax error.

NOTE6: I've made a breaking change to fix a hole in my language. The ~ command is now unused and ; always takes an argument.

NOTE7: Every integer literal is decimal.

xiver77

Posted 2015-06-26T11:01:39.307

Reputation: 257

13Is it just me or is this basically brainfuck? – Claudiu – 2015-06-26T12:47:33.427

can we assume all statements passed are valid? ie all braces are closed – Levi – 2015-07-10T14:38:02.203

Are you sure the first example =9[>1=9-*-1.>-1-1] prints 0 to 9? After it ptints 8 because P[0]=1 it then subtracts 1 just before the end of the loop which makes P[0]=0 and then when it starts the loop again it should exit because P[0]=0 so the example should only print 0 to 8. Or am I just really confused? – Jerry Jeremiah – 2015-08-06T01:55:25.390

@JerryJeremiah hmm.. sure it seems like my mistake, will fix now. – xiver77 – 2015-08-06T05:59:43.167

Answers

4

C 438

Thanks to @ceilingcat for some very nice pieces of golfing - now even shorter

#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?0:-1:1);
#define J(x)for(i=x;*I++F(2)
#define K for(i=!--I;*--I F(0)
#define C break;case
X[999],*P=X,c;main(i,a)int**a;{for(char*I=a[1];*I;)switch(*I++){C'(':while(*I++-41);C'.':printf("%d",*P);C'!':printf(P);C'[':if(!*P)J(1)C']':K C'=':*P=A;C'+':*P+=A;C'-':*P-=A;C'*':*P*=A;C'/':*P/=A;C'>':P+=A;C';':for(c=A;c;c+=c<0?1:-1)if(c>0)J(0)else K}}

Try it online!

and the slightly less golfed version of my original answer:

#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

Posted 2015-06-26T11:01:39.307

Reputation: 1 217

430 bytes – ceilingcat – 2020-02-28T05:14:22.723