1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/ Delimited continuations /
/
When THROWing an exception we unwind the return stack and ignore all the return
addresses until the marker left by CATCH. But we can instead compile them into a
word which when run will push them onto the return stack - and so do the work we
have skipped by THROWing the exception. This word is called a delimited
continuation. We can leave its execution token on top of the data stack.
The following four lines all calculate the same thing:
/
1 2 3 4 × + - / Initial calculation /
1 2 3 4 ⊂ × + - ⊃ ⍎ / Push an execution token for the whole calculation and execute it /
1 2 3 4 ⊂ × ⟦ + - ⊃ ⟧ ⍎ / Do the ×, push an execution token for the + -, execute it /
1 2 3 4 ⊂ × + ⟦ - ⊃ ⟧ ⍎ / Do the × +, push an execution token for the -, execute it /
/
The continuation works even if the [[ is further down
the return stack, e.g.
/
∇ new+ + ⟦ 42 EMIT ∇ / Word containing ⟦ /
1 2 3 4 ⊂ × new+ - ⊃ ⟧ / Do the × and new+ up to ⟦ (so just the +), push xt for rest of work /
⍎ / Execute rest of work, i.e. do rest of new+ (prints B) and the - /
/
The continuation is just like any execution token; in particular it can be
manipulated with combinators. For example to execute it twice we can use ↑ cat
/
1 2 3 4 5 × + - - / Initial calculation /
1 2 3 4 5 ⊂ × + ⟦ - ⊃ ⟧ ↑ cat ⍎ / Capture - in continuation, make execution token to do it twice, execute it /
1 2 3 4 5 ⊂ × new+ - ⊃ ⟧ ↑ cat ⍎ / Prints B twice since continuation executed twice /
/
Below are some examples illustrating how to execute Forth words by pushing their
corresponding return addresses to the return stack. They're probably unnecessary
but they are the experiments I did before writing [[ and ]] so I have left them
in in case they help anyone else.
/
∇ α ↑ ∇ / Example Forth words /
∇ β × ∇
3 α β / α β is just ↑ ×, squares 3 /
∇ ρ find >CFA 8+ ∇ / ρ gets return stack address corresponding to a word /
ρ α CONST rα / Return stack addresses of α and β /
ρ β CONST rβ
∇ ψ rβ >R rα >R ∇ / ψ pushes addresses rβ and rα onto return stack /
3 ψ / ψ does the same thing as α β /
/
We can execute a Forth word by directly pushing the corresponding return stack
address onto the return stack.
We can even jump inside words by adding an offset to their return address.
/
∇ γ 8+ NEG ∇
ρ γ 8+ CONST rγ+
∇ ω rγ+ >R ∇
3 γ / Gives 3 8+ NEG /
3 ω / Gives 3 NEG /
/
If all the addresses are the first in Forth words it is simpler to let DOCOL do
the work.
/
rβ 8- rα 8- ∇ ψ' [ , , ] ∇
3 ψ' / ψ' does the same thing as ψ /
/
This doesn't work trying to jump into a word since in this case there is no
DOCOL 8 bytes before the return address, e.g.
/
rγ+ 8- ∇ ω' [ , ] ∇ / ω' doesn't work! /
∇ ψω rγ+ >R rβ >R rα >R ∇
3 ψω / Gives 3 ↑ × NEG /
|