Research-Stack/2-Search-Space/tardygrada/src/compiler/compiler.c

600 lines
19 KiB
C

/*
* Tardygrada — Compiler Implementation
*
* Simple recursive descent parser.
* Emits spawn instructions for the VM.
*/
#include "compiler.h"
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
/* ============================================
* Parser State
* ============================================ */
typedef struct {
tardy_lexer_t *lex;
int pos; /* current token index */
tardy_program_t *prog;
} tardy_parser_t;
static tardy_token_t *current(tardy_parser_t *p)
{
if (p->pos >= p->lex->count)
return &p->lex->tokens[p->lex->count - 1]; /* EOF */
return &p->lex->tokens[p->pos];
}
static tardy_token_t *advance_tok(tardy_parser_t *p)
{
tardy_token_t *tok = current(p);
if (tok->type != TOK_EOF)
p->pos++;
return tok;
}
static bool check(tardy_parser_t *p, tardy_tok_type_t type)
{
return current(p)->type == type;
}
static bool match(tardy_parser_t *p, tardy_tok_type_t type)
{
if (check(p, type)) {
advance_tok(p);
return true;
}
return false;
}
static void error(tardy_parser_t *p, const char *msg)
{
tardy_token_t *tok = current(p);
snprintf(p->prog->error, sizeof(p->prog->error),
"line %d col %d: %s (got '%s')",
tok->line, tok->col, msg, tok->text);
p->prog->has_error = true;
}
static bool expect(tardy_parser_t *p, tardy_tok_type_t type, const char *msg)
{
if (check(p, type)) {
advance_tok(p);
return true;
}
error(p, msg);
return false;
}
static void emit_inst(tardy_parser_t *p, tardy_instruction_t inst)
{
if (p->prog->count < TARDY_MAX_INSTRUCTIONS)
p->prog->instructions[p->prog->count++] = inst;
}
/* ============================================
* Parse type annotation
* ============================================ */
static tardy_type_t parse_type(tardy_parser_t *p)
{
tardy_token_t *tok = current(p);
tardy_type_t type = TARDY_TYPE_UNIT;
switch (tok->type) {
case TOK_INT: type = TARDY_TYPE_INT; break;
case TOK_FLOAT: type = TARDY_TYPE_FLOAT; break;
case TOK_STR: type = TARDY_TYPE_STR; break;
case TOK_BOOL: type = TARDY_TYPE_BOOL; break;
case TOK_FACT: type = TARDY_TYPE_FACT; break;
default:
error(p, "expected type (int, float, str, bool, Fact)");
return TARDY_TYPE_UNIT;
}
advance_tok(p);
return type;
}
/* ============================================
* Parse trust annotation (@verified, @sovereign, etc.)
* ============================================ */
static tardy_trust_t parse_trust(tardy_parser_t *p)
{
if (check(p, TOK_AT_VERIFIED)) { advance_tok(p); return TARDY_TRUST_VERIFIED; }
if (check(p, TOK_AT_HARDENED)) { advance_tok(p); return TARDY_TRUST_HARDENED; }
if (check(p, TOK_AT_SOVEREIGN)) { advance_tok(p); return TARDY_TRUST_SOVEREIGN; }
return TARDY_TRUST_DEFAULT; /* no annotation = default immutable */
}
/* ============================================
* Parse value binding
*
* let x: int = 5 @verified (immutable)
* x: int = 5 (mutable)
* ============================================ */
static void parse_binding(tardy_parser_t *p, bool immutable)
{
tardy_instruction_t inst = {0};
inst.opcode = OP_SPAWN_VALUE;
/* Name */
if (!check(p, TOK_IDENT)) {
error(p, "expected identifier");
return;
}
strncpy(inst.name, current(p)->text, sizeof(inst.name) - 1);
advance_tok(p);
/* : type */
if (!expect(p, TOK_COLON, "expected ':'"))
return;
inst.type = parse_type(p);
/* = value */
if (!expect(p, TOK_EQUALS, "expected '='"))
return;
tardy_token_t *val = current(p);
switch (val->type) {
case TOK_INT_LIT:
inst.int_val = 0;
{
const char *s = val->text;
int neg = 0;
int i = 0;
if (s[0] == '-') { neg = 1; i = 1; }
for (; s[i]; i++)
inst.int_val = inst.int_val * 10 + (s[i] - '0');
if (neg) inst.int_val = -inst.int_val;
}
advance_tok(p);
break;
case TOK_FLOAT_LIT:
/* Simple float parse */
{
const char *s = val->text;
double v = 0.0;
int neg = 0;
int i = 0;
if (s[0] == '-') { neg = 1; i = 1; }
for (; s[i] && s[i] != '.'; i++)
v = v * 10.0 + (s[i] - '0');
if (s[i] == '.') {
i++;
double frac = 0.1;
for (; s[i]; i++) {
v += (s[i] - '0') * frac;
frac *= 0.1;
}
}
inst.float_val = neg ? -v : v;
}
advance_tok(p);
break;
case TOK_STR_LIT:
strncpy(inst.str_val, val->text, sizeof(inst.str_val) - 1);
advance_tok(p);
break;
case TOK_BOOL_LIT:
inst.bool_val = (strcmp(val->text, "true") == 0);
advance_tok(p);
break;
case TOK_RECEIVE:
/* receive("prompt") — pending slot, filled via MCP */
inst.opcode = OP_RECEIVE;
advance_tok(p); /* skip 'receive' */
if (!expect(p, TOK_LPAREN, "expected '(' after receive"))
return;
if (!check(p, TOK_STR_LIT)) {
error(p, "expected string prompt for receive()");
return;
}
strncpy(inst.str_val, current(p)->text, sizeof(inst.str_val) - 1);
advance_tok(p);
if (!expect(p, TOK_RPAREN, "expected ')' after prompt"))
return;
/* Optional: grounded_in(ontology) */
if (check(p, TOK_GROUNDED_IN)) {
advance_tok(p);
if (!expect(p, TOK_LPAREN, "expected '(' after grounded_in"))
return;
if (check(p, TOK_IDENT) || check(p, TOK_STR_LIT)) {
strncpy(inst.ontology, current(p)->text,
sizeof(inst.ontology) - 1);
advance_tok(p);
}
if (!expect(p, TOK_RPAREN, "expected ')' after ontology"))
return;
inst.grounded = true;
}
break;
case TOK_EXEC:
/* exec("command") — fork/exec shell command, capture stdout */
inst.opcode = OP_EXEC;
advance_tok(p); /* skip 'exec' */
if (!expect(p, TOK_LPAREN, "expected '(' after exec"))
return;
if (!check(p, TOK_STR_LIT)) {
error(p, "expected command string for exec()");
return;
}
strncpy(inst.str_val, current(p)->text, sizeof(inst.str_val) - 1);
advance_tok(p);
if (!expect(p, TOK_RPAREN, "expected ')' after command"))
return;
/* Optional: grounded_in(ontology) */
if (check(p, TOK_GROUNDED_IN)) {
advance_tok(p);
if (!expect(p, TOK_LPAREN, "expected '(' after grounded_in"))
return;
if (check(p, TOK_IDENT) || check(p, TOK_STR_LIT)) {
strncpy(inst.ontology, current(p)->text,
sizeof(inst.ontology) - 1);
advance_tok(p);
}
if (!expect(p, TOK_RPAREN, "expected ')' after ontology"))
return;
inst.grounded = true;
}
break;
default:
error(p, "expected value (int, float, string, bool, receive(), exec())");
return;
}
/* Optional trust annotation */
if (immutable)
inst.trust = parse_trust(p);
else
inst.trust = TARDY_TRUST_MUTABLE;
emit_inst(p, inst);
}
/* ============================================
* Parse agent body
*
* agent Name {
* let x: int = 5
* y: str = "hello"
* }
* ============================================ */
static void parse_agent(tardy_parser_t *p)
{
/* agent keyword already consumed */
/* Name */
if (!check(p, TOK_IDENT)) {
error(p, "expected agent name");
return;
}
tardy_instruction_t agent_inst = {0};
agent_inst.opcode = OP_SPAWN_AGENT;
strncpy(agent_inst.name, current(p)->text, sizeof(agent_inst.name) - 1);
strncpy(p->prog->agent_name, current(p)->text,
sizeof(p->prog->agent_name) - 1);
advance_tok(p);
/* Optional trust annotation on the agent itself */
agent_inst.trust = parse_trust(p);
emit_inst(p, agent_inst);
/* Optional @semantics(key: value, ...) block */
if (check(p, TOK_AT_SEMANTICS)) {
advance_tok(p); /* skip @semantics */
if (!expect(p, TOK_LPAREN, "expected '(' after @semantics"))
return;
while (!check(p, TOK_RPAREN) && !check(p, TOK_EOF) && !p->prog->has_error) {
/* Parse key: value pairs */
tardy_instruction_t sem_inst = {0};
sem_inst.opcode = OP_SET_SEMANTICS;
/* Key is like truth.min_confidence — idents + dots */
char key[64];
int klen = 0;
while ((check(p, TOK_IDENT) || check(p, TOK_DOT)) &&
klen < 62 && !p->prog->has_error) {
const char *t = current(p)->text;
int tlen = (int)strlen(t);
if (klen + tlen < 63) {
memcpy(key + klen, t, tlen);
klen += tlen;
}
advance_tok(p);
}
key[klen] = '\0';
strncpy(sem_inst.sem_key, key, sizeof(sem_inst.sem_key) - 1);
if (!expect(p, TOK_COLON, "expected ':' in @semantics"))
return;
/* Value: number or ident */
if (check(p, TOK_INT_LIT) || check(p, TOK_FLOAT_LIT)) {
strncpy(sem_inst.sem_value, current(p)->text,
sizeof(sem_inst.sem_value) - 1);
advance_tok(p);
} else {
error(p, "expected number value in @semantics");
return;
}
emit_inst(p, sem_inst);
/* Optional comma */
if (check(p, TOK_COMMA))
advance_tok(p);
}
if (!expect(p, TOK_RPAREN, "expected ')' after @semantics"))
return;
}
/* Body */
if (!expect(p, TOK_LBRACE, "expected '{'"))
return;
while (!check(p, TOK_RBRACE) && !check(p, TOK_EOF) && !p->prog->has_error) {
if (match(p, TOK_LET)) {
/* Immutable binding */
parse_binding(p, true);
} else if (match(p, TOK_FORK)) {
/* fork "path/to/module.tardy" as ModuleName */
tardy_instruction_t finst = {0};
finst.opcode = OP_FORK;
if (!check(p, TOK_STR_LIT)) {
error(p, "expected file path string after fork");
return;
}
strncpy(finst.str_val, current(p)->text,
sizeof(finst.str_val) - 1);
advance_tok(p);
/* Optional: as Name */
if (check(p, TOK_IDENT) &&
strcmp(current(p)->text, "as") == 0) {
advance_tok(p);
if (check(p, TOK_IDENT)) {
strncpy(finst.name, current(p)->text,
sizeof(finst.name) - 1);
advance_tok(p);
}
}
emit_inst(p, finst);
} else if (match(p, TOK_COORDINATE)) {
/* coordinate [a, b, c] on("task") consensus(ProofWeight) */
tardy_instruction_t cinst = {0};
cinst.opcode = OP_COORDINATE;
/* [agent_list] */
if (!expect(p, TOK_LBRACE, "expected '[' or '{' after coordinate"))
return;
char agents[256];
int alen = 0;
while (!check(p, TOK_RBRACE) && !check(p, TOK_EOF)) {
if (check(p, TOK_IDENT)) {
const char *name = current(p)->text;
int nlen = (int)strlen(name);
if (alen > 0 && alen < 254) agents[alen++] = ',';
if (alen + nlen < 255) {
memcpy(agents + alen, name, nlen);
alen += nlen;
}
}
advance_tok(p);
if (check(p, TOK_COMMA)) advance_tok(p);
}
agents[alen] = '\0';
strncpy(cinst.coord_agents, agents, sizeof(cinst.coord_agents) - 1);
if (!expect(p, TOK_RBRACE, "expected ']' or '}'"))
return;
/* on("task description") */
if (check(p, TOK_ON)) {
advance_tok(p);
if (!expect(p, TOK_LPAREN, "expected '(' after on"))
return;
if (check(p, TOK_STR_LIT)) {
strncpy(cinst.coord_task, current(p)->text,
sizeof(cinst.coord_task) - 1);
advance_tok(p);
}
if (!expect(p, TOK_RPAREN, "expected ')' after task"))
return;
}
/* Optional: consensus(method) — just consume for now */
if (check(p, TOK_CONSENSUS)) {
advance_tok(p);
if (check(p, TOK_LPAREN)) {
advance_tok(p);
if (check(p, TOK_IDENT)) advance_tok(p);
if (check(p, TOK_RPAREN)) advance_tok(p);
}
}
emit_inst(p, cinst);
} else if (check(p, TOK_INVARIANT)) {
advance_tok(p);
tardy_instruction_t iinst = {0};
iinst.opcode = OP_ADD_INVARIANT;
if (!expect(p, TOK_LPAREN, "expected '(' after invariant"))
return;
/* Parse invariant type */
if (check(p, TOK_IDENT)) {
const char *itype = current(p)->text;
if (strcmp(itype, "trust_min") == 0) {
iinst.invariant_type = 3; /* TARDY_INVARIANT_TRUST_MIN */
advance_tok(p);
if (!expect(p, TOK_COLON, "expected ':'"))
return;
/* Parse trust level */
if (check(p, TOK_AT_VERIFIED)) {
iinst.inv_trust = TARDY_TRUST_VERIFIED;
advance_tok(p);
} else if (check(p, TOK_AT_HARDENED)) {
iinst.inv_trust = TARDY_TRUST_HARDENED;
advance_tok(p);
} else if (check(p, TOK_AT_SOVEREIGN)) {
iinst.inv_trust = TARDY_TRUST_SOVEREIGN;
advance_tok(p);
} else {
error(p, "expected trust level (@verified, @hardened, @sovereign)");
return;
}
} else if (strcmp(itype, "non_empty") == 0) {
iinst.invariant_type = 2; /* TARDY_INVARIANT_NON_EMPTY */
advance_tok(p);
} else if (strcmp(itype, "range") == 0) {
iinst.invariant_type = 1; /* TARDY_INVARIANT_RANGE */
advance_tok(p);
if (!expect(p, TOK_COLON, "expected ':'"))
return;
if (check(p, TOK_INT_LIT)) {
/* parse min */
iinst.inv_min = 0;
const char *s = current(p)->text;
for (int ii = 0; s[ii]; ii++)
iinst.inv_min = iinst.inv_min * 10 + (s[ii] - '0');
advance_tok(p);
}
if (check(p, TOK_COMMA)) advance_tok(p);
if (check(p, TOK_INT_LIT)) {
/* parse max */
iinst.inv_max = 0;
const char *s = current(p)->text;
for (int ii = 0; s[ii]; ii++)
iinst.inv_max = iinst.inv_max * 10 + (s[ii] - '0');
advance_tok(p);
}
} else {
error(p, "unknown invariant type");
return;
}
}
if (!expect(p, TOK_RPAREN, "expected ')'"))
return;
emit_inst(p, iinst);
} else if (match(p, TOK_FREEZE)) {
tardy_instruction_t finst = {0};
finst.opcode = OP_FREEZE;
if (!check(p, TOK_IDENT)) {
error(p, "expected agent name after freeze");
return;
}
strncpy(finst.name, current(p)->text, sizeof(finst.name) - 1);
advance_tok(p);
finst.trust = parse_trust(p);
if (finst.trust < TARDY_TRUST_DEFAULT)
finst.trust = TARDY_TRUST_VERIFIED; /* default freeze to @verified */
emit_inst(p, finst);
} else if (check(p, TOK_IDENT)) {
/* Mutable binding */
parse_binding(p, false);
} else {
error(p, "expected 'let', 'fork', 'coordinate', 'invariant', 'freeze', or identifier");
return;
}
}
if (!expect(p, TOK_RBRACE, "expected '}'"))
return;
/* Emit halt */
tardy_instruction_t halt = {0};
halt.opcode = OP_HALT;
emit_inst(p, halt);
}
/* ============================================
* Compiler Entry Point
* ============================================ */
int tardy_compile(tardy_program_t *prog, const char *src, int len)
{
if (!prog || !src)
return -1;
memset(prog, 0, sizeof(tardy_program_t));
/* Lex */
tardy_lexer_t lex;
if (tardy_lex(&lex, src, len) != 0) {
snprintf(prog->error, sizeof(prog->error), "lexer error");
prog->has_error = true;
return -1;
}
/* Parse */
tardy_parser_t parser = {0};
parser.lex = &lex;
parser.pos = 0;
parser.prog = prog;
while (!check(&parser, TOK_EOF) && !prog->has_error) {
if (match(&parser, TOK_AGENT)) {
parse_agent(&parser);
} else {
error(&parser, "expected 'agent'");
break;
}
}
return prog->has_error ? -1 : 0;
}
/* ============================================
* Compile from file
* ============================================ */
int tardy_compile_file(tardy_program_t *prog, const char *path)
{
int fd = open(path, O_RDONLY);
if (fd < 0) {
snprintf(prog->error, sizeof(prog->error),
"cannot open file: %s", path);
prog->has_error = true;
return -1;
}
struct stat st;
if (fstat(fd, &st) < 0) {
close(fd);
return -1;
}
if (st.st_size > 1024 * 1024) { /* 1MB max source file */
close(fd);
snprintf(prog->error, sizeof(prog->error), "file too large");
prog->has_error = true;
return -1;
}
char buf[1024 * 1024];
ssize_t n = read(fd, buf, st.st_size);
close(fd);
if (n <= 0)
return -1;
return tardy_compile(prog, buf, (int)n);
}