We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

Insert

From Esolang
Jump to navigation Jump to search

Insert is a programming language for self-modifying code, created by uellenberg in 2026. It can be used to create programs that produce modified versions of themselves. For example, the pong game shown below, one of the winners of IOCCC29, outputs a version of its code to run the next frame of the game and draws the current frame inside that code.

Insert compiles to C. Aside from its self-modifying features, its syntax is similar to Rust and TypeScript. For the full language guide and more examples, see the source repository. This document is focused on the quine aspects, and skips over general language features.

Self-modifying code

A quine is a program that prints its own source, and a self-modifying quine is a program that prints a modified version of its source code. For example, it could print a version of itself with an incremented counter, or one frame of a game in the future. This C program (created using Insert) plays a game of pong inside its source code:

#include<stdio.h>
#define z else
#define y return
#define x int
#define w if(
#define v putchar(
#define B v 10);
#define A v 92);

/* IOCCC29, w = up, e = down */

x a= 32 ; x b= 6 ; x c= -1 ; x d= 1 ; x e= 5 ; x f= 10 ; x g= 62 ; x h= 5 ; x i[6]={ 1,3,1,4,1,0} ; char*j[]={  "\
\
#include<stdio.h>'#define$z$else'#define$y$return'#define$x$int'#defin\
e$w$if('#define$v$putchar('#define$B$v$10);'#define$A$v$92);''/*$IOCCC\
29,$w$=$up,$e$=$down$*/''x$a=","32",";x$b=","6",";x$c=","-1",";x$d=","\
1",";x$e=","5",";x$f=","10",";x$g=","62",";x$h=","5",";x$i[6]={1,3,1,4\
,1,0};char*j[]={","","};x$k=0;x$l=1;x$m(){l++;w$l==1)y!v$44);w$l==2)y!\
v$34)   ;char$o=j[k][l-3];w!o){l=0;k++;y!v$34);}w$o==34){A$y$v   $34);\
}w$o=   =92){A$y$A}w$o!=32&&o!=1   0)y!v$o);y$m();}void$n(x$o,   x$p){\
aspri   ntf(j+o,\"%i\",p);}x$mai   n(x$o,char**p){char*q;w$c<2   )a+=c\
;b+=d   ;x$r=b+2>f/2&&b<f/2+5;x$s=a+2==g&&b+2>h&&b<h+5;w$c<2){   w$a==\
e+2&&   r||s){a-=c;b-=d;c=-c;}w$a<0||a>67){w$a<0){c=2;d=0;}a=3   4;b=6\
;}w$b<0||b>13){b-=d;d=-d;}w$f/2>10)f-=2;w$h>10)h--;w$o>1){w*p[1]==119&\
&h>0)h--;w*p[1]==101&&h<10)h++;}s=f/2-b+1;w$s<0)f++;w$s>0)f--;}z{b++;w\
$d<0)d++;w$b>=13){w$o>1&&*p[1]==119)d=-4;b=13;}w$f/2<15-i[c-2])f+=2;z$\
e--;w$h<15-i[c-1])h++;z$g--;w$e+3<=0){c++;w$c<7){e=g;f=h*2;g=70;h=15-i\
[c-1];}z{e=5;g=62;c=1;d=1;}}w$a+2==e&&r||s){c=2;e=5;f=28;g=62;h=12;}}n\
\
(1,a);n(3,b);n(5,c);n(7,d);n(9,e);n(11,f);n(13,g);n(15,h);for(s=0;s<","29",";s++){w$s)v$32);q=j[s];r=1;for(char*t=q;*t;t++)w*t==","36",")v$32);z$w*t==","39",")B$z$w*t!=32&&*t!=10){r=0;v*t);w*t==123||*t==125||*t==59)v$32);}w$r){m();A$B$A$B$for(o=0;o<15;o++){for(x$u=0;u<70;u++)w$k>=","29","||u>=a&&o>=b&&u-a<3&&o-b<2||u>=e&&o>=f/2&&u-e<3&&o-f/2<5||u>=g&&o>=h&&u-g<3&&o-h<5)v$32);z$w$m())u++;w$l)A$B}w$l)A$B$for(;k<","29",";)m();}}B}" } ; x k=0; x l=1; x m(){ l++; w l==1)y!v 44); w l==2)y!v 34); char o=j[k][l-3]; w!o){ l=0; k++; y!v 34); } w o==34){ A y v 34); } w o==92){ A y A} w o!=32&&o!=10)y!v o); y m(); } void n(x o,x p){ asprintf(j+o,"%i",p); } x main(x o,char**p){ char*q; w c<2)a+=c; b+=d; x r=b+2>f/2&&b<f/2+5; x s=a+2==g&&b+2>h&&b<h+5; w c<2){ w a==e+2&&r||s){ a-=c; b-=d; c=-c; } w a<0||a>67){ w a<0){ c=2; d=0; } a=34; b=6; } w b<0||b>13){ b-=d; d=-d; } w f/2>10)f-=2; w h>10)h--; w o>1){ w*p[1]==119&&h>0)h--; w*p[1]==101&&h<10)h++; } s=f/2-b+1; w s<0)f++; w s>0)f--; } z{ b++; w d<0)d++; w b>=13){ w o>1&&*p[1]==119)d=-4; b=13; } w f/2<15-i[c-2])f+=2; z e--; w h<15-i[c-1])h++; z g--; w e+3<=0){ c++; w c<7){ e=g; f=h*2; g=70; h=15-i[c-1]; } z{ e=5; g=62; c=1; d=1; } } w a+2==e&&r||s){ c=2; e=5; f=28; g=62; h=12; } } n(1,a); n(3,b); n(5,c); n(7,d); n(9,e); n(11,f); n(13,g); n(15,h); for(s=0; s< 29 ; s++){ w s)v 32); q=j[s]; r=1; for(char*t=q; *t; t++)w*t== 36 )v 32); z w*t== 39 )B z w*t!=32&&*t!=10){ r=0; v*t); w*t==123||*t==125||*t==59)v 32); } w r){ m(); A B A B for(o=0; o<15; o++){ for(x u=0; u<70; u++)w k>= 29 ||u>=a&&o>=b&&u-a<3&&o-b<2||u>=e&&o>=f/2&&u-e<3&&o-f/2<5||u>=g&&o>=h&&u-g<3&&o-h<5)v 32); z w m())u++; w l)A B} w l)A B for(; k< 29 ; )m(); } } B}

Markers

A marker is a named point in the program that survives all the way to the output. Markers divide the program into sections, and the compiler guarantees that the relative order of those sections is preserved through every compilation pass and into the generated code (however, there are no guarantees that code isn't reordered around markers).

function main() {
    marker mainInner;
    return;
    marker mainOuter;
}

marker mainEnd;

On their own, markers are just anchors. Their real purpose is to mark the boundaries between the string fragments that make up a quine, so that a fragment can be located by its marker. Because markers must keep a stable position, they cannot appear in places where the compiler might duplicate or drop code. In particular, you cannot put a marker inside an inline or helper function, or inside a const, since those get copied to their use sites or removed entirely.

Bindings

A binding wraps a single expression in a pair of markers, one before and one after. This lets you mark exactly one value so it can be replaced.

static incrVal: i32 = binding incrValMarker (0);

Conceptually, this compiles to something like:

int incrVal = /* incrValMarker */ 0 /* incrValMarkerEnd */;

The two comments are the markers that fence off the bound expression. Everything between them is one fragment in the quine array, and the name you gave the binding (incrValMarker) becomes the index of that fragment. Then, you can write:

quineStr[incrValMarker] = string(incrVal + 1);

The binding name is usable as an integer index into the quine array. Assigning to that slot replaces the bound expression's text in the copy you are about to print.

Quines

The quine variables give your program access to its own source:

Variable Type Meaning
$quine &[string] The program's source, split into string fragments.
$quineLen i32 The number of fragments in the quine array.
$quineSpace char The sentinel character that stands in for a space.
$quineLine char The sentinel character that stands in for a newline.

During C lowering, the compiler constructs a full copy of the program, split into fragments at every marker (including the ones bindings insert). That list of fragments becomes the $quine array. One fragment is special: the entry for the quine array itself is left as an empty string and is reconstructed at runtime. When the program prints the array, it walks the fragments in order and prints each one. When it reaches the empty fragment, it knows that is where the array literal belongs, so it prints the array's own text there. The result is a complete copy of the source.

If you want to do any sort of whitespace formatting, the strings in the quine array cannot contain spaces or newlines, so the compiler replaces spaces and newlines in the fragments with chosen sentinel characters, exposed as $quineSpace and $quineLine. When you print a fragment you decode them back. The standard library's printQuineItem does this:

helper function printQuineItem(s: string) {
    for let i: i32 = 0; s[i] != '\0'; i = i + 1; {
        if s[i] == $quineSpace {
            putchar(' ');
        } else if s[i] == $quineLine {
            putchar('\n');
        } else if s[i] != ' ' && s[i] != '\n' {
            putchar(s[i]);
        }
    }
}

You do not normally need to call these helpers yourself. The standard library's printQuine handles all the functionality needed to output a quine.

Examples

Self-incrementing quine

This program prints a copy of itself with an embedded counter incremented each time it runs:

static incrVal: i32 = binding incrValMarker (0);

static quineStr: &[string] = $quine;

import "std/quine.int";

function main() {
    quineStr[incrValMarker] = string(incrVal + 1);
    printQuine(quineStr, $quineLen);
}

Walking through it:

  1. incrVal holds the counter, and its initial value 0 is wrapped in a binding named incrValMarker. This is the value that will change between generations.
  2. quineStr captures the whole program source as an array of fragments.
  3. In main, quineStr[incrValMarker] = string(incrVal + 1) overwrites the fragment that holds the counter's value with the next number. The first run replaces 0 with 1.
  4. printQuine prints the modified source.

Compile and run it once and you get back the same program with 0 replaced by 1. Run that and you get 2, and so on. Each generation is a valid C program that knows its own current count.

Pong

The pong example is the same idea at a larger scale. Every piece of game state (ball position, ball velocity, paddle positions) is a static wrapped in a binding:

static ballX: i32 = binding ballXMarker (WIDTH / 2 - BALL_WIDTH / 2 - 1);
static ballY: i32 = binding ballYMarker (HEIGHT / 2 - BALL_HEIGHT / 2 - 1);
static ballSpeedX: i32 = binding ballSpeedXMarker (-1);
// ... and so on ...

Each frame, the program reads the current state, computes the next frame (including any reaction to the key the player pressed), writes the new values back into the corresponding quine fragments, and prints the result. The printed output is formatted so that it also draws the current frame on screen.

External resources