Added stack overflow exceptions.
[forth.jl.git] / src / forth.jl
index f10ea04..5c09b49 100644 (file)
@@ -1,5 +1,7 @@
 module forth
 
+import Base.REPLCompletions
+
 # VM mem size
 size_mem = 1000000 # 1 mega-int
 
@@ -11,7 +13,7 @@ size_TIB = 1000  # Terminal input buffer size
 # Memory arrays
 mem = Array{Int64,1}(size_mem)
 primitives = Array{Function,1}()
-primNames = Array{ASCIIString,1}()
+primNames = Array{AbstractString,1}()
 
 # Memory geography and built-in variables
 
@@ -46,13 +48,26 @@ function ensurePSDepth(depth::Int64)
     end
 end
 
+function ensurePSCapacity(toAdd::Int64)
+    if reg.PSP + toAdd >= PSP0 + size_PS
+        error("Parameter stack overflow.")
+    end
+end
+
 function ensureRSDepth(depth::Int64)
     if reg.RSP - RSP0 < depth
         error("Return stack underflow.")
     end
 end
 
+function ensureRSCapacity(toAdd::Int64)
+    if reg.RSP + toAdd >= RSP0 + size_RS
+        error("Return stack overflow.")
+    end
+end
+
 function pushRS(val::Int64)
+    ensureRSCapacity(1)
     mem[reg.RSP+=1] = val
 end
 
@@ -65,6 +80,8 @@ function popRS()
 end
 
 function pushPS(val::Int64)
+    ensurePSCapacity(1)
+
     mem[reg.PSP += 1] = val
 end
 
@@ -78,13 +95,14 @@ end
 
 # Handy functions for adding/retrieving strings to/from memory.
 
-getString(addr::Int64, len::Int64) = ASCIIString([Char(c) for c in mem[addr:(addr+len-1)]])
+getString(addr::Int64, len::Int64) = AbstractString([Char(c) for c in mem[addr:(addr+len-1)]])
 
-function putString(str::ASCIIString, addr::Int64)
-    mem[addr:(addr+length(str)-1)] = [Int64(c) for c in str]
+function putString(str::AbstractString, addr::Int64, maxLen::Int64)
+    len = min(length(str), maxLen)
+    mem[addr:(addr+len-1)] = [Int64(c) for c in str]
 end
 
-stringAsInts(str::ASCIIString) = [Int(c) for c in collect(str)]
+stringAsInts(str::AbstractString) = [Int(c) for c in collect(str)]
 
 # Primitive creation and calling functions
 
@@ -116,7 +134,7 @@ function dictWrite(ints::Array{Int64,1})
     mem[H] += length(ints)
 end
 dictWrite(int::Int64) = dictWrite([int])
-dictWriteString(string::ASCIIString) = dictWrite([Int64(c) for c in string])
+dictWriteString(string::AbstractString) = dictWrite([Int64(c) for c in string])
 
 function createHeader(name::AbstractString, flags::Int64)
     mem[mem[H]] = mem[mem[CURRENT]+1]
@@ -585,6 +603,14 @@ end)
 sources = Array{Any,1}()
 currentSource() = sources[length(sources)]
 
+CLOSEFILES_CFA = defPrimWord("CLOSEFILES", () -> begin
+    while currentSource() != STDIN
+        close(pop!(sources))
+    end
+
+    return NEXT
+end)
+
 EOF_CFA = defPrimWord("\x04", () -> begin
     if currentSource() != STDIN
         close(pop!(sources))
@@ -625,13 +651,31 @@ KEY_CFA = defPrimWord("KEY", () -> begin
 end)
 
 function getLineFromSTDIN()
+
+    function getFrag(s)
+        chars = collect(s)
+        slashIdx = findlast(chars, '\\')
+
+        if slashIdx > 0
+            return join(chars[slashIdx:length(chars)])
+        else
+            return nothing
+        end
+    end
+
+    function backspaceStr(s, bsCount)
+        oldLen = length(s)
+        newLen = max(0, oldLen - bsCount)
+        return join(collect(s)[1:newLen])
+    end
+
     line = ""
     while true
         key = Char(getKey())
 
         if key == '\n'
             print(" ")
-            return ASCIIString(line)
+            return AbstractString(line)
 
         elseif key == '\x04'
             if isempty(line)
@@ -640,8 +684,8 @@ function getLineFromSTDIN()
 
         elseif key == '\b'
             if !isempty(line)
-                line = line[1:length(line)-1]
-                print("\b \b")
+                print("\b\033[K")
+                line = backspaceStr(line, 1)
             end
 
         elseif key == '\e'
@@ -656,6 +700,20 @@ function getLineFromSTDIN()
                 end
             end
 
+        elseif key == '\t'
+            # Currently do nothing
+
+            frag = getFrag(line)
+            if frag != nothing
+                if haskey(REPLCompletions.latex_symbols, frag)
+                    print(repeat("\b", length(frag)))
+                    print("\033[K")
+                    comp = REPLCompletions.latex_symbols[frag]
+                    line = string(backspaceStr(line, length(frag)), comp)
+                    print(comp)
+                end
+            end
+
         else
             print(key)
             line = string(line, key)
@@ -679,7 +737,7 @@ EXPECT_CFA = defPrimWord("EXPECT", () -> begin
     end
 
     mem[SPAN] = min(length(line), maxLen)
-    putString(line[1:mem[SPAN]], addr)
+    putString(line, addr, maxLen)
 
     return NEXT
 end)
@@ -1031,7 +1089,7 @@ QUIT_CFA = defWord("QUIT",
     BRANCH_CFA,-4])
 
 ABORT_CFA = defWord("ABORT",
-    [PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
+    [CLOSEFILES_CFA, PSP0_CFA, PSPSTORE_CFA, QUIT_CFA])
 
 BYE_CFA = defPrimWord("BYE", () -> begin
     println("\nBye!")
@@ -1155,7 +1213,7 @@ function dump(startAddr::Int64; count::Int64 = 100, cellsPerLine::Int64 = 10)
             i += 1
         end
 
-        println("\t", ASCIIString(chars))
+        println("\t", AbstractString(chars))
     end
 end