-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial.eqx
254 lines (242 loc) · 7.31 KB
/
tutorial.eqx
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
var counter 0 -> counter
var steps
[
[
"Welcome to the Equinox Forth programming language tutorial."
"Forth-based languages use postfix notation, so instead of writing '1 + 2' you need to use '1 2 +'."
""
"Type:"
" 1 2 +"
"Then hit enter."
]
[
"Forth uses a stack to pass parameters as well as to store return values."
"When you type 1 2, both numbers are pushed onto the stack."
"Then the word '+' pops those two numbers, adds them together, and leaves the result (3) on the stack."
"You see a prompt OK(1), where the number 1 indicates the depth of the stack."
""
"Type:"
" ."
"The dot (.) word will pop and print out the top of the stack, and you should see 3."
]
[
"You can print out the stack anytime in the REPL by typing '.s'."
"To make things easier, let's enable stack visualization after every command by typing 'stack-on'."
""
"Type:"
" stack-on"
""
"You can type 'clear' anytime to remove all items from the stack."
]
[
"Now let's type these numbers again, one by one and see how they're arranged on the stack."
""
"Type:"
" 1"
" 2"
"The number 2 is on top because it was pushed last, and 1 is below."
""
"Type:"
" +"
"After hitting enter, you should see the result, 3 on the stack."
""
"Type:"
" ."
]
[
"Words like '+' or '*' expect two items on the stack, and their order doesn't matter."
"But the word '-' will subtract the top item from the one below. So, if you type 1 2 -, the result will be negative one."
"There are words in Forth for rearranging the stack. One of the simplest ones is 'swap', which swaps the top two items."
""
"Type:"
" 1"
" 2"
" swap"
" -"
" ."
"You should see 1 as the result."
]
[
"'dup' is another stack manipulation word that makes a copy of the top of the stack."
""
"Type:"
"3"
"dup"
"*"
"."
"You should see 9 as the result."
]
[
"'drop' is the opposite of dup, removing the top item from the stack."
"'nip' is similar to drop, but it removes the second item from the top."
""
"Type:"
" 1"
" 2"
" nip"
" drop"
]
[
"'2dup' duplicates the top two items. It works the same as dup but treats two items as one."
""
"Type:"
" 1"
" 2"
" 2dup"
" clear"
]
[
"The colon (:) character is used to extend Forth's dictionary by defining new words."
"Let's define a 'min' word to select the smaller of two numbers."
"If the top of the stack is smaller, we need to nip the second item; otherwise, we need to drop the top."
"Here is how you would write it in Forth."
""
"Type:"
" : min 2dup > if nip else drop then ;"
""
"Pay attention to the leading colon (:) and ending semicolon (;), and keep a space between them and the next word."
]
[
"You have successfully defined a new word called 'min'. To see all available words you can type 'words'."
"Type:"
" words"
""
"Type:"
" 5"
" 2"
" min"
" ."
"If you did it correctly, you will see 2."
""
"Now you can try defining the word 'max' on your own to select the maximum between two numbers."
]
[
"Let's get back to stack manipulation words. The 'rot' and '-rot' words rotate the first three items on the stack."
""
"Type:"
" 1 2 3"
" rot"
" clear"
"You will see that 'rot' brings the third item to the top and shifts the rest."
]
[
"The '-rot' works the opposite way by pushing the top item to the third place."
""
"Type:"
" 1 2 3"
" -rot"
" clear"
"By the way, 'rot rot' is the same as '-rot'."
]
[
"The 'over' word works similarly to 'dup', but instead of making a copy of the top item, it makes a copy of the second item."
"1 2 over will result in 1 2 1."
""
"'tuck' makes a copy of the top item and pushes it below the second item."
"1 2 tuck will result in 2 1 2."
""
"Many of these words are defined as combinations of others, like nip, which is the same as swap drop, and tuck, which is the same as swap over. 2dup is the same as over over."
""
"Try these words out and observe how the stack changes."
]
[
"Let's write a word for generating the Fibonacci sequence, where each number is the sum of the two preceding ones, starting from 0 and 1."
"Example: 0, 1, 1, 2, 3, 5, 8, 13 ... In Forth, this is very easy thanks to the stack."
"Let's put the first two items of the Fibonacci sequence onto the stack."
""
"Type:"
" clear"
" 0 1"
"Then use 2dup to make a copy of each."
""
"Type:"
" 2dup"
"Then simply add these numbers together."
""
"Type:"
" +"
"You should see 0 1 1. If you continue with '2dup +' you will keep getting the next number from the Fibonacci sequence."
]
[
"We just need to put it into a loop and we're done."
""
"Type:"
" clear"
" : fib 0 1 8 0 do 2dup + loop ;"
" fib"
"Here we use a 'do' loop that goes from 0 to 8-1, so it will be executed 8 times."
]
[
"If we want to parameterize the number of iterations, you would need to do something like:"
""
"Type:"
" clear"
" : fib ( n -- .. ) 0 1 rot ( param ) 0 do 2dup + loop ;"
"I added a comment to the word indicating that it expects a number, as well as a 'rot' word in place of the hardcoded 8 to bring this parameter to the top before entering the loop."
""
"Type:"
" 10 fib"
]
[
"'10 fib' generated 12 items, since we already started with two items on the stack."
"You can check the size of the stack by"
""
"Type:"
" depth ."
" clear"
" depth ."
]
[
"Equinox is a hosted language that doesn't have its own standard library but relies on Lua functions."
"When calling a Lua function, you need to indicate its arity (number of parameters)."
""
"Type:"
" 2 8 math.pow/2 ."
""
"This will print out 256."
"The /2 instructs Equinox to compile code that consumes two parameters from the stack when calling the Lua function."
"You can omit the final part when calling a function with no parameters."
""
"Type:"
" os.time ."
]
[
"Some Lua functions don't return anything, or you deliberately want to ignore the return value."
"In such cases, you can use the same synax, but instead of '/' you need to use '!' character."
""
"Type:"
" 123 io.write!1"
""
"io.write/1 leaves a boolean value on the stack, while io.write!1 doesn't."
]
[ "Congratulations, you finished the basic Equinox Forth tutorial." ]
] -> steps
: show-instructions ( -- )
"After you finished, type 'go' to go to the next tutorial step or 'back' to go back." . ;
: len ( -- ) steps size ;
: show-progress ( -- )
"Lesson \(%d/%d\):" counter len string.format/3 . cr
"================" . cr ;
: finished? counter len >= ;
: current ( -- [] ) steps counter at ;
: show-lesson ( -- )
current ipairs: i line
line . cr
end ;
: show ( -- )
show-progress
show-lesson cr
finished? not if show-instructions cr then ;
: clamp ( -- )
counter 1 math.max/2 -> counter
counter len math.min/2 -> counter ;
: step-by ( n -- )
counter swap + -> counter
clamp ;
: go ( -- )
1 step-by
show ;
: back ( -- )
-1 step-by
show ;
go