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