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;