Spinning donut ᴾᴴᴾ - chung-leong/zigar GitHub Wiki

JavaScript | PHP


In this exercise, we're going to take a piece of C code that has gone viral on YouTube and make it in run in PHP. The code in question is in the shape of a donut:

             k;double sin()
         ,cos();main(){float A=
       0,B=0,i,j,z[1760];char b[
     1760];printf("\x1b[2J");for(;;
  ){memset(b,32,1760);memset(z,0,7040)
  ;for(j=0;6.28>j;j+=0.07)for(i=0;6.28
 >i;i+=0.02){float c=sin(i),d=cos(j),e=
 sin(A),f=sin(j),g=cos(A),h=d+2,D=1/(c*
 h*e+f*g+5),l=cos      (i),m=cos(B),n=s\
in(B),t=c*h*g-f*        e;int x=40+30*D*
(l*h*m-t*n),y=            12+15*D*(l*h*n
+t*m),o=x+80*y,          N=8*((f*e-c*d*g
 )*m-c*d*e-f*g-l        *d*n);if(22>y&&
 y>0&&x>0&&80>x&&D>z[o]){z[o]=D;;;b[o]=
 ".,-~:;=!*#$@"[N>0?N:0];}}/*#****!!-*/
  printf("\x1b[H");for(k=0;1761>k;k++)
   putchar(k%80?b[k]:10);A+=0.04;B+=
     0.02;}}/*****####*******!!=;:~
       ~::==!!!**********!!!==::-
         .,~~;;;========;;;:~-.
             ..,--------,*/

When compiled and run, it prints a spinning donut in the terminal. Completely useless? Yes, but it's totally cool!

Creating the spinning donut

Per usual, we'll start by creating the basic directory structure:

mkdir donut
cd donut
mkdir src zig

In the sub-directory zig, create donut.c and paste in the C code above.

In the same directory, create donut.zig:

const std = @import("std");

extern fn c_main(c_int, [*c][*c]u8) c_int;

pub fn run() void {
    _ = c_main(0, null);
}

All the code does is import the main function from the C code and run it.

In the same directory, create build.extra.zig:

const std = @import("std");

const cfg = @import("build.cfg.zig");

pub fn getCSourceFiles(_: *std.Build, args: anytype) []const []const u8 {
    args.module.addCSourceFile(.{
        .file = .{ .cwd_relative = cfg.module_dir ++ "donut.c" },
        .flags = &.{
            "-std=c89",
            "-Dmain=c_main",
        },
    });
    return &.{};
}

This file is used by Zigar's built-in build file. Normally, getCSourceFiles() would return a list of C files that need to compiled into the final executable. We can't do that here because we're using C code that isn't legal anymore. We have to tell the compiler to compile the code in accordance with the C standard circa 1989. We're able to do that since the build file helpfully gives us direct access to the main module.

The second compiler flag renames main() to c_main(). We need to do this since it's illegal to invoke a C program's main function directly. On certain platforms (e.g. Windows), the result is an immediate crash.

You have to use module_dir to get the full path to the C file because Zigar compiles modules in a temporary directory.

The final step involves importing the Zig function and calling it in PHP. In src, create donut.php:

<?php

$m = zigar_use(__DIR__ . '/../zig/donut.zig');
$m->run();

Then run it:

php src/donut.php

You should see a spinning donut.

Source code

You can find the complete source code for this example here.

Conclusion

The C code used in this example is, of course, utterly frivolous. The important takeaway is that you enjoy the full power of a C compiler when you program in Zig. The Zig compiler is actually more powerful than something like Clang because it's a cross-compiler. You can easily, for instance, compile the same code for WebAssembly as you can see here here.


Additional examples