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

Here are some examples.

(print 0 to 8)

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

(print "hi")

(print "hi" with extension for arrays)

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

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
    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.

  • 18
    \$\begingroup\$ Is it just me or is this basically brainfuck? \$\endgroup\$
    – Claudiu
    Jun 26, 2015 at 12:47
  • \$\begingroup\$ can we assume all statements passed are valid? ie all braces are closed \$\endgroup\$
    – Levi
    Jul 10, 2015 at 14:38
  • \$\begingroup\$ 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? \$\endgroup\$ Aug 6, 2015 at 1:55
  • \$\begingroup\$ @JerryJeremiah hmm.. sure it seems like my mistake, will fix now. \$\endgroup\$
    – user41542
    Aug 6, 2015 at 5:59
  • \$\begingroup\$ This is like what BrainFuck wanted to be. \$\endgroup\$
    – S.S. Anne
    Mar 9, 2020 at 20:20

2 Answers 2


C 406

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,c;char*I;X[999],*P=X;Y(){for(;*I++F(2)B(){for(i=!--I;*--I F(0)main(j,a)int**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():({for(c=A;c;c+=c<0?:-1)if(i=c<1)B();else for(;*I++F(2)),"":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[999],*P=X;
int main(int i, char *a[])
  char* I=a[1];
      case '(': while(*I++!=')'); break;
      case '.': printf("%d",*P); break;
      case '!': printf("%c",(char*)*P); break;
      case '[': if(!*P)
      case ']': for(--I,i=0;*--I !='['||i;)
      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)
                    for(--I,i=0;*--I !='['||i;)
  return 0;
  • \$\begingroup\$ @ceilingcat Ok, now ungolfing isn't possible any more so I have no idea how it works. How do you keep making it shorter? \$\endgroup\$ Mar 8, 2020 at 20:56
  • \$\begingroup\$ Just replaced switch(...){case'(':...case'.':... with j=='('?...:j=='.'?... and factored out function calls to fit within the ternary operator. \$\endgroup\$
    – ceilingcat
    Mar 9, 2020 at 0:39
  • \$\begingroup\$ @ceilingcat Thanks for all the golfing - I have learned a heap. \$\endgroup\$ Jan 24, 2021 at 20:34
  • \$\begingroup\$ @ceilingcat I didn't even know you could do that in C - I had to look it up. Thanks for that. \$\endgroup\$ Nov 7, 2021 at 5:14

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.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.