Language Specification

Lambda C is a subset of C89 designed for embedded scripting. This page defines the surface the compiler (lcvmc) accepts and the runtime it targets — the supported types, operators, control flow, and built-in library, together with the deliberate omissions and the implementation limits.

For how the language is compiled and executed, see Architecture. For an API-and-capability catalog, see Features.


Data Types

TypeSizeNotes
char1 Bsigned
unsigned char1 B
short2 B
unsigned short2 B
int4 B
unsigned int4 B
long4 B / 8 Btarget-dependent (LP64-aware)
unsigned long4 B / 8 B
float4 B
double8 B
voidfunction return / void *
pointer4 B / 8 Bint *, char **, … (target-dependent)

Type widths are fixed by the target configuration at build time (LCVM_TARGET_*_SIZE in common.h): on a 64-bit host long and pointers are 8 bytes; on a 32-bit MCU they are 4. long long, _Bool, and the C99 fixed-width types are not supported.


Operators

CategoryOperators
Arithmetic+ - * / %
Comparison== != < <= > >=
Logical&& || !
Bitwise& | ^ ~ << >>
Assignment= += -= *= /= %= &= |= ^= <<= >>=
Increment / decrement++ -- (prefix and postfix)
Unary& (address-of), * (dereference), -, +, !, ~
Member access. -> []
Othersizeof, ? : (ternary), , (comma), (type) cast

Control Flow

if (cond) { ... } else { ... }
while (cond) { ... }
do { ... } while (cond);
for (init; cond; step) { ... }
switch (expr) { case N: ... break; default: ... }
break;
continue;
return expr;
goto label;

Declarations and Definitions

/* Global and static variables */
int  global_var;
static int static_var;
char *string_ptr = "hello";

/* Functions: definition and prototype.
   No fixed parameter-count limit (bounded only by the stack). */
int func(int a, int b) { return a + b; }
int external_func(int x);

/* Arrays, including multi-dimensional (up to 6 dimensions) */
int  array[100];
char buffer[256];
int  matrix[10][10];
int  cube[4][4][4];

/* Pointers, including function pointers */
int  *ptr;
char **argv;
int  (*callback)(int);

/* Structs and unions */
struct Point { int x; int y; };
struct Point  p;
struct Point *pp;

union IntBytes { int value; char bytes[4]; };

/* Enums: explicit values and anonymous enums */
enum Color     { RED, GREEN, BLUE };
enum ErrorCode { OK = 0, ERR = -1, ERR_IO = -2 };
enum           { FLAG_A = 1, FLAG_B = 2 };

/* typedef */
typedef int INT32;
typedef struct Point Point_t;

/* const / volatile are recognized but ignored (treated as plain types) */
const int LIMIT = 100;
volatile int flag;

Type Casts

Casts between integers, floating point, and pointers are supported:

char   c = (char)i;       /* int  -> char (truncating)  */
double d = (double)i;     /* int  -> double             */
int   *p = (int *)ptr;    /* pointer reinterpretation   */
char  *b = (char *)&v;    /* byte view of an int        */

One caveat: an int-to-double widening cast in the middle of an expression is not applied. Perform the arithmetic with floating-point operands, or assign through a double variable first.


Preprocessor

A small inline preprocessor is handled by the lexer. It is not a full C preprocessor — it exists to compose scripts and gate target code, not to metaprogram.

Supported:

#include "header.h"
#include <stdio.h>        /* built-in header (see Standard Library) */

#define MACRO 100         /* object-like macros: numeric / char / string */

#ifdef MACRO
#endif

#ifndef MACRO
#else
#endif

Not supported: #if / #elif, #undef, #pragma, #error / #warning, the ## (token-paste) and # (stringize) operators, defined(), and function-like macros with parameters. A #define body that is not a simple numeric, character, or string constant is rejected.


Standard Library

Scripts reach the runtime through built-in functions exposed via the headers under include/lcvm/. The functions below are implemented as VM intrinsics; #includeing the corresponding header makes their prototypes visible.

stdio.h printf, sprintf, fprintf, scanf, sscanf, fscanf, getchar, putchar, getc, putc, fopen, fclose, fgetc, fputc, fgets, fputs.

stdlib.h malloc, free, atoi, qsort, exit, abort. The managed heap is a bump/region allocator — malloc allocates, but free of an individual block is a no-op. Bulk reclamation is done with the heap_mark / heap_release extension (below). realloc is not provided.

string.h strlen, strcpy, strcat, strncat, strcmp, strncmp, memcpy, memset.

math.h (all operate on double) sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, exp, log, log10, pow, sqrt, ceil, floor, fabs, fmod.

time.h clock, time, difftime, mktime, gmtime, localtime, asctime, ctime, strftime.

search.h / glob.h / regex.h bf_search, bf_re_search, bm_search, bm_re_search (brute-force / Boyer–Moore string and regex search), amatch (glob matcher), rx_search (regex search).

Lambda C Extensions

Beyond the C library, the runtime exposes intrinsics for embedded orchestration:

FunctionPurpose
heap_mark()Record the current heap position (a watermark)
heap_release(mark)Free everything allocated since mark in one step
millis()Milliseconds elapsed (monotonic timer)
lcvm_reset(vm)Request a VM self-reset
vm_reload(reason)Request a hot-reload of the script
vm_interrupt(flag)Set the cooperative-interrupt flag

Headers Without Functions

limits.h and float.h provide only constants (INT_MAX, DBL_DIG, …). ctype.h, assert.h, stdarg.h, setjmp.h, and signal.h are present as stubs and provide no working functions — setjmp / longjmp exist on the host side only. <stddef.h>, <stdint.h>, <stdbool.h>, <errno.h>, and <locale.h> are absent; use 0 for NULL, int for bool, and an explicit typedef for fixed-width integers.


Static Type Mode (-Os)

-Os compiles a module in static type mode, which drops the per-value runtime type tag and the type stack (the bytecode is flagged LCVM_LCBC_FLAG_STATIC_TYPES). This roughly doubles throughput and shrinks stack usage, at the cost of runtime type information.

The mode targets integer and pointer control code. Floating-point (float / double) relies on the type information this mode removes, so -Os modules should be restricted to integers and pointers; built-in functions such as printf continue to work in the normal (non--Os) mode.

lcvmc -Os -oc output.lcbc source.c   # compile in static type mode
lcvmc -rcn output.lcbc               # run the compact bytecode

See Architecture for the benchmarks and the technical background.


Implementation Limits

ItemLimitConfigurable via
Stack size100 KBSTACKSIZE
Global area100 KBGLOBALSIZE
Managed heap256 KBLCVM_HEAP_MAX
VM re-entry / nesting depth8LCVM_MAX_VM_DEPTH
Registered FFI functions64LCVM_FFI_MAX_FUNCTIONS
VM registers13REGSIZE
Identifier length30IDSIZE
String / token buffer2000STRSIZE
Array dimensions6ARR_MAX
Function parametersunboundedstack-limited

The loader additionally rejects bytecode that exceeds its structural ceilings: 65536 instructions, 4096 constants, and 256 KB each of string table and global area.


Notes on Specific Features

Aggregate Initialization

Scalar and string initialization works; struct and array initializer lists are only partially supported. Prefer field-by-field assignment, and confine array initializer lists to globals.

struct Point p; p.x = 10; p.y = 20;   /* recommended */
char str[] = "hello";                 /* OK */
int  arr[] = { 1, 2, 3 };             /* OK at global scope */

String Literals

Adjacent string literals are concatenated:

char *s = "hello" "world";            /* "helloworld" */

Variable-Length Arrays

VLAs are not supported; array sizes must be compile-time constants.


Unsupported Language Features

FeatureReason
Bit-fields (struct { int x:4; })not implemented
User-defined variadic functions (...)printf etc. are built-in intrinsics
Compound literals ((int[]){1,2,3})not implemented
Designated initializers (.x = 1)not implemented
_Bool, inline, restrict, long longC99
Full C preprocessor (#if, function macros, …)see Preprocessor

Coverage

Lambda C implements roughly 80% of C89 — enough for embedded control and orchestration scripts — and deliberately omits the rest: no closures, no GC, no dynamic typing, no exceptions. That absence is what keeps the runtime small enough for ROM deployment and predictable enough for hard real-time control loops.

See Also