Implemented SOURCE.
[forth.jl.git] / src / forth.jl
index f10b963..d911e21 100644 (file)
@@ -9,6 +9,7 @@ size_mem = 1000000 # 1 mega-int
 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)
@@ -25,7 +26,8 @@ CURRENT = nextVarAddr; nextVarAddr += 1        # Current compilation dict
 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
 
@@ -602,7 +604,70 @@ end)
 
 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
+    return NEXT
+end)
+
 
 EMIT_CFA = defPrimWord("EMIT", () -> begin
     print(Char(popPS()))
@@ -873,19 +938,46 @@ end)
 
 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
 
@@ -894,8 +986,8 @@ WORD_CFA = defPrimWord("WORD", () -> begin
 
     # 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)
@@ -1102,38 +1194,13 @@ QUIT_CFA = defWord("QUIT",
     BRANCH_CFA,-4])
 
 ABORT_CFA = defWord("ABORT",
-    [CLOSEFILES_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
+    [CLOSE_FILES_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
 
 BYE_CFA = defPrimWord("BYE", () -> begin
     println("\nBye!")
     return 0
 end)
 
-# File I/O
-
-INCLUDE_CFA = defPrimWord("INCLUDE", () -> begin
-    pushPS(32)
-    callPrim(mem[WORD_CFA])
-    wordAddr = popPS()+1
-    wordLen = mem[wordAddr-1]
-    word = getString(wordAddr, wordLen)
-
-    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"))
-
-    # Clear input buffer
-    mem[NUMTIB] = 0
-
-    return NEXT
-end)
-
-
 #### VM loop ####
 
 initialized = false
@@ -1145,14 +1212,14 @@ elseif isfile(Pkg.dir("forth","src", "lib.4th"))
 end
 
 function run(;initialize=true)
-    # Begin with STDIN as source
-    push!(sources, STDIN)
 
     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.")
@@ -1174,10 +1241,6 @@ function run(;initialize=true)
             showerror(STDOUT, ex)
             println()
 
-            while !isempty(sources) && currentSource() != STDIN
-                close(pop!(sources))
-            end
-
             # QUIT
             reg.IP = ABORT_CFA + 1
             jmp = NEXT