+
+ wordAddr = mem[HERE]
+ offset = 0
+
+ if c == '\n' || c == eof_char
+ # Treat newline as a special word
+
+ mem[wordAddr + offset] = Int64(c)
+ pushPS(wordAddr)
+ pushPS(1)
+ return NEXT
+ end
+
+ while true
+ mem[wordAddr + offset] = Int64(c)
+ offset += 1
+
+ callPrim(mem[KEY])
+ c = Char(popPS())
+
+ if c == ' ' || c == '\t' || c == '\n' || c == eof_char
+ # Rewind KEY
+ mem[TOIN] -= 1
+ break
+ end
+ end
+
+ wordLen = offset
+
+ pushPS(wordAddr)
+ pushPS(wordLen)
+
+ return NEXT
+end)
+
+BASE, BASE_CFA = defNewVar("BASE", 10)
+NUMBER = defPrimWord("NUMBER", () -> begin
+
+ wordLen = popPS()
+ wordAddr = popPS()
+
+ s = getString(wordAddr, wordLen)
+
+ try
+ pushPS(parse(Int64, s, mem[BASE]))
+ pushPS(0)
+ catch
+ pushPS(1) # Error indication
+ end
+
+ return NEXT
+end)
+
+# Dictionary searches
+
+FIND = defPrimWord("FIND", () -> begin
+
+ wordLen = popPS()
+ wordAddr = popPS()
+ 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)
+
+# Compilation
+
+STATE, STATE_CFA = defNewVar("STATE", 0)
+
+HEADER = defPrimWord("HEADER", () -> begin
+
+ wordLen = popPS()
+ wordAddr = popPS()
+ word = getString(wordAddr, wordLen)
+
+ createHeader(word, 0)
+
+ return NEXT
+end)
+
+COMMA = defPrimWord(",", () -> begin
+ mem[mem[HERE]] = popPS()
+ mem[HERE] += 1
+
+ return NEXT
+end)
+
+LBRAC = defPrimWord("[", () -> begin
+ mem[STATE] = 0
+ return NEXT
+end, flags=F_IMMED)
+
+RBRAC = defPrimWord("]", () -> begin
+ mem[STATE] = 1
+ return NEXT
+end, flags=F_IMMED)
+
+HIDDEN = defPrimWord("HIDDEN", () -> begin
+ addr = popPS() + 1
+ mem[addr] = mem[addr] $ F_HIDDEN
+ return NEXT
+end)
+
+HIDE = defWord("HIDE",
+ [WORD,
+ FIND,
+ HIDDEN,
+ EXIT])
+
+COLON = defWord(":",
+ [WORD,
+ HEADER,
+ LIT, DOCOL, COMMA,
+ LATEST_CFA, FETCH, HIDDEN,
+ RBRAC,
+ EXIT])
+
+SEMICOLON = defWord(";",
+ [LIT, EXIT, COMMA,
+ LATEST_CFA, FETCH, HIDDEN,
+ LBRAC,
+ EXIT], flags=F_IMMED)
+
+IMMEDIATE = defPrimWord("IMMEDIATE", () -> begin
+ lenAndFlagsAddr = mem[LATEST] + 1
+ mem[lenAndFlagsAddr] = mem[lenAndFlagsAddr] $ F_IMMED
+ return NEXT
+end, flags=F_IMMED)
+
+TICK = defWord("'",
+ [WORD, FIND, TOCFA, EXIT])
+
+BTICK = defWord("[']",
+ [FROMR, DUP, INCR, TOR, FETCH, EXIT])
+
+# CREATE and DOES>
+
+CREATE = defWord("CREATE",
+ [WORD,
+ HEADER,
+ LIT, DOVAR, COMMA, EXIT]);
+
+DODOES = defPrim(() -> begin
+ pushRS(reg.IP)
+ reg.IP = reg.W + 1
+ return NEXT
+end, name="DOCOL")
+
+defConst("DODOES", DODOES)
+
+FROMDOES_PAREN = defWord("(DOES>)",
+ [DODOES, LATEST, FETCH, TOCFA, STORE, EXIT])
+
+FROMDOES = defWord("DOES>",
+ [BTICK, FROMDOES_PAREN, COMMA, BTICK, EXIT, COMMA,
+ BTICK, LIT, COMMA, LATEST, FETCH, TODFA, COMMA], flags=F_IMMED)
+
+
+# Strings
+
+LITSTRING = defPrimWord("LITSTRING", () -> begin
+ len = mem[reg.IP]
+ reg.IP += 1
+ pushPS(reg.IP)
+ pushPS(len)
+ reg.IP += len
+
+ return NEXT
+end)
+
+TELL = defPrimWord("TELL", () -> begin
+ len = popPS()
+ addr = popPS()
+ str = getString(addr, len)
+ print(str)
+ return NEXT
+end)
+
+# Outer interpreter
+
+EXECUTE = defPrimWord("EXECUTE", () -> begin
+ reg.W = popPS()
+ return mem[reg.W]
+end)
+
+type ParseError <: Exception
+ wordName::ASCIIString
+end
+Base.showerror(io::IO, ex::ParseError) = print(io, "Parse error at word: '$(ex.wordName)'.")
+
+DEBUG, DEBUG_CFA = defNewVar("DEBUG", 0)
+
+INTERPRET = defPrimWord("INTERPRET", () -> begin
+
+ callPrim(mem[WORD])
+
+ wordName = getString(mem[reg.PSP-1], mem[reg.PSP])
+ if mem[DEBUG] != 0
+ println("... ", replace(replace(wordName, "\004", "EOF"), "\n", "\\n"), " ...")
+ end
+
+ callPrim(mem[TWODUP])
+ callPrim(mem[FIND])
+
+ wordAddr = mem[reg.PSP]
+
+ if wordAddr>0
+ # Word in dictionary
+
+ isImmediate = (mem[wordAddr+1] & F_IMMED) != 0
+ callPrim(mem[TOCFA])
+
+ callPrim(mem[NROT]) # get rid of extra copy of word string details
+ popPS()
+ popPS()
+
+ if mem[STATE] == 0 || isImmediate
+ # Execute!
+ return callPrim(mem[EXECUTE])
+ else
+ # Append CFA to dictionary
+ callPrim(mem[COMMA])
+ end
+ else
+ # Not in dictionary, assume number
+
+ popPS()
+
+ callPrim(mem[NUMBER])
+
+ if popPS() != 0
+ throw(ParseError(wordName))
+ end
+
+ if mem[STATE] == 0
+ # Number already on stack!
+ else
+ # Append literal to dictionary
+ pushPS(LIT)
+ callPrim(mem[COMMA])
+ callPrim(mem[COMMA])
+ end
+ end
+
+ return NEXT
+end)
+
+QUIT = defWord("QUIT",
+ [RSP0_CFA, RSPSTORE,
+ INTERPRET,
+ BRANCH,-2])
+
+BYE = defPrimWord("BYE", () -> begin
+ return 0
+end)
+
+PROMPT = defPrimWord("PROMPT", () -> begin
+ println(" ok")
+end)
+
+NL = defPrimWord("\n", () -> begin
+ if mem[STATE] == 0 && currentSource() == STDIN
+ callPrim(mem[PROMPT])
+ end
+ return NEXT
+end, flags=F_IMMED)
+
+INCLUDE = defPrimWord("INCLUDE", () -> begin
+ callPrim(mem[WORD])
+ wordLen = popPS()
+ wordAddr = popPS()
+ word = getString(wordAddr, wordLen)
+
+ push!(sources, open(word, "r"))
+
+ # Clear input buffer
+ mem[NUMTIB] = 0
+
+ return NEXT
+end)
+
+EOF_WORD = defPrimWord("\x04", () -> begin
+ if currentSource() != STDIN
+ close(currentSource())
+ end
+
+ pop!(sources)
+
+ if length(sources)>0
+ if currentSource() == STDIN
+ callPrim(mem[PROMPT])
+ end
+
+ return NEXT
+ else
+ return 0
+ end
+end, flags=F_IMMED)
+
+# Odds and Ends
+
+CHAR = defPrimWord("CHAR", () -> begin
+ callPrim(mem[WORD])
+ wordLen = popPS()
+ wordAddr = popPS()
+ word = getString(wordAddr, wordLen)
+ pushPS(Int64(word[1]))
+
+ return NEXT
+end)
+
+initialized = false
+initFileName = nothing
+if isfile("lib.4th")
+ initFileName = "lib.4th"
+elseif isfile(Pkg.dir("forth/src/lib.4th"))
+ initFileName = Pkg.dir("forth/src/lib.4th")