HTLL is not an academic exercise; it is a brutal, practical tool for creating hyper-efficient, multi-architecture software. It strips away the heavy abstractions of modern languages (like C's dependency on libc) and forces the programmer to interact intimately with memory, registers, and execution flow.
The result is absolute minimal footprint:
- A standard
Hello, World!program in HTLL compiles to a 440-byte x86-64 assembly (.s) file. - When assembled, the final statically linked binary x86-64 file is only 255 bytes.
- A complex program, like a full Bubble Sort algorithm, produces a final statically linked executable of just 1.1 kilobytes.
HTLL targets x86-64, AArch64 (ARM), and the Oryx VM.
- Rules
- The "Types Are a Lie" Doctrine
- File Structure: Includes, Globals, and
main - Variables, Scope, & The Double-Init Law
- Expressions & Bitwise Math (Strict Left-to-Right)
- Arrays: The Universal Byte Buffer
- Control Flow: Conditionals & Jumps
- Control Flow: Loops (Standard, Array, Infinite)
- Functions: Declarations, Returns, & The Parameter Copy Law
- Terminal & File I/O
- System Features: Command Line Args & Syscalls
- Inline Assembly & Target Architecture Blocks
- Function Parameters, Return Values & The Loop Return Ban
- Example: Bubble Sort
- HTLL Quirks & Gotchas: The Survival Guide
- Integers (
int):- 64-bit signed integer.
- Global Scope Rule: All variables defined anywhere (except function params) are global and initialized only once.
- Arrays (
arr):- Universal dynamic byte buffers.
- Can hold ASCII text, integers, or raw data.
- The
raxRegister:- This is the volatile "working memory" of the compiler.
- Behavior: It is overwritten by almost every operation (
.size,.index, math, etc.). - Rule: Use the value in
raximmediately or store it. Do not expect it to persist.
- Operator Spacing: You must put a space around every single binary operator.
- Correct:
int x := 0 - Incorrect:
int x:=0 - Correct:
x += 10 - Incorrect:
x+=10
- Correct:
- Control Flow Spacing: You must put a space between the keyword and the opening parenthesis/brace.
- Correct:
if (x = 1) { - Incorrect:
if(x=1){
- Correct:
- Unary Exception:
++and--are the only operators that stick to the variable.- Correct:
x++
- Correct:
- Comments: Require a preceding space if on the same line (e.g.,
code ; comment).
- Rule: Expressions are limited. ONLY After assignment operators (
:=,+=, etc.) and thereturnkeyword.- Here are all the operators:
+,-,*,//,%,<<,>>,&,|,^ - Legal:
z := x + y - Legal:
return x + y - Illegal:
z := x + (y * 5 + 7) - Illegal:
if (x + 1 > 5) - Illegal:
funcName(x, y + 5) - Illegal:
arrName.index i + 5
- Here are all the operators:
- Allowed Math Operations:
+=,-=,*=,//=,%=,<<=,>>=,&=,|=,^=++(Increment),--(Decrement)- Assignment:
:=
- The 9-Level Limit: You can only nest blocks (Loops/Ifs) up to 9 levels deep.
- Loops:
- Syntax:
Loop, <count> {,Loop, arrName.size {,Loop, rax {, orLoop {(infinite). - Iterator:
A_Index(Always refers to the innermost active loop). continue: Skips to the next iteration of the innermost loop.break: Terminates the innermost loop.
- Syntax:
- Conditionals:
- Syntax:
if (<operand1> <operator> <operand2>) { - Operators:
=,!=,>,<,>=,<=(Spaces required!). - Logic: NO
else. NOand. NOor. Usegotoor nestedifblocks.
- Syntax:
- Jumps:
togo <label>: Defines a jump target. The label name must be unique.goto <label>: Unconditional jump to a previously definedtogolabel.
arradd <arr> <text>: Appends text. NO QUOTES ALLOWED. Trims trailing spaces..add <value>: Appends a raw 64-bit value or an ASCII code..pop: Removes the last element..clear: Resets array size to 0..size: Puts size intorax..index <i_val>: Puts value at indexi_valintorax..set <idx>, <val>: Overwrites value at index..copy <src_arr>: Copies contents of source array.
print(<value>): Prints integer value of a variable or register.print("text"): Prints a double-quoted string literal.print(""): Prints a newline (ASCII 10).print_rax_as_char: Interpretsraxas an ASCII code and prints the character.input <dest_arr>, <prompt_arr>: Displays prompt, reads stdin intodest_arr.
- Read:
fileread <dest_arr>, "file.txt"orfileread_arr <dest_arr>, <filename_arr> - Append:
fileappend "file.txt", <source_arr>orfileappend_arr <filename_arr>, <source_arr> - Delete:
filedelete "file.txt"orfiledelete_arr <filename_arr>
mainLabel: Mandatory if functions are used (must be placed after definitions).args_array: A special global array populated at startup with command-line arguments. Each argument is separated by a newline character (ASCII 10).Sleep, <ms>: Pauses execution via a kernel sleep syscall.
- No Booleans: Use integers (1/0).
- Manual ASCII:
arraddstrips spaces. Use.add 32to add a space to an array. - Limited Expressions: Never do math inside an
ifstatement or a function call. - The Parameter Copy Law: When passing an array parameter to another function, you MUST copy it to a local array first.
- The Double-Init Law: Local variables in functions retain their values. You MUST re-initialize them on every call if you need a clean state.
- Array Assignment: The
:=operator is ONLY used on an array to capture a function's return value. To copy one array to another, you MUST use.copy.
In low-level computing, distinct data types like "string" or "boolean" don't truly exist. There are only bytes of data in memory. A "type" is simply how the programmer decides to interpret those bytes at runtime. HTLL enforces this truth.
- Integers (
int): 64-bit signed integers. Booleans do not exist; use1and0. - Arrays (
arr): Universal, 0-indexed, dynamic byte buffers. You can fill them with ASCII values to act as "strings," or raw numbers to act as "lists." Each element is 64 bits. - No Negative Indices: HTLL does not support negative numbers in its core logic design. Indices and lengths are strictly positive.
An HTLL file has a strict hierarchical structure.
- Includes: Must be at the very top.
- Global Variables: Must be declared before any functions or
main. - Functions: Defined next.
main: The mandatory entry point if functions are present.
include "HTLL_Lib.htll"
; 1. Globals
int g_system_status := 1
; 2. Functions
func do_nothing() {
return 0
}
; 3. Main Entry
main
print("System starting...")Variables in HTLL are strictly managed. Understanding scope and memory allocation is critical.
Any variable declared outside a function or main is global. To prevent compiler errors, all global variables MUST be prefixed with g_ or global_.
You cannot evaluate expressions on the same line as a declaration. Declarations only accept a single numeric literal.
- Illegal:
int x := 5 + 5 - Legal:
int x := 5then on a new line:x += 5
Local variables inside main or a func are allocated statically. They are only zeroed out once when the program launches. If a function is called multiple times, its local variables will retain their values from the previous call. To ensure a clean state, you MUST declare the variable and immediately re‑assign it.
❌ Without double‑init (unexpected behavior):
func increment_test() {
int counter := 0 ; allocated once, initialised to 0 only at program start
counter += 1
print(counter)
}
main
increment_test() ; Prints 1
increment_test() ; Prints 2 (counter kept its value from last call)✅ With double‑init (correct behavior):
func increment_test() {
int counter := 0 ; allocation (occurs once)
counter := 0 ; DOUBLE-INIT: forces reset on every call
counter += 1
print(counter)
}
main
increment_test() ; Prints 1
increment_test() ; Prints 1 (because counter was reset)HTLL supports robust, multi-operand mathematical and bitwise expressions on assignments and return statements.
CRITICAL RULE: No Parentheses Allowed.
Expressions are evaluated strictly from Left to Right. Standard order of operations (PEMDAS) does not apply. 10 + 5 * 2 is evaluated as (10 + 5) * 2 = 30.
- Math:
+,-,*,//(Floor Division),%(Modulo) - Bitwise:
&(AND),|(OR),^(XOR),<<(Left Shift),>>(Right Shift) - Compound:
+=,-=,*=,//=,%=,&=,|=,^=,<<=,>>= - Unary:
++,--(Must be directly attached:x++)
int calc := 0
calc := 0
; Evaluated as: (((10 + 5) - 2) * 3) // 2
calc := 10 + 5 - 2 * 3 // 2
print(calc) ; Prints 19Arrays are 0-indexed dynamic buffers.
arradd <arr> <text>: Appends text directly. NO QUOTES ALLOWED..add <val>: Appends a 64-bit integer or ASCII code (.add 32for space)..pop: Removes the last element..clear: Empties the array..size: Calculates length and puts it intorax..index <idx>: Retrieves value at index and puts it intorax..set <idx>, <val>: Overwrites value at index..copy <src>: Deep copies from another array.
The := operator is only used to assign an array when capturing the return value of a function. To copy the contents of one existing array to another, you must use the .copy method. Direct assignment arr1 := arr2 is illegal.
Rule: You must cache rax into a variable immediately after calling .index or .size before using it.
arr numbers
numbers.add 50
numbers.add 150
numbers.index 1
int val := 0
val := rax ; Cache RAX immediately!
val += 50
print(val) ; Prints 200Conditionals require strict spacing around operators: =, !=, >, <, >=, <=.
There is no else, and, or or keyword. You must use nested if statements or jumps.
CRITICAL RULE: You cannot use rax or array methods directly inside an if ( ) statement.
HTLL uses togo and goto for non-linear control flow.
togo <label>: Defines a jump target. A label is a unique identifier that marks a line of code.goto <label>: Performs an unconditional jump to the specifiedtogolabel.
int status := 0
status := 0
if (status = 1) {
print("Status is 1")
goto skip_else
}
; This code runs only if the 'if' is false
print("Status is NOT 1") ; The 'else' block
togo skip_else
print("Done.")HTLL has a maximum nesting limit of 9 levels. The current iterator is always A_Index (0-indexed).
Loop, <count> {- Fixed iterations.Loop, <arr>.size {- Automatic array iteration.Loop {- Infinite loop (requires manualbreak).
The break and continue keywords only affect the innermost active loop.
Loop, 3 {
if (A_Index = 1) {
continue ; Skips index 1 of the outer loop
}
print("Outer:")
print(A_Index)
}Functions establish true local scope.
The syntax for defining a function is:
func [return_type] <name>([type] <param1>, [type] <param2>, ...)
- Return Type:
intorarr. If omitted, the function cannot use thereturnkeyword. - Parameters: Comma-separated list. Parameter type is optional; if omitted, it defaults to
int.
The return keyword sends a value back to the caller.
- Syntax:
return <value_or_expression> - Rule: Expressions are allowed after
returnand are evaluated strictly Left-to-Right. - A function declared with a return type (
intorarr) must end with areturnstatement.
If your function receives an array as a parameter, and you want to pass that data into another function, you must not pass the parameter directly. You must .copy it to a newly declared local array, and pass the copy. This prevents the compiler from destroying the reference.
func process_text(arr text) {
print("Processing...")
}
func pass_text_down(arr input_param) {
arr safe_copy
safe_copy.copy input_param ; MUST BE COPIED!
process_text(safe_copy) ; SAFE TO PASS
}
main
arr my_str
arradd my_str Data
pass_text_down(my_str)func arr create_buffer() {
arr result
result.clear
result.add 99
return result
}
main
arr my_res
my_res := create_buffer() ; Legal use of := with an array
my_res.index 0
int v := 0
v := rax
print(v) ; Prints 99print(<val>): Prints integer.print("<literal>"): Prints quoted string.print(""): Prints newline (ASCII 10).input <dest>, <prompt>: Reads stdin.
File Syscalls: fileread, fileappend, filedelete (append _arr to use array names instead of literals).
args_array: A reserved global array automatically populated with OS arguments separated by ASCII 10.Sleep, <ms>: Kernel pause.
Embed native assembly or language bytecode. The compiler intelligently strips blocks that don't match the build target.
Rule: Delimiters (___start <target> / ___end <target>) must be on their own completely empty lines.
___start x86-64
mov rdi, 1
___end x86-64
___start arm
mov x0, #1
___end armarrmay appear as a type keyword in the parameter list to indicate an array parameter.- Integer parameters have no type keyword; they are just names.
- Examples:
func process(arr data) ; array parameter func calc(a, b, arr buffer) ; two ints, one array
-
A function may be prefixed with
arrto indicate it returns an array.- If no
arrprefix is used, the function returns an integer viarax.
- If no
-
To return a value, use
return <expr>(simple expression or variable name). -
The returned value is placed in the
raxregister. -
Capturing integer return:
Call the function, then immediately assignraxto an integer variable.
Declaration and assignment must be separate steps.func add(a, b) { return a + b } main int sum ; declare first add(5, 3) sum := rax ; assign after call -
Capturing array return:
Use thearrprefix in the function signature. Call the function and use the:=operator to assign the result to an array variable.
Again, declare the array first, then assign separately.func arr make_buffer() { arr buf buf.add 42 return buf } main arr my_buf ; declare first my_buf := make_buffer() ; assign after call
-
Do not place a
returnstatement inside any loop (Loop,Loop, <count>,Loop, <arr>.size, infinite loop). -
Why: It compiles but will cause a segmentation fault at runtime.
-
Correct pattern: Use a flag variable, break out of the loop, then return after the loop.
❌ Illegal (segfault):
func find_zero(arr data) { Loop, data.size { data.index A_Index int v := 0 v := rax if (v = 0) { return 1 ; FORBIDDEN You are going to get a segmentation fault most likely. } } return 0 }✅ Correct:
func find_zero(arr data) { int found := 0 found := 0 int v := 0 v := 0 Loop, data.size { data.index A_Index v := rax if (v = 0) { found := 1 break } } return found }
- The Parameter Copy Law still applies: if you pass an array parameter to another function, you must copy it first with
.copy. raxis clobbered by many operations; capture it immediately after a function call or array method.- All variable declarations (
int,arr) must be done on their own line before any assignment to them.
This example demonstrates a complete bubble sort implementation that sorts a small array of integers. When compiled for x86‑64 Linux using FASM, the resulting statically linked executable is just 1.1 KiB (1109 bytes).
arr nums
nums.add 90
nums.add 11
nums.add 64
int n := 3 ; size
int i := 0
int temp := 0
int a := 0
int j := 0
int jp1 := 0
int limit := 0
Loop, n {
limit := n - i - 1
Loop, limit {
j := A_Index
jp1 := j + 1
nums.index j
temp := rax
nums.index jp1
a := rax
if (temp > a) {
nums.set j, a
nums.set jp1, temp
}
}
i += 1
}
Loop, n {
nums.index A_Index
print(rax)
}This program creates an array with three integers, sorts them in ascending order, and prints each value on a new line. The binary size is a testament to HTLL’s zero‑bloat philosophy.
If your code is failing to compile or causing a segmentation fault, you have violated one of these core laws:
- THE PARAMETER COPY LAW: You cannot pass an array parameter directly into another function (
func1(arr p) { func2(p) }). You MUST.copyit to a local array first. - THE DOUBLE-INIT LAW: You must re-initialize local variables (
x := 0) inside functions if you need a clean state, otherwise they retain data from previous calls. - STRICT SPACING: You MUST put spaces around binary operators (
x += 5) and after control keywords (if (,Loop {). - NO EXPRESSIONS IN ARGS/CONDITIONALS:
MyFunc(x + 1)andif (x + 1 > 5)are illegal. Calculate the value into a variable first. - NO PARENTHESES IN MATH: Expressions are strictly Left-to-Right.
()does not exist. - CACHE
raxIMMEDIATELY: After.sizeor.index, storeraxin a variable. Do not useraxdirectly in anifstatement. arraddUSES NO QUOTES:arradd my_arr Hellois correct. To append a space, use.add 32.togoDEFINES A LABEL:togo my_labelcreates a jump target.goto my_labeljumps to it.- ARRAY ASSIGNMENT IS FORBIDDEN: You cannot write
arr1 := arr2. Usearr1.copy arr2. The only time:=is used with an array is to receive a function's return value:my_arr := func_that_returns_arr(). - GLOBALS NEED A PREFIX: All top-level variables must start with
g_orglobal_. - NO
else,and,or: These constructs do not exist. Build equivalent logic usinggotoand nestedifstatements. This is intentional. - NESTING IS FINITE: You have 9 levels. If you need more, your code is too complex and must be refactored.
- int size is forbidden: You cannot declare an integer or an array with the name size. This is going to break everything. Size is a reserved keyword. Do not use it as a variable name or an array name. Same with the str. That's also another keyword and a name and you must not use it.
In conclusion: C is bloated.
You have been told your whole life that C is the pinnacle of minimalism. This is a lie. The dependency on libc and the complexity of its toolchains introduce unnecessary bloat.
HTLL is an experiment in true minimalism. The trade-off is its specificity: it only targets a few architectures (x86-64 and AArch64) and one operating system (Linux). But within that domain, it offers unparalleled control and efficiency.