Skip to content
ALB Wiki

Diff — ALB C backend

Comparing rev 5 → rev 6

{{infobox
| title = ALB C backend
| target_flag = `-t c`
| produces = C89 source (.c) + staged build scripts
| output = Native .exe, shared library (.dll/.so/.dylib)
| compiler = gcc `-std=c89 -O2` · MSVC `cl` · any cc
| emitter_unit = `Emit_Native_C` (emit_native_c.adb)
| status = Most complete backend; `--build` supported
}}
The **ALB C backend** is [[AdaLogic BASIC]]'s most complete emitter: it turns an ALB program into **C89 source code** — deliberately the oldest, most portable C dialect — which then compiles on essentially any platform with essentially any compiler. Where [[ALB FASM backend|FASM]] is the precision instrument and [[ALB FASM16 backend|FASM16]] is the retro floor, C is the *everywhere* backend.
```alb One-liner
albt tool.alb -t c --build
→ c_tool/tool.c + build scripts → gcc/cl/cc → tool.exe
```
# Why C89, on purpose
The generated file targets **C89 — ANSI C from 1989** — and the toolchain's own build script proves it: `gcc -std=c89 -O2`. That choice is a feature, not a compromise: C89 compiles on tiny embedded compilers, ancient vendor toolchains, and modern GCC/Clang/MSVC alike. ALB even **typedefs the fixed-width types itself** (`int8_t`…`uint64_t`, handling MSVC vs POSIX spellings) rather than assuming `stdint.h` exists — which it didn't, until C99.
The boilerplate is honest about what it needs:
```c
#include <stdio.h>
#include <stddef.h>   /* OFFSETOF forge */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <setjmp.h>   /* TRY/CATCH */
#include <limits.h>
```
# The platform HAL
Immediately after the boilerplate comes a hardware/OS abstraction layer — emitted as ordinary `#if` blocks:
| Platform | Define | What gets included |
| --- | --- | --- |
| Windows | `ALB_PLAT_WIN` | `windows.h`, winsock2, ws2tcpip |
| POSIX | `ALB_PLAT_POSIX` | unistd, pthread, semaphore, sockets, netinet/in |
So one emitted file carries **both** halves and the host compiler keeps the one that applies — real threads, real sockets, real timers on both families, selected at compile time. On Windows the generated `main()` even enables `ENABLE_VIRTUAL_TERMINAL_PROCESSING` first, because ALB learned the hard way that ANSI colors bleed through on Wine without it.
# TRY/CATCH is setjmp — of course it is
ALB's structured error handling lowers to the classic C mechanism, done properly:
```c
static jmp_buf ALB_Err_Stack[256]; /* Deeply nestable */
if (setjmp(ALB_Err_Stack[++ALB_Err_SP]) == 0) {
  /* TRY body */
  longjmp(ALB_Err_Stack[ALB_Err_SP], 1); /* THROW */
}
```
A 256-deep jump stack means TRY/CATCH nests 256 levels — far past any reasonable program, which is the correct kind of overkill.
# Da Dumptruck — memory management as data
The C backend injects its object manager as ordinary code you can read:
```c
#define ALB_GC_MAX_NODES 1024
typedef struct { uint8_t Alive; uint8_t Refs;
                 uint64_t Child_1; uint64_t Child_2; } ALB_GC_Node;
ALB_GC_Node ALB_GC_Grid[ALB_GC_MAX_NODES + 1] = {0};
```
A 1024-node grid. `CLAIM` allocates a node and hands you its index; `DROP` decrements the refcount; `BIND` links up to two children; `SWEEP(chunk)` walks an incrementing cursor releasing zero-ref nodes and cascading the decrements — **incremental collection**, so a sweep never stops the world, it just takes out a bounded chunk of garbage. The same design in miniature runs on [[ALB FASM16 backend|16-bit DOS]].
# The full feature surface
The C backend implements the largest slice of the language — including things that exist nowhere else in the pipeline:
- **Complete file I/O** — open/read/write/close/len/seek as a full suite
- **The whole draw suite** — put-pixel, line, rect, circle, triangle, fills, image blit, sound load/play (SDL3-linked)
- **FFI in both directions** — `IMPORT_C`/`IMPORT_DLL`/`IMPORT_SO`/`IMPORT_DYLIB` lower to real header includes and a dynamic loader prelude; ALB can also *emit* shared libraries (`-shared`/`-fPIC`/`-dynamiclib`)
- **Bare metal** — `PEEK`/`POKE`/deref, for when the program really does need to touch the address
- **Compile-time oracles** — `SIZEOF`/`OFFSETOF` evaluated through the real C operators
- **The Prolog machinery** — fact registration, queries, and the **CUT operator** (`Emit_Cut_Operator`) for committed-choice logic
- **Contracts** — `REQUIRE`/`ENSURE` and runtime asserts
- **Branchless mode** — the same mask-select optimization as FASM, expressed in C
- **Temporal variables and reversible blocks** — history rings and undoable arithmetic, in portable C
- **Real concurrency** — `ALB_Init_Threads()` (the emitted comment reads *"Spark da forge!"*), genuine SPAWN dispatch and atomic sections — where FASM documents itself synchronous, C actually parallelizes
# The learning runtime, in C
`NEURAL TOPOLOGY`, `TRAIN NETWORK`, `INFER NETWORK`, `MARKOV MODEL`, and `PREDICT MARKOV` are language-level statements, and the C backend implements them too — `albt`'s `Emit_C_Extended_Runtime` forges the same learning layer as `static` C functions, injected only when a model or topology was actually declared:
- **`ALB_Markov_Predict(matrix, states, current_state)`** — the argmax-over-a-transition-row predictor
- **`ALB_NN_Apply_Activation`** — a `switch` on the activation code (linear, ReLU, step, scaled-tanh)
- **`ALB_NN_Infer(meta, input, output)`** — the elegant part: a whole network described by one flat `u64` **meta-descriptor** — `meta[0]` is the layer count, `meta[4..11]` carry pointers to the size list, activation codes, per-layer offsets, weights, biases, and scratch state
- **`ALB_NN_Train(meta, train_data, expect_data, epochs)`** — the same perceptron-style delta rule as the asm twin: sign-of-error nudges on the output layer's bias and weights
Initial weights come from `albt`'s deterministic `Seed_Neural_Weight` — structured compile-time guesses, never RNG — so a C build and a FASM build of the same program start from identical brains. (The [[ALB FASM backend]] keeps one learning feature to itself: `EMOTION` vectors and `PREDICT MARKOV … WITH EMOTION`.)
```note One emitter trick worth seeing
`Emit_Program_Start` emits boilerplate, HAL, routines, and concurrency — then *remembers its position* (`Headers_End_Idx := Global_Len`) so forward declarations discovered later can be injected back into the header region. The buffer is a scratchpad, not a queue.
```
# Not one output — a build kit
`-t c` doesn't just write a .c file. It stages a portable **build kit** — scripts for whatever machine you carry the folder to:
| Script | Toolchain |
| --- | --- |
| `build_windows_gcc.bat` | `gcc -std=c89 -O2 … -lSDL3 -lws2_32 -lwinmm -luser32 -lgdi32 …` |
| `build_windows_cl.bat` | MSVC `cl /nologo` + SDL3 import libs |
| `build_shared_windows.bat` | DLL — gcc preferred, `cl /LD` fallback |
| `build_shared_posix.sh` | `cc -shared -fPIC … -ldl -lpthread -lm` |
| `build_shared_macos.sh` | `cc -dynamiclib` → .dylib |
Emit once on any machine; compile on whichever you deploy to. That is the whole philosophy of the backend in one folder.
# Under the hood
`Emit_Native_C` (~4,500 lines, SPARK-annotated Ada like all its siblings) implements the shared `Emit_*` surface over the same two-buffer design (`Buffer_Global` for declarations, `Buffer_Main` for the `main()` body). Emitted trees land in `c_&lt;name&gt;/`.
The interesting part is the *division of labor*: albt hands the emitter an AST and the emitter streams text — C's job is to be readable, portable, and complete enough that the *real* optimizer (gcc `-O2`) can take it from there. Three different strategies for three different backends: FASM micro-optimizes in its macro layer, FASM16 strips down to fit, and C trusts the optimizing compiler the world spent fifty years building.
# See also
- [[ALB FASM backend]] — the assembly sibling
- [[ALB FASM16 backend]] — the 16-bit sibling
- [[AdaLogic BASIC]] — the language
- [[History of Programming]] — C's place in the arc (1972, Ritchie, Unix)
- [[COBOL]] · [[FORTRAN]] · [[ALGOL]] — the other languages in the backend family tree
# References
-
1. ALB Handbook — alb-lang.org/handbook — the language reference for all backends.
-
2. ALB Technology page — alb-lang.org/technology — the sixteen-backend matrix.
+
1. ALB Handbook — [alb-lang.org/handbook](https://alb-lang.org/handbook) — the language reference for all backends.
+
2. ALB Technology page — [alb-lang.org/technology](https://alb-lang.org/technology) — the sixteen-backend matrix.
3. ALB source — github.com/rockyoliver454/AdaLogic-BASIC-ALB-Programming-Language — the emitter sources are Apache 2.0.
[[Category:Backends]]
[[Category:AdaLogic BASIC]]