# Write an interpreter for my new esoteric language PointerLang

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.

• Is it just me or is this basically brainfuck? – Claudiu Jun 26 '15 at 12:47
• can we assume all statements passed are valid? ie all braces are closed – Levi Jul 10 '15 at 14:38
• Are you sure the first example =9[>1=9-*-1.>-1-1] prints 0 to 9? After it ptints 8 because P=1 it then subtracts 1 just before the end of the loop which makes P=0 and then when it starts the loop again it should exit because P=0 so the example should only print 0 to 8. Or am I just really confused? – Jerry Jeremiah Aug 6 '15 at 1:55
• @JerryJeremiah hmm.. sure it seems like my mistake, will fix now. – xiver77 Aug 6 '15 at 5:59
• This is like what BrainFuck wanted to be. – S.S. Anne Mar 9 at 20:20

# C 411

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

#define L strtol(I++,0,0)
#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,*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;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;}


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,*P=X;
int main(int i, char *a[])
{
char* I=a;
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;
}

• @ceilingcat Ok, now ungolfing isn't possible any more so I have no idea how it works. How do you keep making it shorter? – Jerry Jeremiah Mar 8 at 20:56
• Just replaced switch(...){case'(':...case'.':... with j=='('?...:j=='.'?... and factored out function calls to fit within the ternary operator. – ceilingcat Mar 9 at 0:39

# JavaScript (ES6) - 560 bytes

Uses prompt() for input and alert() for output. It could be shortened if alert() was called for every character instead but I decided it would be better to buffer the characters so they could all be displayed at once.

b=[],d=0,e=[],f=0,g="",h="",k=prompt().replace(/(\s+|$$.*$$)/g,""),l=0;n=()=>{m=g;if(m&&(a=m.substr(1),c=m.charAt(),a="*"==a.charAt()?b[d+parseInt(a.substr(1))]:parseInt(a),"["==c&&(b[d]?e.push(f-1):l++),"]"==c&&l&&(0<l&&(f=e.pop()),0>l&&(l=-l),l--),!l&&("="==c&&(b[d]=a),"+-*/".includes(c)&&eval("b[d]"+c+"=a"),">"==c&&(d+=a),"."==c&&(h+=b[d]),"!"==c&&(h+=String.fromCharCode(b[d])),";"==c&&(0<a&&(l=-l-a),0>a))))for(i=0;i<-a;i++)f=e.pop()}
for(f=0;f<k.length;f++)0<f&&"=+-*/>.![];".includes(k[f])&&k[f-1].match(/[0-9.!]/)&&(n(),g=""),g+=k[f];h&&alert(h);