diff --git a/CoreHelpers.tl b/CoreHelpers.tl index fad1f1f..850ee27 100644 --- a/CoreHelpers.tl +++ b/CoreHelpers.tl @@ -5,8 +5,8 @@ local Pointer = ds.Pointer local InputStream = require("InputStream") local CoreHelpers = {} -function CoreHelpers.defineWord(dict: Dictionary, str: string, func: function(Environment, ...: any): any..., imm: boolean) - local info = WordInfo:new(func, imm) +function CoreHelpers.defineWord(dict: Dictionary, str: string, func: function(Environment): any..., imm: boolean, toFunc: function(Environment) | nil) + local info = WordInfo:new(func, imm, toFunc) dict:define(str, info) end @@ -80,6 +80,14 @@ function CoreHelpers.skipWhitespace(state: Environment) end end +function CoreHelpers.skipSpaces(e: Environment) + local chr = e.activeInputStream:curr() + while chr == " " do + e.activeInputStream:next() + chr = e.activeInputStream:curr() + end +end + function CoreHelpers.popTwoOperands(state: Environment): any, any local stack = CoreHelpers.getActiveDataStack(state) local b: any = stack:pop() @@ -108,9 +116,15 @@ function CoreHelpers.ret(env: Environment) env.instructionPointer = retAddr end -function CoreHelpers.makeCall(body: {function(Environment)}): function(Environment) +function CoreHelpers.makeCall(body: {function(Environment)}, localCount: integer | nil): function(Environment) + if localCount == nil then + localCount = 0 + end return function(e: Environment) e.returnStack:push(e.instructionPointer) + for i = localCount as number, 1, -1 do + e.returnStack:push(-1234) + end e.instructionPointer = Pointer:new(body, 0) end end @@ -164,8 +178,12 @@ function CoreHelpers.recNumber(env: Environment) end function CoreHelpers.searchDictionaries(e: Environment, token: string): WordInfo, Dictionary + local wordinfo = e.locals:lookup(token) + if wordinfo then + return wordinfo + end for _, dictionary in ipairs(e.dictionaries) do - local wordinfo = dictionary:lookup(token) + wordinfo = dictionary:lookup(token) if wordinfo then return wordinfo end @@ -189,8 +207,13 @@ end function CoreHelpers.interpreters_outer(env: Environment) + local dataStack = env.activeDataStack - CoreHelpers.skipWhitespace(env) + if not env.toMode then + CoreHelpers.skipWhitespace(env) + else + CoreHelpers.skipSpaces(env) + end local token: string | nil = CoreHelpers.parseToken(env) if not token then env.running = false @@ -227,7 +250,12 @@ function CoreHelpers.recWord(env: Environment) local wordinfo = CoreHelpers.searchDictionaries(env, token) if wordinfo then -- XT - dataStack:push((wordinfo as WordInfo).func) + if not env.toMode then + dataStack:push((wordinfo as WordInfo).func) + else + dataStack:push((wordinfo as WordInfo).toFunc) + env.toMode = false + end -- interpret dataStack:push(CoreHelpers.execute) diff --git a/CoreWords.tl b/CoreWords.tl index bebcba8..5dfbf3a 100644 --- a/CoreWords.tl +++ b/CoreWords.tl @@ -166,6 +166,7 @@ end local function colon(e: Environment) skipWhitespace(e) + e.locals = Dictionary:new() local name: string | nil = parseToken(e) if not name then e.running = false @@ -176,14 +177,27 @@ local function colon(e: Environment) end local function semicolon(e: Environment) + local localCount = e.locals.wordCount + local cleanup_locals = function(e: Environment) + for _ = localCount, 1, -1 do + e.returnStack:pop() + end + end + table.insert(e.currentDefinition as {function(Environment)}, cleanup_locals) table.insert(e.currentDefinition as {function(Environment)}, helpers.ret) local instrs = e.currentDefinition - local call = helpers.makeCall(instrs as {function(Environment)}) + + local call = helpers.makeCall(instrs as {function(Environment)}, e.locals.wordCount) local dict = e.dictionaries[1] defineWord(dict, e.currentDefinitionName, call, false) e.mostRecentDefinition = e.currentDefinitionName e.currentDefinitionName = nil e.currentDefinition = {} + -- clear locals + e.locals = Dictionary:new() + e.localBuffer = {} + + exitCompileMode(e) end local function toCompileStack(e: Environment) @@ -339,6 +353,51 @@ local function stringLiteral(e: Environment) e.activeDataStack:push(str as string) end +local function forthTo(e: Environment) + e.toMode = true +end + +local function forth_local_(e: Environment) + local localName = e.activeDataStack:pop() as string + if localName == "" then + -- TODO: figure out what to do when you receive an "end of locals" + local frame = #e.localBuffer + for _, f in ipairs(e.localBuffer) do + f(frame, e) + end + + else + -- local offset = e.locals.wordCount + + + local index = #e.localBuffer + 1 + + local postponedLocal = function(frame: integer, e: Environment) + local localAddress = e.returnStack.top - frame + index + local localFunction = function(e: Environment) + local top = e.returnStack.top + e.activeDataStack:push(e.returnStack.contents[top - frame + index]) + end + local localFunctionTo = function(e: Environment) + local top = e.returnStack.top + local newValue = e.activeDataStack:pop() + e.returnStack.contents[top - frame + index] = newValue + end + defineWord(e.locals, localName, localFunction, false, localFunctionTo) + end + table.insert(e.localBuffer, postponedLocal) + end +end + +local function parseName(e: Environment) + helpers.skipSpaces(e) + e.activeDataStack:push(helpers.parseToken(e)) +end + +local function emptys(e: Environment) + e.activeDataStack:push("") +end + local CoreWords = Dictionary:new() local addInfo = WordInfo:new(add, false) local subInfo = WordInfo:new(sub, false) @@ -363,7 +422,7 @@ defineWord(CoreWords, "[", exitCompileMode, true) defineWord(CoreWords, "]", enterCompileMode, false) defineWord(CoreWords, "COMPILE,", compile, true) defineWord(CoreWords, "LITERAL", literal, true) -defineWord(CoreWords, ":", colon, true) +defineWord(CoreWords, ":", colon, false) defineWord(CoreWords, ";", semicolon, true) defineWord(CoreWords, ">C",toCompileStack, false) defineWord(CoreWords, "C>", fromCompileStack, false) @@ -378,13 +437,19 @@ defineWord(CoreWords, "WHILE", forthWhile, true) defineWord(CoreWords, "REPEAT", forthRepeat, true) defineWord(CoreWords, "AGAIN", forthAgain, true) +defineWord(CoreWords, "TO", forthTo, true) +defineWord(CoreWords, "(LOCAL)", forth_local_, true) +defineWord(CoreWords, "PARSE-NAME", parseName, true) +defineWord(CoreWords, "EMPTYS", emptys, true) +defineWord(CoreWords, "\"\"", emptys, false) + defineWord(CoreWords,"<", cmplt, false) defineWord(CoreWords,"<=", cmplte, false) defineWord(CoreWords,"=", cmpe, false) defineWord(CoreWords,">=", cmpgte, false) defineWord(CoreWords,">", cmpgt, false) defineWord(CoreWords,"<>", cmpne, false) -defineWord(CoreWords,"S\"", stringLiteral, false) +defineWord(CoreWords,"S\"", stringLiteral, true) CoreWords:define("+", addInfo) diff --git a/DataStructures.tl b/DataStructures.tl index 440a7f2..b046184 100644 --- a/DataStructures.tl +++ b/DataStructures.tl @@ -3,8 +3,8 @@ local InputStream = require("InputStream") local type DataStructures = record record Stack - contents: {any} - top: number + contents: {integer:any} + top: integer push: function(Stack, any) pop: function(Stack) @@ -13,10 +13,11 @@ local type DataStructures = record record Dictionary contents: {string: WordInfo} - + wordCount: integer define: function(Dictionary, string, WordInfo) end record WordInfo + toFunc: function(Environment) func: function(Environment) immediate: boolean new: function(WordInfo, function(Environment), boolean): WordInfo @@ -49,6 +50,9 @@ local type DataStructures = record currentDefinitionName: string | nil currentDefinition: {function(Environment)} mostRecentDefinition: string + toMode: boolean + locals: Dictionary + localBuffer: {function(integer, Environment)} end end @@ -56,37 +60,11 @@ end local Stack, Environment, Dictionary, WordInfo = DataStructures.Stack, DataStructures.Environment, DataStructures.Dictionary, DataStructures.WordInfo local Pointer = DataStructures.Pointer local wordi_mt = {__index = WordInfo} -function WordInfo:new(funct: function(Environment, ...: any), imm: boolean): WordInfo - return setmetatable({func = funct, immediate = imm} as WordInfo, wordi_mt) +function WordInfo:new(funct: function(Environment, ...: any), imm: boolean, toFunct: function(Environment) | nil): WordInfo + return setmetatable({func = funct, immediate = imm, toFunc = toFunct} as WordInfo, wordi_mt) end -local state_mt = {__index = Environment} -function Environment:new(input: InputStream, dict: {Dictionary} ): Environment - local dicts = dict or {} - - return setmetatable( - { - activeDataStack= Stack:new(), - compilerStack = Stack:new(), - returnStack = Stack:new(), - dictionaries = dicts, - activeInputStream = input, - running = true, - recognizers = {}, - compileState = false, - currentDefinition = {} - } as Environment, - state_mt) -end - -function Environment:addDataStack(data: Stack) - table.insert(self.dataStacks, data) -end - -function Environment:changeCompilerStack(compilerStack: Stack) - self.compilerStack = compilerStack -end --function Environment:changeActiveDataStack(stackIndex: number) -- assert(stackIndex <= #self.dataStacks and stackIndex > 0) -- self.activeDataStack = self.dataStacks[stackIndex] @@ -94,7 +72,6 @@ end - function Stack:push(val: any) self.top = self.top + 1 table.insert(self.contents,val) @@ -122,13 +99,42 @@ function Dictionary:lookup(word: string): WordInfo | nil end function Dictionary:define(word: string, info: WordInfo) + if not self.contents[word] then self.wordCount = self.wordCount + 1 end self.contents[word] = info end local dict_mt = {__index = Dictionary} function Dictionary:new(): Dictionary - return setmetatable({contents = {}} as Dictionary, dict_mt) + return setmetatable({contents = {}, wordCount = 0} as Dictionary, dict_mt) end +local state_mt = {__index = Environment} +function Environment:new(input: InputStream, dict: {Dictionary} ): Environment + local dicts = dict or {} + + return setmetatable( + { + activeDataStack= Stack:new(), + compilerStack = Stack:new(), + returnStack = Stack:new(), + dictionaries = dicts, + activeInputStream = input, + running = true, + recognizers = {}, + compileState = false, + currentDefinition = {}, + locals = Dictionary:new(), + localBuffer = {} + } as Environment, + state_mt) +end + +function Environment:addDataStack(data: Stack) + table.insert(self.dataStacks, data) +end + +function Environment:changeCompilerStack(compilerStack: Stack) + self.compilerStack = compilerStack +end function Pointer:deref(): any return self.referant[self.index] end diff --git a/tests.lua b/tests.lua index fc06164..efa81fc 100644 --- a/tests.lua +++ b/tests.lua @@ -138,6 +138,13 @@ describe("Dictionary tests", function() dict:define("TEST",wi) assert.are.same(dict:lookup("TEST"),wi) end) + it("keeps track of the number of words in the dictionary", function() + local env = Environment:new() + local dict = Dictionary:new() + local wi = WordInfo:new(function(env) end, false) + dict:define("TEST", wi) + assert.are.equal(dict.wordCount, 1) + end) end)