Refactor DataStructures, add initial implementation of ForthInterpreter

This commit is contained in:
Starfflame 2021-05-13 00:23:42 -05:00
parent c20df3883f
commit 04ed7427cd
4 changed files with 161 additions and 71 deletions

View File

@ -3,15 +3,15 @@ local Dictionary, Stack, WordInfo, State = ds.Dictionary, ds.Stack, ds.WordInfo,
-- helper functions -- helper functions
function areNumbers(a: any, b: any): boolean local function areNumbers(state: State, a: any, b: any): boolean
return a is number and b is number return a is number and b is number
end end
function getActiveDataStack(state: State): Stack local function getActiveDataStack(state: State): Stack
return state.activeDataStack return state.activeDataStack
end end
function isNumber(token: string): boolean local function isNumber(state: State, token: string): boolean
if tonumber(token) ~= nil then if tonumber(token) ~= nil then
return true return true
else else
@ -19,7 +19,7 @@ function isNumber(token: string): boolean
end end
end end
function isWhitespace(chr: string): boolean local function isWhitespace(state: State, chr: string): boolean
return (chr == " " or return (chr == " " or
chr == "\t" or chr == "\t" or
chr == "\r" or chr == "\r" or
@ -28,7 +28,7 @@ function isWhitespace(chr: string): boolean
chr == "\f") chr == "\f")
end end
function popTwoOperands(state: State): any, any local function popTwoOperands(state: State): any, any
local stack = getActiveDataStack(state) local stack = getActiveDataStack(state)
local b: any = stack:pop() local b: any = stack:pop()
local a: any = stack:pop() local a: any = stack:pop()
@ -37,36 +37,36 @@ end
-- Mathematical operations -- Mathematical operations
function add(state: State) local function add(state: State)
local a, b = popTwoOperands(state) local a, b = popTwoOperands(state)
if areNumbers(a,b) then if areNumbers(state, a,b) then
local c = (a as number) + (b as number) local c = (a as number) + (b as number)
getActiveDataStack(state):push(c) getActiveDataStack(state):push(c)
else else
error("invalid operands for add operation!") error("invalid operands for add operation!")
end end
end end
function sub(state: State) local function sub(state: State)
local a, b = popTwoOperands(state) local a, b = popTwoOperands(state)
if areNumbers(a, b) then if areNumbers(state, a, b) then
local c = (a as number) - (b as number) local c = (a as number) - (b as number)
getActiveDataStack(state):push(c) getActiveDataStack(state):push(c)
else else
error("invalid operands for sub operation!") error("invalid operands for sub operation!")
end end
end end
function mul(state: State) local function mul(state: State)
local a, b = popTwoOperands(state) local a, b = popTwoOperands(state)
if areNumbers(a, b) then if areNumbers(state, a, b) then
local c = (a as number) * (b as number) local c = (a as number) * (b as number)
getActiveDataStack(state):push(c) getActiveDataStack(state):push(c)
else else
error("invalid operands for mul operation!") error("invalid operands for mul operation!")
end end
end end
function div(state: State) local function div(state: State)
local a, b = popTwoOperands(state) local a, b = popTwoOperands(state)
if areNumbers(a, b) then if areNumbers(state, a, b) then
local c = (a as number) / (b as number) local c = (a as number) / (b as number)
getActiveDataStack(state):push(c) getActiveDataStack(state):push(c)
else else
@ -74,41 +74,61 @@ function div(state: State)
end end
end end
-- I/O operations -- I/O operations
function dot(state: State) local function dot(state: State)
local out = state.activeDataStack:pop() local out = state.activeDataStack:pop()
print(out) print(out)
end end
function skipWhitespace(state: State) local function skipWhitespace(state: State)
local chr = state.activeInputStream:readCurrentCharacter() local chr = state.activeInputStream:readCurrentCharacter()
while (isWhitespace(chr)) do while (isWhitespace(state, chr)) do
print("SKIPPING WHITESPACE")
chr = state.activeInputStream:advanceOffset() chr = state.activeInputStream:advanceOffset()
end end
end end
function parseToken(state: State): string local function parseToken(state: State): string
local chr = state.activeInputStream:readCurrentCharacter() local chr = state.activeInputStream:readCurrentCharacter()
local token = "" local token = ""
while(not isWhitespace(chr)) do print("paresToken called")
while(not isWhitespace(state, chr)) do
print("parsing....", chr)
token = token..chr token = token..chr
chr = state.activeInputStream:advanceOffset() chr = state.activeInputStream:advanceOffset()
print(chr)
end end
print("parsed", token)
return token
end end
local CoreWords = Dictionary:new() local CoreWords = Dictionary:new()
local AddInfo = WordInfo:new(add, false) local addInfo = WordInfo:new(add, false)
local SubInfo = WordInfo:new(sub, false) local subInfo = WordInfo:new(sub, false)
local MulInfo = WordInfo:new(mul, false) local mulInfo = WordInfo:new(mul, false)
local DivInfo = WordInfo:new(div, false) local divInfo = WordInfo:new(div, false)
local dotInfo = WordInfo:new(dot, false)
CoreWords:define("+", AddInfo) local getActiveDataStackInfo = WordInfo:new(getActiveDataStack, true)
CoreWords:define("-", SubInfo) local areNumbersInfo = WordInfo:new(areNumbers, false)
CoreWords:define("*", MulInfo) local isNumberInfo = WordInfo:new(isNumber, false)
CoreWords:define("/", DivInfo) local isWhitespaceInfo = WordInfo:new(isWhitespace, false)
local popTwoOperandsInfo = WordInfo:new(popTwoOperands, false)
local parseTokenInfo = WordInfo:new(parseToken, true)
local skipWhitespaceInfo = WordInfo:new(skipWhitespace, true)
CoreWords:define("+", addInfo)
CoreWords:define("-", subInfo)
CoreWords:define("*", mulInfo)
CoreWords:define("/", divInfo)
CoreWords:define(".", dotInfo)
CoreWords:define("GetActiveData", getActiveDataStackInfo)
CoreWords:define("areNumbers", areNumbersInfo)
CoreWords:define("isNumber", isNumberInfo)
CoreWords:define("isWhitespace", isWhitespaceInfo)
CoreWords:define("popTwo", popTwoOperandsInfo)
CoreWords:define("parseToken", parseTokenInfo)
CoreWords:define("skipWhitespace", skipWhitespaceInfo)
return CoreWords return CoreWords

View File

@ -1,20 +1,29 @@
local InputStream = require("InputStream")
local type DataStructures = record local type DataStructures = record
record Stack record Stack
contents: {any} contents: {any}
top: number top: number
push: function(Stack, any) push: function(Stack, any)
pop: function(Stack) pop: function(Stack)
new: function(Stack): Stack new: function(Stack): Stack
end end
record Dictionary record Dictionary
contents: {string: WordInfo} contents: {string: WordInfo}
define: function(Dictionary, string, WordInfo)
end end
record WordInfo record WordInfo
func: function(State) func: function(State, ...: any)
immediate: boolean immediate: boolean
new: function(WordInfo, function(State), boolean): WordInfo
end end
record State record State
dataStacks: {Stack} dataStacks: {Stack}
compilerStack: Stack compilerStack: Stack
@ -22,57 +31,63 @@ local type DataStructures = record
interrupts: {function(State)} interrupts: {function(State)}
activeInputStream: InputStream activeInputStream: InputStream
dictionaries: {Dictionary} dictionaries: {Dictionary}
new: function(State): State
addDataStack: function(State, Stack)
changeCompilerStack: function(State, Stack)
changeActiveDataStack: function(State, number)
end end
end end
local ds = DataStructures local Stack, State, Dictionary, WordInfo = DataStructures.Stack, DataStructures.State, DataStructures.Dictionary, DataStructures.WordInfo
local wordi_mt = {__index = WordInfo}
local wordi_mt = {__index = ds.WordInfo} function WordInfo:new(funct: function(State, ...: any), imm: boolean): WordInfo
function ds.WordInfo:new(funct: function(State), imm: boolean): ds.WordInfo return setmetatable({func = funct, immediate = imm} as WordInfo, wordi_mt)
return setmetatable({func = funct, immediate = imm} as ds.WordInfo, wordi_mt)
end end
local state_mt = {__index = State} local state_mt = {__index = State}
function ds.State:new(): State function State:new(): State
return setmetatable( return setmetatable(
{ {
dataStacks = {}, dataStacks = {},
compilerStack = Stack:new() compilerStack = Stack:new(),
dictionaries = {}
} as State, } as State,
state_mt) state_mt)
end end
function ds.State:addDataStack(data: Stack) function State:addDataStack(data: Stack)
table.insert(self.dataStacks, data) table.insert(self.dataStacks, data)
end end
function ds.State:changeCompilerStack(compilerStack: Stack) function State:changeCompilerStack(compilerStack: Stack)
self.compilerStack = compilerStack self.compilerStack = compilerStack
end end
function ds.State:changeActiveDataStack(stackIndex: number) --function State:changeActiveDataStack(stackIndex: number)
assert(stackIndex <= #self.dataStacks and stackIndex > 0) -- assert(stackIndex <= #self.dataStacks and stackIndex > 0)
self.activeDataStack = self.dataStacks[stackIndex] -- self.activeDataStack = self.dataStacks[stackIndex]
end --end
function ds.Stack:push(val: any) function Stack:push(val: any)
self.top = self.top + 1 self.top = self.top + 1
table.insert(self.contents,val) table.insert(self.contents,val)
end end
function ds.Stack:pop(): any function Stack:pop(): any
self.top = self.top -1 self.top = self.top -1
if self.top < 0 then if self.top < 0 then
error("Stack underflow") error("Stack underflow")
end end
return table.remove(self.contents) return table.remove(self.contents)
end end
local stack_mt = {__index = ds.Stack} local stack_mt = {__index = Stack}
function ds.Stack:new(): ds.Stack function Stack:new(): Stack
return setmetatable({contents = {}, top = 0} as ds.Stack, stack_mt) return setmetatable({contents = {}, top = 0} as Stack, stack_mt)
end end
@ -81,13 +96,19 @@ end
function Dictionary:lookup(word: string): WordInfo | nil
return self.contents[word]
end
function ds.Dictionary:define(word: string, info: WordInfo) function Dictionary:define(word: string, info: WordInfo)
self.contents[word] = info self.contents[word] = info
end end
local dict_mt = {__index = Dictionary} local dict_mt = {__index = Dictionary}
function ds.Dictionary:new(): ds.Dictionary function Dictionary:new(): Dictionary
return setmetatable({contents = {}} as Dictionary, dict_mt) return setmetatable({contents = {}} as Dictionary, dict_mt)
end end
return DataStructures

View File

@ -1,24 +1,48 @@
local CoreWords = require("CoreWords")
local ds = require("DataStructures")
local istream = require("InputStream")
local Stack, Dictionary, WordInfo, State = ds.Stack, ds.Dictionary, ds.WordInfo, ds.State
-- helper functions local machineState = State:new()
function isNumber(token: string): boolean function standardInputRefill(): string
if tonumber(token) ~= nil then local input = io.read().."\n"
return true return input
end
local standardInputStream = istream:new(standardInputRefill)
machineState.activeInputStream = standardInputStream
machineState.activeDataStack = Stack:new()
table.insert(machineState.dictionaries, CoreWords)
local getActiveDataStacks = (CoreWords:lookup("GetActiveData") as WordInfo).func
local areNumbers = (CoreWords:lookup("areNumbers") as WordInfo).func
local isNumber = (CoreWords:lookup("isNumber") as WordInfo).func
local isWhitespace = (CoreWords:lookup("isWhitespace") as WordInfo).func
local popTwoOperands = (CoreWords:lookup("popTwo") as WordInfo).func
local skipWhitespace = (CoreWords:lookup("skipWhitespace") as WordInfo).func
local parseToken = (CoreWords:lookup("parseToken") as WordInfo).func
while(true) do
print("NEW LOOP")
skipWhitespace(machineState)
local token: string = parseToken(machineState)
if isNumber(machineState, token) then
machineState.activeDataStack:push(tonumber(token))
print("ok")
else else
return false for i, dictionary in ipairs(machineState.dictionaries) do
end local wordinfo = dictionary:lookup(token)
if wordinfo then
(wordinfo as WordInfo).func(machineState)
print("ok")
goto ok
end
end
print(string.format('%q ?', token))
::ok::
end
end end
for line in io.lines() do
local tokens = {}
for token in line:gmatch("%S+") do
table.insert(tokens, token)
end
for idx, val in ipairs(tokens) do
print(idx, val, isNumber(val))
end
end

View File

@ -2,12 +2,24 @@ local type InputStream = record
str: string str: string
offset: number offset: number
refill: function(): string refill: function(): string
new: function(InputStream): InputStream
new: function(InputStream, function(): string): InputStream
setRefill: function(InputStream, function(): string)
readCurrentCharacter: function(): string
advanceOffset: function(): string
end end
local istream_mt = {__index = InputStream} local istream_mt = {__index = InputStream}
function InputStream:_manageBuffer() function InputStream:_manageBuffer()
if self.offset > #self.str then if not self.str then
self.str = self.refill()
self.offset = 1
end
if self.offset > #self.str + 1 then
self.str = self.refill() self.str = self.refill()
self.offset = 1 self.offset = 1
end end
@ -24,6 +36,19 @@ function InputStream:new(): InputStream
} as InputStream, } as InputStream,
istream_mt) istream_mt)
end end
function InputStream:new(refill: function(): string): InputStream
local mt = setmetatable(
{
string = "",
offset = 1
} as InputStream,
istream_mt)
mt:setRefill(refill)
return mt
end
-- setters/getters -- setters/getters
function InputStream:setRefill(func: function(): string) function InputStream:setRefill(func: function(): string)
@ -33,13 +58,13 @@ end
-- --
function InputStream:readCurrentCharacter(): string function InputStream:readCurrentCharacter(): string
self:_manageBuffer() self:_manageBuffer()
return self.str:sub(self.offset, 1) return self.str:sub(self.offset, self.offset)
end end
function InputStream:advanceOffset(): string function InputStream:advanceOffset(): string
self.offset = self.offset + 1 self.offset = self.offset + 1
self:_manageBuffer() self:_manageBuffer()
return self.str:sub(self.offset, 1) return self.str:sub(self.offset, self.offset)
end end
return InputStream return InputStream