Files
armorpaint/base/sources/libs/minic.c
T
2026-08-21 22:35:30 +02:00

2654 lines
84 KiB
C

// Minimal C interpreter
#include "minic.h"
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
// ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║
// ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║
// ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║
// ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║
// ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝
#define MINIC_TOK_LIST \
X(TOK_INT, "'int'") \
X(TOK_FLOAT, "'float'") \
X(TOK_CHAR, "'char'") \
X(TOK_DOUBLE, "'double'") \
X(TOK_BOOL, "'bool'") \
X(TOK_RETURN, "'return'") \
X(TOK_IF, "'if'") \
X(TOK_ELSE, "'else'") \
X(TOK_WHILE, "'while'") \
X(TOK_FOR, "'for'") \
X(TOK_BREAK, "'break'") X(TOK_CONTINUE, "'continue'") X(TOK_STRUCT, "'struct'") X(TOK_TYPEDEF, "'typedef'") X(TOK_ENUM, "'enum'") X(TOK_VOID, "'void'") \
X(TOK_IDENT, "identifier") X(TOK_NUMBER, "number") X(TOK_CHAR_LIT, "char literal") X(TOK_STR_LIT, "string literal") X(TOK_LPAREN, "'('") \
X(TOK_RPAREN, "')'") X(TOK_LBRACE, "'{'") X(TOK_RBRACE, "'}'") X(TOK_LBRACKET, "'['") X(TOK_RBRACKET, "']'") X(TOK_SEMICOLON, "';'") \
X(TOK_COMMA, "','") X(TOK_ASSIGN, "'='") X(TOK_PLUS_ASSIGN, "'+='") X(TOK_MINUS_ASSIGN, "'-='") X(TOK_MUL_ASSIGN, "'*='") \
X(TOK_DIV_ASSIGN, "'/='") X(TOK_MOD_ASSIGN, "'%='") X(TOK_SHL_ASSIGN, "'<<='") X(TOK_SHR_ASSIGN, "'>>='") X(TOK_AND_ASSIGN, "'&='") \
X(TOK_OR_ASSIGN, "'|='") X(TOK_XOR_ASSIGN, "'^='") X(TOK_EQ, "'=='") X(TOK_NEQ, "'!='") X(TOK_LT, "'<'") X(TOK_GT, "'>'") \
X(TOK_LE, "'<='") X(TOK_GE, "'>='") X(TOK_AND, "'&&'") X(TOK_OR, "'||'") X(TOK_NOT, "'!'") X(TOK_AMP, "'&'") X(TOK_PLUS, "'+'") \
X(TOK_MINUS, "'-'") X(TOK_INC, "'++'") X(TOK_DEC, "'--'") X(TOK_STAR, "'*'") X(TOK_SLASH, "'/'") X(TOK_PERCENT, "'%'") \
X(TOK_SHL, "'<<'") X(TOK_SHR, "'>>'") X(TOK_BITOR, "'|'") X(TOK_XOR, "'^'") X(TOK_BITNOT, "'~'") X(TOK_DOT, "'.'") \
X(TOK_ARROW, "'->'") X(TOK_EOF, "end of file")
typedef enum {
#define X(t, s) t,
MINIC_TOK_LIST
#undef X
} minic_tok_type_t;
static const char *minic_tok_names[] = {
#define X(t, s) s,
MINIC_TOK_LIST
#undef X
};
typedef struct {
minic_tok_type_t type;
char text[MINIC_MAX_NAME];
minic_val_t val; // TOK_NUMBER, TOK_CHAR_LIT, TOK_STR_LIT
} minic_token_t;
typedef struct {
const char *src;
int pos;
minic_token_t cur;
} minic_lexer_t;
static minic_u8 *minic_active_mem = NULL;
static int *minic_active_mem_used = NULL;
static const struct {
const char *kw;
minic_tok_type_t tok;
} minic_keywords[] = {
{"int", TOK_INT}, {"float", TOK_FLOAT}, {"char", TOK_CHAR}, {"double", TOK_DOUBLE}, {"bool", TOK_BOOL}, {"void", TOK_VOID},
{"return", TOK_RETURN}, {"if", TOK_IF}, {"else", TOK_ELSE}, {"while", TOK_WHILE}, {"for", TOK_FOR}, {"break", TOK_BREAK},
{"continue", TOK_CONTINUE}, {"struct", TOK_STRUCT}, {"typedef", TOK_TYPEDEF}, {"enum", TOK_ENUM},
};
// Three-char operators are matched before the two-char table below
static const struct {
char a, b, c;
minic_tok_type_t tok;
} minic_ops3[] = {
{'<', '<', '=', TOK_SHL_ASSIGN},
{'>', '>', '=', TOK_SHR_ASSIGN},
};
// Two-char operators must come before their one-char prefixes
static const struct {
char a, b;
minic_tok_type_t tok;
} minic_ops[] = {
{'+', '+', TOK_INC}, {'+', '=', TOK_PLUS_ASSIGN}, {'-', '-', TOK_DEC}, {'-', '=', TOK_MINUS_ASSIGN},
{'-', '>', TOK_ARROW}, {'*', '=', TOK_MUL_ASSIGN}, {'/', '=', TOK_DIV_ASSIGN}, {'=', '=', TOK_EQ},
{'!', '=', TOK_NEQ}, {'&', '&', TOK_AND}, {'|', '|', TOK_OR}, {'<', '=', TOK_LE},
{'>', '=', TOK_GE}, {'<', '<', TOK_SHL}, {'>', '>', TOK_SHR}, {'%', '=', TOK_MOD_ASSIGN},
{'&', '=', TOK_AND_ASSIGN}, {'|', '=', TOK_OR_ASSIGN}, {'^', '=', TOK_XOR_ASSIGN}, {'+', 0, TOK_PLUS},
{'-', 0, TOK_MINUS}, {'*', 0, TOK_STAR}, {'/', 0, TOK_SLASH}, {'%', 0, TOK_PERCENT},
{'=', 0, TOK_ASSIGN}, {'!', 0, TOK_NOT}, {'&', 0, TOK_AMP}, {'|', 0, TOK_BITOR},
{'^', 0, TOK_XOR}, {'~', 0, TOK_BITNOT}, {'<', 0, TOK_LT}, {'>', 0, TOK_GT},
{'(', 0, TOK_LPAREN}, {')', 0, TOK_RPAREN}, {'{', 0, TOK_LBRACE}, {'}', 0, TOK_RBRACE},
{'[', 0, TOK_LBRACKET}, {']', 0, TOK_RBRACKET}, {';', 0, TOK_SEMICOLON}, {',', 0, TOK_COMMA},
{'.', 0, TOK_DOT},
};
static int minic_escape(char c) {
switch (c) {
case 'n':
return '\n';
case 't':
return '\t';
case 'r':
return '\r';
case '\\':
return '\\';
case '"':
return '"';
case '\'':
return '\'';
default:
return '\0';
}
}
static void minic_lex_next(minic_lexer_t *l) {
for (;;) {
// Skip whitespace, comments and preprocessor directives
while (l->src[l->pos] != '\0' && isspace((unsigned char)l->src[l->pos])) {
l->pos++;
}
if ((l->src[l->pos] == '/' && l->src[l->pos + 1] == '/') || l->src[l->pos] == '#') {
while (l->src[l->pos] != '\0' && l->src[l->pos] != '\n') {
l->pos++;
}
continue;
}
if (l->src[l->pos] == '/' && l->src[l->pos + 1] == '*') {
l->pos += 2;
while (l->src[l->pos] != '\0' && !(l->src[l->pos] == '*' && l->src[l->pos + 1] == '/')) {
l->pos++;
}
if (l->src[l->pos] != '\0') {
l->pos += 2;
}
continue;
}
char c = l->src[l->pos];
if (c == '\0') {
l->cur.type = TOK_EOF;
return;
}
if (c == '0' && (l->src[l->pos + 1] == 'x' || l->src[l->pos + 1] == 'X')) {
l->pos += 2; // Consume '0x'
unsigned int n = 0;
while (isxdigit((unsigned char)l->src[l->pos])) {
char h = l->src[l->pos++];
int digit = (h >= '0' && h <= '9') ? h - '0' : (h >= 'a' && h <= 'f') ? h - 'a' + 10 : h - 'A' + 10;
n = n * 16 + digit;
}
l->cur.val = minic_val_int((int)n);
l->cur.type = TOK_NUMBER;
return;
}
if (isdigit((unsigned char)c)) {
double n = 0;
while (isdigit((unsigned char)l->src[l->pos])) {
n = n * 10 + (l->src[l->pos++] - '0');
}
bool is_float = false;
if (l->src[l->pos] == '.') {
l->pos++;
double frac = 0.1;
while (isdigit((unsigned char)l->src[l->pos])) {
n += (l->src[l->pos++] - '0') * frac;
frac *= 0.1;
}
is_float = true;
}
if (l->src[l->pos] == 'f' || l->src[l->pos] == 'F') {
l->pos++;
is_float = true;
}
l->cur.val = is_float ? minic_val_float((float)n) : minic_val_int((int)n);
l->cur.type = TOK_NUMBER;
return;
}
if (c == '"') {
l->pos++; // Consume opening '"'
// Write the string into the active context's arena
int start = (*minic_active_mem_used + 7) & ~7;
int wi = start;
while (l->src[l->pos] != '"' && l->src[l->pos] != '\0') {
char ch = l->src[l->pos++];
if (ch == '\\') {
char esc = l->src[l->pos++];
if (esc == '\n') {
continue; // Line continuation: backslash-newline, skip both
}
if (esc == '\r') { // Handle \r\n line endings
if (l->src[l->pos] == '\n') {
l->pos++;
}
continue;
}
ch = (char)minic_escape(esc);
}
minic_active_mem[wi++] = (minic_u8)ch;
}
minic_active_mem[wi++] = '\0';
*minic_active_mem_used = (wi + 7) & ~7;
if (l->src[l->pos] == '"') {
l->pos++; // Consume closing '"'
}
l->cur.type = TOK_STR_LIT;
l->cur.val = minic_val_ptr((void *)&minic_active_mem[start]);
return;
}
if (c == '\'') {
l->pos++; // Consume opening '
int v;
if (l->src[l->pos] == '\\') {
l->pos++;
v = minic_escape(l->src[l->pos++]);
}
else {
v = (unsigned char)l->src[l->pos++];
}
l->pos++; // Consume closing '
l->cur.type = TOK_CHAR_LIT;
l->cur.val = minic_val_int(v);
return;
}
if (isalpha((unsigned char)c) || c == '_') {
int i = 0;
while (isalnum((unsigned char)l->src[l->pos]) || l->src[l->pos] == '_') {
l->cur.text[i++] = l->src[l->pos++];
}
l->cur.text[i] = '\0';
for (size_t k = 0; k < sizeof(minic_keywords) / sizeof(minic_keywords[0]); ++k) {
if (strcmp(l->cur.text, minic_keywords[k].kw) == 0) {
l->cur.type = minic_keywords[k].tok;
return;
}
}
if (strcmp(l->cur.text, "true") == 0 || strcmp(l->cur.text, "false") == 0) {
l->cur.type = TOK_NUMBER;
l->cur.val = minic_val_int(l->cur.text[0] == 't');
return;
}
l->cur.type = TOK_IDENT;
return;
}
for (size_t k = 0; k < sizeof(minic_ops3) / sizeof(minic_ops3[0]); ++k) {
if (c == minic_ops3[k].a && l->src[l->pos + 1] == minic_ops3[k].b && l->src[l->pos + 2] == minic_ops3[k].c) {
l->pos += 3;
l->cur.type = minic_ops3[k].tok;
return;
}
}
for (size_t k = 0; k < sizeof(minic_ops) / sizeof(minic_ops[0]); ++k) {
if (c == minic_ops[k].a && (minic_ops[k].b == 0 || l->src[l->pos + 1] == minic_ops[k].b)) {
l->pos += minic_ops[k].b != 0 ? 2 : 1;
l->cur.type = minic_ops[k].tok;
return;
}
}
l->pos++; // Unknown character: skip it
}
}
// ███████╗██╗ ██╗███╗ ██╗ ██████╗███████╗
// ██╔════╝██║ ██║████╗ ██║██╔════╝██╔════╝
// █████╗ ██║ ██║██╔██╗ ██║██║ ███████╗
// ██╔══╝ ██║ ██║██║╚██╗██║██║ ╚════██║
// ██║ ╚██████╔╝██║ ╚████║╚██████╗███████║
// ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝╚══════╝
void *minic_alloc(int size) {
// Align to 8 bytes
int aligned = (*minic_active_mem_used + 7) & ~7;
*minic_active_mem_used = aligned + size;
return &minic_active_mem[aligned];
}
typedef struct {
char name[MINIC_MAX_NAME];
minic_val_t val;
} minic_var_t;
typedef struct {
char name[MINIC_MAX_NAME];
int offset; // index into global arr_data[]
int count;
minic_type_t elem_type;
} minic_arr_t;
typedef struct {
char name[MINIC_MAX_NAME];
char params[MINIC_MAX_PARAMS][MINIC_MAX_NAME];
char param_structs[MINIC_MAX_PARAMS][MINIC_MAX_NAME]; // struct type name per param, or ""
minic_type_t param_types[MINIC_MAX_PARAMS];
int param_count;
int body_pos; // lexer position of '{' that starts the body
minic_type_t ret_type;
minic_ctx_t *ctx; // owning context, set at parse time
} minic_func_t;
// Maps a variable name to its struct type name
typedef struct {
char var_name[MINIC_MAX_NAME];
char struct_name[MINIC_MAX_NAME];
} minic_vartype_t;
typedef struct minic_env_s {
minic_lexer_t lex;
const char *filename;
minic_var_t *vars;
int var_count;
int var_cap;
minic_arr_t *arrs;
int arr_count;
int arr_cap;
minic_val_t *arr_data; // global array element storage
int *arr_data_used; // pointer to shared counter
minic_func_t *funcs;
int func_count;
int func_cap;
minic_struct_t *structs; // shared across calls
int struct_count;
int struct_cap;
minic_vartype_t *vartypes; // local: var->struct type mapping
int vartype_count;
int vartype_cap;
bool returning;
bool breaking;
bool continuing;
bool error;
minic_val_t return_val;
struct minic_env_s *global_env; // top-level env that owns the script globals
} minic_env_t;
struct minic_ctx_s {
minic_u8 *mem;
int mem_used;
minic_env_t e;
float result;
char *src_copy;
};
static minic_val_t minic_parse_cond(minic_env_t *e);
static void minic_parse_stmt(minic_env_t *e);
static void minic_parse_block(minic_env_t *e);
#define MINIC_INC_DELTA(l) ((l)->cur.type == TOK_INC ? 1.0 : -1.0)
static int minic_current_line(minic_env_t *e) {
int line = 1;
for (int i = 0; i < e->lex.pos; i++) {
if (e->lex.src[i] == '\n') {
line++;
}
}
return line;
}
void console_log(char *s);
static void minic_error(minic_env_t *e, const char *fmt, ...) {
if (e->error) {
return;
}
char msg[256];
va_list args;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
char log[512];
snprintf(log, sizeof(log), "%s:%d: error: %s (got %s)", e->filename, minic_current_line(e), msg, minic_tok_names[e->lex.cur.type]);
console_log(log);
e->error = true;
e->returning = true;
}
static void minic_expect(minic_env_t *e, minic_tok_type_t expected) {
if (e->lex.cur.type != expected) {
minic_error(e, "expected %s", minic_tok_names[expected]);
return;
}
minic_lex_next(&e->lex);
}
static bool minic_tok_is_type(minic_tok_type_t t) {
return t == TOK_INT || t == TOK_FLOAT || t == TOK_CHAR || t == TOK_DOUBLE || t == TOK_BOOL || t == TOK_VOID;
}
static minic_type_t minic_tok_to_type(minic_tok_type_t t) {
switch (t) {
case TOK_INT:
case TOK_CHAR:
case TOK_BOOL:
return MINIC_T_INT;
case TOK_FLOAT:
case TOK_DOUBLE:
return MINIC_T_FLOAT;
default:
return MINIC_T_PTR; // void * -> PTR
}
}
// Compute the result of an (op)= compound assignment; TOK_ASSIGN returns b
static double minic_apply_op(minic_tok_type_t op, double a, double b) {
switch (op) {
case TOK_PLUS_ASSIGN:
return a + b;
case TOK_MINUS_ASSIGN:
return a - b;
case TOK_MUL_ASSIGN:
return a * b;
case TOK_DIV_ASSIGN:
return b != 0.0 ? a / b : 0.0;
case TOK_MOD_ASSIGN:
return b != 0.0 ? fmod(a, b) : 0.0;
case TOK_SHL_ASSIGN:
case TOK_SHR_ASSIGN: {
int ia = (int)a;
int ib = (int)b;
return (double)(op == TOK_SHL_ASSIGN ? (int)((unsigned int)ia << ib) : (ia >> ib));
}
case TOK_AND_ASSIGN:
return (double)((int)a & (int)b);
case TOK_OR_ASSIGN:
return (double)((int)a | (int)b);
case TOK_XOR_ASSIGN:
return (double)((int)a ^ (int)b);
default:
return b;
}
}
static bool minic_is_compound_assign(minic_tok_type_t t) {
return t == TOK_PLUS_ASSIGN || t == TOK_MINUS_ASSIGN || t == TOK_MUL_ASSIGN || t == TOK_DIV_ASSIGN || t == TOK_MOD_ASSIGN || t == TOK_SHL_ASSIGN ||
t == TOK_SHR_ASSIGN || t == TOK_AND_ASSIGN || t == TOK_OR_ASSIGN || t == TOK_XOR_ASSIGN;
}
static minic_var_t *minic_var_find(minic_env_t *e, const char *name) {
for (int i = e->var_count - 1; i >= 0; --i) {
if (strcmp(e->vars[i].name, name) == 0) {
return &e->vars[i];
}
}
if (e->global_env != NULL) {
minic_env_t *g = e->global_env;
for (int i = g->var_count - 1; i >= 0; --i) {
if (strcmp(g->vars[i].name, name) == 0) {
return &g->vars[i];
}
}
}
return NULL;
}
static minic_var_t *minic_var_push(minic_env_t *e, const char *name, minic_val_t val) {
if (e->var_count >= e->var_cap) {
minic_error(e, "too many local variables (max %d), cannot declare '%s'", e->var_cap, name);
return NULL;
}
minic_var_t *v = &e->vars[e->var_count++];
strncpy(v->name, name, MINIC_MAX_NAME - 1);
v->val = val;
return v;
}
static minic_val_t minic_var_get(minic_env_t *e, const char *name) {
minic_var_t *v = minic_var_find(e, name);
return v != NULL ? v->val : minic_val_int(0);
}
static void minic_var_set(minic_env_t *e, const char *name, minic_val_t val) {
minic_var_t *v = minic_var_find(e, name);
if (v == NULL) {
minic_var_push(e, name, val);
return;
}
// Preserve declared type, coerce if needed
if (v->val.type != val.type) {
val = minic_val_cast(val, v->val.type);
}
// Preserve deref_type for pointer variables (it was set at declaration)
if (v->val.type == MINIC_T_PTR && v->val.deref_type != MINIC_T_PTR) {
val.deref_type = v->val.deref_type;
}
v->val = val;
}
static void minic_var_decl(minic_env_t *e, const char *name, minic_type_t type, minic_val_t init) {
// Coerce init to declared type; typed pointers keep the deref_type from init
minic_val_t v = minic_val_cast(init, type);
if (type == MINIC_T_PTR) {
v.deref_type = init.deref_type;
}
minic_var_push(e, name, v);
}
static minic_val_t minic_var_addr(minic_env_t *e, const char *name) {
minic_var_t *v = minic_var_find(e, name);
if (v == NULL) {
v = minic_var_push(e, name, minic_val_int(0));
if (v == NULL) {
return minic_val_ptr(NULL);
}
}
// Address of a minic_val_t
return minic_val_typed_ptr(&v->val, MINIC_T_PTR);
}
static minic_arr_t *minic_arr_get(minic_env_t *e, const char *name) {
for (int i = 0; i < e->arr_count; ++i) {
if (strcmp(e->arrs[i].name, name) == 0) {
return &e->arrs[i];
}
}
if (e->global_env != NULL) {
minic_env_t *g = e->global_env;
for (int i = 0; i < g->arr_count; ++i) {
if (strcmp(g->arrs[i].name, name) == 0) {
return &g->arrs[i];
}
}
}
return NULL;
}
static void minic_arr_decl(minic_env_t *e, const char *name, int count, minic_type_t elem_type) {
if (e->arr_count >= e->arr_cap) {
return;
}
minic_arr_t *a = &e->arrs[e->arr_count++];
strncpy(a->name, name, MINIC_MAX_NAME - 1);
a->offset = *e->arr_data_used;
a->count = count;
a->elem_type = elem_type;
*e->arr_data_used += count;
// Zero-initialise
for (int i = 0; i < count; i++) {
e->arr_data[a->offset + i] = minic_val_coerce(0.0, elem_type);
}
}
// Subscript a native C pointer by its deref element type
static minic_val_t minic_ptr_index_get(minic_val_t pv, int idx) {
if (pv.type != MINIC_T_PTR || pv.p == NULL) {
return minic_val_int(0);
}
switch (pv.deref_type) {
case MINIC_T_FLOAT:
return minic_val_float(((float *)pv.p)[idx]);
case MINIC_T_INT:
return minic_val_int(((int32_t *)pv.p)[idx]);
default:
return minic_val_ptr(((void **)pv.p)[idx]);
}
}
static void minic_ptr_index_set(minic_val_t pv, int idx, minic_val_t val) {
if (pv.type != MINIC_T_PTR || pv.p == NULL) {
return;
}
switch (pv.deref_type) {
case MINIC_T_FLOAT:
((float *)pv.p)[idx] = (float)minic_val_to_d(val);
break;
case MINIC_T_INT:
((int32_t *)pv.p)[idx] = (int32_t)minic_val_to_d(val);
break;
default:
((void **)pv.p)[idx] = minic_val_to_ptr(val);
break;
}
}
static minic_val_t minic_arr_elem_get(minic_env_t *e, const char *name, int idx) {
minic_arr_t *a = minic_arr_get(e, name);
if (a != NULL && idx >= 0 && idx < a->count) {
return e->arr_data[a->offset + idx];
}
if (idx < 0) {
minic_error(e, "negative index %d on '%s'", idx, name);
return minic_val_int(0);
}
return minic_ptr_index_get(minic_var_get(e, name), idx);
}
static void minic_arr_elem_set(minic_env_t *e, const char *name, int idx, minic_val_t val) {
minic_arr_t *a = minic_arr_get(e, name);
if (a != NULL && idx >= 0 && idx < a->count) {
e->arr_data[a->offset + idx] = minic_val_cast(val, a->elem_type);
return;
}
if (idx < 0) {
minic_error(e, "negative index %d on '%s'", idx, name);
return;
}
minic_ptr_index_set(minic_var_get(e, name), idx, val); // native pointer subscript
}
// Read through a pointer: a MINIC_T_PTR deref_type means "points at a boxed minic_val_t"
// (interpreter-internal), any other deref_type means a native C scalar at that address
static minic_val_t minic_deref(minic_val_t pv) {
void *ptr = minic_val_to_ptr(pv);
if (ptr == NULL) {
return minic_val_int(0);
}
switch (pv.deref_type) {
case MINIC_T_INT: {
int v;
memcpy(&v, ptr, sizeof(int));
return minic_val_int(v);
}
case MINIC_T_FLOAT: {
float v;
memcpy(&v, ptr, sizeof(float));
return minic_val_float(v);
}
default: {
minic_val_t v;
memcpy(&v, ptr, sizeof(minic_val_t));
return v;
}
}
}
// Write through a pointer, with optional compound op: *pv = v, *pv += v, ...
static void minic_store_op(minic_val_t pv, minic_tok_type_t op, minic_val_t v) {
void *ptr = minic_val_to_ptr(pv);
if (ptr == NULL) {
return;
}
switch (pv.deref_type) {
case MINIC_T_INT: {
int ov;
memcpy(&ov, ptr, sizeof(int));
int nv = (int)minic_apply_op(op, (double)ov, minic_val_to_d(v));
memcpy(ptr, &nv, sizeof(int));
break;
}
case MINIC_T_FLOAT: {
float ov;
memcpy(&ov, ptr, sizeof(float));
float nv = (float)minic_apply_op(op, (double)ov, minic_val_to_d(v));
memcpy(ptr, &nv, sizeof(float));
break;
}
default: {
minic_val_t ov;
memcpy(&ov, ptr, sizeof(minic_val_t));
minic_val_t nv = (op == TOK_ASSIGN) ? v : minic_val_coerce(minic_apply_op(op, minic_val_to_d(ov), minic_val_to_d(v)), ov.type);
memcpy(ptr, &nv, sizeof(minic_val_t));
break;
}
}
}
static minic_func_t *minic_func_get(minic_env_t *e, const char *name) {
for (int i = 0; i < e->func_count; ++i) {
if (strcmp(e->funcs[i].name, name) == 0) {
return &e->funcs[i];
}
}
return NULL;
}
static minic_struct_t *minic_struct_get(minic_env_t *e, const char *name) {
for (int i = 0; i < e->struct_count; ++i) {
if (strcmp(e->structs[i].name, name) == 0) {
return &e->structs[i];
}
}
return NULL;
}
static void minic_vartype_set(minic_env_t *e, const char *var_name, const char *struct_name) {
if (e->vartype_count >= e->vartype_cap) {
minic_error(e, "too many struct-typed variables (max %d), cannot type '%s' as '%s'", e->vartype_cap, var_name, struct_name);
return;
}
strncpy(e->vartypes[e->vartype_count].var_name, var_name, MINIC_MAX_NAME - 1);
strncpy(e->vartypes[e->vartype_count].struct_name, struct_name, MINIC_MAX_NAME - 1);
e->vartype_count++;
}
static minic_struct_t *minic_var_struct(minic_env_t *e, const char *var_name) {
for (int i = e->vartype_count - 1; i >= 0; --i) {
if (strcmp(e->vartypes[i].var_name, var_name) == 0) {
return minic_struct_get(e, e->vartypes[i].struct_name);
}
}
if (e->global_env != NULL) {
minic_env_t *g = e->global_env;
for (int i = g->vartype_count - 1; i >= 0; --i) {
if (strcmp(g->vartypes[i].var_name, var_name) == 0) {
return minic_struct_get(e, g->vartypes[i].struct_name);
}
}
}
return NULL;
}
static int minic_struct_field_idx(minic_struct_t *def, const char *field) {
for (int i = 0; i < def->field_count; ++i) {
if (strcmp(def->fields[i], field) == 0) {
return i;
}
}
return -1;
}
bool minic_in_arena(void *p) {
return p != NULL && (minic_u8 *)p >= minic_active_mem && (minic_u8 *)p < minic_active_mem + MINIC_MEM_SIZE;
}
static minic_val_t minic_struct_field_get_base(minic_env_t *e, void *base, minic_struct_t *def, const char *field) {
if (base == NULL) {
minic_error(e, "null pointer access on '%s->%s'", def->name, field);
return minic_val_int(0);
}
int idx = minic_struct_field_idx(def, field);
if (idx < 0) {
minic_error(e, "struct '%s' has no field '%s'", def->name, field);
return minic_val_int(0);
}
if (def->native && !minic_in_arena(base)) {
char *p = (char *)base + def->offsets[idx];
switch (def->types[idx]) {
case MINIC_T_PTR:
return minic_val_typed_ptr(*(void **)p, def->deref_types[idx]);
case MINIC_T_EMBED:
return minic_val_typed_ptr(p, def->deref_types[idx]);
case MINIC_T_FLOAT:
return minic_val_float(*(float *)p);
case MINIC_T_BOOL:
return minic_val_int(*(bool *)p);
default:
return minic_val_int(*(int32_t *)p);
}
}
minic_val_t v;
memcpy(&v, (minic_val_t *)base + idx, sizeof(minic_val_t));
return v;
}
static void minic_struct_field_set_base(minic_env_t *e, void *base, minic_struct_t *def, const char *field, minic_val_t val) {
if (base == NULL) {
minic_error(e, "null pointer access on '%s->%s'", def->name, field);
return;
}
int idx = minic_struct_field_idx(def, field);
if (idx < 0) {
minic_error(e, "struct '%s' has no field '%s'", def->name, field);
return;
}
if (def->native && !minic_in_arena(base)) {
char *p = (char *)base + def->offsets[idx];
switch (def->types[idx]) {
case MINIC_T_PTR:
*(void **)p = minic_val_to_ptr(val);
break;
case MINIC_T_EMBED:
break; // embedded structs are mutated through their own field accessors
case MINIC_T_FLOAT:
*(float *)p = (float)minic_val_to_d(val);
break;
case MINIC_T_BOOL:
*(bool *)p = (minic_val_to_d(val) != 0.0);
break;
default:
*(int32_t *)p = (int32_t)minic_val_to_d(val);
break;
}
return;
}
memcpy((minic_val_t *)base + idx, &val, sizeof(minic_val_t));
}
static bool minic_index_in_range(minic_env_t *e, void *base, minic_struct_t *def, const char *field, int idx) {
if (strcmp(field, "buffer") != 0 || minic_struct_field_idx(def, "length") < 0) {
return true;
}
int len = (int)minic_val_to_d(minic_struct_field_get_base(e, base, def, "length"));
if (idx < 0 || idx >= len) {
minic_error(e, "index %d out of range for '%s' of length %d", idx, def->name, len);
return false;
}
return true;
}
static minic_val_t minic_call(minic_env_t *e, minic_func_t *fn, minic_val_t *args, int argc) {
minic_env_t child = {0};
child.lex.src = e->lex.src;
child.lex.pos = fn->body_pos;
child.filename = e->filename;
child.var_cap = MINIC_MAX_VARS;
child.vars = minic_alloc(child.var_cap * (int)sizeof(minic_var_t));
child.global_env = e->global_env != NULL ? e->global_env : e;
child.arr_cap = 32;
child.arrs = minic_alloc(child.arr_cap * (int)sizeof(minic_arr_t));
child.arr_data = e->arr_data;
child.arr_data_used = e->arr_data_used;
child.func_count = e->func_count;
child.func_cap = e->func_cap;
child.funcs = e->funcs;
child.struct_count = e->struct_count;
child.struct_cap = e->struct_cap;
child.structs = e->structs;
child.vartype_cap = MINIC_MAX_VARTYPES;
child.vartypes = minic_alloc(child.vartype_cap * (int)sizeof(minic_vartype_t));
// Bind parameters
for (int i = 0; i < argc && i < fn->param_count; ++i) {
minic_var_decl(&child, fn->params[i], fn->param_types[i], minic_val_cast(args[i], fn->param_types[i]));
if (fn->param_structs[i][0] != '\0') {
minic_vartype_set(&child, fn->params[i], fn->param_structs[i]);
}
}
minic_lex_next(&child.lex);
minic_parse_block(&child);
return child.return_val;
}
static minic_val_t minic_call_in_ctx(minic_ctx_t *ctx, minic_func_t *fn, minic_val_t *args, int argc) {
minic_u8 *prev_mem = minic_active_mem;
int *prev_mem_used = minic_active_mem_used;
minic_active_mem = ctx->mem;
minic_active_mem_used = &ctx->mem_used;
int saved_used = ctx->mem_used;
minic_val_t r = minic_call(&ctx->e, fn, args, argc);
ctx->mem_used = saved_used;
minic_active_mem = prev_mem;
minic_active_mem_used = prev_mem_used;
return r;
}
minic_val_t minic_call_fn(void *fn_ptr, minic_val_t *args, int argc) {
minic_func_t *fn = (minic_func_t *)fn_ptr;
if (fn == NULL || fn->ctx == NULL) {
return minic_val_int(0);
}
return minic_call_in_ctx(fn->ctx, fn, args, argc);
}
minic_val_t minic_ctx_call_fn(minic_ctx_t *ctx, void *fn_ptr, minic_val_t *args, int argc) {
if (ctx == NULL || fn_ptr == NULL) {
return minic_val_int(0);
}
return minic_call_in_ctx(ctx, (minic_func_t *)fn_ptr, args, argc);
}
static minic_val_t minic_arith(minic_val_t a, minic_val_t b, minic_tok_type_t op) {
// Determine result type (widening: int < float < ptr)
minic_type_t rt;
if (a.type == MINIC_T_PTR || b.type == MINIC_T_PTR) {
rt = MINIC_T_PTR;
}
else if (a.type == MINIC_T_FLOAT || b.type == MINIC_T_FLOAT) {
rt = MINIC_T_FLOAT;
}
else {
rt = MINIC_T_INT;
}
double da = minic_val_to_d(a);
double db = minic_val_to_d(b);
if (op == TOK_PERCENT) {
double r = 0.0;
if (rt == MINIC_T_FLOAT) {
r = db != 0.0 ? fmod(da, db) : 0.0;
}
else {
int ib = (int)db;
r = ib != 0 ? (double)((int)da % ib) : 0.0;
}
return minic_val_coerce(r, rt);
}
double r = op == TOK_PLUS ? da + db : op == TOK_MINUS ? da - db : op == TOK_STAR ? da * db : (db != 0.0 ? da / db : 0.0);
return minic_val_coerce(r, rt);
}
// Parse a call argument list (after '(') and invoke a script or extern function
static minic_val_t minic_parse_call(minic_env_t *e, const char *name) {
minic_val_t args[MINIC_MAX_PARAMS];
int argc = 0;
while (e->lex.cur.type != TOK_RPAREN && e->lex.cur.type != TOK_EOF) {
minic_val_t v = minic_parse_cond(e);
if (argc < MINIC_MAX_PARAMS) {
args[argc++] = v;
}
if (e->lex.cur.type == TOK_COMMA) {
minic_lex_next(&e->lex);
}
}
minic_expect(e, TOK_RPAREN);
minic_func_t *fn = minic_func_get(e, name);
if (fn != NULL) {
return minic_call(e, fn, args, argc);
}
minic_ext_func_t *ext = minic_ext_func_get(name);
if (ext != NULL) {
return minic_dispatch(ext, args, argc);
}
minic_error(e, "unknown function '%s'", name);
return minic_val_int(0);
}
// primary: '&' IDENT | '*' primary | '-' primary | '!' primary | '~' primary | '++'/'--' IDENT |
// NUMBER | CHAR_LIT | STR_LIT | IDENT ['[' expr ']' | '(' args ')' | ('.'|'->') field...] | '(' expr ')'
static minic_val_t minic_parse_primary(minic_env_t *e) {
if (e->lex.cur.type == TOK_AMP) {
minic_lex_next(&e->lex); // Consume '&'
char aname[MINIC_MAX_NAME];
strncpy(aname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_arr_t *arr = minic_arr_get(e, aname);
minic_struct_t *def = minic_var_struct(e, aname);
minic_val_t addr;
if (arr != NULL) {
addr = minic_val_ptr(&e->arr_data[arr->offset]);
}
else if (def != NULL) {
addr = minic_var_get(e, aname); // struct var holds the real base pointer
}
else {
addr = minic_var_addr(e, aname);
}
minic_lex_next(&e->lex); // Consume ident
// Handle &var->field or &var.field: follow the member-access chain
while (def != NULL && (e->lex.cur.type == TOK_ARROW || e->lex.cur.type == TOK_DOT)) {
minic_lex_next(&e->lex); // Consume '->' or '.'
char field[MINIC_MAX_NAME];
strncpy(field, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume field name
addr = minic_struct_field_get_base(e, minic_val_to_ptr(addr), def, field);
// Advance def to the field's struct type for further chaining
int fidx = minic_struct_field_idx(def, field);
def = (fidx >= 0 && def->field_structs[fidx][0] != '\0') ? minic_struct_get(e, def->field_structs[fidx]) : NULL;
}
return addr;
}
if (e->lex.cur.type == TOK_STAR) {
minic_lex_next(&e->lex); // Consume '*'
return minic_deref(minic_parse_primary(e));
}
if (e->lex.cur.type == TOK_MINUS) {
minic_lex_next(&e->lex);
minic_val_t v = minic_parse_primary(e);
return minic_val_coerce(-minic_val_to_d(v), v.type);
}
if (e->lex.cur.type == TOK_NOT) {
minic_lex_next(&e->lex);
return minic_val_int(!minic_val_is_true(minic_parse_primary(e)));
}
if (e->lex.cur.type == TOK_BITNOT) {
minic_lex_next(&e->lex);
return minic_val_int(~(int)minic_val_to_d(minic_parse_primary(e)));
}
if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_lex_next(&e->lex);
char name[MINIC_MAX_NAME];
strncpy(name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
minic_val_t ov = minic_var_get(e, name);
minic_val_t nv = minic_val_coerce(minic_val_to_d(ov) + delta, ov.type);
minic_var_set(e, name, nv);
return nv;
}
if (e->lex.cur.type == TOK_NUMBER || e->lex.cur.type == TOK_CHAR_LIT || e->lex.cur.type == TOK_STR_LIT) {
minic_val_t v = e->lex.cur.val;
minic_lex_next(&e->lex);
return v;
}
if (e->lex.cur.type == TOK_IDENT) {
char name[MINIC_MAX_NAME];
strncpy(name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
if (strcmp(name, "sizeof") == 0) {
minic_expect(e, TOK_LPAREN);
char type_name[MINIC_MAX_NAME];
strncpy(type_name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume type name
minic_expect(e, TOK_RPAREN);
minic_struct_t *def = minic_struct_get(e, type_name);
return minic_val_int(def != NULL ? def->size : 0);
}
if (e->lex.cur.type == TOK_LBRACKET) {
minic_lex_next(&e->lex); // Consume '['
int idx = (int)minic_val_to_d(minic_parse_cond(e));
minic_expect(e, TOK_RBRACKET);
return minic_arr_elem_get(e, name, idx);
}
if (e->lex.cur.type == TOK_LPAREN) {
minic_lex_next(&e->lex); // Consume '('
return minic_parse_call(e, name);
}
if (e->lex.cur.type == TOK_DOT || e->lex.cur.type == TOK_ARROW) {
minic_struct_t *def = minic_var_struct(e, name);
if (def == NULL) {
minic_error(e, "'%s' is not a struct", name);
return minic_val_int(0);
}
void *base = minic_val_to_ptr(minic_var_get(e, name));
char field[MINIC_MAX_NAME];
minic_lex_next(&e->lex); // Consume '.' or '->'
strncpy(field, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
minic_val_t v = minic_struct_field_get_base(e, base, def, field);
// Handle chained '->' / '.' access (e.g. node->inputs->buffer)
while ((e->lex.cur.type == TOK_ARROW || e->lex.cur.type == TOK_DOT) && !e->error) {
int fidx = minic_struct_field_idx(def, field);
if (fidx < 0 || def->field_structs[fidx][0] == '\0') {
break;
}
minic_struct_t *next_def = minic_struct_get(e, def->field_structs[fidx]);
if (next_def == NULL) {
break;
}
minic_lex_next(&e->lex); // Consume '->' or '.'
strncpy(field, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
base = minic_val_to_ptr(v);
def = next_def;
v = minic_struct_field_get_base(e, base, def, field);
}
if (e->lex.cur.type == TOK_LBRACKET) {
minic_lex_next(&e->lex); // Consume '['
int idx = (int)minic_val_to_d(minic_parse_cond(e));
minic_expect(e, TOK_RBRACKET);
if (!minic_index_in_range(e, base, def, field, idx)) {
return minic_val_int(0);
}
return minic_ptr_index_get(v, idx);
}
return v;
}
// If not a variable, check if it's a minic function (pass-as-pointer)
minic_func_t *fn = minic_func_get(e, name);
if (fn != NULL) {
return minic_val_ptr(fn);
}
// Check for known enum constant
int ec = minic_enum_const_get(name);
if (ec >= 0) {
return minic_val_int(ec);
}
// Check for a registered host global
minic_val_t gv;
if (minic_var_find(e, name) == NULL && minic_global_get(name, &gv)) {
return gv;
}
return minic_var_get(e, name);
}
if (e->lex.cur.type == TOK_LPAREN) {
minic_lex_next(&e->lex);
minic_val_t v = minic_parse_cond(e);
minic_expect(e, TOK_RPAREN);
return v;
}
return minic_val_int(0);
}
// term: primary (('*' | '/' | '%') primary)*
static minic_val_t minic_parse_term(minic_env_t *e) {
minic_val_t v = minic_parse_primary(e);
while (e->lex.cur.type == TOK_STAR || e->lex.cur.type == TOK_SLASH || e->lex.cur.type == TOK_PERCENT) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
v = minic_arith(v, minic_parse_primary(e), op);
}
return v;
}
// expr: term (('+' | '-') term)*
static minic_val_t minic_parse_expr(minic_env_t *e) {
minic_val_t v = minic_parse_term(e);
while (e->lex.cur.type == TOK_PLUS || e->lex.cur.type == TOK_MINUS) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
v = minic_arith(v, minic_parse_term(e), op);
}
return v;
}
// shift: expr (('<<' | '>>') expr)*
static minic_val_t minic_parse_shift(minic_env_t *e) {
minic_val_t v = minic_parse_expr(e);
while (e->lex.cur.type == TOK_SHL || e->lex.cur.type == TOK_SHR) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
int a = (int)minic_val_to_d(v);
int b = (int)minic_val_to_d(minic_parse_expr(e));
v = minic_val_int(op == TOK_SHL ? (int)((unsigned int)a << b) : (a >> b));
}
return v;
}
// cmp: shift (('=='|'!='|'<'|'>'|'<='|'>=') shift)?
static minic_val_t minic_parse_cmp(minic_env_t *e) {
minic_val_t v = minic_parse_shift(e);
minic_tok_type_t op = e->lex.cur.type;
if (op == TOK_EQ || op == TOK_NEQ || op == TOK_LT || op == TOK_GT || op == TOK_LE || op == TOK_GE) {
minic_lex_next(&e->lex);
double a = minic_val_to_d(v);
double b = minic_val_to_d(minic_parse_shift(e));
int res;
switch (op) {
case TOK_EQ:
res = a == b;
break;
case TOK_NEQ:
res = a != b;
break;
case TOK_LT:
res = a < b;
break;
case TOK_GT:
res = a > b;
break;
case TOK_LE:
res = a <= b;
break;
default:
res = a >= b;
break;
}
return minic_val_int(res);
}
return v;
}
// bitand: cmp ('&' cmp)*
// '&' is only binary here; a leading '&' is consumed as address-of by minic_parse_primary
static minic_val_t minic_parse_bitand(minic_env_t *e) {
minic_val_t v = minic_parse_cmp(e);
while (e->lex.cur.type == TOK_AMP) {
minic_lex_next(&e->lex);
int a = (int)minic_val_to_d(v);
int b = (int)minic_val_to_d(minic_parse_cmp(e));
v = minic_val_int(a & b);
}
return v;
}
// bitxor: bitand ('^' bitand)*
static minic_val_t minic_parse_bitxor(minic_env_t *e) {
minic_val_t v = minic_parse_bitand(e);
while (e->lex.cur.type == TOK_XOR) {
minic_lex_next(&e->lex);
int a = (int)minic_val_to_d(v);
int b = (int)minic_val_to_d(minic_parse_bitand(e));
v = minic_val_int(a ^ b);
}
return v;
}
// bitor: bitxor ('|' bitxor)*
static minic_val_t minic_parse_bitor(minic_env_t *e) {
minic_val_t v = minic_parse_bitxor(e);
while (e->lex.cur.type == TOK_BITOR) {
minic_lex_next(&e->lex);
int a = (int)minic_val_to_d(v);
int b = (int)minic_val_to_d(minic_parse_bitxor(e));
v = minic_val_int(a | b);
}
return v;
}
// cond: bitor (('&&' | '||') bitor)*
static minic_val_t minic_parse_cond(minic_env_t *e) {
minic_val_t v = minic_parse_bitor(e);
while (e->lex.cur.type == TOK_AND || e->lex.cur.type == TOK_OR) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
int vi = minic_val_is_true(v);
int ri = minic_val_is_true(minic_parse_bitor(e));
v = minic_val_int(op == TOK_AND ? (vi && ri) : (vi || ri));
}
return v;
}
// Skip the parenthesised header of an if/for/while, leaving the first body token current
static void minic_skip_header(minic_env_t *e) {
int depth = 0;
do {
if (e->lex.cur.type == TOK_LPAREN) {
depth++;
}
if (e->lex.cur.type == TOK_RPAREN) {
depth--;
}
minic_lex_next(&e->lex);
} while (depth > 0 && e->lex.cur.type != TOK_EOF);
}
// Skip one statement without executing it
static void minic_skip_block(minic_env_t *e) {
if (e->lex.cur.type == TOK_LBRACE) {
minic_lex_next(&e->lex); // Consume '{'
int depth = 1;
while (depth > 0 && e->lex.cur.type != TOK_EOF) {
if (e->lex.cur.type == TOK_LBRACE) {
depth++;
}
if (e->lex.cur.type == TOK_RBRACE) {
depth--;
}
minic_lex_next(&e->lex);
}
return;
}
// Control statement: skip its own header, then its body
if (e->lex.cur.type == TOK_IF || e->lex.cur.type == TOK_FOR || e->lex.cur.type == TOK_WHILE) {
bool is_if = (e->lex.cur.type == TOK_IF);
minic_lex_next(&e->lex); // Consume the keyword
minic_skip_header(e);
minic_skip_block(e);
if (is_if && e->lex.cur.type == TOK_ELSE) {
minic_lex_next(&e->lex); // Consume 'else'
minic_skip_block(e);
}
return;
}
// Plain statement: up to the next ';' that is not inside parentheses
int depth = 0;
while (e->lex.cur.type != TOK_EOF && !(e->lex.cur.type == TOK_SEMICOLON && depth == 0)) {
if (e->lex.cur.type == TOK_LPAREN) {
depth++;
}
if (e->lex.cur.type == TOK_RPAREN) {
depth--;
}
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_SEMICOLON) {
minic_lex_next(&e->lex); // Consume ';'
}
}
// struct value or pointer declaration; the struct type name has been consumed
static void minic_parse_struct_decl(minic_env_t *e, const char *sname) {
minic_struct_t *def = minic_struct_get(e, sname);
if (def == NULL) {
minic_error(e, "unknown struct '%s'", sname);
return;
}
bool is_ptr = (e->lex.cur.type == TOK_STAR);
if (is_ptr) {
minic_lex_next(&e->lex);
}
char vname[MINIC_MAX_NAME];
strncpy(vname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
minic_vartype_set(e, vname, sname);
if (is_ptr) {
minic_val_t v = minic_val_ptr(NULL);
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
v = minic_parse_cond(e);
}
minic_var_decl(e, vname, MINIC_T_PTR, v);
}
else {
// Value declaration: boxed field storage in the arena
void *base = minic_alloc(def->field_count * (int)sizeof(minic_val_t));
memset(base, 0, def->field_count * sizeof(minic_val_t));
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
minic_val_t v = minic_parse_cond(e);
if (v.type == MINIC_T_PTR && v.p != NULL) {
memcpy(base, v.p, def->field_count * sizeof(minic_val_t));
}
}
minic_var_decl(e, vname, MINIC_T_PTR, minic_val_ptr(base));
}
minic_expect(e, TOK_SEMICOLON);
}
// The increment clause of a for loop: ++i, i++, i += x, i = x
static void minic_parse_for_incr(minic_env_t *e) {
if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_lex_next(&e->lex);
minic_val_t ov = minic_var_get(e, e->lex.cur.text);
minic_var_set(e, e->lex.cur.text, minic_val_coerce(minic_val_to_d(ov) + delta, ov.type));
return;
}
if (e->lex.cur.type != TOK_IDENT) {
return;
}
char name[MINIC_MAX_NAME];
strncpy(name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_val_t ov = minic_var_get(e, name);
minic_var_set(e, name, minic_val_coerce(minic_val_to_d(ov) + delta, ov.type));
}
else if (minic_is_compound_assign(e->lex.cur.type)) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
minic_val_t dv = minic_parse_cond(e);
minic_val_t ov = minic_var_get(e, name);
minic_var_set(e, name, minic_val_coerce(minic_apply_op(op, minic_val_to_d(ov), minic_val_to_d(dv)), ov.type));
}
else if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
minic_var_set(e, name, minic_parse_cond(e));
}
}
static void minic_parse_stmt(minic_env_t *e) {
// Skip bare typedef declarations inside function bodies
if (e->lex.cur.type == TOK_TYPEDEF) {
while (e->lex.cur.type != TOK_SEMICOLON && e->lex.cur.type != TOK_EOF) {
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_SEMICOLON) {
minic_lex_next(&e->lex);
}
return;
}
if (e->lex.cur.type == TOK_STRUCT) {
minic_lex_next(&e->lex); // Consume 'struct'
char sname[MINIC_MAX_NAME];
strncpy(sname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume struct type name
minic_parse_struct_decl(e, sname);
return;
}
// Typedef'd int name (e.g. from typedef enum): alias_t var = expr;
if (e->lex.cur.type == TOK_IDENT && minic_is_int_typedef(e->lex.cur.text)) {
minic_lex_next(&e->lex); // Consume alias name
char vname[MINIC_MAX_NAME];
strncpy(vname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume var name
minic_val_t v = minic_val_int(0);
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
v = minic_parse_cond(e);
}
minic_var_decl(e, vname, MINIC_T_INT, minic_val_cast(v, MINIC_T_INT));
minic_expect(e, TOK_SEMICOLON);
return;
}
// Typedef'd struct name used as variable type: alias_t var; or alias_t *var = ...;
if (e->lex.cur.type == TOK_IDENT && minic_struct_get(e, e->lex.cur.text) != NULL) {
char sname[MINIC_MAX_NAME];
strncpy(sname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume alias name
minic_parse_struct_decl(e, sname);
return;
}
if (minic_tok_is_type(e->lex.cur.type)) {
minic_type_t base_type = minic_tok_to_type(e->lex.cur.type); // Type before '*'
minic_type_t dtype = base_type;
minic_lex_next(&e->lex); // Consume type keyword
bool is_ptr = (e->lex.cur.type == TOK_STAR);
if (is_ptr) {
dtype = MINIC_T_PTR;
minic_lex_next(&e->lex);
}
char name[MINIC_MAX_NAME];
strncpy(name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
if (e->lex.cur.type == TOK_LBRACKET) {
minic_lex_next(&e->lex); // Consume '['
int count = (int)minic_val_to_d(minic_parse_cond(e));
minic_expect(e, TOK_RBRACKET);
minic_arr_decl(e, name, count, dtype);
minic_expect(e, TOK_SEMICOLON);
return;
}
// Optional initializer: default to 0 / NULL when omitted
minic_val_t v = is_ptr ? minic_val_ptr(NULL) : minic_val_coerce(0.0, dtype);
if (is_ptr) {
v.deref_type = base_type;
}
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
v = minic_parse_cond(e);
if (is_ptr) {
if (v.type != MINIC_T_PTR) {
// NULL literal passed as integer 0
v = minic_val_ptr((void *)(uintptr_t)(uint64_t)minic_val_to_d(v));
}
// Only stamp the declared element type for native C pointers.
// Pointers into the active arena (e.g. from &var) use the MINIC_T_PTR sentinel
// to signal that dereferencing reads a full minic_val_t.
if (!minic_in_arena(v.p)) {
v.deref_type = base_type;
}
}
}
minic_var_decl(e, name, dtype, v);
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_RETURN) {
minic_lex_next(&e->lex);
e->return_val = minic_parse_cond(e);
e->returning = true;
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_IDENT) {
char name[MINIC_MAX_NAME];
strncpy(name, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
// Unknown opaque type followed by '*' + ident: local pointer declaration.
// e.g. ui_handle_t *h; or my_t *p = create_p();
if (e->lex.cur.type == TOK_STAR) {
minic_lex_next(&e->lex); // Consume '*'
char vname[MINIC_MAX_NAME];
strncpy(vname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex); // Consume var name
minic_val_t v = minic_val_ptr(NULL);
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex);
v = minic_parse_cond(e);
}
minic_var_decl(e, vname, MINIC_T_PTR, v);
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_DOT || e->lex.cur.type == TOK_ARROW) {
bool is_arrow = (e->lex.cur.type == TOK_ARROW);
minic_lex_next(&e->lex);
char field[MINIC_MAX_NAME];
strncpy(field, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
minic_struct_t *def = minic_var_struct(e, name);
if (def == NULL) {
minic_error(e, "'%s' is not a struct%s", name, is_arrow ? " pointer" : "");
return;
}
void *base = minic_val_to_ptr(minic_var_get(e, name));
// Descend chained member access to the last field (e.g. o->transform->radius = x)
while ((e->lex.cur.type == TOK_DOT || e->lex.cur.type == TOK_ARROW) && !e->error) {
int fidx = minic_struct_field_idx(def, field);
if (fidx < 0 || def->field_structs[fidx][0] == '\0') {
break;
}
minic_struct_t *next_def = minic_struct_get(e, def->field_structs[fidx]);
if (next_def == NULL) {
break;
}
minic_lex_next(&e->lex); // Consume '->' or '.'
base = minic_val_to_ptr(minic_struct_field_get_base(e, base, def, field));
def = next_def;
strncpy(field, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_LBRACKET) {
minic_lex_next(&e->lex);
int idx = (int)minic_val_to_d(minic_parse_cond(e));
minic_expect(e, TOK_RBRACKET);
minic_expect(e, TOK_ASSIGN);
minic_val_t v = minic_parse_cond(e);
if (minic_index_in_range(e, base, def, field, idx)) {
minic_ptr_index_set(minic_struct_field_get_base(e, base, def, field), idx, v);
}
}
else if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_lex_next(&e->lex);
minic_val_t ov = minic_struct_field_get_base(e, base, def, field);
minic_struct_field_set_base(e, base, def, field, minic_val_coerce(minic_val_to_d(ov) + delta, ov.type));
}
else if (minic_is_compound_assign(e->lex.cur.type)) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
minic_val_t dv = minic_parse_cond(e);
minic_val_t ov = minic_struct_field_get_base(e, base, def, field);
minic_struct_field_set_base(e, base, def, field, minic_val_coerce(minic_apply_op(op, minic_val_to_d(ov), minic_val_to_d(dv)), ov.type));
}
else {
minic_expect(e, TOK_ASSIGN);
minic_struct_field_set_base(e, base, def, field, minic_parse_cond(e));
}
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_LPAREN) {
minic_lex_next(&e->lex);
minic_parse_call(e, name);
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_lex_next(&e->lex);
minic_val_t ov = minic_var_get(e, name);
minic_var_set(e, name, minic_val_coerce(minic_val_to_d(ov) + delta, ov.type));
minic_expect(e, TOK_SEMICOLON);
return;
}
if (minic_is_compound_assign(e->lex.cur.type)) {
minic_tok_type_t op = e->lex.cur.type;
minic_lex_next(&e->lex);
minic_val_t dv = minic_parse_cond(e);
minic_val_t ov = minic_var_get(e, name);
minic_var_set(e, name, minic_val_coerce(minic_apply_op(op, minic_val_to_d(ov), minic_val_to_d(dv)), ov.type));
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_LBRACKET) {
minic_lex_next(&e->lex);
int idx = (int)minic_val_to_d(minic_parse_cond(e));
minic_expect(e, TOK_RBRACKET);
minic_expect(e, TOK_ASSIGN);
minic_arr_elem_set(e, name, idx, minic_parse_cond(e));
minic_expect(e, TOK_SEMICOLON);
return;
}
minic_expect(e, TOK_ASSIGN);
minic_var_set(e, name, minic_parse_cond(e));
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_IF) {
minic_lex_next(&e->lex);
minic_expect(e, TOK_LPAREN);
int taken = minic_val_is_true(minic_parse_cond(e));
minic_expect(e, TOK_RPAREN);
if (taken) {
minic_parse_block(e);
}
else {
minic_skip_block(e);
}
while (e->lex.cur.type == TOK_ELSE && !e->error) {
minic_lex_next(&e->lex);
int cond = 1;
if (e->lex.cur.type == TOK_IF) {
minic_lex_next(&e->lex);
minic_expect(e, TOK_LPAREN);
cond = minic_val_is_true(minic_parse_cond(e));
minic_expect(e, TOK_RPAREN);
}
if (!taken && cond) {
minic_parse_block(e);
taken = 1;
}
else {
minic_skip_block(e);
}
}
return;
}
if (e->lex.cur.type == TOK_FOR) {
minic_lex_next(&e->lex);
minic_expect(e, TOK_LPAREN);
// Init clause
if (minic_tok_is_type(e->lex.cur.type)) {
minic_lex_next(&e->lex);
}
{
char iname[MINIC_MAX_NAME];
strncpy(iname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
minic_expect(e, TOK_ASSIGN);
minic_var_set(e, iname, minic_parse_cond(e));
}
int cond_pos = e->lex.pos;
minic_lex_next(&e->lex); // Consume ';'
// Scan ahead for the increment clause and body positions
int incr_pos, body_pos;
{
minic_lexer_t tmp = {0};
tmp.src = e->lex.src;
tmp.pos = cond_pos;
minic_lex_next(&tmp);
int depth = 0;
while (tmp.cur.type != TOK_EOF && !(tmp.cur.type == TOK_SEMICOLON && depth == 0)) {
if (tmp.cur.type == TOK_LPAREN) {
depth++;
}
if (tmp.cur.type == TOK_RPAREN) {
depth--;
}
minic_lex_next(&tmp);
}
incr_pos = tmp.pos;
minic_lex_next(&tmp);
depth = 0;
while (tmp.cur.type != TOK_EOF && !(tmp.cur.type == TOK_RPAREN && depth == 0)) {
if (tmp.cur.type == TOK_LPAREN) {
depth++;
}
if (tmp.cur.type == TOK_RPAREN) {
depth--;
}
minic_lex_next(&tmp);
}
// tmp.pos sits just past ')', where the body starts
body_pos = tmp.pos;
}
for (;;) {
e->continuing = false;
e->lex.pos = cond_pos;
minic_lex_next(&e->lex);
int cond = minic_val_is_true(minic_parse_cond(e));
if (!cond || e->returning || e->breaking) {
e->lex.pos = body_pos;
minic_lex_next(&e->lex);
minic_skip_block(e);
e->breaking = false;
break;
}
e->lex.pos = body_pos;
minic_lex_next(&e->lex);
minic_parse_block(e);
if (e->returning || e->breaking) {
e->breaking = false;
break;
}
e->lex.pos = incr_pos;
minic_lex_next(&e->lex);
minic_parse_for_incr(e);
}
return;
}
if (e->lex.cur.type == TOK_STAR) {
// Pointer write: *expr = val; or *expr += val; or *expr++; etc.
minic_lex_next(&e->lex);
minic_val_t pv = minic_parse_primary(e);
if (e->lex.cur.type == TOK_INC || e->lex.cur.type == TOK_DEC) {
double delta = MINIC_INC_DELTA(&e->lex);
minic_lex_next(&e->lex);
minic_store_op(pv, TOK_PLUS_ASSIGN, minic_val_float((float)delta));
minic_expect(e, TOK_SEMICOLON);
return;
}
minic_tok_type_t op = e->lex.cur.type; // TOK_ASSIGN or a compound assign
minic_lex_next(&e->lex);
minic_store_op(pv, op, minic_parse_cond(e));
minic_expect(e, TOK_SEMICOLON);
return;
}
if (e->lex.cur.type == TOK_BREAK) {
minic_lex_next(&e->lex);
minic_expect(e, TOK_SEMICOLON);
e->breaking = true;
return;
}
if (e->lex.cur.type == TOK_CONTINUE) {
minic_lex_next(&e->lex);
minic_expect(e, TOK_SEMICOLON);
e->continuing = true;
return;
}
if (e->lex.cur.type == TOK_WHILE) {
minic_lex_next(&e->lex);
int cond_pos = e->lex.pos - 1;
for (;;) {
e->continuing = false;
e->lex.pos = cond_pos;
minic_lex_next(&e->lex);
minic_lex_next(&e->lex); // Consume '('
int cond = minic_val_is_true(minic_parse_cond(e));
minic_lex_next(&e->lex); // Consume ')'
if (!cond || e->returning || e->breaking) {
minic_skip_block(e);
e->breaking = false;
break;
}
minic_parse_block(e);
if (e->breaking) {
e->breaking = false;
break;
}
}
return;
}
minic_error(e, "unexpected token at start of statement");
}
static void minic_parse_block(minic_env_t *e) {
int saved_var_count = e->var_count;
int saved_vartype_count = e->vartype_count;
if (e->lex.cur.type != TOK_LBRACE) {
// Single-statement body without braces
minic_parse_stmt(e);
}
else {
minic_expect(e, TOK_LBRACE);
while (e->lex.cur.type != TOK_RBRACE && e->lex.cur.type != TOK_EOF && !e->returning && !e->breaking && !e->continuing && !e->error) {
minic_parse_stmt(e);
}
if (e->lex.cur.type == TOK_RBRACE) {
minic_lex_next(&e->lex); // Consume '}'
}
else {
// Left early (return/break/continue/error): skip to the matching '}'
int depth = 1;
while (depth > 0 && e->lex.cur.type != TOK_EOF) {
if (e->lex.cur.type == TOK_LBRACE) {
depth++;
}
if (e->lex.cur.type == TOK_RBRACE) {
depth--;
}
minic_lex_next(&e->lex);
}
}
}
e->var_count = saved_var_count;
e->vartype_count = saved_vartype_count;
}
// ██████╗ ██╗ ██╗███╗ ██╗
// ██╔══██╗██║ ██║████╗ ██║
// ██████╔╝██║ ██║██╔██╗ ██║
// ██╔══██╗██║ ██║██║╚██╗██║
// ██║ ██║╚██████╔╝██║ ╚████║
// ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
// Consume a type specifier, return true if found
static bool minic_lex_type(minic_env_t *e) {
if (minic_tok_is_type(e->lex.cur.type)) {
minic_lex_next(&e->lex);
return true;
}
if (e->lex.cur.type == TOK_STRUCT) {
minic_lex_next(&e->lex); // Consume 'struct'
minic_lex_next(&e->lex); // Consume struct name
return true;
}
// Typedef'd struct or int name used as a type specifier
if (e->lex.cur.type == TOK_IDENT && (minic_struct_get(e, e->lex.cur.text) != NULL || minic_is_int_typedef(e->lex.cur.text))) {
minic_lex_next(&e->lex);
return true;
}
return false;
}
// Zero pass: scan for enum and struct definitions
static void minic_register_structs(minic_env_t *e) {
minic_lexer_t l = {0};
l.src = e->lex.src;
minic_lex_next(&l);
while (l.cur.type != TOK_EOF) {
bool is_typedef = (l.cur.type == TOK_TYPEDEF);
if (is_typedef) {
minic_lex_next(&l); // Consume 'typedef'
}
if (l.cur.type == TOK_ENUM) {
minic_lex_next(&l); // Consume 'enum'
if (l.cur.type == TOK_IDENT) {
minic_lex_next(&l); // Optional tag name
}
if (l.cur.type != TOK_LBRACE) {
continue;
}
minic_lex_next(&l); // Consume '{'
int val = 0;
while (l.cur.type != TOK_RBRACE && l.cur.type != TOK_EOF) {
if (l.cur.type == TOK_IDENT) {
char cname[MINIC_MAX_NAME];
strncpy(cname, l.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&l);
if (l.cur.type == TOK_ASSIGN) {
minic_lex_next(&l); // Consume '='
val = (int)minic_val_to_d(l.cur.val);
minic_lex_next(&l); // Consume number
}
minic_enum_const_add(cname, val);
val++;
}
else {
minic_lex_next(&l);
}
if (l.cur.type == TOK_COMMA) {
minic_lex_next(&l);
}
}
if (l.cur.type == TOK_RBRACE) {
minic_lex_next(&l);
}
if (is_typedef && l.cur.type == TOK_IDENT) {
minic_int_typedef_add(l.cur.text);
minic_lex_next(&l);
}
}
else if (l.cur.type == TOK_STRUCT) {
minic_lex_next(&l); // Consume 'struct'
// Optional struct tag name
char struct_name[MINIC_MAX_NAME] = "";
if (l.cur.type == TOK_IDENT) {
strncpy(struct_name, l.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&l); // Consume struct name
}
if (l.cur.type != TOK_LBRACE) {
continue; // Forward decl or typedef-without-body
}
if (e->struct_count >= e->struct_cap) {
break;
}
minic_struct_t *def = &e->structs[e->struct_count];
memset(def, 0, sizeof(minic_struct_t));
strncpy(def->name, struct_name, MINIC_MAX_NAME - 1);
minic_lex_next(&l); // Consume '{'
while (l.cur.type != TOK_RBRACE && l.cur.type != TOK_EOF) {
// Field type: builtin keyword, 'struct name' or typedef'd name
if (l.cur.type == TOK_STRUCT) {
minic_lex_next(&l);
minic_lex_next(&l);
}
else if (minic_tok_is_type(l.cur.type) || l.cur.type == TOK_IDENT) {
minic_lex_next(&l);
}
else {
minic_lex_next(&l);
continue;
}
if (l.cur.type == TOK_STAR) {
minic_lex_next(&l);
}
if (l.cur.type == TOK_IDENT && def->field_count < MINIC_MAX_STRUCT_FIELDS) {
strncpy(def->fields[def->field_count++], l.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&l);
}
while (l.cur.type != TOK_SEMICOLON && l.cur.type != TOK_RBRACE && l.cur.type != TOK_EOF) {
minic_lex_next(&l);
}
if (l.cur.type == TOK_SEMICOLON) {
minic_lex_next(&l);
}
}
if (l.cur.type == TOK_RBRACE) {
minic_lex_next(&l);
}
if (is_typedef && l.cur.type == TOK_IDENT) {
// typedef struct [Name] { ... } alias;
char alias[MINIC_MAX_NAME];
strncpy(alias, l.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&l); // Consume alias name
if (struct_name[0] != '\0') {
// Register under the tag name, plus a copy under the alias name
e->struct_count++;
if (e->struct_count < e->struct_cap) {
minic_struct_t *adef = &e->structs[e->struct_count++];
*adef = *def;
strncpy(adef->name, alias, MINIC_MAX_NAME - 1);
}
}
else {
// Anonymous struct: name it after the alias
strncpy(def->name, alias, MINIC_MAX_NAME - 1);
e->struct_count++;
}
}
else if (struct_name[0] != '\0') {
// Plain struct definition: must have a tag name to be usable
e->struct_count++;
}
}
else {
minic_lex_next(&l);
continue;
}
while (l.cur.type != TOK_SEMICOLON && l.cur.type != TOK_EOF) {
minic_lex_next(&l);
}
if (l.cur.type == TOK_SEMICOLON) {
minic_lex_next(&l);
}
}
}
// First pass: register all function definitions and globals, stop at 'main'
static void minic_register_funcs(minic_env_t *e) {
while (e->lex.cur.type != TOK_EOF) {
// Remember the return-type token before consuming it
minic_type_t ret_type = minic_tok_to_type(e->lex.cur.type);
char decl_struct[MINIC_MAX_NAME] = "";
if (e->lex.cur.type == TOK_IDENT && minic_struct_get(e, e->lex.cur.text) != NULL) {
strncpy(decl_struct, e->lex.cur.text, MINIC_MAX_NAME - 1);
}
if (!minic_lex_type(e)) {
if (e->lex.cur.type == TOK_IDENT) {
// Unknown typedef type
strncpy(decl_struct, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
ret_type = MINIC_T_PTR;
}
else {
minic_lex_next(&e->lex);
continue;
}
}
if (e->lex.cur.type == TOK_STAR) {
ret_type = MINIC_T_PTR;
minic_lex_next(&e->lex);
}
if (e->lex.cur.type != TOK_IDENT) {
continue;
}
char fname[MINIC_MAX_NAME];
strncpy(fname, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
if (e->lex.cur.type != TOK_LPAREN) {
// Global variable declaration: type [*] ident [= expr] ;
if (decl_struct[0] != '\0') {
minic_vartype_set(e, fname, decl_struct);
}
minic_val_t init = minic_val_coerce(0.0, ret_type);
if (e->lex.cur.type == TOK_ASSIGN) {
minic_lex_next(&e->lex); // Consume '='
init = minic_parse_cond(e);
}
minic_var_decl(e, fname, ret_type, init);
while (e->lex.cur.type != TOK_SEMICOLON && e->lex.cur.type != TOK_EOF) {
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_SEMICOLON) {
minic_lex_next(&e->lex);
}
continue;
}
minic_lex_next(&e->lex); // Consume '('
minic_func_t fn = {0};
strncpy(fn.name, fname, MINIC_MAX_NAME - 1);
fn.ret_type = ret_type;
while (e->lex.cur.type != TOK_RPAREN && e->lex.cur.type != TOK_EOF) {
char pstruct[MINIC_MAX_NAME] = "";
minic_type_t ptype = MINIC_T_INT;
if (e->lex.cur.type == TOK_STRUCT) {
minic_lex_next(&e->lex);
strncpy(pstruct, e->lex.cur.text, MINIC_MAX_NAME - 1);
minic_lex_next(&e->lex);
ptype = MINIC_T_PTR;
}
else {
// Capture typedef'd struct name before consuming the type token
if (e->lex.cur.type == TOK_IDENT && minic_struct_get(e, e->lex.cur.text) != NULL) {
strncpy(pstruct, e->lex.cur.text, MINIC_MAX_NAME - 1);
}
ptype = minic_tok_to_type(e->lex.cur.type);
minic_lex_type(e); // Consume type
}
if (e->lex.cur.type == TOK_STAR) {
ptype = MINIC_T_PTR;
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_IDENT && fn.param_count < MINIC_MAX_PARAMS) {
int pi = fn.param_count++;
strncpy(fn.params[pi], e->lex.cur.text, MINIC_MAX_NAME - 1);
strncpy(fn.param_structs[pi], pstruct, MINIC_MAX_NAME - 1);
fn.param_types[pi] = ptype;
minic_lex_next(&e->lex);
}
if (e->lex.cur.type == TOK_COMMA) {
minic_lex_next(&e->lex);
}
}
minic_lex_next(&e->lex); // Consume ')'
fn.body_pos = e->lex.pos - 1;
if (strcmp(fname, "main") == 0) {
break;
}
if (e->func_count < e->func_cap) {
e->funcs[e->func_count++] = fn;
}
// Skip function body
int depth = 1;
minic_lex_next(&e->lex); // Consume '{'
while (depth > 0 && e->lex.cur.type != TOK_EOF) {
if (e->lex.cur.type == TOK_LBRACE) {
depth++;
}
if (e->lex.cur.type == TOK_RBRACE) {
depth--;
}
minic_lex_next(&e->lex);
}
}
}
minic_ctx_t *minic_eval_named(const char *src, const char *filename) {
minic_register_builtins();
minic_ctx_t *ctx = (minic_ctx_t *)calloc(1, sizeof(minic_ctx_t));
ctx->mem = (minic_u8 *)calloc(1, MINIC_MEM_SIZE);
// Copy the source so the context stays valid after the caller frees its buffer
int src_len = (int)strlen(src);
ctx->src_copy = (char *)malloc(src_len + 1);
memcpy(ctx->src_copy, src, src_len + 1);
// Save and install arena pointers so minic_alloc and the lexer use this context
minic_u8 *prev_mem = minic_active_mem;
int *prev_mem_used = minic_active_mem_used;
minic_active_mem = ctx->mem;
minic_active_mem_used = &ctx->mem_used;
minic_env_t *e = &ctx->e;
e->lex.src = ctx->src_copy;
e->filename = filename;
e->var_cap = MINIC_MAX_VARS;
e->vars = minic_alloc(e->var_cap * (int)sizeof(minic_var_t));
e->arr_cap = 32;
e->arrs = minic_alloc(e->arr_cap * (int)sizeof(minic_arr_t));
e->arr_data = minic_alloc(512 * (int)sizeof(minic_val_t));
e->arr_data_used = minic_alloc((int)sizeof(int));
*e->arr_data_used = 0;
e->func_cap = 32;
e->funcs = minic_alloc(e->func_cap * (int)sizeof(minic_func_t));
e->struct_cap = MINIC_MAX_STRUCTS;
e->structs = minic_alloc(e->struct_cap * (int)sizeof(minic_struct_t));
e->vartype_cap = MINIC_MAX_VARTYPES;
e->vartypes = minic_alloc(e->vartype_cap * (int)sizeof(minic_vartype_t));
// Seed env with globally pre-registered struct definitions
for (int i = 0; i < minic_struct_count && e->struct_count < e->struct_cap; ++i) {
e->structs[e->struct_count++] = minic_structs[i];
}
minic_register_structs(e);
minic_lex_next(&e->lex);
minic_register_funcs(e);
for (int i = 0; i < e->func_count; ++i) {
e->funcs[i].ctx = ctx;
}
if (e->lex.cur.type == TOK_RPAREN) {
minic_lex_next(&e->lex);
}
minic_parse_block(e);
minic_active_mem = prev_mem;
minic_active_mem_used = prev_mem_used;
ctx->result = e->error ? -1.0f : (float)minic_val_to_d(e->return_val);
return ctx;
}
minic_ctx_t *minic_eval(const char *src) {
return minic_eval_named(src, "<script>");
}
void minic_ctx_free(minic_ctx_t *ctx) {
if (ctx != NULL) {
free(ctx->mem);
free(ctx->src_copy);
free(ctx);
}
}
float minic_ctx_result(minic_ctx_t *ctx) {
return ctx != NULL ? ctx->result : -1.0f;
}
// ███████╗██╗ ██╗████████╗███████╗██████╗ ███╗ ██╗ █████╗ ██╗
// ██╔════╝╚██╗██╔╝╚══██╔══╝██╔════╝██╔══██╗████╗ ██║██╔══██╗██║
// █████╗ ╚███╔╝ ██║ █████╗ ██████╔╝██╔██╗ ██║███████║██║
// ██╔══╝ ██╔██╗ ██║ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══██║██║
// ███████╗██╔╝ ██╗ ██║ ███████╗██║ ██║██║ ╚████║██║ ██║███████╗
// ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝
typedef struct {
char name[MINIC_MAX_NAME];
int value;
} minic_enum_const_t;
typedef struct {
char name[MINIC_MAX_NAME];
const void *ptr; // points at the live host variable
minic_type_t type; // MINIC_T_INT or MINIC_T_FLOAT
} minic_global_t;
static minic_ext_func_t minic_ext_funcs[MINIC_MAX_EXTFUNS];
static int minic_ext_func_count = 0;
static minic_enum_const_t minic_enum_consts[MINIC_MAX_ENUM_CONSTS];
static int minic_enum_const_count = 0;
static char minic_int_typedefs[MINIC_MAX_INT_TYPEDEFS][MINIC_MAX_NAME];
static int minic_int_typedef_count = 0;
static minic_global_t minic_globals[MINIC_MAX_GLOBALS];
static int minic_global_count = 0;
minic_struct_t minic_structs[MINIC_MAX_STRUCTS];
int minic_struct_count = 0;
static minic_struct_t *minic_struct_cur = NULL;
void minic_struct_begin(const char *name, int size) {
minic_struct_cur = NULL;
for (int i = 0; i < minic_struct_count; ++i) {
if (strcmp(minic_structs[i].name, name) == 0) {
minic_struct_cur = &minic_structs[i];
break;
}
}
if (minic_struct_cur == NULL) {
if (minic_struct_count >= MINIC_MAX_STRUCTS) {
return;
}
minic_struct_cur = &minic_structs[minic_struct_count++];
}
memset(minic_struct_cur, 0, sizeof(minic_struct_t));
strncpy(minic_struct_cur->name, name, MINIC_MAX_NAME - 1);
minic_struct_cur->size = size;
}
void minic_struct_field(const char *field, int offset, minic_type_t type, minic_type_t deref_type, const char *struct_type) {
minic_struct_t *s = minic_struct_cur;
if (s == NULL || s->field_count >= MINIC_MAX_STRUCT_FIELDS) {
return;
}
int i = s->field_count++;
strncpy(s->fields[i], field, MINIC_MAX_NAME - 1);
s->offsets[i] = offset;
s->types[i] = type;
s->deref_types[i] = deref_type;
if (struct_type != NULL) {
strncpy(s->field_structs[i], struct_type, MINIC_MAX_NAME - 1);
}
if (offset >= 0) {
s->native = true;
}
}
void minic_register_struct(const char *name, const char **fields, int field_count) {
minic_struct_begin(name, 0);
for (int i = 0; i < field_count; ++i) {
minic_struct_field(fields[i], -1, MINIC_T_INT, MINIC_T_INT, NULL);
}
}
void minic_enum_const_add(const char *name, int value) {
for (int i = 0; i < minic_enum_const_count; ++i) {
if (strcmp(minic_enum_consts[i].name, name) == 0) {
return;
}
}
if (minic_enum_const_count >= MINIC_MAX_ENUM_CONSTS) {
return;
}
strncpy(minic_enum_consts[minic_enum_const_count].name, name, MINIC_MAX_NAME - 1);
minic_enum_consts[minic_enum_const_count].value = value;
minic_enum_const_count++;
}
int minic_enum_const_get(const char *name) {
for (int i = 0; i < minic_enum_const_count; ++i) {
if (strcmp(minic_enum_consts[i].name, name) == 0) {
return minic_enum_consts[i].value;
}
}
return -1;
}
void minic_register_global(const char *name, const void *ptr, minic_type_t type) {
for (int i = 0; i < minic_global_count; ++i) {
if (strcmp(minic_globals[i].name, name) == 0) {
minic_globals[i].ptr = ptr;
minic_globals[i].type = type;
return;
}
}
if (minic_global_count >= MINIC_MAX_GLOBALS) {
return;
}
strncpy(minic_globals[minic_global_count].name, name, MINIC_MAX_NAME - 1);
minic_globals[minic_global_count].ptr = ptr;
minic_globals[minic_global_count].type = type;
minic_global_count++;
}
bool minic_global_get(const char *name, minic_val_t *out) {
for (int i = 0; i < minic_global_count; ++i) {
if (strcmp(minic_globals[i].name, name) == 0) {
if (minic_globals[i].type == MINIC_T_FLOAT) {
*out = minic_val_float(*(const float *)minic_globals[i].ptr);
}
else {
*out = minic_val_int(*(const int *)minic_globals[i].ptr);
}
return true;
}
}
return false;
}
void minic_int_typedef_add(const char *name) {
if (minic_is_int_typedef(name) || minic_int_typedef_count >= MINIC_MAX_INT_TYPEDEFS) {
return;
}
strncpy(minic_int_typedefs[minic_int_typedef_count++], name, MINIC_MAX_NAME - 1);
}
bool minic_is_int_typedef(const char *name) {
for (int i = 0; i < minic_int_typedef_count; ++i) {
if (strcmp(minic_int_typedefs[i], name) == 0) {
return true;
}
}
return false;
}
void minic_register_enum(const char *typedef_name, const char **names, const int *values, int count) {
if (typedef_name != NULL) {
minic_int_typedef_add(typedef_name);
}
for (int i = 0; i < count; ++i) {
minic_enum_const_add(names[i], values != NULL ? values[i] : i);
}
}
static minic_type_t minic_sig_char(char c) {
switch (c) {
case 'f':
return MINIC_T_FLOAT;
case 'p':
return MINIC_T_PTR;
case 'b':
return MINIC_T_BOOL;
case 'c':
return MINIC_T_CHAR;
case 'v':
return MINIC_T_VOID;
default:
return MINIC_T_INT;
}
}
static void minic_parse_sig(minic_ext_func_t *ef) {
const char *s = ef->sig;
ef->ret_type = minic_sig_char(*s);
if (*s) {
s++; // skip ret type char
}
if (*s == '(') {
s++; // skip '('
}
ef->param_count = 0;
while (*s && *s != ')') {
if (*s != ',' && ef->param_count < MINIC_MAX_PARAMS) {
ef->param_types[ef->param_count++] = minic_sig_char(*s);
}
s++;
}
}
static minic_ext_func_t *minic_ext_func_add(const char *name) {
minic_ext_func_t *ef = minic_ext_func_get(name);
if (ef == NULL && minic_ext_func_count < MINIC_MAX_EXTFUNS) {
ef = &minic_ext_funcs[minic_ext_func_count++];
memset(ef, 0, sizeof(*ef));
strncpy(ef->name, name, MINIC_MAX_NAME - 1);
}
return ef;
}
void minic_register(const char *name, const char *sig, minic_ext_fn_raw_t fn) {
minic_ext_func_t *ef = minic_ext_func_add(name);
if (ef == NULL) {
return;
}
strncpy(ef->sig, sig != NULL ? sig : "i()", MINIC_MAX_SIG - 1);
ef->fn = fn;
minic_parse_sig(ef);
}
void minic_register_native(const char *name, minic_native_fn_t fn) {
minic_ext_func_t *ef = minic_ext_func_add(name);
if (ef != NULL) {
ef->native_fn = fn;
}
}
minic_ext_func_t *minic_ext_func_get(const char *name) {
for (int i = 0; i < minic_ext_func_count; ++i) {
if (strcmp(minic_ext_funcs[i].name, name) == 0) {
return &minic_ext_funcs[i];
}
}
return NULL;
}
int minic_ext_func_count_get(void) {
return minic_ext_func_count;
}
const char *minic_ext_func_name_at(int i) {
return minic_ext_funcs[i].name;
}
const char *minic_ext_func_sig_at(int i) {
return minic_ext_funcs[i].sig;
}
int minic_global_count_get(void) {
return minic_global_count;
}
const char *minic_global_name_at(int i) {
return minic_globals[i].name;
}
minic_type_t minic_global_type_at(int i) {
return minic_globals[i].type;
}
int minic_enum_const_count_get(void) {
return minic_enum_const_count;
}
const char *minic_enum_const_name_at(int i) {
return minic_enum_consts[i].name;
}
int minic_enum_const_value_at(int i) {
return minic_enum_consts[i].value;
}
// ██████╗ ██╗███████╗██████╗ █████╗ ████████╗ ██████╗██╗ ██╗
// ██╔══██╗██║██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██║ ██║
// ██║ ██║██║███████╗██████╔╝███████║ ██║ ██║ ███████║
// ██║ ██║██║╚════██║██╔═══╝ ██╔══██║ ██║ ██║ ██╔══██║
// ██████╔╝██║███████║██║ ██║ ██║ ██║ ╚██████╗██║ ██║
// ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
//
// Args are normalized into int/float/ptr slots and the C function is called
// through an exactly-typed cast (required for wasm and all native ABIs).
// One D-line per supported signature; add new combinations to the table below.
typedef union {
int i;
float f;
void *p;
} minic_arg_t;
// Per-class C types and argument accessors
#define TY_i int
#define TY_f float
#define TY_p void *
#define A_i(k) a[k].i
#define A_f(k) a[k].f
#define A_p(k) a[k].p
// C return types and result boxing per minic return class
#define CT_INT int
#define CT_BOOL bool
#define CT_CHAR char
#define CT_FLOAT float
#define CT_PTR void *
#define CT_VOID void
#define RET_INT(x) return minic_val_int(x)
#define RET_BOOL(x) return minic_val_int((int)(x))
#define RET_CHAR(x) return minic_val_int((int)(x))
#define RET_FLOAT(x) return minic_val_float(x)
#define RET_PTR(x) return minic_val_ptr(x)
#define RET_VOID(x) \
do { \
x; \
return minic_val_int(0); \
} while (0)
// Exactly-typed call builders per arity
#define C0(R) ((CT_##R (*)(void))fn)()
#define C1(R, a0) ((CT_##R (*)(TY_##a0))fn)(A_##a0(0))
#define C2(R, a0, a1) ((CT_##R (*)(TY_##a0, TY_##a1))fn)(A_##a0(0), A_##a1(1))
#define C3(R, a0, a1, a2) ((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2))fn)(A_##a0(0), A_##a1(1), A_##a2(2))
#define C4(R, a0, a1, a2, a3) ((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3))
#define C5(R, a0, a1, a2, a3, a4) ((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4))
#define C6(R, a0, a1, a2, a3, a4, a5) \
((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4, TY_##a5))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4), A_##a5(5))
#define C7(R, a0, a1, a2, a3, a4, a5, a6) \
((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4, TY_##a5, TY_##a6))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4), A_##a5(5), A_##a6(6))
#define C8(R, a0, a1, a2, a3, a4, a5, a6, a7) \
((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4, TY_##a5, TY_##a6, TY_##a7))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4), A_##a5(5), \
A_##a6(6), A_##a7(7))
#define C9(R, a0, a1, a2, a3, a4, a5, a6, a7, a8) \
((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4, TY_##a5, TY_##a6, TY_##a7, TY_##a8))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4), \
A_##a5(5), A_##a6(6), A_##a7(7), A_##a8(8))
#define C10(R, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
((CT_##R (*)(TY_##a0, TY_##a1, TY_##a2, TY_##a3, TY_##a4, TY_##a5, TY_##a6, TY_##a7, TY_##a8, \
TY_##a9))fn)(A_##a0(0), A_##a1(1), A_##a2(2), A_##a3(3), A_##a4(4), A_##a5(5), A_##a6(6), A_##a7(7), A_##a8(8), A_##a9(9))
// Dispatch table entries: match return class + arg descriptor, then call
#define D0(R) \
if (rt == MINIC_T_##R && adesc[0] == '\0') \
RET_##R(C0(R))
#define D1(R, a0) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0) == 0) \
RET_##R(C1(R, a0))
#define D2(R, a0, a1) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1) == 0) \
RET_##R(C2(R, a0, a1))
#define D3(R, a0, a1, a2) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2) == 0) \
RET_##R(C3(R, a0, a1, a2))
#define D4(R, a0, a1, a2, a3) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3) == 0) \
RET_##R(C4(R, a0, a1, a2, a3))
#define D5(R, a0, a1, a2, a3, a4) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4) == 0) \
RET_##R(C5(R, a0, a1, a2, a3, a4))
#define D6(R, a0, a1, a2, a3, a4, a5) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4 #a5) == 0) \
RET_##R(C6(R, a0, a1, a2, a3, a4, a5))
#define D7(R, a0, a1, a2, a3, a4, a5, a6) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4 #a5 #a6) == 0) \
RET_##R(C7(R, a0, a1, a2, a3, a4, a5, a6))
#define D8(R, a0, a1, a2, a3, a4, a5, a6, a7) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4 #a5 #a6 #a7) == 0) \
RET_##R(C8(R, a0, a1, a2, a3, a4, a5, a6, a7))
#define D9(R, a0, a1, a2, a3, a4, a5, a6, a7, a8) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4 #a5 #a6 #a7 #a8) == 0) \
RET_##R(C9(R, a0, a1, a2, a3, a4, a5, a6, a7, a8))
#define D10(R, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
if (rt == MINIC_T_##R && strcmp(adesc, #a0 #a1 #a2 #a3 #a4 #a5 #a6 #a7 #a8 #a9) == 0) \
RET_##R(C10(R, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9))
minic_val_t minic_dispatch(minic_ext_func_t *ef, minic_val_t *args, int argc) {
if (ef->native_fn != NULL) {
return ef->native_fn(args, argc);
}
// Normalize args by declared param type, build the arg descriptor
minic_arg_t a[MINIC_MAX_PARAMS] = {0};
char adesc[MINIC_MAX_PARAMS + 1];
int n = argc < ef->param_count ? argc : ef->param_count;
for (int i = 0; i < n; i++) {
double dv = minic_val_to_d(args[i]);
switch (ef->param_types[i]) {
case MINIC_T_FLOAT:
a[i].f = (float)dv;
adesc[i] = 'f';
break;
case MINIC_T_EMBED:
case MINIC_T_PTR:
a[i].p = (args[i].type == MINIC_T_PTR) ? args[i].p : ((dv == 0.0) ? NULL : (void *)(uintptr_t)(uint64_t)dv);
adesc[i] = 'p';
break;
default:
a[i].i = (int)dv;
adesc[i] = 'i';
break;
}
}
adesc[n] = '\0';
minic_ext_fn_raw_t fn = ef->fn;
minic_type_t rt = ef->ret_type;
D0(INT);
D1(INT, i);
D1(INT, f);
D1(INT, p);
D2(INT, i, i);
D2(INT, f, f);
D2(INT, i, p);
D2(INT, p, f);
D2(INT, p, i);
D2(INT, p, p);
D3(INT, i, i, i);
D3(INT, p, i, i);
D3(INT, p, i, p);
D3(INT, p, p, i);
D3(INT, p, p, p);
D4(INT, i, i, i, i);
D4(INT, p, i, i, p);
D4(INT, p, p, p, p);
D6(INT, p, p, f, f, f, f);
D6(INT, p, p, p, i, i, i);
D7(INT, p, i, f, f, i, p, p);
D7(INT, p, i, i, i, i, i, i);
D0(BOOL);
D1(BOOL, i);
D1(BOOL, f);
D1(BOOL, p);
D2(BOOL, p, i);
D2(BOOL, p, p);
D3(BOOL, p, i, i);
D3(BOOL, p, i, p);
D3(BOOL, p, p, i);
D3(BOOL, p, p, p);
D4(BOOL, f, f, f, f);
D4(BOOL, p, i, p, p);
D4(BOOL, p, p, i, i);
D5(BOOL, p, p, i, i, i);
D6(BOOL, p, i, i, i, i, i);
D0(CHAR);
D1(CHAR, i);
D1(CHAR, p);
D2(CHAR, p, i);
D2(CHAR, p, p);
D0(FLOAT);
D1(FLOAT, f);
D1(FLOAT, i);
D1(FLOAT, p);
D2(FLOAT, f, f);
D2(FLOAT, p, f);
D2(FLOAT, p, i);
D2(FLOAT, p, p);
D3(FLOAT, f, f, f);
D3(FLOAT, p, f, f);
D3(FLOAT, p, p, i);
D4(FLOAT, f, f, f, f);
D4(FLOAT, p, f, f, f);
D4(FLOAT, p, p, i, f);
D5(FLOAT, f, f, f, f, f);
D5(FLOAT, p, f, f, f, f);
D5(FLOAT, p, i, p, i, i);
D6(FLOAT, f, f, f, f, f, f);
D6(FLOAT, p, f, f, f, f, f);
D7(FLOAT, f, f, f, f, f, f, f);
D7(FLOAT, p, f, f, f, f, f, f);
D8(FLOAT, p, f, f, f, f, f, f, f);
D9(FLOAT, f, f, f, f, f, f, f, f, f);
D9(FLOAT, p, p, f, f, i, f, i, i, i);
D0(VOID);
D1(VOID, f);
D1(VOID, i);
D1(VOID, p);
D2(VOID, f, f);
D2(VOID, f, p);
D2(VOID, i, i);
D2(VOID, p, f);
D2(VOID, p, i);
D2(VOID, p, p);
D3(VOID, f, f, f);
D3(VOID, i, i, f);
D3(VOID, i, i, i);
D3(VOID, i, p, p);
D3(VOID, p, f, f);
D3(VOID, p, i, f);
D3(VOID, p, i, i);
D3(VOID, p, i, p);
D3(VOID, p, p, f);
D3(VOID, p, p, i);
D3(VOID, p, p, p);
D4(VOID, f, f, f, f);
D4(VOID, f, f, f, i);
D4(VOID, f, f, f, p);
D4(VOID, i, i, i, i);
D4(VOID, p, f, f, f);
D4(VOID, p, i, i, f);
D4(VOID, p, i, i, i);
D4(VOID, p, i, i, p);
D4(VOID, p, i, p, i);
D4(VOID, p, p, i, f);
D4(VOID, p, p, i, i);
D4(VOID, p, p, i, p);
D4(VOID, p, p, p, i);
D4(VOID, p, p, p, p);
D5(VOID, f, f, f, f, f);
D5(VOID, f, f, f, f, i);
D5(VOID, f, f, f, i, f);
D5(VOID, i, f, f, f, f);
D5(VOID, p, f, f, f, f);
D5(VOID, p, f, f, i, i);
D5(VOID, p, i, i, i, i);
D5(VOID, p, p, p, p, p);
D6(VOID, f, f, f, f, f, f);
D6(VOID, f, f, f, f, i, f);
D6(VOID, i, i, f, f, f, f);
D6(VOID, i, i, i, i, i, i);
D6(VOID, p, i, i, f, f, f);
D6(VOID, p, p, p, i, i, f);
D7(VOID, p, f, f, f, f, f, f);
D7(VOID, p, f, f, f, f, i, i);
D7(VOID, p, i, i, f, f, f, f);
D9(VOID, p, f, f, f, f, f, f, f, f);
D10(VOID, p, p, p, p, p, p, p, p, p, p);
D0(PTR);
D1(PTR, f);
D1(PTR, i);
D1(PTR, p);
D2(PTR, f, f);
D2(PTR, i, i);
D2(PTR, p, i);
D2(PTR, p, p);
D3(PTR, f, f, f);
D3(PTR, i, i, i);
D3(PTR, p, f, f);
D3(PTR, p, i, i);
D3(PTR, p, p, i);
D3(PTR, p, p, p);
D4(PTR, f, f, f, f);
D4(PTR, p, p, p, i);
D4(PTR, p, p, p, p);
D5(PTR, f, f, f, f, f);
D5(PTR, p, i, i, p, i);
D5(PTR, p, p, i, i, i);
D5(PTR, p, p, p, p, f);
D5(PTR, p, p, p, p, p);
fprintf(stderr, "minic: unsupported signature '%s' for '%s'\n", ef->sig, ef->name);
return minic_val_int(0);
}