3 import REPL.REPLCompletions, Base.invokelatest, Pkg
6 size_mem = 1000000 # 1 mega-int
9 size_RS = 1000 # Return stack size
10 size_PS = 1000 # Parameter stack size
11 size_TIB = 1000 # Terminal input buffer size
12 size_FIB = 1000 # File input buffer size
15 mem = Array{Int64,1}(undef,size_mem)
16 primitives = Array{Function,1}(undef, 0)
17 primNames = Array{AbstractString,1}(undef, 0)
19 # Memory geography and built-in variables
22 H = nextVarAddr; nextVarAddr += 1 # Next free memory address
23 FORTH_LATEST = nextVarAddr; nextVarAddr += 1 # FORTH dict latest
24 CURRENT = nextVarAddr; nextVarAddr += 1 # Current compilation dict
26 RSP0 = nextVarAddr # bottom of RS
27 PSP0 = RSP0 + size_RS # bottom of PS
28 TIB = PSP0 + size_PS # address of terminal input buffer
29 FIB = TIB + size_TIB # address of terminal input buffer
30 mem[H] = FIB + size_FIB # location of bottom of dictionary
31 mem[FORTH_LATEST] = 0 # zero FORTH dict latest (no previous def)
32 mem[CURRENT] = FORTH_LATEST-1 # Compile words to system dict initially
34 DICT = mem[H] # Save bottom of dictionary as constant
38 RSP::Int64 # Return stack pointer
39 PSP::Int64 # Parameter/data stack pointer
40 IP::Int64 # Instruction pointer
41 W::Int64 # Working register
43 reg = Reg(RSP0, PSP0, 0, 0)
45 # Stack manipulation functions
47 function ensurePSDepth(depth::Int64)
48 if reg.PSP - PSP0 < depth
49 error("Parameter stack underflow.")
53 function ensurePSCapacity(toAdd::Int64)
54 if reg.PSP + toAdd >= PSP0 + size_PS
55 error("Parameter stack overflow.")
59 function ensureRSDepth(depth::Int64)
60 if reg.RSP - RSP0 < depth
61 error("Return stack underflow.")
65 function ensureRSCapacity(toAdd::Int64)
66 if reg.RSP + toAdd >= RSP0 + size_RS
67 error("Return stack overflow.")
71 function pushRS(val::Int64)
84 function pushPS(val::Int64)
87 mem[reg.PSP += 1] = val
98 # Handy functions for adding/retrieving strings to/from memory.
100 getString(addr::Int64, len::Int64) = String([Char(c) for c in mem[addr:(addr+len-1)]])
102 function putString(str::AbstractString, addr::Int64)
103 mem[addr:(addr+length(str)-1)] = [Int64(c) for c in str]
106 function putString(str::AbstractString, addr::Int64, maxLen::Int64)
107 len = min(length(str), maxLen)
108 mem[addr:(addr+len-1)] = [Int64(c) for c in str]
111 stringAsInts(str::AbstractString) = [Int(c) for c in collect(str)]
113 # Primitive creation and calling functions
115 function defPrim(f::Function; name="nameless")
117 push!(primNames, replace(name, "\004" => "EOF"))
119 return -length(primitives)
122 function callPrim(addr::Int64)
123 if addr >=0 || -addr>length(primitives)
124 error("Attempted to execute non-existent primitive at address $addr.")
126 invokelatest(primitives[-addr])
129 getPrimName(addr::Int64) = primNames[-addr]
131 # Word creation functions
138 function dictWrite(ints::Array{Int64,1})
139 mem[mem[H]:(mem[H]+length(ints)-1)] = ints
140 mem[H] += length(ints)
142 dictWrite(int::Int64) = dictWrite([int])
143 dictWriteString(string::AbstractString) = dictWrite([Int64(c) for c in string])
145 function createHeader(name::AbstractString, flags::Int64)
146 mem[mem[H]] = mem[mem[CURRENT]+1]
147 mem[mem[CURRENT]+1] = mem[H]
150 dictWrite(length(name) | flags | NFA_MARK)
151 dictWriteString(name)
154 function defPrimWord(name::AbstractString, f::Function; flags::Int64=0)
155 createHeader(name, flags)
157 codeWordAddr = mem[H]
158 dictWrite(defPrim(f, name=name))
163 function defWord(name::AbstractString, wordAddrs::Array{Int64,1}; flags::Int64=0)
164 createHeader(name, flags)
174 # Variable creation functions
176 function defExistingVar(name::AbstractString, varAddr::Int64; flags::Int64=0)
178 defPrimWord(name, eval(:(() -> begin
184 function defNewVar(name::AbstractString, initial::Array{Int64,1}; flags::Int64=0)
185 createHeader(name, flags)
187 codeWordAddr = mem[H]
193 return varAddr, codeWordAddr
196 defNewVar(name::AbstractString, initial::Int64; flags::Int64=0) =
197 defNewVar(name, [initial]; flags=flags)
199 function defConst(name::AbstractString, val::Int64; flags::Int64=0)
200 createHeader(name, flags)
202 codeWordAddr = mem[H]
210 # Threading Primitives (inner interpreter)
212 NEXT = defPrim(() -> begin
218 DOCOL = defPrim(() -> begin
224 DOVAR = defPrim(() -> begin
229 DOCON = defPrim(() -> begin
230 pushPS(mem[reg.W + 1])
234 EXIT_CFA = defPrimWord("EXIT", () -> begin
239 # Dictionary entries for core built-in variables, constants
241 H_CFA = defExistingVar("H", H)
243 PSP0_CFA = defConst("PSP0", PSP0)
244 RSP0_CFA = defConst("RSP0", RSP0)
246 defConst("DOCOL", DOCOL)
247 defConst("DOCON", DOCON)
248 defConst("DOVAR", DOVAR)
250 defConst("DICT", DICT)
251 defConst("MEMSIZE", size_mem)
253 F_IMMED_CFA = defConst("F_IMMED", F_IMMED)
254 F_HIDDEN_CFA = defConst("F_HIDDEN", F_HIDDEN)
255 F_LENMASK_CFA = defConst("F_LENMASK", F_LENMASK)
256 NFA_MARK_CFA = defConst("NFA_MARK", NFA_MARK)
258 # Basic forth primitives
260 DROP_CFA = defPrimWord("DROP", () -> begin
265 SWAP_CFA = defPrimWord("SWAP", () -> begin
273 DUP_CFA = defPrimWord("DUP", () -> begin
279 OVER_CFA = defPrimWord("OVER", () -> begin
281 pushPS(mem[reg.PSP-1])
285 ROT_CFA = defPrimWord("ROT", () -> begin
295 NROT_CFA = defPrimWord("-ROT", () -> begin
306 TWODROP_CFA = defPrimWord("2DROP", () -> begin
312 TWODUP_CFA = defPrimWord("2DUP", () -> begin
321 TWOSWAP_CFA = defPrimWord("2SWAP", () -> begin
333 TWOOVER_CFA = defPrimWord("2OVER", () -> begin
342 QDUP_CFA = defPrimWord("?DUP", () -> begin
351 INCR_CFA = defPrimWord("1+", () -> begin
357 DECR_CFA = defPrimWord("1-", () -> begin
363 INCR2_CFA = defPrimWord("2+", () -> begin
369 DECR2_CFA = defPrimWord("2-", () -> begin
375 ADD_CFA = defPrimWord("+", () -> begin
382 SUB_CFA = defPrimWord("-", () -> begin
389 MUL_CFA = defPrimWord("*", () -> begin
396 DIVMOD_CFA = defPrimWord("/MOD", () -> begin
405 TWOMUL_CFA = defPrimWord("2*", () -> begin
410 TWODIV_CFA = defPrimWord("2/", () -> begin
415 EQ_CFA = defPrimWord("=", () -> begin
418 pushPS(a==b ? -1 : 0)
422 NE_CFA = defPrimWord("<>", () -> begin
425 pushPS(a!=b ? -1 : 0)
429 LT_CFA = defPrimWord("<", () -> begin
436 GT_CFA = defPrimWord(">", () -> begin
443 LE_CFA = defPrimWord("<=", () -> begin
446 pushPS(a<=b ? -1 : 0)
450 GE_CFA = defPrimWord(">=", () -> begin
453 pushPS(a>=b ? -1 : 0)
457 ZE_CFA = defPrimWord("0=", () -> begin
458 pushPS(popPS() == 0 ? -1 : 0)
462 ZNE_CFA = defPrimWord("0<>", () -> begin
463 pushPS(popPS() != 0 ? -1 : 0)
467 ZLT_CFA = defPrimWord("0<", () -> begin
468 pushPS(popPS() < 0 ? -1 : 0)
472 ZGT_CFA = defPrimWord("0>", () -> begin
473 pushPS(popPS() > 0 ? -1 : 0)
477 ZLE_CFA = defPrimWord("0<=", () -> begin
478 pushPS(popPS() <= 0 ? -1 : 0)
482 ZGE_CFA = defPrimWord("0>=", () -> begin
483 pushPS(popPS() >= 0 ? -1 : 0)
487 AND_CFA = defPrimWord("AND", () -> begin
494 OR_CFA = defPrimWord("OR", () -> begin
501 XOR_CFA = defPrimWord("XOR", () -> begin
508 INVERT_CFA = defPrimWord("INVERT", () -> begin
515 LIT_CFA = defPrimWord("LIT", () -> begin
523 STORE_CFA = defPrimWord("!", () -> begin
530 FETCH_CFA = defPrimWord("@", () -> begin
536 ADDSTORE_CFA = defPrimWord("+!", () -> begin
543 SUBSTORE_CFA = defPrimWord("-!", () -> begin
553 TOR_CFA = defPrimWord(">R", () -> begin
558 FROMR_CFA = defPrimWord("R>", () -> begin
563 RFETCH_CFA = defPrimWord("R@", () -> begin
568 RSPFETCH_CFA = defPrimWord("RSP@", () -> begin
573 RSPSTORE_CFA = defPrimWord("RSP!", () -> begin
578 RDROP_CFA = defPrimWord("RDROP", () -> begin
585 PSPFETCH_CFA = defPrimWord("PSP@", () -> begin
590 PSPSTORE_CFA = defPrimWord("PSP!", () -> begin
597 WFETCH_CFA = defPrimWord("W@", () -> begin
602 WSTORE_CFA = defPrimWord("W!", () -> begin
609 openFiles = Dict{Int64,IOStream}()
616 FAM_RO_CFA = defConst("R/O", FAM_RO)
617 FAM_WO_CFA = defConst("W/O", FAM_WO)
619 function fileOpener(create::Bool)
624 fname = getString(fnameAddr, fnameLen)
626 if create && !isfile(fname)
639 openFiles[nextFileID] = open(fname, mode)
646 OPEN_FILE_CFA = defPrimWord("OPEN-FILE", () -> begin
651 CREATE_FILE_CFA = defPrimWord("CREATE-FILE", () -> begin
656 CLOSE_FILE_CFA = defPrimWord("CLOSE-FILE", () -> begin
658 close(openFiles[fid])
659 delete!(openFiles, fid)
661 pushPS(0) # Result code 0
665 CLOSE_FILES_CFA = defPrimWord("CLOSE-FILES", () -> begin
666 for fh in values(openFiles)
671 pushPS(0) # Result code 0
675 READ_LINE_CFA = defPrimWord("READ-LINE", () -> begin
680 if !(fid in keys(openFiles))
681 error(string("Invalid FID ", fid, "."))
685 line = readline(fh, keep=true)
687 eofFlag = endswith(line, '\n') ? 0 : -1
690 putString(line, addr, maxSize)
699 READ_FILE_CFA = defPrimWord("READ-FILE", () -> begin
706 string = join(map(x -> Char(x), read(fh, size)), "")
708 eofFlag = length(string) == size ? 0 : -1 ;
710 putString(string, addr, length(string))
712 pushPS(length(string))
719 EMIT_CFA = defPrimWord("EMIT", () -> begin
724 function raw_mode!(mode::Bool)
725 if ccall(:jl_tty_set_mode, Int32, (Ptr{Nothing}, Int32), stdin.handle, mode) != 0
726 throw("FATAL: Terminal unable to enter raw mode.")
732 byte = read(stdin, 1)[1]
744 KEY_CFA = defPrimWord("KEY", () -> begin
745 pushPS(Int(getKey()))
749 function getLineFromSTDIN()
753 slashIdx = findlast(isequal('\\'), chars)
755 if slashIdx != nothing
756 return join(chars[slashIdx:length(chars)])
762 function backspaceStr(s, bsCount)
764 newLen = max(0, oldLen - bsCount)
765 return join(collect(s)[1:newLen])
778 return string("\x04")
784 line = backspaceStr(line, 1)
788 # Strip ANSI escape sequence
789 nextKey = Char(getKey())
792 nextKey = Char(getKey())
793 if nextKey >= '@' || nextKey <= '~'
800 # Currently do nothing
804 if haskey(REPLCompletions.latex_symbols, frag)
805 print(repeat("\b", length(frag)))
807 comp = REPLCompletions.latex_symbols[frag]
808 line = string(backspaceStr(line, length(frag)), comp)
815 line = string(line, key)
820 SPAN, SPAN_CFA = defNewVar("SPAN", 0)
821 EXPECT_CFA = defPrimWord("EXPECT", () -> begin
825 line = getLineFromSTDIN()
827 mem[SPAN] = min(length(line), maxLen)
828 putString(line, addr, maxLen)
833 BASE, BASE_CFA = defNewVar("BASE", 10)
834 NUMBER_CFA = defPrimWord("NUMBER", () -> begin
836 wordLen = mem[wordAddr-1]
838 s = getString(wordAddr, wordLen)
840 pushPS(parse(Int64, s, base=mem[BASE]))
845 # Dictionary searches
847 FROMLINK_CFA = defPrimWord("LINK>", () -> begin
850 lenAndFlags = mem[addr+1]
851 len = lenAndFlags & F_LENMASK
853 pushPS(addr + 2 + len)
858 NUMCONTEXT, NUMCONTEXT_CFA = defNewVar("#CONTEXT", 1)
860 createHeader("FORTH", 0)
862 dictWrite(defPrim(() -> begin
863 mem[CONTEXT + mem[NUMCONTEXT] - 1] = reg.W
866 dictWrite(0) # cell for latest
868 CURRENT_CFA = defExistingVar("CURRENT", CURRENT)
870 # Switch to new FORTH vocabulary cfa
871 mem[FORTH_CFA+1] = mem[mem[CURRENT]+1]
872 mem[CURRENT] = FORTH_CFA
874 CONTEXT, CONTEXT_CFA = defNewVar("CONTEXT", zeros(Int64, 10))
875 mem[CONTEXT] = FORTH_CFA
877 FINDVOCAB_CFA = defPrimWord("FINDVOCAB", () -> begin
879 countedAddr = popPS()
881 wordAddr = countedAddr + 1
882 wordLen = mem[countedAddr]
883 word = lowercase(getString(wordAddr, wordLen))
888 while (lfa = mem[lfa]) > 0
890 lenAndFlags = mem[lfa+1]
891 len = lenAndFlags & F_LENMASK
892 hidden = (lenAndFlags & F_HIDDEN) == F_HIDDEN
894 if hidden || len != wordLen
898 thisWord = lowercase(getString(lfa+2, len))
907 callPrim(mem[FROMLINK_CFA])
908 if (lenAndFlags & F_IMMED) == F_IMMED
921 FIND_CFA = defPrimWord("FIND", () -> begin
923 countedAddr = popPS()
924 context = mem[CONTEXT:(CONTEXT+mem[NUMCONTEXT]-1)]
926 for vocabCFA in reverse(context)
929 callPrim(mem[FINDVOCAB_CFA])
931 callPrim(mem[DUP_CFA])
949 BRANCH_CFA = defPrimWord("BRANCH", () -> begin
950 reg.IP += mem[reg.IP]
954 ZBRANCH_CFA = defPrimWord("0BRANCH", () -> begin
956 reg.IP += mem[reg.IP]
966 LITSTRING_CFA = defPrimWord("LITSTRING", () -> begin
976 TYPE_CFA = defPrimWord("TYPE", () -> begin
979 str = getString(addr, len)
984 # Interpreter/Compiler-specific I/O
986 TIB_CFA = defConst("TIB", TIB)
987 NUMTIB, NUMTIB_CFA = defNewVar("#TIB", 0)
989 FIB_CFA = defConst("FIB", FIB)
990 NUMFIB, NUMFIB_CFA = defNewVar("#FIB", 0)
992 IB_CFA = defPrimWord("IB", () -> begin
993 pushPS(mem[SOURCE_ID_VAR] == 0 ? TIB : FIB)
997 NUMIB_CFA = defPrimWord("#IB", () -> begin
998 pushPS(mem[SOURCE_ID_VAR] == 0 ? NUMTIB : NUMFIB)
1002 TOIN, TOIN_CFA = defNewVar(">IN", 0)
1004 SOURCE_ID_VAR, SOURCE_ID_VAR_CFA = defNewVar("SOURCE-ID-VAR", 0)
1006 QUERY_CFA = defWord("QUERY",
1007 [TIB_CFA, LIT_CFA, 160, EXPECT_CFA,
1008 SPAN_CFA, FETCH_CFA, NUMTIB_CFA, STORE_CFA,
1009 LIT_CFA, 0, TOIN_CFA, STORE_CFA,
1012 EOF_FLAG, EOF_FLAG_CFA = defNewVar("EOF-FLAG", 0)
1015 # EOF-FLAG set to true if EOF is reached
1016 QUERY_FILE_CFA = defWord("QUERY-FILE",
1017 [FIB_CFA, LIT_CFA, 160, ROT_CFA, READ_LINE_CFA,
1018 DROP_CFA, EOF_FLAG_CFA, STORE_CFA,
1019 NUMFIB_CFA, STORE_CFA,
1020 LIT_CFA, 0, TOIN_CFA, STORE_CFA,
1023 WORD_CFA = defPrimWord("WORD", () -> begin
1026 if mem[SOURCE_ID_VAR] == 0
1034 # Chew up initial occurrences of delim
1035 while (mem[TOIN]<mem[sizeAddr] && mem[bufferAddr+mem[TOIN]] == delim)
1042 # Start reading in word
1044 while (mem[TOIN]<mem[sizeAddr])
1045 mem[addr] = mem[bufferAddr+mem[TOIN]]
1048 if (mem[addr] == delim)
1057 mem[countAddr] = count
1060 #println("Processing word: '$(getString(countAddr+1,mem[countAddr]))' (state $(mem[STATE]))")
1067 STATE, STATE_CFA = defNewVar("STATE", 0)
1069 COMMA_CFA = defPrimWord(",", () -> begin
1070 mem[mem[H]] = popPS()
1076 HERE_CFA = defWord("HERE",
1077 [H_CFA, FETCH_CFA, EXIT_CFA])
1079 HEADER_CFA = defPrimWord("HEADER", () -> begin
1080 wordAddr = popPS()+1
1081 wordLen = mem[wordAddr-1]
1082 word = getString(wordAddr, wordLen)
1084 createHeader(word, 0)
1089 CREATE_CFA = defWord("CREATE",
1090 [LIT_CFA, 32, WORD_CFA, HEADER_CFA,
1091 LIT_CFA, DOVAR, COMMA_CFA,
1094 DODOES = defPrim(() -> begin
1101 DOES_HELPER_CFA = defPrimWord("(DOES>)", () -> begin
1103 pushPS(mem[mem[CURRENT]+1])
1104 callPrim(mem[FROMLINK_CFA])
1107 runtimeAddr = popPS()
1109 mem[cfa] = defPrim(eval(:(() -> begin
1110 pushPS($(runtimeAddr))
1112 end)), name="doesPrim")
1115 end, flags=F_IMMED | F_HIDDEN)
1117 DOES_CFA = defWord("DOES>",
1118 [LIT_CFA, LIT_CFA, COMMA_CFA, HERE_CFA, LIT_CFA, 3, ADD_CFA, COMMA_CFA,
1119 LIT_CFA, DOES_HELPER_CFA, COMMA_CFA, LIT_CFA, EXIT_CFA, COMMA_CFA, EXIT_CFA],
1122 LBRAC_CFA = defPrimWord("[", () -> begin
1127 RBRAC_CFA = defPrimWord("]", () -> begin
1132 HIDDEN_CFA = defPrimWord("HIDDEN", () -> begin
1133 lenAndFlagsAddr = mem[mem[CURRENT]+1] + 1
1134 mem[lenAndFlagsAddr] = xor(mem[lenAndFlagsAddr], F_HIDDEN)
1138 COLON_CFA = defWord(":",
1139 [LIT_CFA, 32, WORD_CFA,
1141 LIT_CFA, DOCOL, COMMA_CFA,
1146 SEMICOLON_CFA = defWord(";",
1147 [LIT_CFA, EXIT_CFA, COMMA_CFA,
1150 EXIT_CFA], flags=F_IMMED)
1152 IMMEDIATE_CFA = defPrimWord("IMMEDIATE", () -> begin
1153 lenAndFlagsAddr = mem[mem[CURRENT]+1] + 1
1154 mem[lenAndFlagsAddr] = xor(mem[lenAndFlagsAddr], F_IMMED)
1158 # ( addr n -- primAddr )
1159 CREATE_PRIM_CFA = defPrimWord("CREATE-PRIM", () -> begin
1163 exprString = string("() -> begin\n",
1164 getString(addr, len), "\n",
1167 func = eval(parse(exprString))
1169 pushPS(defPrim(func))
1175 EXECUTE_CFA = defPrimWord("EXECUTE", () -> begin
1180 INTERPRET_CFA = defWord("INTERPRET",
1181 [LIT_CFA, 32, WORD_CFA, # Read next space-delimited word
1183 DUP_CFA, FETCH_CFA, ZE_CFA, ZBRANCH_CFA, 3,
1184 DROP_CFA, EXIT_CFA, # Exit if input buffer is exhausted
1186 STATE_CFA, FETCH_CFA, ZBRANCH_CFA, 24,
1188 FIND_CFA, QDUP_CFA, ZBRANCH_CFA, 13,
1191 LIT_CFA, -1, EQ_CFA, INVERT_CFA, ZBRANCH_CFA, 4,
1193 # Immediate: Execute!
1194 EXECUTE_CFA, BRANCH_CFA, -26,
1196 # Not immediate: Compile!
1197 COMMA_CFA, BRANCH_CFA, -29,
1199 # No word found, parse number
1200 NUMBER_CFA, LIT_CFA, LIT_CFA, COMMA_CFA, COMMA_CFA, BRANCH_CFA, -36,
1203 FIND_CFA, QDUP_CFA, ZBRANCH_CFA, 5,
1205 # Found word. Execute!
1206 DROP_CFA, EXECUTE_CFA, BRANCH_CFA, -44,
1208 # No word found, parse number and leave on stack
1209 NUMBER_CFA, BRANCH_CFA, -47,
1212 PROMPT_CFA = defPrimWord("PROMPT", () -> begin
1221 QUIT_CFA = defWord("QUIT",
1222 [LIT_CFA, 0, STATE_CFA, STORE_CFA, # Set mode to interpret
1223 LIT_CFA, 0, SOURCE_ID_VAR_CFA, STORE_CFA, # Set terminal as input stream
1224 LIT_CFA, 0, NUMTIB_CFA, STORE_CFA, # Clear the input buffer
1225 RSP0_CFA, RSPSTORE_CFA, # Clear the return stack
1226 QUERY_CFA, # Read line of input
1227 INTERPRET_CFA, PROMPT_CFA, # Interpret line
1228 BRANCH_CFA,-4]) # Loop
1230 INCLUDED_CFA = defWord("INCLUDED",
1231 [LIT_CFA, 0, STATE_CFA, STORE_CFA, # Set mode to interpret
1232 FAM_RO_CFA, OPEN_FILE_CFA, DROP_CFA, # Open the file
1233 SOURCE_ID_VAR_CFA, FETCH_CFA, SWAP_CFA, # Store current source on stack
1234 SOURCE_ID_VAR_CFA, STORE_CFA, # Mark this as the current source
1235 SOURCE_ID_VAR_CFA, FETCH_CFA, QUERY_FILE_CFA, # Read line from file
1236 EOF_FLAG_CFA, FETCH_CFA,
1237 NUMFIB_CFA, FETCH_CFA, ZE_CFA, AND_CFA, # Test for EOF and empty line
1238 INVERT_CFA, ZBRANCH_CFA, 4, # Break out if EOF
1239 INTERPRET_CFA, # Interpret line
1240 BRANCH_CFA, -14, # Loop
1241 SOURCE_ID_VAR_CFA, FETCH_CFA,
1242 CLOSE_FILE_CFA, DROP_CFA, # Close file
1243 SOURCE_ID_VAR_CFA, STORE_CFA, # Restore input source
1244 LIT_CFA, 0, NUMIB_CFA, STORE_CFA, # Zero #IB
1245 LIT_CFA, 0, TOIN_CFA, STORE_CFA, # Zero >IN
1248 INCLUDE_CFA = defWord("INCLUDE", [LIT_CFA, 32, WORD_CFA,
1250 SWAP_CFA, FETCH_CFA,
1251 INCLUDED_CFA, EXIT_CFA]);
1253 ABORT_CFA = defWord("ABORT",
1254 [CLOSE_FILES_CFA, DROP_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
1256 BYE_CFA = defPrimWord("BYE", () -> begin
1257 if mem[SOURCE_ID_VAR] == 0
1263 EOF_CFA = defPrimWord("\x04", () -> begin
1267 ### Library loading ###
1270 SETLIBCWD_CFA = defPrimWord("SETLIBCWD", () -> begin
1271 global oldCWD = pwd()
1272 if !isfile("lib.4th") # Exception for debugging.
1273 cd(Pkg.dir("forth","src"))
1278 RESTORECWD_CFA = defPrimWord("RESTORECWD", () -> begin
1283 INCLUDED_LIB_CFA = defWord("INCLUDED-LIB",
1284 [SETLIBCWD_CFA, INCLUDED_CFA, RESTORECWD_CFA, EXIT_CFA])
1286 INCLUDE_LIB_CFA = defWord("INCLUDE-LIB", [LIT_CFA, 32, WORD_CFA,
1288 SWAP_CFA, FETCH_CFA,
1289 INCLUDED_LIB_CFA, EXIT_CFA]);
1291 SKIP_WELCOME, SKIP_WELCOME_CFA = defNewVar("SKIP-WELCOME", 0)
1296 libFileName = "lib.4th"
1298 function run(fileName=nothing; initialize=true)
1300 # Start with IP pointing to first instruction of outer interpreter
1303 # Include optional file
1304 if fileName != nothing
1305 putString(fileName, mem[H])
1307 mem[H] += length(fileName)
1308 pushPS(length(fileName))
1309 pushRS(INCLUDED_CFA+1)
1311 mem[SKIP_WELCOME] = -1
1314 # Load library files
1315 global initialized, libFileName
1316 if !initialized && initialize
1317 if libFileName != nothing
1318 #print("Including definitions from $libFileName...")
1320 putString(libFileName, mem[H])
1322 pushPS(length(libFileName))
1323 pushRS(INCLUDED_LIB_CFA+1)
1327 println("No library file found. Only primitive words available.")
1332 # Primitive processing loop.
1333 # Everyting else is simply a consequence of this loop!
1337 #print("Entering prim $(getPrimName(jmp)), PS: ")
1343 println(string("Error in primitive '", getPrimName(jmp), "' at address ", jmp))
1344 showerror(stdout, ex)
1348 reg.IP = ABORT_CFA + 1
1356 TRACE_CFA = defPrimWord("TRACE", () -> begin
1357 println("reg.W: $(reg.W) reg.IP: $(reg.IP)")
1358 print("PS: "); printPS()
1359 print("RS: "); printRS()
1366 function dump(startAddr::Int64; count::Int64 = 100, cellsPerLine::Int64 = 10)
1367 chars = Array{Char,1}(undef, cellsPerLine)
1369 lineStartAddr = cellsPerLine*div((startAddr-1),cellsPerLine) + 1
1370 endAddr = startAddr + count - 1
1372 q, r = divrem((endAddr-lineStartAddr+1), cellsPerLine)
1373 numLines = q + (r > 0 ? 1 : 0)
1379 for c in 1:cellsPerLine
1380 if i >= startAddr && i <= endAddr
1382 if mem[i]>=32 && mem[i]<128
1383 chars[c] = Char(mem[i])
1395 println("\t", String(chars))
1400 count = reg.PSP - PSP0
1404 for i in (PSP0+1):reg.PSP
1409 println("Parameter stack empty")
1414 count = reg.RSP - RSP0
1418 for i in (RSP0+1):reg.RSP
1423 println("Return stack empty")
1427 DUMP = defPrimWord("DUMP", () -> begin
1432 dump(addr, count=count)