| | {{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_<name>/`. |
| | |
| | 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]] |
| | |