size_RS = 1000 # Return stack size
size_PS = 1000 # Parameter stack size
size_TIB = 1000 # Terminal input buffer size
+size_FIB = 1000 # File input buffer size
# Memory arrays
mem = Array{Int64,1}(size_mem)
RSP0 = nextVarAddr # bottom of RS
PSP0 = RSP0 + size_RS # bottom of PS
TIB = PSP0 + size_PS # address of terminal input buffer
-mem[H] = TIB + size_TIB # location of bottom of dictionary
+FIB = TIB + size_TIB # address of terminal input buffer
+mem[H] = FIB + size_FIB # location of bottom of dictionary
mem[FORTH_LATEST] = 0 # zero FORTH dict latest (no previous def)
mem[CURRENT] = FORTH_LATEST-1 # Compile words to system dict initially
openFiles = Dict{Int64,IOStream}()
nextFileID = 1
-SOURCE_ID, SOURCE_ID_CFA = defNewVar("SOURCE-ID", 0)
+
+
+## File access modes
+FAM_RO = 0
+FAM_WO = 1
+FAM_RO_CFA = defConst("R/O", FAM_RO)
+FAM_WO_CFA = defConst("W/O", FAM_WO)
+
+function fileOpener(create::Bool)
+ fnameLen = popPS()
+ fnameAddr = popPS()
+ fam = popPS()
+
+ fname = getString(fnameAddr, fnameLen)
+
+ if create && !isfile(fname)
+ pushPS(0)
+ pushPS(-1) # error
+ return NEXT
+ end
+
+ if (fam == FAM_RO)
+ mode = "r"
+ else
+ mode = "w"
+ end
+
+ openFiles[nextFileID] = open(fname, mode)
+ pushPS(nextFileID)
+ pushPS(0)
+
+ nextFileID += 1
+end
+
+OPEN_FILE_CFA = defPrimWord("OPEN-FILE", () -> begin
+ fileOpener(false)
+ return NEXT
+end);
+
+CREATE_FILE_CFA = defPrimWord("CREATE-FILE", () -> begin
+ fileOpener(true)
+ return NEXT
+end);
+
+CLOSE_FILE_CFA = defPrimWord("CLOSE-FILE", () -> begin
+ fid = popPS()
+ close(openFiles[fid])
+ delete!(openFiles, fid)
+ return NEXT
+end)
+
+CLOSE_FILES_CFA = defPrimWord("CLOSE-FILES", () -> begin
+ for fh in values(openFiles)
+ close(fh)
+ end
+ empty!(openFiles)
+
+ return NEXT
+end)
+
+READ_LINE_CFA = defPrimWord("READ-LINE", () -> begin
+ fid = popPS()
+ maxSize = popPS()
+ addr = popPS()
+
+ fh = openFiles[fid]
+ line = readline(fh)
+
+ eofFlag = endswith(line, '\n') ? 0 : -1
+ line = chomp(line)
+
+ putString(line, addr, maxSize)
+
+ pushPS(length(line))
+ pushPS(eofFlag)
+ pushPS(0)
+
+ return NEXT
+end)
+
EMIT_CFA = defPrimWord("EMIT", () -> begin
print(Char(popPS()))
TIB_CFA = defConst("TIB", TIB)
NUMTIB, NUMTIB_CFA = defNewVar("#TIB", 0)
+
+FIB_CFA = defConst("FIB", TIB)
+NUMFIB, NUMFIB_CFA = defNewVar("#FIB", 0)
+
TOIN, TOIN_CFA = defNewVar(">IN", 0)
+SOURCE_ID, SOURCE_ID_CFA = defNewVar("SOURCE-ID", 0)
+
+SOURCE_CFA = defPrimWord("SOURCE", () -> begin
+ if mem[SOURCE_ID] == 0
+ pushPS(TIB)
+ pushPS(NUMTIB)
+ else
+ pushPS(FIB)
+ pushPS(NUMFIB)
+ end
+ return NEXT
+end)
+
QUERY_CFA = defWord("QUERY",
[TIB_CFA, LIT_CFA, 160, EXPECT_CFA,
SPAN_CFA, FETCH_CFA, NUMTIB_CFA, STORE_CFA,
LIT_CFA, 0, TOIN_CFA, STORE_CFA,
EXIT_CFA])
+QUERY_FILE_CFA = defWord("QUERY-FILE",
+ [FIB_CFA, LIT_CFA, 160, ROT_CFA, READ_LINE_CFA,
+ DROP_CFA, SWAP_CFA,
+ NUMFIB_CFA, STORE_CFA,
+ EXIT_CFA])
+
WORD_CFA = defPrimWord("WORD", () -> begin
delim = popPS()
+ callPrim(mem[SOURCE_CFA])
+ sizeAddr = popPS()
+ bufferAddr = popPS()
+
# Chew up initial occurrences of delim
- while (mem[TOIN]<mem[NUMTIB] && mem[TIB+mem[TOIN]] == delim)
+ while (mem[TOIN]<mem[sizeAddr] && mem[bufferAddr+mem[TOIN]] == delim)
mem[TOIN] += 1
end
# Start reading in word
count = 0
- while (mem[TOIN]<mem[NUMTIB])
- mem[addr] = mem[TIB+mem[TOIN]]
+ while (mem[TOIN]<mem[sizeAddr])
+ mem[addr] = mem[bufferAddr+mem[TOIN]]
mem[TOIN] += 1
if (mem[addr] == delim)
[LIT_CFA, 32, WORD_CFA, # Read next space-delimited word
DUP_CFA, FETCH_CFA, ZE_CFA, ZBRANCH_CFA, 3,
- DROP_CFA, EXIT_CFA, # Exit if TIB is exhausted
+ DROP_CFA, EXIT_CFA, # Exit if input buffer is exhausted
STATE_CFA, FETCH_CFA, ZBRANCH_CFA, 24,
# Compiling
INTERPRET_CFA, PROMPT_CFA,
BRANCH_CFA,-4])
-ABORT_CFA = defWord("ABORT",
- [CLOSEFILES_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
+INTERPRET_CFA = defWord("INTERPRET",
+ [SOURCE_ID_CFA, FETCH_CFA, TOR_CFA, # Store current source on return stack
-BYE_CFA = defPrimWord("BYE", () -> begin
- println("\nBye!")
- return 0
-end)
+ LIT_CFA, 32, WORD_CFA, # Read next word from current input source
-# File I/O
+ FAM_RO_CFA, OPEN_FILE_CFA, DROP_CFA, # Open the file named by this word.
-INCLUDE_CFA = defPrimWord("INCLUDE", () -> begin
- pushPS(32)
- callPrim(mem[WORD_CFA])
- wordAddr = popPS()+1
- wordLen = mem[wordAddr-1]
- word = getString(wordAddr, wordLen)
+ DUP_CFA, SOURCE_ID_CFA, STORE_CFA, # Mark this as the current source
- fname = word
- if !isfile(fname)
- fname = Pkg.dir("forth","src",word)
- if !isfile(fname)
- error("No file named $word found in current directory or package source directory.")
- end
- end
- push!(sources, open(fname, "r"))
+ DUP_CFA, QUERY_FILE_CFA, # Read line from file
- # Clear input buffer
- mem[NUMTIB] = 0
+ INTERPRET_CFA,
- return NEXT
+ BRANCH_CFA, -4])
+
+
+ABORT_CFA = defWord("ABORT",
+ [CLOSE_FILES_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
+
+BYE_CFA = defPrimWord("BYE", () -> begin
+ println("\nBye!")
+ return 0
end)
+EOF_CFA = defPrimWord("\x04", () -> begin
+ return 0
+end)
#### VM loop ####
end
function run(;initialize=true)
- # Begin with STDIN as source
- push!(sources, STDIN)
+ # Start with IP pointing to first instruction of outer interpreter
+ pushRS(QUIT_CFA+1)
+
+ # Load library files
global initialized, initFileName
if !initialized && initialize
if initFileName != nothing
print("Including definitions from $initFileName...")
- push!(sources, open(initFileName, "r"))
+
+ # TODO
+
initialized = true
else
println("No library file found. Only primitive words available.")
end
end
- # Start with IP pointing to first instruction of outer interpreter
- reg.IP = QUIT_CFA + 1
# Primitive processing loop.
# Everyting else is simply a consequence of this loop!
- jmp = NEXT
+ jmp = mem[EXIT_CFA]
while jmp != 0
try
#println("Entering prim $(getPrimName(jmp))")
showerror(STDOUT, ex)
println()
- while !isempty(sources) && currentSource() != STDIN
- close(pop!(sources))
- end
-
# QUIT
reg.IP = ABORT_CFA + 1
jmp = NEXT