An educational programming language designed to teach low-level programming concepts
Reflang is positioned between assembly and C programming. It's designed to help students and developers learn the fundamental mental models they'll need when working with assembly language and C, while providing a more visual and approachable syntax than raw assembly.
Reflang serves as a pedagogical bridge that teaches:
- Explicit data flow through visual arrow syntax (
->
) - Register-based thinking with direct register manipulation (R1, R2, R3, etc.)
- Memory management concepts with visible pointer operations (
&
,*
) - Low-level control flow with explicit jumps and labels
- Function calling conventions and parameter passing
- Assembly-like mindset without assembly's syntactic complexity
Students learn concepts that directly transfer to:
- Assembly programming (registers, memory addressing, control flow)
- C programming (pointers, memory layout, function calls)
- Systems programming (explicit resource management)
- Computer architecture understanding
Reflang is a low-level programming language designed to be more visual than assembly but less abstract than C. It emphasizes explicit data flow through arrow syntax and treats everything as void*
pointers.
- No type system: Everything is treated as
void*
- Visual data flow:
->
operator shows explicit data movement - Sequential execution: No implicit parallelism, clear execution order
- Register-oriented: Direct register manipulation with limited register set
- Memory explicit: All memory operations are visible and controllable
- Syntax:
R1
,R2
,R3
,R4
,R5
(implementation-defined limit) - Default interpretation:
RX
= data/value stored in register - Address interpretation:
&RX
= treat register contents as memory address
- Constraint: Variable name alone is INVALID
- Address form:
&variable
= treat variable as address - Dereference form:
*variable
= dereference variable (get value at address)
The &
and *
operators always dominate arithmetic:
&ptr + 4 // (address of ptr) + 4
*base + offset // (value at base) + offset
source -> destination
R1 -> R2 // Copy value from R1 to R2
&R1 -> R2 // Load from address in R1 to R2
R2 -> &R1 // Store R2 at address in R1
&var -> R1 // Load variable address into R1
*var -> R1 // Load value at variable address into R1
R1 -> &var // Store R1 at variable address
R1 -> *var // Store R1 at address pointed by variable
0x1000 -> R1 // Load immediate value
R1 -> &0x1000 // Store at literal address
R1 + R2 -> R3 // Addition
R1 - R2 -> R3 // Subtraction
R1 * R2 -> R3 // Multiplication
R1 / R2 -> R3 // Division
R1 % R2 -> R3 // Modulo
R1 & R2 -> R3 // Bitwise AND
R1 | R2 -> R3 // Bitwise OR
R1 ^ R2 -> R3 // Bitwise XOR
R1 << R2 -> R3 // Left shift
R1 >> R2 -> R3 // Right shift
R1 == R2 -> R3 // Equality (1 if true, 0 if false)
R1 != R2 -> R3 // Inequality
R1 < R2 -> R3 // Less than
R1 > R2 -> R3 // Greater than
R1 <= R2 -> R3 // Less than or equal
R1 >= R2 -> R3 // Greater than or equal
-> label_name
RX -> ?label_name // Jump if RX is non-zero
RX -> ?function_name $arg1 $arg2
routine routine_name $param1 $param2:
// routine body
-> end
- Parameters are accessed as
$1
,$2
, etc. - Parameters are addresses (pointers)
function function_name $param1 $param2 -> $return1 $return2:
// function body
-> ret
input1 -> input2 -> function_name -> output1 -> output2
&heap -> R1 // Get current heap position
R1 -> *heap // Store at current heap position
&variable = 0x1000 // Direct assignment
0x1000 -> &variable // Alternative syntax
R1 -> stdout // Output register contents
stdin -> R1 // Read input to register
routine main:
// Simple calculator: add two numbers and compare
10 -> R1 // first number
20 -> R2 // second number
R1 + R2 -> R3 // sum
// Check if sum > 25
25 -> R4
R3 - R4 -> R5 // R5 = R3 - 25
R5 -> ?big_number // jump if positive
// Small number path
R3 -> stdout
-> done
big_number:
999 -> stdout // output 999 for big numbers
done:
-> end
routine memory_demo:
// Initialize heap
0x1000 -> &heap // set heap start
// Allocate space for two variables
&heap -> &ptr1 // first variable address
&heap + 1 -> &ptr2 // second variable address
&heap + 2 -> &heap // advance heap
// Store values
42 -> *ptr1 // store 42 at first location
84 -> *ptr2 // store 84 at second location
// Swap values using registers
*ptr1 -> R1 // load first value
*ptr2 -> R2 // load second value
R2 -> *ptr1 // store second at first location
R1 -> *ptr2 // store first at second location
// Output swapped values
*ptr1 -> stdout
*ptr2 -> stdout
-> end
function divide_with_remainder $dividend $divisor -> $quotient $remainder:
// Perform division and return both quotient and remainder
*dividend -> R1 // load dividend
*divisor -> R2 // load divisor
// Calculate quotient
R1 / R2 -> R3
R3 -> $quotient
// Calculate remainder
R3 * R2 -> R4 // quotient * divisor
R1 - R4 -> R5 // dividend - (quotient * divisor)
R5 -> $remainder
-> ret
routine main:
// Set up memory for parameters and results
0x2000 -> &heap
&heap -> ÷nd
&heap + 1 -> &divisor
&heap + 2 -> "ient
&heap + 3 -> &remainder
// Store input values
17 -> *dividend // 17 ÷ 5
5 -> *divisor
// Call function with chained parameters
÷nd -> &divisor -> divide_with_remainder -> "ient -> &remainder
// Output results
*quotient -> stdout // should output 3
*remainder -> stdout // should output 2
-> end
routine array_sum:
// Sum an array of 5 numbers
0x3000 -> &heap
// Initialize array with values
10 -> *heap
&heap + 1 -> &heap
20 -> *heap
&heap + 1 -> &heap
30 -> *heap
&heap + 1 -> &heap
40 -> *heap
&heap + 1 -> &heap
50 -> *heap
// Reset to array start
0x3000 -> ¤t
0 -> R1 // sum accumulator
0 -> R2 // counter
5 -> R3 // array length
sum_loop:
R3 - R2 -> R4 // check if counter < length
R4 -> ?continue_sum
-> sum_done
continue_sum:
*current -> R5 // load current array element
R1 + R5 -> R1 // add to sum
¤t + 1 -> ¤t // advance pointer
R2 + 1 -> R2 // increment counter
-> sum_loop
sum_done:
R1 -> stdout // output sum (should be 150)
-> end
function factorial $n -> $result:
*n -> R1
1 -> R2
R1 - R2 -> R3 // n - 1
R3 -> ?recursive_case
// Base case: n <= 1
1 -> $result
-> ret
recursive_case:
// Allocate space for recursive call
&heap -> &temp_n
&heap + 1 -> &temp_result
&heap + 2 -> &heap
// Store n-1 for recursive call
R3 -> *temp_n
// Recursive call: factorial(n-1)
&temp_n -> factorial -> &temp_result
// Multiply n * factorial(n-1)
R1 -> R2 // n
*temp_result -> R3 // factorial(n-1)
R2 * R3 -> $result
-> ret
routine main:
0x4000 -> &heap
&heap -> &input
&heap + 1 -> &output
&heap + 2 -> &heap
5 -> *input // calculate 5!
&input -> factorial -> &output
*output -> stdout // should output 120
-> end
- Keywords:
routine
,function
,->
,end
,ret
,stdin
,stdout
,heap
- Operators:
+
,-
,*
,/
,%
,&
,|
,^
,<<
,>>
,==
,!=
,<
,>
,<=
,>=
- Symbols:
&
,*
,?
,$
,:
- Identifiers: Variables, labels, function names
- Literals: Decimal and hexadecimal numbers
- Arrow expressions: The
->
operator is the core parsing unit - Chained expressions: Handle function calls with multiple parameters/returns
- Address arithmetic: Parse expressions like
&ptr + 4
correctly - Parameter references:
$1
,$2
, etc. in function/routine contexts
- Register allocation: Track register usage, enforce limits
- Memory management: Track heap usage, ensure bounds checking
- Type consistency: While "typeless," ensure pointer/value consistency
- Control flow: Validate jump targets exist
- Function signatures: Match parameter counts in calls
- Register mapping: Map R1-R5 to target architecture registers
- Memory model: Implement heap as linear memory region
- Jump tables: Handle conditional and unconditional jumps
- Function calling: Implement parameter passing convention
- I/O operations: Map stdin/stdout to system calls
- Stack management: For function calls and local variables
- Heap boundaries: Enforce heap size limits
- Error handling: Division by zero, memory bounds violations
- I/O buffering: Efficient stdin/stdout operations
- Register allocation: Minimize register spills
- Dead code elimination: Remove unused computations
- Constant folding: Evaluate constant expressions at compile time
- Jump optimization: Optimize conditional jump patterns
// Memory allocation with size
function alloc $size -> $address:
&heap -> $address
&heap + *size -> &heap
-> ret
// Memory copy
function memcpy $src $dst $size -> :
// Implementation for copying memory blocks
-> ret
// String length
function strlen $str -> $length:
// Implementation for string length calculation
-> ret
This guide provides the complete syntax and semantic model needed to implement a Reflang compiler, with comprehensive examples demonstrating all language features.
- No types: Everything is treated as
void*
- Arrow flow:
->
indicates data movement direction - Sequential execution: Fully sequential with routines and functions for organization
- Visual clarity: Data flow is explicit and left-to-right
Registers (R1, R2, etc.):
R1
= the value stored in register R1 (data by default)&R1
= treat R1's contents as a memory address
Variables/Pointers (p, myvar, etc.):
p
alone = NOT VALID - must be explicit&p
= treat p as an address*p
= dereference p (get value at address stored in p)
The &
or *
symbol always dominates arithmetic:
&add + 1 // (address of add) + 1
*ptr + 5 // (value at ptr) + 5
&($3 + 1) // address of parameter ($3 + 1)
// Register to register
R1->R2 // move data from R1 to R2
// Register memory access
&R1->R2 // load from address stored in R1 into R2
R2->&R1 // store R2 at address stored in R1
// Variable operations
&p->R1 // load address from variable p into R1
*p->R1 // load value at address p into R1
R1->&p // store R1 at address in variable p
R1->*p // store R1 at address pointed to by p
// Immediate values
0x1000->R1 // store immediate value into R1
R2->&0x1000 // store R2 at literal address 0x1000
R1+R2->R3 // add R1 and R2, store in R3
R1&R2->R3 // bitwise AND
R1==R2->R3 // comparison (0 or 1 result)
R1<<4->R2 // bit shift
R1->?jump_label // conditional jump if R1 is non-zero
->jump_label // unconditional jump
R1->?myroutine $arg // conditional routine call
routine myroutine $1 $2:
*$1->R1 // load value at address $1 into R1
R1+1->R1 // increment R1
R1->*$2 // store R1 at address $2
->end // end routine
Functions return values and use Haskell-style parameter passing:
function myfun $1 $2 $3 -> $4 $5:
&($3 + 1) -> &p // address of ($3 + 1) goes to address p
0x01 -> &q // immediate 0x01 goes to address q
$1 -> *p // parameter $1 goes to address pointed by p
&$2 -> &q // address of $2 goes to address q
*p -> *q -> add -> *p // call add function with chained params/returns
*p -> $4 // return *p as first output
*q -> $5 // return *q as second output
-> ret // end function
function add $1 $2 -> $3:
$1 + $2 -> $3 // add parameters, store in return
-> ret
Multi-parameter functions use chained arrow syntax:
*input1 -> &input2 -> myfunction -> &output1 -> *output2
// ^ ^ ^ ^ ^
// param1 param2 function return1 return2
// Memory management
&heap->R1 // load heap root/current position into R1
R1->*heap // store R1 at current heap position
// (No freeing mechanism designed yet)
// I/O
R1->stdout // output
stdin->R1 // input
&p=0x00 // p contains address 0x00 (direct memory assignment)
0x00 -> &heap // alternative syntax
&heap + 1 -> &a // address arithmetic in assignment
routine main:
0x00 -> &heap // initialize heap pointer
&heap + 1 -> &a // set up variable addresses
&heap + 2 -> &b
0x10 -> *a // store values
0x10 -> *b
*a -> *b -> &heap -> myfun -> *a -> *b // call function
->end