Generator Programming Language - awesome-llama/procedural-sandbox GitHub Wiki
This is an experimental feature. Some instructions may change but overall the language should be easy to migrate to any new version. The most likely future changes would be:
- fixing bugs (it's not fully tested)
- adding more instructions for other features.
A purpose-built low-level assembly language is provided. This allows you to make your own custom generators without needing to edit the project. The design of the language favours instruction execution speed in Scratch. The data types are also similar to Scratch and Scratch is responsible for type casting. It is not case-sensitive.
Syntax
Statements are separated by semicolons ;
. The language does not rely on newlines or indentation as it may be inputted through a Scratch ask-and-wait block. Comments also must be ended with semicolons.
There are 3 kinds of statements: instructions, comments, and labels. Instructions are for code that executes. Comments are for providing human-readable documentation to the code and have no impact on execution speed. Labels are locations that the instruction pointer may jump to through use of a jump instruction. Labels are the only way to create branching code.
An instruction consists of a name followed by a number of arguments, separated by spaces. The following prints the string Hello, world!
to the output list:
print "Hello, world!";
Comments start with the #
symbol and may follow with any characters, until a semicolon indicating the end of the comment.
# This is a comment!;
Labels must start with the @
symbol. This is followed by 1 or more alphanumeric characters. Don't forget to end the statement with a semicolon.
@loop_start;
Many instructions have arguments that take values. There are two literal types: numbers and strings. They are not distinguished any differently from how Scratch handles them. Casting is handled by Scratch. You may also use variables in the arguments by instead writing a name starting with a letter or underscore. Variables are automatically created.
To initalise a variable to a particular value, use the set
instruction or any similar instruction that writes a value to a given variable.
set var1 5; # set var1 to the number 5;
set var2 "hello"; # set var1 to the string "hello";
set var3 var1; # copy the value from var1 into var3;
add var4 3 4; # set var4 to the number 7 (by adding 3 and 4);
mul var5 var1 var2; # multiply var1 and var2 and write the result to var5;
In many other programming languages the above lines would be equivalent to:
var1 = 5;
var2 = "hello";
var3 = var1;
var4 = 3 + 4;
var5 = var1 * var2;
Variables should be initalised with a value before they are used. It isn't an error to do so, though the behaviour is not defined. It may be an empty string, or it may be something else.
set var1 3.4;
add var3 var1 var2; # not recommended, var2 was not initalised with a value;
Instructions
The full list of instructions are in src/lang/instructions.py
.
Argument symbol | Meaning |
---|---|
L | Label |
V | Value, either a literal or variable |
A | Variable only (a memory address) |
Branching
Instruction | Description |
---|---|
jump L; |
Jump to a label |
jump_if_true L V; |
Jump to the given label if the condition is true |
jump_if_false L V; |
Jump to the given label if the condition is false |
jump_by V; |
Jump over a number of instructions |
call L; |
Jump to a subroutine with the intention of returning back after it finishes. The return instruction must be called to jump back. |
return; |
Return to where the most recent subroutine was called from. |
Simple operations
Instruction | Description |
---|---|
set A V; |
Set the variable to a value |
inc A; |
Increment the variable by 1 |
dec A; |
Decrement the variable by 1 |
add A V V; |
Add two values |
sub A V V; |
Subtract |
mul A V V; |
Multiply |
div A V V; |
Divide |
mod A V V; |
Modulo (remainder of division) |
pow A V V; |
Power |
eq A V V; |
Equality |
lt A V V; |
Less than |
gt A V V; |
Greater than |
lteq A V V; |
Less than or equal to |
gteq A V V; |
Greater than or equal to |
and A V V; |
Boolean AND |
or A V V; |
Boolean OR |
not A V; |
Boolean NOT |
abs A V; |
Absolute value (makes a number positive) |
round A V; |
Round a value to the nearest whole number |
floor A V; |
Round a value down to a whole number |
ceil A V; |
Round a value up to a whole number |
sqrt A V; |
Square root |
sin A V; |
Sine |
cos A V; |
Cosine |
tan A V; |
Tangent |
asin A V; |
Arcsin |
acos A V; |
Arccos |
atan A V; |
Arctan |
ln A V; |
Natural logarithm |
log A V; |
Base 10 logarithm |
antiln A V; |
e^x |
antilog A V; |
10^x |
min A V V; |
Minimum of two values |
max A V V; |
Maximum of two values |
random A V V; |
Pick a random number. Same behaviour as Scratch. |
atan2 A V V; |
y, x. Angle from 2D vector (east-counter-clockwise) |
mag2 A V V; |
x, y. Magnitude of a 2D vector |
letter_of A V V; |
string, index. Get a character from a string |
join A V V; |
Concatenate two strings |
lerp A V V V; |
a, b, t. Linear interpolation. Allows for reversed intervals. |
unlerp A V V V; |
a, b, x. Opposite of linear interpolation. |
select A V V V; |
condition, a, b. Pick value a if condition is true, else b. |
Project
Instruction | Description |
---|---|
print V; |
print a value to the output list |
clear_canvas V V V; |
size_x, size_y, size_z. clear the canvas and give it new dimensions |
get_canvas_size A A A; |
get the dimensions of the canvas and write the values to 3 variables. |
add_canvas_as_template; |
add the current canvas as a depositor template |
reset_depositor; |
reset depositor to default values |
set_depositor_to_air; |
set the depositor to air (has 0 opacity) |
set_depositor_from_sRGB V V V; |
r, g, b. Set the depositor to an opaque voxel with rgb values |
set_depositor_from_HSV V V V; |
h, s, v. Set the depositor to an opaque voxel from hsv |
set_depositor_from_voxel V V V; |
x, y, z. Set the depositor to a voxel copied from the canvas at a particular coordinate. |
set_depositor_to_template V V V V; |
index, ox, oy, oz. Set the depositor to draw from a template. The last 3 arguments offset the template. |
set_depositor_opacity V; |
set depositor opacity, no other property affected |
set_depositor_emission V; |
set depositor emission, no other property affected |
get_depositor A A A A A; |
get the depositor voxel data: r, g, b, opacity, emission. Write to 5 variables. |
set_voxel V V V; |
x, y, z. Draw a single voxel at a coordinate. |
draw_ray V V V V V V V; |
x, y, z, dx, dy, dz, length. Draw a single-voxel thick line from a start point, in a direction, with a given length. |
draw_cuboid_corner_size V V V V V V; |
x, y, z, size_x, size_y, size_z. Draw a solid cuboid starting from a point, going in the positive axis directions. |
draw_sphere V V V V; |
x, y, z, radius. Draw a filled sphere. |
draw_cylinder V V V V V; |
x, y, z, radius, height. Draw a filled cylinder. |
generate_value_noise V V V V; |
size_x, size_y, scale, octaves. Generate a canvas of greyscale value noise. |
Examples
Fibonacci
set a 0;
set b 1;
set i 0;
@loop_start;
print a;
add temp a b;
set a b;
set b temp;
inc i;
lteq should_repeat i 15;
jump_if_true @loop_start should_repeat;
Random noise
set canvas_size_x 12;
set canvas_size_y 12;
clear_canvas canvas_size_x canvas_size_y 1;
set iy 0;
@loop_start_y;
set ix 0;
@loop_start_x;
random _h "0.0" "1.0";
random _s "0.0" "0.5";
random _v "0.5" "1.0";
set_depositor_from_HSV _h _s _v;
set_voxel ix iy 0;
inc ix;
lt res ix canvas_size_x;
jump_if_true @loop_start_x res;
inc iy;
lt res iy canvas_size_y;
jump_if_true @loop_start_y res;
Sphere grid
set grid_size_x 12;
set grid_size_y 12;
set cell_size 8;
mul sphere_radius cell_size 0.4;
mul canvas_size_x grid_size_x cell_size;
mul canvas_size_y grid_size_y cell_size;
set canvas_size_z cell_size;
clear_canvas canvas_size_x canvas_size_y canvas_size_z;
set_depositor_from_sRGB 0.5 0.5 0.5;
draw_cuboid_corner_size 0 0 0 canvas_size_x canvas_size_y 1;
set iy 0;
@loop_start_y;
add sphere_y iy 0.5;
mul sphere_y sphere_y cell_size;
div ty iy grid_size_y;
set ix 0;
@loop_start_x;
add sphere_x ix 0.5;
mul sphere_x sphere_x cell_size;
div tx ix grid_size_x;
set_depositor_from_sRGB tx 0.5 ty;
draw_sphere sphere_x sphere_y sphere_radius sphere_radius;
inc ix;
lt res ix grid_size_x;
jump_if_true @loop_start_x res;
inc iy;
lt res iy grid_size_y;
jump_if_true @loop_start_y res;
Mandelbrot set
set canvas_size_x 128;
set canvas_size_y 128;
set max_iterations 100;
set cam_x -0.5;
set cam_y 0.0;
set zoom 0.3;
div ox canvas_size_x 2;
div oy canvas_size_y 2;
max canvas_size_max canvas_size_x canvas_size_y;
print canvas_size_max;
max max_iterations max_iterations 1;
clear_canvas canvas_size_x canvas_size_y 1;
set iy 0;
@loop_start_y;
sub y0 iy oy;
div y0 y0 canvas_size_max;
div y0 y0 zoom;
add y0 y0 cam_y;
set ix 0;
@loop_start_x;
sub x0 ix ox;
div x0 x0 canvas_size_max;
div x0 x0 zoom;
add x0 x0 cam_x;
set x x0;
set y y0;
set iterations 0;
@loop_start_iterations;
mul x_squared x x;
mul y_squared y y;
sub res x_squared y_squared;
add xtemp res x0;
mul res 2 x;
mul res res y;
add y res y0;
set x xtemp;
inc iterations;
add res x_squared y_squared;
gt res res 4;
jump_if_true @iteration_end res;
gteq res iterations max_iterations;
jump_if_true @iteration_end res;
jump @loop_start_iterations;
@iteration_end;
gteq res iterations max_iterations;
jump_if_true @black res;
@gradient;
div res iterations 20;
min res res 1;
set_depositor_from_HSV res res 1;
jump @set_voxel;
@black;
set_depositor_from_sRGB 0 0 0;
@set_voxel;
set_voxel ix iy 0;
inc ix;
lt res ix canvas_size_x;
jump_if_true @loop_start_x res;
inc iy;
lt res iy canvas_size_y;
jump_if_true @loop_start_y res;