After completing my interpreter construction tutorial, I could not help but wonder how my little toy would fare in the real world. That, and I came up with a more general way to implement words and control structures. MuScript is the result.
Some code examples:
"Hello, world" print cr (self-explanatory)
(count from 1 to 10)
0 begin
incr dup print cr
end 10 times
drop
(the same with an explicit test)
1 begin
dup 10 > then break
dup . cr incr
end repeat drop
(Simple conditional statements)
true false both? then
begin 'Is too!' . cr end iftrue
begin 'Is not!' . cr end iffalse
3 4 dup * swap dup * + sqrt . cr (calculate a hypotenuse)
3 4 begin dup * swap dup * + sqrt end run . cr
(the same, with a block of code)
3 4 begin dup * swap dup * + sqrt end word eval . cr
(the same, with an anonymous word)
: hypot dup * swap dup * + sqrt ;
3 4 hypot . cr (the same, with a colon definition)
x: 3 !
x: @ . cr (variables)
hello: begin "Hello, world!" print cr end word !
hello: @ eval (runtime word definition and storing/calling)
(Defining a select-case control structure.
Note how 'case' works; it is effectively a macro.)
: select run ;
: case
swap: @
then: @
iftrue: @
break: @
; immediate
begin
false begin 'Case 1' . cr end case
true begin 'Case 2' . cr end case
1 2 < begin "Shouldn't happen" . cr end case
end select
I accepted a few limitations in order to maintain simplicity.
For example, immediate only works after a word
definition, not inside it. And you can't have words starting with
a number (hence incr instead of 1+),
because of parseFloat's overly generous behavior. Oh,
and because a new word doesn't exist until after the semicolon,
recursive calls must use the @ eval idiom.
Also, for now the interpreter works by precompiling the whole script before running. A truly interactive mode is being planned.
