Compare commits
3 Commits
5dc66425f5
...
fe6bc4f8cb
Author | SHA1 | Date |
---|---|---|
Starfflame | fe6bc4f8cb | |
Starfflame | 04ed7427cd | |
Starfflame | c20df3883f |
103
CoreWords.tl
103
CoreWords.tl
|
@ -1,18 +1,34 @@
|
||||||
local Dictionary = require("Dictionary")
|
local ds = require("DataStructures")
|
||||||
local Stack = require("Stack")
|
local Dictionary, Stack, WordInfo, State = ds.Dictionary, ds.Stack, ds.WordInfo, ds.State
|
||||||
local WordInfo = require("WordInfo")
|
|
||||||
local State = require("State")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- 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 popTwoOperands(state: State): any, any
|
|
||||||
|
local function isNumber(state: State, token: string): boolean
|
||||||
|
if tonumber(token) ~= nil then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isWhitespace(state: State, chr: string): boolean
|
||||||
|
return (chr == " " or
|
||||||
|
chr == "\t" or
|
||||||
|
chr == "\r" or
|
||||||
|
chr == "\n" or
|
||||||
|
chr == "\v" or
|
||||||
|
chr == "\f")
|
||||||
|
end
|
||||||
|
|
||||||
|
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()
|
||||||
|
@ -21,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
|
||||||
|
@ -58,23 +74,56 @@ 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
|
||||||
|
|
||||||
|
local function skipWhitespace(state: State)
|
||||||
|
local chr = state.activeInputStream:readCurrentCharacter()
|
||||||
|
while (isWhitespace(state, chr)) do
|
||||||
|
chr = state.activeInputStream:advanceOffset()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseToken(state: State): string
|
||||||
|
local chr = state.activeInputStream:readCurrentCharacter()
|
||||||
|
local token = ""
|
||||||
|
while(not isWhitespace(state, chr)) do
|
||||||
|
token = token..chr
|
||||||
|
chr = state.activeInputStream:advanceOffset()
|
||||||
|
end
|
||||||
|
return token
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
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, ...: any)
|
||||||
|
immediate: boolean
|
||||||
|
new: function(WordInfo, function(State), boolean): WordInfo
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
record State
|
||||||
|
dataStacks: {Stack}
|
||||||
|
compilerStack: Stack
|
||||||
|
activeDataStack: Stack
|
||||||
|
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 Stack, State, Dictionary, WordInfo = DataStructures.Stack, DataStructures.State, DataStructures.Dictionary, DataStructures.WordInfo
|
||||||
|
|
||||||
|
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 State:new(): State
|
||||||
|
return setmetatable(
|
||||||
|
{
|
||||||
|
dataStacks = {},
|
||||||
|
compilerStack = Stack:new(),
|
||||||
|
dictionaries = {}
|
||||||
|
} as State,
|
||||||
|
state_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function State:addDataStack(data: Stack)
|
||||||
|
table.insert(self.dataStacks, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function State:changeCompilerStack(compilerStack: Stack)
|
||||||
|
self.compilerStack = compilerStack
|
||||||
|
end
|
||||||
|
--function State:changeActiveDataStack(stackIndex: number)
|
||||||
|
-- assert(stackIndex <= #self.dataStacks and stackIndex > 0)
|
||||||
|
-- self.activeDataStack = self.dataStacks[stackIndex]
|
||||||
|
--end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Stack:push(val: any)
|
||||||
|
self.top = self.top + 1
|
||||||
|
table.insert(self.contents,val)
|
||||||
|
end
|
||||||
|
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 = Stack}
|
||||||
|
function Stack:new(): Stack
|
||||||
|
return setmetatable({contents = {}, top = 0} as Stack, stack_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- operations
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Dictionary:lookup(word: string): WordInfo | nil
|
||||||
|
return self.contents[word]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Dictionary:define(word: string, info: WordInfo)
|
||||||
|
self.contents[word] = info
|
||||||
|
end
|
||||||
|
local dict_mt = {__index = Dictionary}
|
||||||
|
function Dictionary:new(): Dictionary
|
||||||
|
return setmetatable({contents = {}} as Dictionary, dict_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
return DataStructures
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
local State = require("State")
|
|
||||||
local WordInfo = require("WordInfo")
|
|
||||||
|
|
||||||
|
|
||||||
local type Dictionary = record
|
|
||||||
contents: {string: WordInfo}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Dictionary:define(word: string, info: WordInfo)
|
|
||||||
self.contents[word] = info
|
|
||||||
end
|
|
||||||
local dict_mt = {__index = Dictionary}
|
|
||||||
function Dictionary:new(): Dictionary
|
|
||||||
return setmetatable({contents = {}} as Dictionary, dict_mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return Dictionary
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
local machineState = State:new()
|
||||||
|
|
||||||
|
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
|
||||||
|
skipWhitespace(machineState)
|
||||||
|
local token: string = parseToken(machineState)
|
||||||
|
if isNumber(machineState, token) then
|
||||||
|
machineState.activeDataStack:push(tonumber(token))
|
||||||
|
else
|
||||||
|
for i, dictionary in ipairs(machineState.dictionaries) do
|
||||||
|
local wordinfo = dictionary:lookup(token)
|
||||||
|
if wordinfo then
|
||||||
|
(wordinfo as WordInfo).func(machineState)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(string.format('%q ?', token))
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
28
Stack.tl
28
Stack.tl
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
local type Stack = record
|
|
||||||
contents: {any}
|
|
||||||
top: number
|
|
||||||
push: function(Stack, any)
|
|
||||||
pop: function(Stack)
|
|
||||||
new: function(Stack): Stack
|
|
||||||
end
|
|
||||||
function Stack:push(val: any)
|
|
||||||
self.top = self.top + 1
|
|
||||||
table.insert(self.contents,val)
|
|
||||||
end
|
|
||||||
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 = Stack}
|
|
||||||
function Stack:new(): Stack
|
|
||||||
return setmetatable({contents = {}, top = 0} as Stack, stack_mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- operations
|
|
||||||
|
|
||||||
return Stack
|
|
40
State.tl
40
State.tl
|
@ -1,40 +0,0 @@
|
||||||
local Stack = require("Stack")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local type State = record
|
|
||||||
dataStacks: {Stack}
|
|
||||||
compilerStack: Stack
|
|
||||||
activeDataStack: Stack
|
|
||||||
interrupts: {function(State)}
|
|
||||||
-- dictionaries: {string:any}
|
|
||||||
end
|
|
||||||
|
|
||||||
local state_mt = {__index = State}
|
|
||||||
function State:new(): State
|
|
||||||
return setmetatable(
|
|
||||||
{
|
|
||||||
dataStacks = {},
|
|
||||||
compilerStack = Stack:new()
|
|
||||||
} as State,
|
|
||||||
state_mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
function State:addDataStack(data: Stack)
|
|
||||||
table.insert(self.dataStacks, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function State:changeCompilerStack(compilerStack: Stack)
|
|
||||||
self.compilerStack = compilerStack
|
|
||||||
end
|
|
||||||
function State:changeActiveDataStack(stackIndex: number)
|
|
||||||
assert(stackIndex <= #self.dataStacks and stackIndex > 0)
|
|
||||||
self.activeDataStack = self.dataStacks[stackIndex]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return State
|
|
14
WordInfo.tl
14
WordInfo.tl
|
@ -1,14 +0,0 @@
|
||||||
local State = require("State")
|
|
||||||
|
|
||||||
local type WordInfo = record
|
|
||||||
func: function(State)
|
|
||||||
immediate: boolean
|
|
||||||
end
|
|
||||||
local wordi_mt = {__index = WordInfo}
|
|
||||||
function WordInfo:new(funct: function(State), imm: boolean): WordInfo
|
|
||||||
return setmetatable({func = funct, immediate = imm} as WordInfo, wordi_mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
return WordInfo
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue