-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathindex.html
337 lines (316 loc) · 12.9 KB
/
index.html
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Smalltalk78</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="index.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="msapplication-TileImage" content="index.png">
<style>
body {
background: #888;
color: #EEE;
font-family: Avenir, sans-serif;
}
a {
color: #EEE;
}
pre, tt {
background: #EEE;
color: #000;
}
pre {
padding: 10px;
border-radius: 10px;
width: fit-content;
line-height: 1.5;
}
tt {
padding: 3px;
border-radius: 4px;
white-space: nowrap;
}
dt {
font-weight: bold;
}
#canvas {
background: #000;
border-radius: 10px;
cursor: none;
}
#UI {
max-width: 600px;
margin: 0 auto;
padding: 10px;
}
#help, #syntax {
display: none;
}
body.fullscreen {
background: #000;
margin: 0;
}
body.fullscreen > *:not(#canvascontainer) {
display: none;
}
#canvascontainer {
display: flex;
justify-content: center;
align-items: center;
}
body.fullscreen #canvascontainer {
width: 100vw;
height: 100vh;
}
body.fullscreen #canvas {
border-radius: 0;
}
body:not(.fullscreen) #canvas {
width: 100%;
max-width: 1024px;
}
body.fullscreen #canvas {
width: auto;
height: 100%;
}
@media (max-aspect-ratio: 4/3) {
body.fullscreen #canvas {
width: 100%;
height: auto;
}
}
</style>
<script src="Smalltalk78.js"></script>
</head>
<body id="body">
<div id="canvascontainer"><canvas id="canvas" width="1024" height="768"></canvas></div>
<div id="UI">
<p>This is the <a href="https://github.com/codefrau/Smalltalk78" target="_blank">Smalltalk-78</a> VM
by Vanessa Freudenberg and Dan Ingalls. Changes you make are stored in your own browser when you choose
“save” from the right-click menu. Use <a href="."> / </a> to load the saved snapshot, or
<a href="?fresh">/?fresh</a> to start afresh. You can also append <a href="?fullscreen">?fullscreen</a>
to the URL to remove this help text.<br>
For details please read our
<a href="https://freudenbergs.de/vanessa/publications/Ingalls-2014-Smalltalk78.pdf" target="_blank">paper</a>.
Also check out the
<a href="https://smalltalkzoo.thechm.org/HOPL-St78.html" target="_blank">Lively VM Debugger</a>.</p>
<button id="helpbutton">Mouse and Keyboard Help</button>
<button id="syntaxbutton">Syntax Help</button>
<button id="fullscreenbutton">Fullscreen</button>
<pre id="help">
<b>Mouse</b>
Left-click: select ("red button")
Right-click: context menu ("yellow button")
Ctrl-click/middle-click: window menu ("blue button")
<b>Keyboard</b>
⌾: @ (make point)
←: _ (assignment)
⇑: ^ (return)
ⓢ: ` (backtick, 's operator)
↑: Ctrl-^ (Ctrl-6) ^^
¬: Ctrl-- (Ctrl-minus, unary minus, ¬1 instead of -1)
◢: Ctrl-] (Doit)
◦: Ctrl-A (<b>A</b>t) ..
⦂: Ctrl-C (open <b>C</b>olon) ::
∢: Ctrl-E (<b>E</b>ye) <)
⇒: Ctrl-F (i<b>F</b>) =>
≥: Ctrl-G (<b>G</b>reater or equal) >=
Ctrl-H (backspace)
Ctrl-I (tab)
≤: Ctrl-L (<b>L</b>ess or equal) <=
≠: Ctrl-N (<b>N</b>ot equal) !=
↪: Ctrl-U (<b>U</b>nique string, symbol) #
◣: Ctrl-P (<b>P</b>rompt)
ⓢ: Ctrl-S ('s operator, eval in object)
≡: Ctrl-T (<b>T</b>riple equal, identity) ==
➲: Ctrl-V (in<b>V</b>erse arrow)
◻: Ctrl-Q (s<b>Q</b>uare)
▱: Ctrl-X (wiggly line)
#: ## (because # is ↪)
Cmd-C/Alt-C: copy text
Cmd-V/Alt-V: paste text
Cmd-B/Alt-B: bold
Cmd-I/Alt-I: italic
Cmd-U/Alt-U: underline
Cmd-X/Alt-X: reset emphasis
Cmd-0...9, +/-: change font
Cmd-D doit
Cmd-P printit
Cmd-S compile
Cmd-J again
Cmd-A select all
Cmd-L cancel
Cmd-Z undo
Cmd-. interrupt
Tab/Shift-Tab indent/outdent selection
Ctrl-] do it in eval (Dispframe in the topleft corner)
Ctrl-d done with eval
</pre>
<div id="syntax">
<p>The syntax of Smalltalk-78 is unchanged from Smalltalk-76 (see
<a href="https://smalltalkzoo.thechm.org/papers/The%20Smalltalk-76%20Programming%20System.PDF" target="_blank">here</a>),
which introduced the regularity of unary, binary, and keyword messages
that is still used in modern Smalltalks. There are not yet blocks as
in Smalltalk-80, no booleans, and no meta classes.</p>
<dl>
<dt>Character Set</dt>
<dd>
<p>The system uses the ASCII-1963 character set which had <tt>↑</tt> and
<tt>←</tt> arrows in place of the <tt>^</tt> and <tt>_</tt> characters in later
ASCII versions. From the control character range, only <tt>Tab</tt> (9) and
<tt>Carriage Return</tt> (13) are used as in ASCII, while some others are used
for special characters not found in ASCII. These include return and implication
arrows <tt>⇑ ⇒</tt>, comparison operators <tt>≤ ≥ ≠ ≡</tt>, indexing
and point creation operators <tt>◦ ⊚</tt>, a curved arrow <tt>↪</tt> for literal
quoting (used like <tt>#</tt> in Smalltalk-80), a unary minus <tt>¬</tt> (for prefixing
negative numbers, as in <tt>3 − −4</tt>), a possessive operator <tt>’s</tt> (evaluates
an expression in the context of another object, e.g. <tt>obj ’s 'instvar'</tt>),
and a few more. See
<a href="https://freudenbergs.de/vanessa/publications/Ingalls-2014-Smalltalk78.pdf" target="_blank">the paper</a>
for a complete list, and refer to the <a href="javascript:helpbutton.click()">keyboard help</a>.</p>
</dd>
<dt>Conditional execution</dt>
<dd>
<p>A block of code can be conditionally evaluated using the <tt>⇒</tt> operator.
It maps directly to the “jump if not false” bytecode (where <tt>false</tt>
is just an instance of <b>Object</b> known to the VM compared by identity).
If the condition is false, the following block of code is skipped.
Any object other than false causes the conditional jump not to be
taken, so the code block is executed, followed by an unconditional
jump to the end of the surrounding code block. This allows putting
an “else” case right after the conditional block, as the implementation
of <b>Object≫and:</b> demonstrates:
<pre>
and: x
[self⇒[⇑x] ⇑false]
</pre>
If execution should resume after the else case, another set of
brackets needs to be put around it.</p>
</dd>
<dt>Deferred evaluation</dt>
<dd>
<p>Brackets <tt>[]</tt> are merely a syntactic device to group statements, they do
not create a block object as in Smalltalk-80. How then does e.g.
<tt>for⦂from:do⦂</tt> work? The magic is not in the brackets, but in the two
variants of the colon in a keyword message. The regular colon <tt>:</tt>
causes the keyword argument to be evaluated immediately.
For an open colon <tt>⦂</tt> however, the compiler generates a “remote copy”
of the current context. This <b>RemoteCode</b> can be evaluated later,
and even repeatedly if desired.</p>
<p>There are two variants for evaluation, with and without an
argument. The form with an argument <tt>value←</tt> can be used to assign
a value to a variable, the form without an argument
(eval) just evaluates the code. These work together, as there
is no way to pass arguments to a block other than “remotely”
assigning to temporary variables. This is perhaps best explained
with an example. A possible implementation of <tt>for⦂from:do⦂</tt>
could look like this:
<pre>
for⦂ var from: vec do⦂ code | stream item
[
stream ← vec asStream.
while⦂ [item ← stream next] do⦂ [
var value← item.
code eval.
]
]
</pre>
Note that <tt>for⦂from:do⦂</tt> is not actually a method, but a compiler
macro (as evidenced by the missing receiver). It could, however,
be a regular method as just described.</p>
</dd>
<dt>Assignment Selector</dt>
<dd>
<p>The last part of a keyword selector can be an
assignment arrow <tt>←</tt>. E.g. <tt>stream next← 5</tt> writes an
item to the stream, whereas <tt>stream next: 5</tt> reads 5 items
from the stream. To the method this is just like any other argument.
But on the sending side, the parser treats the expression after <tt>←</tt>
as if it was an assignment. That means no parentheses are needed around
that expression. This also works for binary operators extended
by an assignment arrow, making it a ternary operator. E.g.
<tt>a◦1 ← b◦1</tt> uses the <tt>◦</tt> and <tt>◦←</tt> operators.
It is equivalent to Smalltalk-80’s <tt>a at: 1 put: (b at: 1)</tt> but
reads a lot nicer and needs no parentheses.</p>
</dd>
<dt>No metaclasses</dt>
<dd>
<p>The expression <tt>Stream default</tt> looks like it
invokes a “class-side method” if it was Smalltalk-80. But
Smalltalk-78 did not have a metaclass hierarchy in parallel
to the regular class hierarchy. All classes are an instance of
<b>Class</b> so no class-specific methods are possible. Instead,
<b>Class</b> provides a couple of methods that dispatch to a new
instance. E.g. the implementation of <b>Class≫default</b> is
<tt>⇑self new default</tt>. In the <b>Stream</b> case, this invokes
<b>Stream≫default</b> which initializes thestream for writing on a
new <b>String</b>.</p>
</dd>
</dl>
</div> <!-- syntax -->
</div> <!-- UI -->
<script>
let width, height, fullscreen, black, white;
const params = new URL(window.location.href).searchParams;
if (params.has("alto")) {
width = 608;
height = 808;
fullscreen = true;
black = 0x3E5F66;
white = 0x52F0FF;
}
if (params.get("width")) width = +params.get("width");
if (params.get("height")) height = +params.get("height");
if (params.has("fullscreen")) fullscreen = true;
if (params.has("black")) black = +("0xFF" + params.get("black"));
if (params.has("white")) white = +("0xFF" + params.get("white"));
if (navigator.standalone) fullscreen = true;
if (width) canvas.width = width;
if (height) canvas.height = height;
if (fullscreen) {
body.classList.add("fullscreen");
if (black) body.style.backgroundColor = "#" + black.toString(16).slice(-6);
}
module("main").requires("Smalltalk78").toRun(function() {
if (black) NT.BLACK = 0xFF000000 | ((black & 0xFF0000) >> 16) | (black & 0xFF00) | ((black & 0xFF) << 16);
if (white) NT.WHITE = 0xFF000000 | ((white & 0xFF0000) >> 16) | (white & 0xFF00) | ((white & 0xFF) << 16);
Smalltalk78.run("https://smalltalkzoo.thechm.org/images/updated.st78", canvas);
});
let helpTimeout;
helpbutton.onclick = () => {
clearTimeout(helpTimeout);
if (help.style.display) {
canvas.scrollIntoView({ behavior: 'smooth' });
helpTimeout = setTimeout(() => help.style.display = '', 1000);
} else {
if (syntax.style.display) syntax.style.display = '';
help.style.display = 'block';
helpbutton.scrollIntoView({ behavior: 'smooth' });
}
}
let syntaxTimeout;
syntaxbutton.onclick = () => {
clearTimeout(syntaxTimeout);
if (syntax.style.display) {
canvas.scrollIntoView({ behavior: 'smooth' });
syntaxTimeout = setTimeout(() => syntax.style.display = '', 1000);
} else {
if (help.style.display) help.style.display = '';
syntax.style.display = 'block';
syntaxbutton.scrollIntoView({ behavior: 'smooth' });
}
}
fullscreenbutton.onclick = () => {
const wantsFullscreen = document.fullscreenElement !== canvascontainer;
body.classList.toggle("fullscreen", wantsFullscreen);
if (wantsFullscreen) canvascontainer.requestFullscreen();
}
document.onfullscreenchange = () => {
const isFullscreen = document.fullscreenElement === canvascontainer;
body.classList.toggle("fullscreen", isFullscreen);
}
</script>
</body>
</html>