+PSPFETCH = defPrimWord("PSP@", () -> begin
+ pushPS(reg.PSP)
+ return NEXT
+end)
+
+PSPSTORE = defPrimWord("PSP!", () -> begin
+ PSP = popPS()
+ return NEXT
+end)
+
+# Working Register
+
+WFETCH = defPrimWord("W@", () -> begin
+ pushPS(reg.W)
+ return NEXT
+end)
+
+WSTORE = defPrimWord("W!", () -> begin
+ reg.W = popPS()
+ return NEXT
+end)
+
+# I/O
+
+sources = Array{Any,1}()
+currentSource() = sources[length(sources)]
+
+EOF_CFA = defConst("EOF", 4)
+
+EMIT = defPrimWord("EMIT", () -> begin
+ print(Char(popPS()))
+ return NEXT
+end)
+
+SPAN, SPAN_CFA = defNewVar("SPAN", 0)
+EXPECT = defPrimWord("EXPECT", () -> begin
+ maxLen = popPS()
+ addr = popPS()
+
+ if !eof(currentSource())
+ line = chomp(readline(currentSource()))
+ mem[SPAN] = min(length(line), maxLen)
+ putString(line[1:mem[SPAN]], addr)
+ else
+ mem[SPAN] = 1
+ mem[addr] = EOF
+ end
+
+ return NEXT
+end)
+
+BASE, BASE_CFA = defNewVar("BASE", 10)
+NUMBER = defPrimWord("NUMBER", () -> begin
+ wordAddr = popPS()+1
+ wordLen = mem[wordAddr-1]
+
+ s = getString(wordAddr, wordLen)
+
+ pushPS(parse(Int64, s, mem[BASE]))
+
+ return NEXT
+end)
+
+# Dictionary searches
+
+FIND = defPrimWord("FIND", () -> begin
+
+ countedAddr = popPS()
+ wordAddr = countedAddr + 1
+ wordLen = mem[countedAddr]
+ word = lowercase(getString(wordAddr, wordLen))
+
+ latest = LATEST
+
+ i = 0
+ while (latest = mem[latest]) > 0
+ lenAndFlags = mem[latest+1]
+ len = lenAndFlags & F_LENMASK
+ hidden = (lenAndFlags & F_HIDDEN) == F_HIDDEN
+
+ if hidden || len != wordLen
+ continue
+ end
+
+ thisAddr = latest+2
+ thisWord = lowercase(getString(thisAddr, len))
+
+ if lowercase(thisWord) == lowercase(word)
+ break
+ end
+ end
+
+ pushPS(latest)
+
+ return NEXT
+end)
+
+TOCFA = defPrimWord(">CFA", () -> begin
+
+ addr = popPS()
+ lenAndFlags = mem[addr+1]
+ len = lenAndFlags & F_LENMASK
+
+ pushPS(addr + 2 + len)
+
+ return NEXT
+end)
+
+TODFA = defWord(">DFA", [TOCFA, INCR, EXIT])
+
+# Branching
+
+BRANCH = defPrimWord("BRANCH", () -> begin
+ reg.IP += mem[reg.IP]
+ return NEXT
+end)
+
+ZBRANCH = defPrimWord("0BRANCH", () -> begin
+ if (popPS() == 0)
+ reg.IP += mem[reg.IP]
+ else
+ reg.IP += 1
+ end
+
+ return NEXT
+end)
+
+# Strings
+
+LITSTRING = defPrimWord("LITSTRING", () -> begin
+ len = mem[reg.IP]
+ reg.IP += 1
+ pushPS(reg.IP)
+ pushPS(len)
+ reg.IP += len
+
+ return NEXT
+end)
+
+TYPE = defPrimWord("TYPE", () -> begin
+ len = popPS()
+ addr = popPS()
+ str = getString(addr, len)
+ print(str)
+ return NEXT
+end)
+
+# Outer interpreter
+
+TRACE = defPrimWord("TRACE", () -> begin
+ print("RS: "); printRS()
+ print("PS: "); printPS()
+ print("[paused]")
+ readline()
+
+ return NEXT
+end)
+
+COMMA = defPrimWord(",", () -> begin
+ mem[mem[HERE]] = popPS()
+ mem[HERE] += 1
+
+ return NEXT
+end)
+
+BTICK = defWord("[']",
+ [FROMR, DUP, INCR, TOR, FETCH, EXIT])
+
+EXECUTE = defPrimWord("EXECUTE", () -> begin
+ reg.W = popPS()
+ return mem[reg.W]
+end)
+
+TIB_CFA = defConst("TIB", TIB)
+NUMTIB, NUMTIB_CFA = defNewVar("#TIB", 0)
+TOIN, TOIN_CFA = defNewVar(">IN", 0)
+
+QUERY = defWord("QUERY",
+ [TIB_CFA, LIT, 80, EXPECT,
+ SPAN_CFA, FETCH, NUMTIB_CFA, STORE,
+ LIT, 0, TOIN_CFA, STORE,
+ EXIT])
+
+WORD = defPrimWord("WORD", () -> begin
+ delim = popPS()
+
+ # Chew up initial occurrences of delim
+ while (mem[TOIN]<mem[NUMTIB] && mem[TIB+mem[TOIN]] == delim)
+ mem[TOIN] += 1
+ end
+
+ countAddr = mem[HERE]
+ addr = mem[HERE]+1
+
+ # Start reading in word
+ count = 0
+ while (mem[TOIN]<mem[NUMTIB] && mem[TIB+mem[TOIN]] != delim)
+ mem[addr] = mem[TIB+mem[TOIN]]
+ count += 1
+ addr += 1
+ mem[TOIN] += 1
+ end
+
+ # Record count
+ mem[countAddr] = count
+ pushPS(countAddr)
+
+ return NEXT
+end)
+
+STATE, STATE_CFA = defNewVar("STATE", 0)
+
+INTERPRET = defWord("INTERPRET",
+ [LIT, 32, WORD, # Read next space-delimited word
+
+ DUP, FETCH, ZE, ZBRANCH, 3,
+ DROP, EXIT, # Exit if TIB is exhausted
+
+ STATE_CFA, FETCH, ZBRANCH, 27,
+ # Compiling
+ DUP, FIND, ZBRANCH, 17,
+
+ # Found word.
+ DUP, TOCFA, SWAP, INCR, FETCH, LIT, F_IMMED, AND, ZBRANCH, 4,
+ # Immediate: Execute!
+ EXECUTE, BRANCH, -30,
+
+ # Not immediate: Compile!
+ COMMA, BRANCH, -33,
+
+ # No word found, parse number
+ NUMBER, BTICK, LIT, COMMA, BRANCH, -39,
+
+ # Interpreting
+ DUP, FIND, QDUP, ZBRANCH, 7,
+
+ # Found word. Execute!
+ SWAP, DROP, TOCFA, EXECUTE, BRANCH, -50,
+
+ # No word found, parse number and leave on stack
+ NUMBER, BRANCH, -53,
+ EXIT]
+)