This is the Smalltalk-78 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  /  to load the saved snapshot, or /?fresh to start afresh. You can also append ?fullscreen to the URL to remove this help text.
For details please read our paper. Also check out the Lively VM Debugger.

Mouse
Left-click:              select       ("red button")
Right-click:             context menu ("yellow button")
Ctrl-click/middle-click: window menu  ("blue button")
Keyboard
⌾: @ (make point)
←: _ (assignment)
⇑: ^ (return)
ⓢ: ` (backtick, 's operator)
↑: Ctrl-^ (Ctrl-6)                  ^^
¬: Ctrl-- (Ctrl-minus, unary minus, ¬1 instead of -1)
◢: Ctrl-] (Doit)
◦: Ctrl-A (At)                      ..
⦂: Ctrl-C (open Colon)              ::
∢: Ctrl-E (Eye)                     <)
⇒: Ctrl-F (iF)                      =>
≥: Ctrl-G (Greater or equal)        >=
   Ctrl-H (backspace)
   Ctrl-I (tab)
≤: Ctrl-L (Less or equal)           <=
≠: Ctrl-N (Not equal)               !=
↪: Ctrl-U (Unique string, symbol)   #
◣: Ctrl-P (Prompt)
ⓢ: Ctrl-S ('s operator, eval in object)
≡: Ctrl-T (Triple equal, identity)  ==
➲: Ctrl-V (inVerse arrow)
◻: Ctrl-Q (sQuare)
▱: 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

The syntax of Smalltalk-78 is unchanged from Smalltalk-76 (see here), 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.

Character Set

The system uses the ASCII-1963 character set which had and arrows in place of the ^ and _ characters in later ASCII versions. From the control character range, only Tab (9) and Carriage Return (13) are used as in ASCII, while some others are used for special characters not found in ASCII. These include return and implication arrows ⇑ ⇒, comparison operators ≤ ≥ ≠ ≡, indexing and point creation operators ◦ ⊚, a curved arrow for literal quoting (used like # in Smalltalk-80), a unary minus ¬ (for prefixing negative numbers, as in 3 − −4), a possessive operator ’s (evaluates an expression in the context of another object, e.g. obj ’s 'instvar'), and a few more. See the paper for a complete list, and refer to the keyboard help.

Conditional execution

A block of code can be conditionally evaluated using the operator. It maps directly to the “jump if not false” bytecode (where false is just an instance of Object 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 Object≫and: demonstrates:

and: x
    [self⇒[⇑x] ⇑false]
If execution should resume after the else case, another set of brackets needs to be put around it.

Deferred evaluation

Brackets [] are merely a syntactic device to group statements, they do not create a block object as in Smalltalk-80. How then does e.g. for⦂from:do⦂ work? The magic is not in the brackets, but in the two variants of the colon in a keyword message. The regular colon : causes the keyword argument to be evaluated immediately. For an open colon however, the compiler generates a “remote copy” of the current context. This RemoteCode can be evaluated later, and even repeatedly if desired.

There are two variants for evaluation, with and without an argument. The form with an argument value← 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 for⦂from:do⦂ could look like this:

for⦂ var from: vec do⦂ code | stream item
[
    stream ← vec asStream.
    while⦂ [item ← stream next] do⦂ [
        var value← item.
        code eval.
    ]
]
Note that for⦂from:do⦂ 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.

Assignment Selector

The last part of a keyword selector can be an assignment arrow . E.g. stream next← 5 writes an item to the stream, whereas stream next: 5 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 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. a◦1 ← b◦1 uses the and ◦← operators. It is equivalent to Smalltalk-80’s a at: 1 put: (b at: 1) but reads a lot nicer and needs no parentheses.

No metaclasses

The expression Stream default 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 Class so no class-specific methods are possible. Instead, Class provides a couple of methods that dispatch to a new instance. E.g. the implementation of Class≫default is ⇑self new default. In the Stream case, this invokes Stream≫default which initializes thestream for writing on a new String.