Dr. Brian Robert Callahan

academic, developer, with an eye towards a brighter techno-social life



[prev]
[next]

2022-02-20
I wrote the least-C C program I could

All source code for this blog post can be found here.

As a fun afternoon challenge, I decided to write a C program that looked nothing like C. Not necessarily obfuscated, though there is a competition for that if you are so inclined.

No, instead I thought to myself, what if I turned C into an entirely different language, then wrote a program in that language, then used the C compiler to compile the program. That to me sounds like the least-C C program I could write.

Here it is, in all its glory:

#include"cpaint.h"

var a, b, c, h, i, l, v, x, y, q, w, p size 65535 ,
packed n size 13 ꞉integer ;

procedure display(r,s,c) ;
begin
  LOOP
    call A(Z) ;
    call H(y,x) ;
    call B(Z)
  POOL ;

  y ꞉= r;
  x ꞉= s;

  call A(c) ;
  call H(y,x) ;
  call B(c) ;

  call refresh()
end ;

procedure fill(y,x,c,a) ;
begin
  if(y<0 or y>w-1 or x<0 or x>q-1 or c = a or Z <> a)fill꞉= -1 ;

  call draw(c) ;
  call fill(y+1,x,c,a) ;
  call fill(y-1,x,c,a) ;
  call fill(y,x-1,c,a) ;
  call fill(y,x+1,c,a)
end ;

procedure save(r,s) ;
begin
  i ꞉= 0 ;
  while(i<13)do
  begin
    n[i] ꞉= 0 ;
    i ꞉= i+1
  end ;

  call move(w>>1,(q>>1)-6) ;
  call printw("Save: ") ;
  call echo() ;
  call getnstr(n,12) ;
  call noecho() ;

  call open(n,"w+") ;
  call writeChar(83) ;
  call writeChar(w) ;
  call writeChar(q) ;

  LOOP
    call writeChar(Z)
  POOL ;

  y ꞉= r;
  x ꞉= s;

  call close ;
  call move(y,x)
end ;

procedure load(packed ʌ n) ;
begin
  call open(n,"r") ;
  call check ;

  LOOP
    readln(c);
    call draw(c)
  POOL ;

  c ꞉= 0;

  call close
end ;

procedure m() ;
begin
  l ꞉= 0 ;
  v ꞉= 1 ;

  call A(c) ;
  call H(0,0) ;
  call B(c) ;
  call refresh() ;

  while(v)do
  begin
    read(inp) ;
    '/':l ꞉= not l ; if(l)call draw(c) ; OK
    'k':y ꞉= y-1 ; if(y<0)y ꞉= 0 ; if(l)call draw(c) ; OK
    'j':y ꞉= y+1 ; if(y>w-1)y ꞉= w-1 ; if(l)call draw(c) ; OK
    'h':x ꞉= x-1 ; if(x<0)x ꞉= 0 ; if(l)call draw(c) ; OK
    'l':x ꞉= x+1 ; if(x>q-1)x ꞉= q-1 ; if(l)call draw(c) ; OK
    ' ':call draw(c) ; OK
    'c':c ꞉= c+1 ; if(c = M)c ꞉= 0 ; OK
    'd':call draw(15) ; OK
    'f':call fill(y,x,c,Z) ; OK
    's':call save(y,x) ; OK
    'q':v ꞉= 0 ; OK
    'v':c ꞉= c-1 ; if(c = N)c ꞉= M-1 ; CALL display(y,x,c)
  end
end ;

procedure main(I c,packed ʌ ʌ v) ;
begin
  call start ;
  call getmaxyx(stdscr,w,q) ;
  if(w>M)w ꞉= M ;if(q>M)q ꞉= M ;
  call start_color() ;

  while(x<M)do
  begin
    call init_pair(x,x,x) ;
    x ꞉= x+1
  end ;

  LOOP
    call draw(15)
  POOL ;

  if(c = 2)call load(v[c-1]) ; call display(0,0,0) ; call m() ; call endwin()
end ;

call main.

I hear what you're saying: "This is literally not C. It has all the markings of a Pascal language, with the semicolon as statement separator rather than statement terminator, the := for assignment, and maybe some Algol with the LOOP .. POOL syntax." It even has the Pascal-style return assignment where you assign the function a value and that's its return value (see the fill procedure).

Caught me. I had recently heard that Arthur Whitney, author of the A+, k, and q languages (which are array programming languages like APL and J), would use the C preprocessor to create his own language and then write the implementation of his language in that self-defined language. I decided to try to do the same.

I loosely based my self-defined language off of PL/0, which we wrote a compiler for previously (yes, I know I have to finish the self-hosting blog series; I will finish it).

The hero of this exercise was the fact that the C compilers understand UTF-8 characters as valid characters for identifiers. I use lots of characters that look like ASCII but are in fact not ASCII but nonetheless accepted as valid identifier characters. The C preprocessor happily accepts macros that transform these identifiers into whatever you need them to. You can see the hidden away header file here. Creative use of whitespace helped finish it off.

If you want to see what the C code truly looks like, trying running:

$ cc -E cpaint.c | clang-format | less

For a short afternoon, it was amusing. I probably won't try something like this again. But it was fun to discover some interesting ways to abuse the C preprocessor.

Oh, and if you actually want to use the program, it's a simple terminal paint program.

Top

RSS