Removed unnecessary ['] primitive.
[forth.jl.git] / src / forth.jl
1 module forth
2
3 # VM mem size
4 size_mem = 1000000 # 1 mega-int
5
6 # Buffer sizes
7 size_RS = 1000   # Return stack size
8 size_PS = 1000   # Parameter stack size
9 size_TIB = 1000  # Terminal input buffer size
10
11 # Memory arrays
12 mem = Array{Int64,1}(size_mem)
13 primitives = Array{Function,1}()
14 primNames = Array{ASCIIString,1}()
15
16 # Built-in variables
17
18 nextVarAddr = 1
19 H = nextVarAddr; nextVarAddr += 1
20 LATEST = nextVarAddr; nextVarAddr += 1
21
22 RSP0 = nextVarAddr                  # bottom of RS
23 PSP0 = RSP0 + size_RS               # bottom of PS
24 TIB = PSP0 + size_PS                # address of terminal input buffer
25 mem[H] = TIB + size_TIB          # location of bottom of dictionary
26 mem[LATEST] = 0                     # no previous definition
27
28 DICT = mem[H] # Save bottom of dictionary as constant
29
30 # VM registers
31 type Reg
32     RSP::Int64  # Return stack pointer
33     PSP::Int64  # Parameter/data stack pointer
34     IP::Int64   # Instruction pointer
35     W::Int64    # Working register
36 end
37 reg = Reg(RSP0, PSP0, 0, 0)
38
39 # Stack manipulation functions
40
41 function ensurePSDepth(depth::Int64)
42     if reg.PSP - PSP0 < depth
43         error("Parameter stack underflow.")
44     end
45 end
46
47 function ensureRSDepth(depth::Int64)
48     if reg.RSP - RSP0 < depth
49         error("Return stack underflow.")
50     end
51 end
52
53 function pushRS(val::Int64)
54     mem[reg.RSP+=1] = val
55 end
56
57 function popRS()
58     ensureRSDepth(1)
59
60     val = mem[reg.RSP]
61     reg.RSP -= 1
62     return val
63 end
64
65 function pushPS(val::Int64)
66     mem[reg.PSP += 1] = val
67 end
68
69 function popPS()
70     ensurePSDepth(1)
71
72     val = mem[reg.PSP]
73     reg.PSP -= 1
74     return val
75 end
76
77 # Handy functions for adding/retrieving strings to/from memory.
78
79 getString(addr::Int64, len::Int64) = ASCIIString([Char(c) for c in mem[addr:(addr+len-1)]])
80
81 function putString(str::ASCIIString, addr::Int64)
82     mem[addr:(addr+length(str)-1)] = [Int64(c) for c in str]
83 end
84
85 # Primitive creation and calling functions
86
87 function defPrim(f::Function; name="nameless")
88     push!(primitives, f)
89     push!(primNames, replace(name, "\004", "EOF"))
90
91     return -length(primitives)
92 end
93
94 function callPrim(addr::Int64)
95     if addr >=0 || -addr>length(primitives)
96         error("Attempted to execute non-existent primitive at address $addr.")
97     else
98         primitives[-addr]()
99     end
100 end
101 getPrimName(addr::Int64) = primNames[-addr]
102
103 # Word creation functions
104
105 F_LENMASK = 31
106 F_IMMED = 32
107 F_HIDDEN = 64
108 NFA_MARK = 128
109
110 function createHeader(name::AbstractString, flags::Int64)
111     mem[mem[H]] = mem[LATEST]
112     mem[LATEST] = mem[H]
113     mem[H] += 1
114
115     mem[mem[H]] = length(name) | flags | NFA_MARK; mem[H] += 1
116     putString(name, mem[H]); mem[H] += length(name)
117 end
118
119 function defPrimWord(name::AbstractString, f::Function; flags::Int64=0)
120     createHeader(name, flags)
121
122     codeWordAddr = mem[H]
123     mem[codeWordAddr] = defPrim(f, name=name)
124     mem[H] += 1
125
126     return codeWordAddr
127 end
128
129 function defWord(name::AbstractString, wordAddrs::Array{Int64,1}; flags::Int64=0)
130     createHeader(name, flags)
131
132     addr = mem[H]
133     mem[mem[H]] = DOCOL
134     mem[H] += 1
135
136     for wordAddr in wordAddrs
137         mem[mem[H]] = wordAddr
138         mem[H] += 1
139     end
140
141     return addr
142 end
143
144 # Variable creation functions
145
146 function defExistingVar(name::AbstractString, varAddr::Int64; flags::Int64=0)
147
148     defPrimWord(name, eval(:(() -> begin
149         pushPS($(varAddr))
150         return NEXT
151     end)))
152 end
153
154 function defNewVar(name::AbstractString, initial::Int64; flags::Int64=0)
155     createHeader(name, flags)
156     
157     codeWordAddr = mem[H]
158     varAddr = mem[H] + 1
159
160     mem[mem[H]] = DOVAR; mem[H] += 1
161     mem[mem[H]] = initial; mem[H] += 1
162
163     return varAddr, codeWordAddr
164 end
165
166 function defConst(name::AbstractString, val::Int64; flags::Int64=0)
167     createHeader(name, flags)
168
169     codeWordAddr = mem[H]
170
171     mem[mem[H]] = DOCON; mem[H] += 1
172     mem[mem[H]] = val; mem[H] += 1
173
174     return codeWordAddr
175 end
176
177 # Threading Primitives (inner interpreter)
178
179 NEXT = defPrim(() -> begin
180     reg.W = mem[reg.IP]
181     reg.IP += 1
182     return mem[reg.W]
183 end, name="NEXT")
184
185 DOCOL = defPrim(() -> begin
186     pushRS(reg.IP)
187     reg.IP = reg.W + 1
188     return NEXT
189 end, name="DOCOL")
190
191 DOVAR = defPrim(() -> begin
192     pushPS(reg.W + 1)
193     return NEXT
194 end, name="DOVAR")
195
196 DOCON = defPrim(() -> begin
197     pushPS(mem[reg.W + 1])
198     return NEXT
199 end, name="DOVAR")
200
201 EXIT_CFA = defPrimWord("EXIT", () -> begin
202     reg.IP = popRS()
203     return NEXT
204 end)
205
206 # Dictionary entries for core built-in variables, constants
207
208 H_CFA = defExistingVar("H", H)
209 LATEST_CFA = defExistingVar("LATEST", LATEST)
210
211 PSP0_CFA = defConst("PSP0", PSP0)
212 RSP0_CFA = defConst("RSP0", RSP0)
213
214 defConst("DOCOL", DOCOL)
215 defConst("DOCON", DOCON)
216 defConst("DOVAR", DOVAR)
217
218 defConst("DICT", DICT)
219 defConst("MEMSIZE", size_mem)
220
221 F_IMMED_CFA = defConst("F_IMMED", F_IMMED)
222 F_HIDDEN_CFA = defConst("F_HIDDEN", F_HIDDEN)
223 F_LENMASK_CFA = defConst("F_LENMASK", F_LENMASK)
224 NFA_MARK_CFA = defConst("NFA_MARK", NFA_MARK)
225
226 # Basic forth primitives
227
228 DROP_CFA = defPrimWord("DROP", () -> begin
229     popPS()
230     return NEXT
231 end)
232
233 SWAP_CFA = defPrimWord("SWAP", () -> begin
234     a = popPS()
235     b = popPS()
236     pushPS(a)
237     pushPS(b)
238     return NEXT
239 end)
240
241 DUP_CFA = defPrimWord("DUP", () -> begin
242     ensurePSDepth(1)
243     pushPS(mem[reg.PSP])
244     return NEXT
245 end)
246
247 OVER_CFA = defPrimWord("OVER", () -> begin
248     ensurePSDepth(2)
249     pushPS(mem[reg.PSP-1])
250     return NEXT
251 end)
252
253 ROT_CFA = defPrimWord("ROT", () -> begin
254     a = popPS()
255     b = popPS()
256     c = popPS()
257     pushPS(b)
258     pushPS(a)
259     pushPS(c)
260     return NEXT
261 end)
262
263 NROT_CFA = defPrimWord("-ROT", () -> begin
264     a = popPS()
265     b = popPS()
266     c = popPS()
267     pushPS(a)
268     pushPS(c)
269     pushPS(b)
270     return NEXT
271 end)
272
273
274 TWODROP_CFA = defPrimWord("2DROP", () -> begin
275     popPS()
276     popPS()
277     return NEXT
278 end)
279
280 TWODUP_CFA = defPrimWord("2DUP", () -> begin
281     ensurePSDepth(2)
282     a = mem[reg.PSP-1]
283     b = mem[reg.PSP]
284     pushPS(a)
285     pushPS(b)
286     return NEXT
287 end)
288
289 TWOSWAP_CFA = defPrimWord("2SWAP", () -> begin
290     a = popPS()
291     b = popPS()
292     c = popPS()
293     d = popPS()
294     pushPS(b)
295     pushPS(a)
296     pushPS(d)
297     pushPS(c)
298     return NEXT
299 end)
300
301 TWOOVER_CFA = defPrimWord("2OVER", () -> begin
302     ensurePSDepth(4)
303     a = mem[reg.PSP-3]
304     b = mem[reg.PSP-2]
305     pushPS(a)
306     pushPS(b)
307     return NEXT
308 end)
309
310 QDUP_CFA = defPrimWord("?DUP", () -> begin
311     ensurePSDepth(1)
312     val = mem[reg.PSP]
313     if val != 0
314         pushPS(val)
315     end
316     return NEXT
317 end)
318
319 INCR_CFA = defPrimWord("1+", () -> begin
320     ensurePSDepth(1)
321     mem[reg.PSP] += 1
322     return NEXT
323 end)
324
325 DECR_CFA = defPrimWord("1-", () -> begin
326     ensurePSDepth(1)
327     mem[reg.PSP] -= 1
328     return NEXT
329 end)
330
331 INCR2_CFA = defPrimWord("2+", () -> begin
332     ensurePSDepth(1)
333     mem[reg.PSP] += 2
334     return NEXT
335 end)
336
337 DECR2_CFA = defPrimWord("2-", () -> begin
338     ensurePSDepth(1)
339     mem[reg.PSP] -= 2
340     return NEXT
341 end)
342
343 ADD_CFA = defPrimWord("+", () -> begin
344     b = popPS()
345     a = popPS()
346     pushPS(a+b)
347     return NEXT
348 end)
349
350 SUB_CFA = defPrimWord("-", () -> begin
351     b = popPS()
352     a = popPS()
353     pushPS(a-b)
354     return NEXT
355 end)
356
357 MUL_CFA = defPrimWord("*", () -> begin
358     b = popPS()
359     a = popPS()
360     pushPS(a*b)
361     return NEXT
362 end)
363
364 DIVMOD_CFA = defPrimWord("/MOD", () -> begin
365     b = popPS()
366     a = popPS()
367     q,r = divrem(a,b)
368     pushPS(r)
369     pushPS(q)
370     return NEXT
371 end)
372
373 TWOMUL_CFA = defPrimWord("2*", () -> begin
374     pushPS(popPS() << 1)
375     return NEXT
376 end)
377
378 TWODIV_CFA = defPrimWord("2/", () -> begin
379     pushPS(popPS() >> 1)
380     return NEXT
381 end)
382
383 EQ_CFA = defPrimWord("=", () -> begin
384     b = popPS()
385     a = popPS()
386     pushPS(a==b ? -1 : 0)
387     return NEXT
388 end)
389
390 NE_CFA = defPrimWord("<>", () -> begin
391     b = popPS()
392     a = popPS()
393     pushPS(a!=b ? -1 : 0)
394     return NEXT
395 end)
396
397 LT_CFA = defPrimWord("<", () -> begin
398     b = popPS()
399     a = popPS()
400     pushPS(a<b ? -1 : 0)
401     return NEXT
402 end)
403
404 GT_CFA = defPrimWord(">", () -> begin
405     b = popPS()
406     a = popPS()
407     pushPS(a>b ? -1 : 0)
408     return NEXT
409 end)
410
411 LE_CFA = defPrimWord("<=", () -> begin
412     b = popPS()
413     a = popPS()
414     pushPS(a<=b ? -1 : 0)
415     return NEXT
416 end)
417
418 GE_CFA = defPrimWord(">=", () -> begin
419     b = popPS()
420     a = popPS()
421     pushPS(a>=b ? -1 : 0)
422     return NEXT
423 end)
424
425 ZE_CFA = defPrimWord("0=", () -> begin
426     pushPS(popPS() == 0 ? -1 : 0)
427     return NEXT
428 end)
429
430 ZNE_CFA = defPrimWord("0<>", () -> begin
431     pushPS(popPS() != 0 ? -1 : 0)
432     return NEXT
433 end)
434
435 ZLT_CFA = defPrimWord("0<", () -> begin
436     pushPS(popPS() < 0 ? -1 : 0)
437     return NEXT
438 end)
439
440 ZGT_CFA = defPrimWord("0>", () -> begin
441     pushPS(popPS() > 0 ? -1 : 0)
442     return NEXT
443 end)
444
445 ZLE_CFA = defPrimWord("0<=", () -> begin
446     pushPS(popPS() <= 0 ? -1 : 0)
447     return NEXT
448 end)
449
450 ZGE_CFA = defPrimWord("0>=", () -> begin
451     pushPS(popPS() >= 0 ? -1 : 0)
452     return NEXT
453 end)
454
455 AND_CFA = defPrimWord("AND", () -> begin
456     b = popPS()
457     a = popPS()
458     pushPS(a & b)
459     return NEXT
460 end)
461
462 OR_CFA = defPrimWord("OR", () -> begin
463     b = popPS()
464     a = popPS()
465     pushPS(a | b)
466     return NEXT
467 end)
468
469 XOR_CFA = defPrimWord("XOR", () -> begin
470     b = popPS()
471     a = popPS()
472     pushPS(a $ b)
473     return NEXT
474 end)
475
476 INVERT_CFA = defPrimWord("INVERT", () -> begin
477     pushPS(~popPS())
478     return NEXT
479 end)
480
481 # Literals
482
483 LIT_CFA = defPrimWord("LIT", () -> begin
484     pushPS(mem[reg.IP])
485     reg.IP += 1
486     return NEXT
487 end)
488
489 # Memory primitives
490
491 STORE_CFA = defPrimWord("!", () -> begin
492     addr = popPS()
493     dat = popPS()
494     mem[addr] = dat
495     return NEXT
496 end)
497
498 FETCH_CFA = defPrimWord("@", () -> begin
499     addr = popPS()
500     pushPS(mem[addr])
501     return NEXT
502 end)
503
504 ADDSTORE_CFA = defPrimWord("+!", () -> begin
505     addr = popPS()
506     toAdd = popPS()
507     mem[addr] += toAdd
508     return NEXT
509 end)
510
511 SUBSTORE_CFA = defPrimWord("-!", () -> begin
512     addr = popPS()
513     toSub = popPS()
514     mem[addr] -= toSub
515     return NEXT
516 end)
517
518
519 # Return Stack
520
521 TOR_CFA = defPrimWord(">R", () -> begin
522     pushRS(popPS())
523     return NEXT
524 end)
525
526 FROMR_CFA = defPrimWord("R>", () -> begin
527     pushPS(popRS())
528     return NEXT
529 end)
530
531 RFETCH_CFA = defPrimWord("R@", () -> begin
532     pushPS(mem[reg.RSP])
533     return NEXT
534 end)
535
536 RSPFETCH_CFA = defPrimWord("RSP@", () -> begin
537     pushPS(reg.RSP)
538     return NEXT
539 end)
540
541 RSPSTORE_CFA = defPrimWord("RSP!", () -> begin
542     reg.RSP = popPS()
543     return NEXT
544 end)
545
546 RDROP_CFA = defPrimWord("RDROP", () -> begin
547     popRS()
548     return NEXT
549 end)
550
551 # Parameter Stack
552
553 PSPFETCH_CFA = defPrimWord("PSP@", () -> begin
554     pushPS(reg.PSP)
555     return NEXT
556 end)
557
558 PSPSTORE_CFA = defPrimWord("PSP!", () -> begin
559     reg.PSP = popPS()
560     return NEXT
561 end)
562
563 # Working Register
564
565 WFETCH_CFA = defPrimWord("W@", () -> begin
566     pushPS(reg.W)
567     return NEXT
568 end)
569
570 WSTORE_CFA = defPrimWord("W!", () -> begin
571     reg.W = popPS()
572     return NEXT
573 end)
574
575 # I/O
576
577 sources = Array{Any,1}()
578 currentSource() = sources[length(sources)]
579
580 EOF_CFA = defPrimWord("\x04", () -> begin
581     if currentSource() != STDIN
582         close(pop!(sources))
583         return NEXT
584     else
585         return 0
586     end
587 end)
588
589 EMIT_CFA = defPrimWord("EMIT", () -> begin
590     print(Char(popPS()))
591     return NEXT
592 end)
593
594 function raw_mode!(mode::Bool)
595     if ccall(:jl_tty_set_mode, Int32, (Ptr{Void}, Int32), STDIN.handle, mode) != 0
596         throw("FATAL: Terminal unable to enter raw mode.")
597     end
598 end
599
600 function getKey()
601     raw_mode!(true)
602     byte = readbytes(STDIN, 1)[1]
603     raw_mode!(false)
604
605     if byte == 0x0d
606         return 0x0a
607     elseif byte == 127
608         return 0x08
609     else
610         return byte
611     end
612 end
613
614 KEY_CFA = defPrimWord("KEY", () -> begin
615     pushPS(Int(getKey()))
616     return NEXT
617 end)
618
619 function getLineFromSTDIN()
620     line = ""
621     while true
622         key = Char(getKey())
623
624         if key == '\n'
625             print(" ")
626             return ASCIIString(line)
627
628         elseif key == '\x04'
629             if isempty(line)
630                 return string("\x04")
631             end
632
633         elseif key == '\b'
634             if !isempty(line)
635                 line = line[1:length(line)-1]
636                 print("\b \b")
637             end
638
639         elseif key == '\e'
640             # Strip ANSI escape sequence
641             nextKey = Char(getKey())
642             if nextKey == '['
643                 while true
644                     nextKey = Char(getKey())
645                     if nextKey >= '@' || nextKey <= '~'
646                         break
647                     end
648                 end
649             end
650
651         else
652             print(key)
653             line = string(line, key)
654         end
655     end
656 end
657
658 SPAN, SPAN_CFA = defNewVar("SPAN", 0)
659 EXPECT_CFA = defPrimWord("EXPECT", () -> begin
660     maxLen = popPS()
661     addr = popPS()
662
663     if currentSource() == STDIN
664         line = getLineFromSTDIN()
665     else
666         if !eof(currentSource())
667             line = chomp(readline(currentSource()))
668         else
669             line = "\x04" # eof
670         end
671     end
672
673     mem[SPAN] = min(length(line), maxLen)
674     putString(line[1:mem[SPAN]], addr)
675
676     return NEXT
677 end)
678
679 BASE, BASE_CFA = defNewVar("BASE", 10)
680 NUMBER_CFA = defPrimWord("NUMBER", () -> begin
681     wordAddr = popPS()+1
682     wordLen = mem[wordAddr-1]
683
684     s = getString(wordAddr, wordLen)
685
686     pushPS(parse(Int64, s, mem[BASE]))
687
688     return NEXT
689 end)
690
691 # Dictionary searches
692
693 TOCFA_CFA = defPrimWord(">CFA", () -> begin
694
695     addr = popPS()
696     lenAndFlags = mem[addr+1]
697     len = lenAndFlags & F_LENMASK
698
699     pushPS(addr + 2 + len)
700
701     return NEXT
702 end)
703
704 TOBODY_CFA = defWord(">BODY", [INCR_CFA, EXIT_CFA])
705
706 FIND_CFA = defPrimWord("FIND", () -> begin
707
708     countedAddr = popPS()
709     wordAddr = countedAddr + 1
710     wordLen = mem[countedAddr]
711     word = lowercase(getString(wordAddr, wordLen))
712
713     latest = LATEST
714     lenAndFlags = 0
715     
716     i = 0
717     while (latest = mem[latest]) > 0
718         lenAndFlags = mem[latest+1]
719         len = lenAndFlags & F_LENMASK
720         hidden = (lenAndFlags & F_HIDDEN) == F_HIDDEN
721
722         if hidden || len != wordLen
723             continue
724         end
725         
726         thisAddr = latest+2
727         thisWord = lowercase(getString(thisAddr, len))
728
729         if lowercase(thisWord) == lowercase(word)
730             break
731         end
732     end
733
734     if latest > 0
735         pushPS(latest)
736         callPrim(mem[TOCFA_CFA])
737         if (lenAndFlags & F_IMMED) == F_IMMED
738             pushPS(1)
739         else
740             pushPS(-1)
741         end
742     else
743         pushPS(countedAddr)
744         pushPS(0)
745     end
746
747     return NEXT
748 end)
749
750
751 # Branching
752
753 BRANCH_CFA = defPrimWord("BRANCH", () -> begin
754     reg.IP += mem[reg.IP]
755     return NEXT
756 end)
757
758 ZBRANCH_CFA = defPrimWord("0BRANCH", () -> begin
759     if (popPS() == 0)
760         reg.IP += mem[reg.IP]
761     else
762         reg.IP += 1
763     end
764
765     return NEXT
766 end)
767
768 # Strings
769
770 LITSTRING_CFA = defPrimWord("LITSTRING", () -> begin
771     len = mem[reg.IP]
772     reg.IP += 1
773     pushPS(reg.IP)
774     pushPS(len)
775     reg.IP += len
776
777     return NEXT
778 end)
779
780 TYPE_CFA = defPrimWord("TYPE", () -> begin
781     len = popPS()
782     addr = popPS()
783     str = getString(addr, len)
784     print(str)
785     return NEXT
786 end)
787
788 # Interpreter/Compiler-specific I/O
789
790 TIB_CFA = defConst("TIB", TIB)
791 NUMTIB, NUMTIB_CFA = defNewVar("#TIB", 0)
792 TOIN, TOIN_CFA = defNewVar(">IN", 0)
793
794 QUERY_CFA = defWord("QUERY",
795     [TIB_CFA, LIT_CFA, 160, EXPECT_CFA,
796     SPAN_CFA, FETCH_CFA, NUMTIB_CFA, STORE_CFA,
797     LIT_CFA, 0, TOIN_CFA, STORE_CFA,
798     EXIT_CFA])
799
800 WORD_CFA = defPrimWord("WORD", () -> begin
801     delim = popPS()
802
803     # Chew up initial occurrences of delim
804     while (mem[TOIN]<mem[NUMTIB] && mem[TIB+mem[TOIN]] == delim)
805         mem[TOIN] += 1
806     end
807
808     countAddr = mem[H]
809     addr = mem[H]+1
810
811     # Start reading in word
812     count = 0
813     while (mem[TOIN]<mem[NUMTIB])
814         mem[addr] = mem[TIB+mem[TOIN]]
815         mem[TOIN] += 1
816
817         if (mem[addr] == delim)
818             break
819         end
820
821         count += 1
822         addr += 1
823     end
824
825     # Record count
826     mem[countAddr] = count
827     pushPS(countAddr)
828
829     return NEXT
830 end)
831
832 # Compilation
833
834 STATE, STATE_CFA = defNewVar("STATE", 0)
835
836 COMMA_CFA = defPrimWord(",", () -> begin
837     mem[mem[H]] = popPS()
838     mem[H] += 1
839
840     return NEXT
841 end)
842
843 HERE_CFA = defWord("HERE",
844     [H_CFA, FETCH_CFA, EXIT_CFA])
845
846 HEADER_CFA = defPrimWord("HEADER", () -> begin
847     wordAddr = popPS()+1
848     wordLen = mem[wordAddr-1]
849     word = getString(wordAddr, wordLen)
850
851     createHeader(word, 0)
852
853     return NEXT
854 end)
855
856 CREATE_CFA = defWord("CREATE",
857     [LIT_CFA, 32, WORD_CFA, HEADER_CFA,
858     LIT_CFA, DOVAR, COMMA_CFA,
859     EXIT_CFA])
860
861 DODOES = defPrim(() -> begin
862     pushRS(reg.IP)
863     reg.IP = popPS()
864     pushPS(reg.W + 1)
865     return NEXT
866 end, name="DODOES")
867
868 DOES_HELPER_CFA = defPrimWord("(DOES>)", () -> begin
869
870     pushPS(mem[LATEST])
871     callPrim(mem[TOCFA_CFA])
872     cfa = popPS()
873
874     runtimeAddr = popPS()
875
876     mem[cfa] = defPrim(eval(:(() -> begin
877         pushPS($(runtimeAddr))
878         return DODOES
879     end)), name="doesPrim")
880
881     return NEXT
882 end, flags=F_IMMED)
883
884 DOES_CFA = defWord("DOES>",
885     [LIT_CFA, LIT_CFA, COMMA_CFA, HERE_CFA, LIT_CFA, 3, ADD_CFA, COMMA_CFA,
886     LIT_CFA, DOES_HELPER_CFA, COMMA_CFA, LIT_CFA, EXIT_CFA, COMMA_CFA, EXIT_CFA],
887     flags=F_IMMED)
888
889 LBRAC_CFA = defPrimWord("[", () -> begin
890     mem[STATE] = 0
891     return NEXT
892 end, flags=F_IMMED)
893
894 RBRAC_CFA = defPrimWord("]", () -> begin
895     mem[STATE] = 1
896     return NEXT
897 end, flags=F_IMMED)
898
899 HIDDEN_CFA = defPrimWord("HIDDEN", () -> begin
900     lenAndFlagsAddr = mem[LATEST] + 1
901     mem[lenAndFlagsAddr] = mem[lenAndFlagsAddr] $ F_HIDDEN
902     return NEXT
903 end)
904
905 COLON_CFA = defWord(":",
906     [LIT_CFA, 32, WORD_CFA,
907     HEADER_CFA,
908     LIT_CFA, DOCOL, COMMA_CFA,
909     HIDDEN_CFA,
910     RBRAC_CFA,
911     EXIT_CFA])
912
913 SEMICOLON_CFA = defWord(";",
914     [LIT_CFA, EXIT_CFA, COMMA_CFA,
915     HIDDEN_CFA,
916     LBRAC_CFA,
917     EXIT_CFA], flags=F_IMMED)
918
919 IMMEDIATE_CFA = defPrimWord("IMMEDIATE", () -> begin
920     lenAndFlagsAddr = mem[LATEST] + 1
921     mem[lenAndFlagsAddr] = mem[lenAndFlagsAddr] $ F_IMMED
922     return NEXT
923 end, flags=F_IMMED)
924
925 # Outer Interpreter
926
927 EXECUTE_CFA = defPrimWord("EXECUTE", () -> begin
928     reg.W = popPS()
929     return mem[reg.W]
930 end)
931
932 INTERPRET_CFA = defWord("INTERPRET",
933     [LIT_CFA, 32, WORD_CFA, # Read next space-delimited word
934
935     DUP_CFA, FETCH_CFA, ZE_CFA, ZBRANCH_CFA, 3,
936         DROP_CFA, EXIT_CFA, # Exit if TIB is exhausted
937
938     STATE_CFA, FETCH_CFA, ZBRANCH_CFA, 24,
939         # Compiling
940         FIND_CFA, QDUP_CFA, ZBRANCH_CFA, 13,
941
942             # Found word. 
943             LIT_CFA, -1, EQ_CFA, INVERT_CFA, ZBRANCH_CFA, 4,
944
945                 # Immediate: Execute!
946                 EXECUTE_CFA, BRANCH_CFA, -26,
947
948                 # Not immediate: Compile!
949                 COMMA_CFA, BRANCH_CFA, -29,
950
951             # No word found, parse number
952             NUMBER_CFA, LIT_CFA, LIT_CFA, COMMA_CFA, COMMA_CFA, BRANCH_CFA, -36,
953         
954        # Interpreting
955         FIND_CFA, QDUP_CFA, ZBRANCH_CFA, 5,
956
957             # Found word. Execute!
958             DROP_CFA, EXECUTE_CFA, BRANCH_CFA, -44,
959
960             # No word found, parse number and leave on stack
961             NUMBER_CFA, BRANCH_CFA, -47,
962     EXIT_CFA])
963
964 PROMPT_CFA = defPrimWord("PROMPT", () -> begin
965     if (mem[STATE] == 0 && currentSource() == STDIN)
966         println(" ok")
967     end
968
969     return NEXT
970 end)
971
972 QUIT_CFA = defWord("QUIT",
973     [LIT_CFA, 0, STATE_CFA, STORE_CFA,
974     LIT_CFA, 0, NUMTIB_CFA, STORE_CFA,
975     RSP0_CFA, RSPSTORE_CFA,
976     QUERY_CFA,
977     INTERPRET_CFA, PROMPT_CFA,
978     BRANCH_CFA,-4])
979
980 ABORT_CFA = defWord("ABORT",
981     [PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
982
983 BYE_CFA = defPrimWord("BYE", () -> begin
984     println("\nBye!")
985     return 0
986 end)
987
988 # File I/O
989
990 INCLUDE_CFA = defPrimWord("INCLUDE", () -> begin
991     pushPS(32)
992     callPrim(mem[WORD_CFA])
993     wordAddr = popPS()+1
994     wordLen = mem[wordAddr-1]
995     word = getString(wordAddr, wordLen)
996
997     push!(sources, open(word, "r"))
998
999     # Clear input buffer
1000     mem[NUMTIB] = 0
1001
1002     return NEXT
1003 end)
1004
1005
1006 #### VM loop ####
1007
1008 initialized = false
1009 initFileName = nothing
1010 if isfile("lib.4th")
1011     initFileName = "lib.4th"
1012 elseif isfile(Pkg.dir("forth/src/lib.4th"))
1013     initFileName = Pkg.dir("forth/src/lib.4th")
1014 end
1015
1016 function run(;initialize=true)
1017     # Begin with STDIN as source
1018     push!(sources, STDIN)
1019
1020     global initialized, initFileName
1021     if !initialized && initialize
1022         if initFileName != nothing
1023             print("Including definitions from $initFileName...")
1024             push!(sources, open(initFileName, "r"))
1025             initialized = true
1026         else
1027             println("No library file found. Only primitive words available.")
1028         end
1029     end
1030
1031     # Start with IP pointing to first instruction of outer interpreter
1032     reg.IP = QUIT_CFA + 1
1033
1034     # Primitive processing loop.
1035     # Everyting else is simply a consequence of this loop!
1036     jmp = NEXT
1037     while jmp != 0
1038         try
1039             #println("Entering prim $(getPrimName(jmp))")
1040             jmp = callPrim(jmp)
1041
1042         catch ex
1043             showerror(STDOUT, ex)
1044             println()
1045
1046             while !isempty(sources) && currentSource() != STDIN
1047                 close(pop!(sources))
1048             end
1049
1050             # QUIT
1051             reg.IP = ABORT_CFA + 1
1052             jmp = NEXT
1053         end
1054     end
1055 end
1056
1057 # Debugging tools
1058
1059 TRACE_CFA = defPrimWord("TRACE", () -> begin
1060     println("reg.W: $(reg.W) reg.IP: $(reg.IP)")
1061     print("PS: "); printPS()
1062     print("RS: "); printRS()
1063     print("[paused]")
1064     readline()
1065
1066     return NEXT
1067 end)
1068
1069 function dump(startAddr::Int64; count::Int64 = 100, cellsPerLine::Int64 = 10)
1070     chars = Array{Char,1}(cellsPerLine)
1071
1072     lineStartAddr = cellsPerLine*div((startAddr-1),cellsPerLine) + 1
1073     endAddr = startAddr + count - 1
1074
1075     q, r = divrem((endAddr-lineStartAddr+1), cellsPerLine)
1076     numLines = q + (r > 0 ? 1 : 0)
1077
1078     i = lineStartAddr
1079     for l in 1:numLines
1080         print(i,":")
1081
1082         for c in 1:cellsPerLine
1083             if i >= startAddr && i <= endAddr
1084                 print("\t",mem[i])
1085                 if mem[i]>=32 && mem[i]<128
1086                     chars[c] = Char(mem[i])
1087                 else
1088                     chars[c] = '.'
1089                 end
1090             else
1091                 print("\t")
1092                 chars[c] = ' '
1093             end
1094
1095             i += 1
1096         end
1097
1098         println("\t", ASCIIString(chars))
1099     end
1100 end
1101
1102 function printPS()
1103     count = reg.PSP - PSP0
1104
1105     if count > 0
1106         print("<$count>")
1107         for i in (PSP0+1):reg.PSP
1108             print(" $(mem[i])")
1109         end
1110         println()
1111     else
1112         println("Parameter stack empty")
1113     end
1114 end
1115
1116 function printRS()
1117     count = reg.RSP - RSP0
1118
1119     if count > 0
1120         print("<$count>")
1121         for i in (RSP0+1):reg.RSP
1122             print(" $(mem[i])")
1123         end
1124         println()
1125     else
1126         println("Return stack empty")
1127     end
1128 end
1129
1130 DUMP = defPrimWord("DUMP", () -> begin
1131     count = popPS()
1132     addr = popPS()
1133
1134     println()
1135     dump(addr, count=count)
1136
1137     return NEXT
1138 end)
1139
1140 end