(LOCAL) word, along with helper words for using it.

This commit is contained in:
Starfflame 2021-07-19 19:30:02 -05:00
parent b92df463f1
commit fa9daaebb0
4 changed files with 148 additions and 42 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)