From 04ed7427cd15fa03607525fee579307febeef3d9 Mon Sep 17 00:00:00 2001 From: Starfflame Date: Thu, 13 May 2021 00:23:42 -0500 Subject: [PATCH] Refactor DataStructures, add initial implementation of ForthInterpreter --- CoreWords.tl | 76 ++++++++++++++++++++++++++++----------------- DataStructures.tl | 65 +++++++++++++++++++++++++------------- ForthInterpreter.tl | 60 ++++++++++++++++++++++++----------- InputStream.tl | 31 ++++++++++++++++-- 4 files changed, 161 insertions(+), 71 deletions(-) diff --git a/CoreWords.tl b/CoreWords.tl index e4940d1..5c84b14 100644 --- a/CoreWords.tl +++ b/CoreWords.tl @@ -3,15 +3,15 @@ local Dictionary, Stack, WordInfo, State = ds.Dictionary, ds.Stack, ds.WordInfo, -- 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 end -function getActiveDataStack(state: State): Stack +local function getActiveDataStack(state: State): Stack return state.activeDataStack end -function isNumber(token: string): boolean +local function isNumber(state: State, token: string): boolean if tonumber(token) ~= nil then return true else @@ -19,7 +19,7 @@ function isNumber(token: string): boolean end end -function isWhitespace(chr: string): boolean +local function isWhitespace(state: State, chr: string): boolean return (chr == " " or chr == "\t" or chr == "\r" or @@ -28,7 +28,7 @@ function isWhitespace(chr: string): boolean chr == "\f") end -function popTwoOperands(state: State): any, any +local function popTwoOperands(state: State): any, any local stack = getActiveDataStack(state) local b: any = stack:pop() local a: any = stack:pop() @@ -37,36 +37,36 @@ end -- Mathematical operations -function add(state: State) +local function add(state: 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) getActiveDataStack(state):push(c) else error("invalid operands for add operation!") end end -function sub(state: State) +local function sub(state: 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) getActiveDataStack(state):push(c) else error("invalid operands for sub operation!") end end -function mul(state: State) +local function mul(state: 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) getActiveDataStack(state):push(c) else error("invalid operands for mul operation!") end end -function div(state: State) +local function div(state: 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) getActiveDataStack(state):push(c) else @@ -74,41 +74,61 @@ function div(state: State) end end -- I/O operations -function dot(state: State) +local function dot(state: State) local out = state.activeDataStack:pop() print(out) end -function skipWhitespace(state: State) +local function skipWhitespace(state: State) local chr = state.activeInputStream:readCurrentCharacter() - while (isWhitespace(chr)) do + while (isWhitespace(state, chr)) do + print("SKIPPING WHITESPACE") chr = state.activeInputStream:advanceOffset() end end -function parseToken(state: State): string +local function parseToken(state: State): string local chr = state.activeInputStream:readCurrentCharacter() local token = "" - while(not isWhitespace(chr)) do + print("paresToken called") + while(not isWhitespace(state, chr)) do + print("parsing....", chr) token = token..chr chr = state.activeInputStream:advanceOffset() + print(chr) end + print("parsed", token) + return token end local CoreWords = Dictionary:new() -local AddInfo = WordInfo:new(add, false) -local SubInfo = WordInfo:new(sub, false) -local MulInfo = WordInfo:new(mul, false) -local DivInfo = WordInfo:new(div, false) - -CoreWords:define("+", AddInfo) -CoreWords:define("-", SubInfo) -CoreWords:define("*", MulInfo) -CoreWords:define("/", DivInfo) - +local addInfo = WordInfo:new(add, false) +local subInfo = WordInfo:new(sub, false) +local mulInfo = WordInfo:new(mul, false) +local divInfo = WordInfo:new(div, false) +local dotInfo = WordInfo:new(dot, false) +local getActiveDataStackInfo = WordInfo:new(getActiveDataStack, true) +local areNumbersInfo = WordInfo:new(areNumbers, false) +local isNumberInfo = WordInfo:new(isNumber, false) +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 diff --git a/DataStructures.tl b/DataStructures.tl index 5a3dd22..56ac24e 100644 --- a/DataStructures.tl +++ b/DataStructures.tl @@ -1,20 +1,29 @@ +local InputStream = require("InputStream") + + local type DataStructures = record record Stack contents: {any} top: number push: function(Stack, any) pop: function(Stack) + new: function(Stack): Stack end record Dictionary contents: {string: WordInfo} + + define: function(Dictionary, string, WordInfo) end record WordInfo - func: function(State) + func: function(State, ...: any) immediate: boolean + new: function(WordInfo, function(State), boolean): WordInfo end + + record State dataStacks: {Stack} compilerStack: Stack @@ -22,57 +31,63 @@ local type DataStructures = record interrupts: {function(State)} activeInputStream: InputStream dictionaries: {Dictionary} + + new: function(State): State + addDataStack: function(State, Stack) + changeCompilerStack: function(State, Stack) + changeActiveDataStack: function(State, number) end + end -local ds = DataStructures +local Stack, State, Dictionary, WordInfo = DataStructures.Stack, DataStructures.State, DataStructures.Dictionary, DataStructures.WordInfo - -local wordi_mt = {__index = ds.WordInfo} -function ds.WordInfo:new(funct: function(State), imm: boolean): ds.WordInfo - return setmetatable({func = funct, immediate = imm} as ds.WordInfo, wordi_mt) +local wordi_mt = {__index = WordInfo} +function WordInfo:new(funct: function(State, ...: any), imm: boolean): WordInfo + return setmetatable({func = funct, immediate = imm} as WordInfo, wordi_mt) end local state_mt = {__index = State} -function ds.State:new(): State +function State:new(): State return setmetatable( { dataStacks = {}, - compilerStack = Stack:new() + compilerStack = Stack:new(), + dictionaries = {} } as State, state_mt) end -function ds.State:addDataStack(data: Stack) +function State:addDataStack(data: Stack) table.insert(self.dataStacks, data) end -function ds.State:changeCompilerStack(compilerStack: Stack) +function State:changeCompilerStack(compilerStack: Stack) self.compilerStack = compilerStack end -function ds.State:changeActiveDataStack(stackIndex: number) - assert(stackIndex <= #self.dataStacks and stackIndex > 0) - self.activeDataStack = self.dataStacks[stackIndex] -end +--function State:changeActiveDataStack(stackIndex: number) +-- assert(stackIndex <= #self.dataStacks and stackIndex > 0) +-- self.activeDataStack = self.dataStacks[stackIndex] +--end -function ds.Stack:push(val: any) +function Stack:push(val: any) self.top = self.top + 1 table.insert(self.contents,val) end -function ds.Stack:pop(): any +function Stack:pop(): any self.top = self.top -1 if self.top < 0 then error("Stack underflow") end return table.remove(self.contents) end -local stack_mt = {__index = ds.Stack} -function ds.Stack:new(): ds.Stack - return setmetatable({contents = {}, top = 0} as ds.Stack, stack_mt) +local stack_mt = {__index = Stack} +function Stack:new(): Stack + return setmetatable({contents = {}, top = 0} as Stack, stack_mt) 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 end local dict_mt = {__index = Dictionary} -function ds.Dictionary:new(): ds.Dictionary +function Dictionary:new(): Dictionary return setmetatable({contents = {}} as Dictionary, dict_mt) end +return DataStructures + + + diff --git a/ForthInterpreter.tl b/ForthInterpreter.tl index 51a9701..bab51cc 100644 --- a/ForthInterpreter.tl +++ b/ForthInterpreter.tl @@ -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 - if tonumber(token) ~= nil then - return true +function standardInputRefill(): string + local input = io.read().."\n" + 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 - return false - end + for i, dictionary in ipairs(machineState.dictionaries) do + 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 - - - -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 diff --git a/InputStream.tl b/InputStream.tl index f8782e9..e046123 100644 --- a/InputStream.tl +++ b/InputStream.tl @@ -2,12 +2,24 @@ local type InputStream = record str: string offset: number 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 local istream_mt = {__index = InputStream} 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.offset = 1 end @@ -24,6 +36,19 @@ function InputStream:new(): InputStream } as InputStream, istream_mt) 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 function InputStream:setRefill(func: function(): string) @@ -33,13 +58,13 @@ end -- function InputStream:readCurrentCharacter(): string self:_manageBuffer() - return self.str:sub(self.offset, 1) + return self.str:sub(self.offset, self.offset) end function InputStream:advanceOffset(): string self.offset = self.offset + 1 self:_manageBuffer() - return self.str:sub(self.offset, 1) + return self.str:sub(self.offset, self.offset) end return InputStream