Add function storage to Context. When executeStatement sees fn, read the name, the parameter list (with types), the return type, and save the body position. Skip the body without executing it.
Add these fields to Context:
fnNames: [64][]const u8 = undefined,
fnParams: [64][8][]const u8 = undefined,
fnParamCounts: [64]usize = undefined,
fnStarts: [64]usize = undefined,
fnCount: usize = 0,
Sixty-four functions, up to eight parameters each. If you need more, raise the numbers.
Add to executeStatement, alongside the var/const/if/while handlers:
if (stringsEqual(firstWord, "fn")) {
const fname = readIdentifier(ctx.source, &ctx.pos);
ctx.fnNames[ctx.fnCount] = fname;
if (ctx.source[ctx.pos] == '(') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
var pc: usize = 0;
while (ctx.source[ctx.pos] != ')' and ctx.source[ctx.pos] != 0) {
ctx.fnParams[ctx.fnCount][pc] = readIdentifier(ctx.source, &ctx.pos);
if (ctx.source[ctx.pos] == ':') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
_ = readIdentifier(ctx.source, &ctx.pos); // skip param type
}
pc += 1;
if (ctx.source[ctx.pos] == ',') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
}
ctx.fnParamCounts[ctx.fnCount] = pc;
if (ctx.source[ctx.pos] == ')') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
if (isAlpha(ctx.source[ctx.pos])) {
_ = readIdentifier(ctx.source, &ctx.pos); // skip return type
}
ctx.fnStarts[ctx.fnCount] = ctx.pos;
skipBlock(ctx);
ctx.fnCount += 1;
return 0;
}
skipBlock takes ctx now -- it only reads ctx.source and advances ctx.pos, but that's enough to need the context.
We parse fn fib(n: i64) i64 { ... } and store the name, parameter names, and body position. The types are parsed and discarded -- for now. Later: (func $fib (param $n i64) (result i64) ...).