Fixed bug: use references to mutate the stack instead of values.

master
James T. Martin 2022-09-10 09:07:38 -07:00
parent fcd61f6c5f
commit 4c4ebeecfc
Signed by: james
GPG Key ID: D6FB2F9892F9B225
3 changed files with 152 additions and 75 deletions

View File

@ -143,38 +143,38 @@ static void push_new_jump(label label) {
}
static void push_argument(var ref) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == EXPR_CRUMB);
struct expr_crumb exprc = ctx.data.expr;
if (exprc.argument_count > MAX_ARGUMENTS) {
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == EXPR_CRUMB);
struct expr_crumb* exprc = &ctx->data.expr;
if (exprc->argument_count > MAX_ARGUMENTS) {
fprintf(stderr, "error: exceeded maximum number of arguments in expression\n");
exit(1);
}
exprc.arguments[exprc.argument_count] = ref;
exprc.argument_count++;
exprc->arguments[exprc->argument_count] = ref;
exprc->argument_count++;
}
static void push_cvar_name(char* name) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == LOOP_CRUMB);
struct loop_crumb loopc = ctx.data.loop;
if (loopc.assignment_count == MAX_ASSIGNMENTS) {
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == LOOP_CRUMB);
struct loop_crumb* loopc = &ctx->data.loop;
if (loopc->assignment_count == MAX_ASSIGNMENTS) {
fprintf(stderr, "error: exceed maximum number of assignments in loop cvars\n");
exit(1);
}
loopc.assignments[loopc.assignment_count].name = copy_str(name);
loopc->assignments[loopc->assignment_count].name = copy_str(name);
}
static void push_cvar(var ref) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == LOOP_CRUMB);
struct loop_crumb loopc = ctx.data.loop;
if (loopc.assignment_count > MAX_ASSIGNMENTS) {
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == LOOP_CRUMB);
struct loop_crumb* loopc = &ctx->data.loop;
if (loopc->assignment_count > MAX_ASSIGNMENTS) {
fprintf(stderr, "error: exceed maximum number of assignments in loop cvars\n");
exit(1);
}
loopc.initializers[loopc.assignment_count] = ref;
loopc.assignment_count++;
loopc->initializers[loopc->assignment_count] = ref;
loopc->assignment_count++;
}
static var lookup_assignment(
@ -267,9 +267,17 @@ static label lookup_label(enum label_type type, char* name) {
exit(1);
}
void reduce_expression(struct expr_crumb* exprc) {
// TODO FIXME
return;
}
void enter_block(void) {
struct crumb ctx = context[context_depth - 1];
switch (ctx.type) {
printf("** enter_block\n");
struct crumb* ctx = &context[context_depth - 1];
switch (ctx->type) {
case BLOCK_CRUMB:
// we should have seen a stmt_assign or stmt_expr first,
// either of which pushes an expr crumb.
@ -280,7 +288,7 @@ void enter_block(void) {
break;
}
case IF_CRUMB: {
struct if_crumb ifc = ctx.data.if_;
struct if_crumb ifc = ctx->data.if_;
switch (ifc.state) {
case IF_COND:
assert(0);
@ -294,14 +302,14 @@ void enter_block(void) {
break;
}
case LOOP_CRUMB: {
struct loop_crumb loopc = ctx.data.loop;
assert(loopc.state == LOOP_CLEAN);
loopc.state = LOOP_BODY;
struct loop_crumb* loopc = &ctx->data.loop;
assert(loopc->state == LOOP_CLEAN);
loopc->state = LOOP_BODY;
var args[MAX_ASSIGNMENTS];
define(loopc.next, args);
define(loopc->next, args);
// TODO NOTE: is this the correct order?
for (uint32_t i = 0; i < loopc.assignment_count; i++) {
loopc.assignments[i].ref = args[i];
for (uint32_t i = 0; i < loopc->assignment_count; i++) {
loopc->assignments[i].ref = args[i];
}
break;
}
@ -312,29 +320,32 @@ void enter_block(void) {
}
void stmt_assign(char* name) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == BLOCK_CRUMB);
struct block_crumb blockc = ctx.data.block;
assert(blockc.state == BLOCK_CLEAN);
if (blockc.assignment_count == MAX_ASSIGNMENTS) {
printf("** stmt_assign\n");
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == BLOCK_CRUMB);
struct block_crumb* blockc = &ctx->data.block;
assert(blockc->state == BLOCK_CLEAN);
if (blockc->assignment_count == MAX_ASSIGNMENTS) {
fprintf(stderr, "error: exceeded maximum number of assignments in block\n");
exit(1);
}
blockc.state = BLOCK_ASSIGN;
blockc.assignments[blockc.assignment_count].name = copy_str(name);
blockc->state = BLOCK_ASSIGN;
blockc->assignments[blockc->assignment_count].name = copy_str(name);
push_new_expr();
}
void stmt_expr(void) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == BLOCK_CRUMB);
struct block_crumb blockc = ctx.data.block;
assert(blockc.state == BLOCK_CLEAN);
blockc.state = BLOCK_EXPR;
printf("** stmt_expr\n");
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == BLOCK_CRUMB);
struct block_crumb* blockc = &ctx->data.block;
assert(blockc->state == BLOCK_CLEAN);
blockc->state = BLOCK_EXPR;
push_new_expr();
}
void exit_block(void) {
printf("** exit_block\n");
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == BLOCK_CRUMB);
struct block_crumb blockc = ctx.data.block;
@ -379,55 +390,58 @@ void exit_block(void) {
}
void exit_expr(void) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == EXPR_CRUMB);
struct expr_crumb exprc = ctx.data.expr;
assert(exprc.argument_count > 0);
if (exprc.operator_count > 0 || exprc.argument_count > 1) {
// TODO FIXME
fprintf(stderr, "error: I don't know how to evaluate exprs yet\n");
printf("** exit_expr\n");
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == EXPR_CRUMB);
struct expr_crumb* exprc = &ctx->data.expr;
assert(exprc->argument_count > 0);
reduce_expression(exprc);
if (exprc->operator_count > 0 || exprc->argument_count > 1) {
fprintf(stderr, "error: failed to reduce expression\n");
exit(1);
}
var ret = exprc.arguments[0];
var ret = exprc->arguments[0];
context_depth--;
ctx = context[context_depth - 1];
switch (ctx.type) {
ctx = &context[context_depth - 1];
switch (ctx->type) {
case BLOCK_CRUMB: {
struct block_crumb blockc = ctx.data.block;
blockc.final = ret;
switch (blockc.state) {
struct block_crumb* blockc = &ctx->data.block;
blockc->final = ret;
switch (blockc->state) {
case BLOCK_CLEAN:
assert(0);
case BLOCK_EXPR:
blockc->state = BLOCK_CLEAN;
break;
case BLOCK_ASSIGN:
blockc.assignments[blockc.assignment_count].ref = ret;
blockc.assignment_count++;
blockc->assignments[blockc->assignment_count].ref = ret;
blockc->assignment_count++;
blockc->state = BLOCK_CLEAN;
break;
}
break;
}
case IF_CRUMB: {
struct if_crumb ifc = ctx.data.if_;
assert(ifc.state == IF_COND);
jump_if(ifc.then, ret, NULL);
jump(ifc.else_, NULL);
ifc.state = IF_THEN;
struct if_crumb* ifc = &ctx->data.if_;
assert(ifc->state == IF_COND);
jump_if(ifc->then, ret, NULL);
jump(ifc->else_, NULL);
ifc->state = IF_THEN;
break;
}
case EXPR_CRUMB:
push_argument(ret);
break;
case LOOP_CRUMB: {
struct loop_crumb loopc = ctx.data.loop;
assert(loopc.state == LOOP_CVAR_INIT);
struct loop_crumb* loopc = &ctx->data.loop;
assert(loopc->state == LOOP_CVAR_INIT);
push_cvar(ret);
loopc.state = LOOP_CLEAN;
loopc->state = LOOP_CLEAN;
break;
}
case JUMP_CRUMB: {
// TODO FIXME: this is *completely wrong* for `next`!
label label = ctx.data.jump;
label label = ctx->data.jump;
jump(label, &ret);
// TODO: better way to handle returning impossible value
push_argument(ret);
@ -437,6 +451,7 @@ void exit_expr(void) {
}
void enter_if(void) {
printf("** enter_if\n");
enter();
label then = declare(0);
label else_ = declare(0);
@ -458,6 +473,7 @@ void enter_if(void) {
}
void exit_if(void) {
printf("** exit_if\n");
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == IF_CRUMB);
struct if_crumb ifc = ctx.data.if_;
@ -479,6 +495,7 @@ void exit_if(void) {
}
void enter_loop(char* label_name) {
printf("** enter_loop\n");
enter();
label exit = declare_exit(1);
struct loop_crumb loopc = {
@ -497,62 +514,73 @@ void enter_loop(char* label_name) {
}
void cvar_pass(char* name) {
printf("** cvar_pass\n");
push_cvar_name(name);
push_cvar(lookup_var(name));
}
void cvar_init(char* name) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == LOOP_CRUMB);
struct loop_crumb loopc = ctx.data.loop;
loopc.state = LOOP_CVAR_INIT;
printf("** cvar_init\n");
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == LOOP_CRUMB);
struct loop_crumb* loopc = &ctx->data.loop;
loopc->state = LOOP_CVAR_INIT;
push_cvar_name(name);
push_new_expr();
}
void expr_next(char* label) {
printf("** expr_next\n");
push_new_jump(lookup_label(NEXT_LABEL, label));
push_new_expr();
}
void expr_exit(char* label) {
printf("** expr_exit\n");
push_new_jump(lookup_label(EXIT_LABEL, label));
push_new_expr();
}
void expr_return(void) {
printf("** expr_return\n");
push_new_jump(lookup_label(RETURN_LABEL, NULL));
push_new_expr();
}
void enter_group(void) {
printf("** enter_group\n");
push_new_expr();
}
void exit_group(void) {
printf("** exit_group\n");
// exit_expr is sufficient
}
void expr_op(enum operator_ op) {
struct crumb ctx = context[context_depth - 1];
assert(ctx.type == EXPR_CRUMB);
struct expr_crumb exprc = ctx.data.expr;
if (exprc.operator_count > MAX_OPERATORS) {
printf("** expr_op %ir\n", op);
struct crumb* ctx = &context[context_depth - 1];
assert(ctx->type == EXPR_CRUMB);
struct expr_crumb* exprc = &ctx->data.expr;
if (exprc->operator_count > MAX_OPERATORS) {
fprintf(stderr, "error: exceeded maximum number of operators in expression\n");
exit(1);
}
exprc.operators[exprc.operator_count] = op;
exprc.operator_count++;
exprc->operators[exprc->operator_count] = op;
exprc->operator_count++;
}
void expr_string(char* string) {
printf("** expr_string %s\n", string);
push_argument(lit_string(string));
}
void expr_integer(int64_t num) {
printf("** expr_integer %lli\n", num);
push_argument(lit((uint64_t) num));
}
void expr_var(char* var) {
printf("** expr_var %s\n", var);
push_argument(lookup_var(var));
}

View File

@ -45,6 +45,8 @@ enum operator_ {
OP_TYPE, // :
OP_FUN, // ->
OP_JUXT, // space! but this is not emitted by the lexer.
};
union token_data {

View File

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <string.h>
#include "lang.h"
#include "lex.h"
#include "parse.h"
@ -15,9 +16,12 @@ enum state {
ST_BLOCK_CLOSE,
ST_ASSIGN,
ST_EXPR,
ST_EXPR_HACK,
ST_EXPR_CONT,
ST_EXPR_END,
ST_GROUP,
ST_IF_ELSE,
ST_IF_END,
ST_LOOP_VARS,
ST_LOOP_VARS_CONT,
};
@ -46,6 +50,12 @@ const char* state_name(enum state st) {
return "v";
case ST_LOOP_VARS_CONT:
return ",";
case ST_EXPR_END:
return "E";
case ST_EXPR_HACK:
return "H";
case ST_IF_END:
return "i";
}
}
@ -78,6 +88,7 @@ static enum state pop(void) {
static _Bool is_assignment(struct token tok, struct token next) {
return tok.type == TOK_NAME && next.type == TOK_OPERATOR && next.data.op == OP_EQ;
}
@ -104,6 +115,7 @@ void parse(void) {
if (tok.type == TOK_OPEN_BLOCK) {
push(ST_BLOCK_CLOSE);
push(ST_BLOCK_BODY);
enter_block();
break;
}
syntax_error("expected beginning of block");
@ -112,11 +124,13 @@ void parse(void) {
if (is_assignment(tok, nxt)) {
push(ST_BLOCK_CONT);
push(ST_ASSIGN);
stmt_assign(tok.data.name);
break;
}
if (is_expr(tok)) {
push(ST_BLOCK_CONT);
push(ST_EXPR);
stmt_expr();
continue;
}
continue;
@ -128,6 +142,7 @@ void parse(void) {
continue;
case ST_BLOCK_CLOSE:
if (tok.type == TOK_CLOSE_BLOCK) {
exit_block();
break;
}
syntax_error("expected end of block");
@ -136,20 +151,28 @@ void parse(void) {
push(ST_EXPR);
break;
case ST_EXPR:
push(ST_EXPR_END);
push(ST_EXPR_HACK);
continue;
case ST_EXPR_HACK:
if (tok.type == TOK_STRING) {
push(ST_EXPR_CONT);
expr_string(tok.data.string);
break;
}
if (tok.type == TOK_INTEGER) {
push(ST_EXPR_CONT);
expr_integer(tok.data.int_);
break;
}
if (tok.type == TOK_NAME) {
char* name = tok.data.name;
if (strcmp(name, "if") == 0) {
push(ST_IF_END);
push(ST_IF_ELSE);
push(ST_BLOCK);
push(ST_EXPR);
enter_if();
break;
}
if (strcmp(name, "loop") == 0) {
@ -157,6 +180,9 @@ void parse(void) {
push(ST_LOOP_VARS);
if (nxt.type == TOK_LABEL) {
next();
enter_loop(nxt.data.label);
} else {
enter_loop(NULL);
}
break;
}
@ -164,6 +190,9 @@ void parse(void) {
push(ST_LOOP_VARS);
if (nxt.type == TOK_LABEL) {
next();
expr_next(nxt.data.label);
} else {
expr_next(NULL);
}
break;
}
@ -171,25 +200,32 @@ void parse(void) {
push(ST_EXPR);
if (nxt.type == TOK_LABEL) {
next();
expr_exit(nxt.data.label);
} else {
expr_exit(NULL);
}
break;
}
if (strcmp(name, "return") == 0) {
push(ST_EXPR);
expr_return();
break;
}
push(ST_EXPR_CONT);
expr_var(tok.data.name);
break;
}
if (tok.type == TOK_OPEN_GROUP) {
push(ST_EXPR_CONT);
push(ST_GROUP);
push(ST_EXPR);
enter_group();
break;
}
if (tok.type == TOK_OPERATOR && is_unary(tok.data.op)) {
push(ST_EXPR_CONT);
push(ST_EXPR);
push(ST_EXPR_HACK);
expr_op(tok.data.op);
break;
}
if (tok.type == TOK_OPEN_BLOCK) {
@ -199,16 +235,22 @@ void parse(void) {
syntax_error("expected expression");
case ST_EXPR_CONT:
if (is_expr(tok)) {
push(ST_EXPR);
push(ST_EXPR_HACK);
expr_op(OP_JUXT);
continue;
}
if (tok.type == TOK_OPERATOR && is_binary(tok.data.op)) {
push(ST_EXPR);
push(ST_EXPR_HACK);
expr_op(tok.data.op);
break;
}
continue;
case ST_EXPR_END:
exit_expr();
continue;
case ST_GROUP:
if (tok.type == TOK_CLOSE_GROUP) {
exit_group();
break;
}
syntax_error("mismatched parentheses");
@ -218,14 +260,19 @@ void parse(void) {
break;
}
continue;
case ST_IF_END:
exit_if();
continue;
case ST_LOOP_VARS:
if (is_assignment(tok, nxt)) {
push(ST_LOOP_VARS_CONT);
push(ST_ASSIGN);
cvar_init(tok.data.name);
break;
}
if (tok.type == TOK_NAME) {
push(ST_LOOP_VARS_CONT);
cvar_pass(tok.data.name);
break;
}
continue;