Interactive Bootstraps“Going FORTH” →Go with the control-flow

a8959110f9e20af657787eaef03f3cccc40e5a4b
🐶
Make non-linear programs easier
By not just executing the program one instruction after another, we make
it easier to create branching instructions, and an instruction to stop
executing.
We could have created branches previously too, by modifying the global
`pc` variable, but instead we pass the program counter around. This way
we avoid having a single global, which can allow us to have multiple
interpreters at the same time.
We could have had instructions to stop executing previously too, by
using either the `exit` function from the standard library (maybe
combined with `atexit` to print the stack), or by using the `setjmp` and
`longjmp` function pairs, but this way is simplest.
TODO: Big interpreter loop with switch statements
TODO: Tail-call elimination & proof of it

⟨forth.c⟩≡

@@ -3,7+3,7@@
3 3
 
4 4
 #define ASIZE(x) (sizeof((x)) / sizeof((x)[0]))
5 5
 
6
-typedef void instruction(void);
6
+typedef void instruction(void **pc);
7 7
 
8 8
 int stack[100];
9 9
 unsigned stack_p;
@@ -12,19+12,29@@void push(int value) { stack[stack_p++] = value; }
12 12
 int pop(void) { return stack[--stack_p]; }
13 13
 
14 14
 instruction lit;
15
+instruction stop;
15 16
 
16 17
 instruction *prog[] = {
17 18
 	&lit,
18
-	(instruction *)2,
19
+	(instruction *)3,
20
+	&stop,
19 21
 };
20
-int pc = 0;
21 22
 
22
-void lit(void) { push((int)(intptr_t)prog[++pc]); }
23
+void next(void **pc) {
24
+	instruction *next_instr = *pc;
25
+	next_instr(pc + 1);
26
+}
27
+
28
+void lit(void **pc) {
29
+	push(*(int *)pc++);
30
+	next(pc);
31
+}
32
+
33
+void stop(void **pc) {}
23 34
 
24 35
 int main(int argc, char *argv[]) {
25
-	for (pc = 0; pc < ASIZE(prog); pc++) {
26
-		prog[pc]();
27
-	}
36
+	int start = 0;
37
+	prog[start]((void **)&prog[start + 1]);
28 38
 
29 39
 	// Print stack after exit
30 40
 	printf("<%u>", stack_p);