Chapter 1
The step rule
An Ether program is a list of words separated by spaces. That is the whole syntax: there are no parentheses to balance, no operators with precedence, no statements and expressions. Everything else you will meet is built from one rule that the interpreter applies over and over.
Every example on these pages runs. Change it and press Run (or ⌘/Ctrl + Enter) to see what your version does.
hello world
values: ( "world" "hello" )
The program pushed two words onto a stack called the values
stack. The output shows the values a program leaves behind, top first:
world was pushed last, so it is on top and comes first. This page
always writes a stack the same way, top first, inside ( ).
A process
The interpreter runs a process, which has:
- values: a stack of things the program is working with;
- instructions: a stack of words still to run, the next one on top;
- macros: a table that maps names to lists of instructions.
When a program starts, all of its words go onto the instructions stack with the first word on top. Then the interpreter repeats one step until no instructions are left:
The step rule. Take the top instruction.
- If it is a function, run it. A function is a piece of the interpreter itself; it can do anything to the process.
- Otherwise, if the macro table has an entry for it, put that entry's list on the instructions stack.
- Otherwise, push it onto the values stack.
hello and world have no macros, so rule 3 pushes
them. A word like debug has a macro: its list holds a single
function, which removes the top value and prints it.
hello debug
hello values: ( )
Nothing is left on the values stack: debug took
hello off it. Every built-in word works this way: it is a macro
whose list holds one function. Words you define yourself are macros too, so a
word you write and a word the interpreter provides look the same to the
step rule.
Anything between << and >> is a comment
and is skipped. Comments can be nested.
<< this is not run >> hello
values: ( "hello" )
Numbers and text
A word is just bytes: 3 on its own is the one-byte word
"3", not a number. The word # reads the word after it and pushes
the number it spells.
# 3 3
values: ( "3" 3 )
Both print as 3, but they are different things: the top one is
the word "3" (shown in quotes), the one below is the number 3. Numbers are
whole numbers, positive or negative. Both a number and a piece of text are
stored as a block of bytes, which Ether calls a rom; you will see that
name in error messages.
To write text with spaces in it, put it between " words. The
quote marks are words themselves, so they need spaces around them, and the
words between them are joined with single spaces.
" hello, world " debug
hello, world values: ( )
Inside the quotes, three words stand for characters you cannot type as a
word: \n a new line, \t a tab and \s a
space. Each must be a word of its own, with spaces around it, and it replaces
the spaces on both sides of it. So " one \n two " has no spaces
around the new line, and \s is how you put a space where it would
otherwise be dropped: at either end, or next to another escape. The second
line below is 8 bytes long, the six letters and a space at each end.
" one \n two " debug " \s padded \s " size debug
one two 8 values: ( )
Arithmetic, and which value comes first
+, -, * and / take two
numbers off the values stack and push the result.
# 2 # 3 + debug
5 values: ( )
For - and / the order matters, and Ether has one
rule for it that holds for every word in the language:
The subject is on top. A word works on the value on top of the
stack, its subject; anything else it needs is below. Read
x y word as "y word x".
So # 3 # 10 - is 10 − 3, and # 2 # 10 / is 10 ÷ 2.
Division keeps the whole part and drops any fraction.
# 3 # 10 - debug # 2 # 10 / debug # 2 # 7 / debug
7 5 3 values: ( )
less-than pushes true or false, by the
same rule: # 10 # 3 less-than reads "3 less-than 10".
# 10 # 3 less-than debug # 3 # 10 less-than debug
true false values: ( )
Getting this order backwards is the most common mistake in Ether, and it
fails quietly: - gives a negative number and
less-than gives the opposite answer. When a result surprises you,
check the order first.
Moving values around
Four words rearrange the top of the values stack. Here, and everywhere on
this site, a stack is written top first, as in the output, so
( a b ) means a is on top. The word
list gives every word in the same notation.
| word | before | after | |
|---|---|---|---|
dup | ( a … ) | ( a a … ) | copies the top |
del | ( a … ) | ( … ) | drops the top |
swap | ( a b … ) | ( b a … ) | exchanges the top two |
swap2 | ( a b c … ) | ( c a b … ) | brings the third to the top |
a b c swap2
values: ( "a" "c" "b" )
The program pushed a, b, c, so the
stack was ( "c" "b" "a" ); swap2 brought the third
value, a, to the top. With these words, a number can be used
twice:
# 7 dup * debug
49 values: ( )
When something goes wrong
A word that cannot do its job stops the program with an
exception. The output says so, and shows the exception (a table:
its message says what went wrong and its trace which
word failed) followed by the values that were on the stack:
# 1 + debug
Unhandled exception
( { "data":{ "expected":"rom rom" "found":( 1 ) } "message":"Expected rom and rom from the values stack but the values stack only contains one rom" "trace":( "+" ) "type":"check" } 1 )
+ needs two numbers and found one. Chapter 6 shows how a
program can catch exceptions and carry on.
Exercise 1.1
Print the number 12 by adding 5 and 7.
A correct answer prints:
12 values: ( )
One answer
# 5 # 7 + debug
12 values: ( )
Exercise 1.2
Starting from # 4 and using no other number, print
4 × 4 × 4.
A correct answer prints:
64 values: ( )
One answer
# 4 dup dup * * debug
64 values: ( )
Exercise 1.3
Push a b c, then use only swap and
swap2 to reverse them, so the program leaves
( "a" "b" "c" ): a on top and c at the
bottom.
A correct answer leaves:
values: ( "a" "b" "c" )
One answer
a b c swap swap2
values: ( "a" "b" "c" )
Exercise 1.4
Compute 100 − 7 × 3 and print it.
A correct answer prints:
79 values: ( )
One answer
# 7 # 3 * # 100 - debug
79 values: ( )