diff --git a/CoreHelpers.tl b/CoreHelpers.tl index 0d11f44..33022b7 100644 --- a/CoreHelpers.tl +++ b/CoreHelpers.tl @@ -11,7 +11,7 @@ function CoreHelpers.defineWord(dict: Dictionary, str: string, func: function(En end function CoreHelpers.standardInputRefill(): string - local input = io.read().."\n\n" + local input = io.read().."\n" return input end @@ -73,7 +73,8 @@ end function CoreHelpers.skipWhitespace(state: Environment) local chr = state.activeInputStream:curr() while (CoreHelpers.isWhitespace(chr)) do - chr = state.activeInputStream:next() + state.activeInputStream:next() + chr = state.activeInputStream:curr() end end @@ -85,11 +86,12 @@ function CoreHelpers.popTwoOperands(state: Environment): any, any end -function CoreHelpers.parseToken(state: Environment): string - local chr = "" - local token = "" - while(not CoreHelpers.isWhitespace(chr)) do - token = token..chr +function CoreHelpers.parseToken(state: Environment): string | nil + local token: string | nil = "" + local chr: string | nil = "" + if not state.activeInputStream:curr() then return nil end + while(not CoreHelpers.isWhitespace(chr) and chr ~= nil) do + token = token..(chr as string) chr = state.activeInputStream:next() end return token diff --git a/CoreWords.tl b/CoreWords.tl index c07410d..02fda28 100644 --- a/CoreWords.tl +++ b/CoreWords.tl @@ -78,7 +78,7 @@ end -- I/O operations local function dot(state: Environment) local out = state.activeDataStack:pop() - print(out) + io.write(out as string.."\n") end local function twoDup(state: Environment) over(state) diff --git a/DataStructures.tl b/DataStructures.tl index 441795c..c206643 100644 --- a/DataStructures.tl +++ b/DataStructures.tl @@ -43,10 +43,11 @@ local type DataStructures = record addDataStack: function(Environment, Stack) changeCompilerStack: function(Environment, Stack) changeActiveDataStack: function(Environment, number) - + recognizers: {function(Environment)} instructionPointer: Pointer + running: boolean end - + end local Stack, Environment, Dictionary, WordInfo = DataStructures.Stack, DataStructures.Environment, DataStructures.Dictionary, DataStructures.WordInfo @@ -58,12 +59,17 @@ end local state_mt = {__index = Environment} -function Environment:new(): Environment +function Environment:new(input: InputStream, dict: {Dictionary} ): Environment + local dicts = dict or {} + return setmetatable( { - dataStacks = {}, + activeDataStack= Stack:new(), compilerStack = Stack:new(), - dictionaries = {} + returnStack = Stack:new(), + dictionaries = dicts, + activeInputStream = input, + running = true } as Environment, state_mt) end @@ -79,7 +85,7 @@ end -- assert(stackIndex <= #self.dataStacks and stackIndex > 0) -- self.activeDataStack = self.dataStacks[stackIndex] --end - + diff --git a/ForthMachine.tl b/ForthMachine.tl index c1e7b6d..23c9f33 100644 --- a/ForthMachine.tl +++ b/ForthMachine.tl @@ -2,14 +2,19 @@ local istream = require("InputStream") local helpers = require("CoreHelpers") local interpreters = require("Interpreters") local standardInputRefill = helpers.standardInputRefill +local CoreWords = require("CoreWords") local oneReadInputStream = helpers.oneReadInputStream -local fileStream = helpers.fileStream - +-- local fileStream = helpers.fileStream +local Environment = require("DataStructures").Environment --interpreters.start(fileStream("codetest.txt")) +local core_dicts = {CoreWords} +--local env = Environment:new(istream:new(standardInputRefill), core_dicts) +local env = Environment:new(oneReadInputStream("14 11 + ."), core_dicts) + +interpreters.start(env) -interpreters.start(istream:new(standardInputRefill)) diff --git a/InputStream.tl b/InputStream.tl index eb260a3..28a1a73 100644 --- a/InputStream.tl +++ b/InputStream.tl @@ -20,7 +20,6 @@ function InputStream:_manageBuffer() else length = -1 end - print("self.str = ",self.str, self.offset,length) if not self.str then self.str = self.refill() self.offset = 1 @@ -55,7 +54,6 @@ end -- setters/getters function InputStream:__setRefill(func: function(): string) self.refill = func - print("setrefill") self:_manageBuffer() end @@ -71,7 +69,6 @@ function InputStream:curr(): string | nil end function InputStream:next(): string | nil - print("next") if self.offset > #self.str then self:_manageBuffer() end diff --git a/Interpreters.tl b/Interpreters.tl index 1e1a67e..ea35633 100644 --- a/Interpreters.tl +++ b/Interpreters.tl @@ -10,27 +10,26 @@ local type Interpreters = record outer: function(Environment) end -function Interpreters.start(input: istream) - local machineEnvironment = Environment:new() - machineEnvironment.activeInputStream = input - machineEnvironment.activeDataStack = Stack:new() - machineEnvironment.returnStack = Stack:new() - - table.insert(machineEnvironment.dictionaries, CoreWords) +function Interpreters.start(env: Environment) + table.insert(env.dictionaries, CoreWords) local interpreterInstructions = {} table.insert(interpreterInstructions, Interpreters.outer) table.insert(interpreterInstructions, helpers.reset) local startPointer = Pointer:new() startPointer.referant = interpreterInstructions - machineEnvironment.instructionPointer = startPointer - Interpreters.inner(machineEnvironment) + env.instructionPointer = startPointer + Interpreters.inner(env) end function Interpreters.outer(env: Environment) skipWhitespace(env) - local token: string = parseToken(env) + local token: string | nil = parseToken(env) + if not token then + env.running = false + return + end if isNumber(token) then env.activeDataStack:push(tonumber(token)) else @@ -47,7 +46,7 @@ function Interpreters.outer(env: Environment) end function Interpreters.inner(env: Environment) - while(true) do + while(env.running) do env.instructionPointer:deref() as function(Environment)(env) env.instructionPointer:inc() end diff --git a/codetest.txt b/codetest.txt index 7faba6f..3efac07 100644 --- a/codetest.txt +++ b/codetest.txt @@ -4,4 +4,4 @@ 10 DUP * . 78 1 - 5 * . - +11 1 - . diff --git a/tests.lua b/tests.lua index 8cc505b..f23b4b6 100644 --- a/tests.lua +++ b/tests.lua @@ -2,7 +2,8 @@ local istream = require("InputStream") local ds = require("DataStructures") local Stack, Dictionary, WordInfo, Environment = ds.Stack, ds.Dictionary, ds.WordInfo, ds.Environment local CoreHelpers = require("CoreHelpers") - +local CoreWords = require("CoreWords") +local interpreters = require("Interpreters") function pushThree(s) s:push(1) s:push(2) @@ -131,4 +132,87 @@ describe("Dictionary tests", function() end) +function buildEnvironment(testString) + local testEnv = Environment:new(CoreHelpers.oneReadInputStream(testString), {CoreWords}) + return testEnv +end + +local twoRandom = function() return math.random(100000), math.random(100000) end + + +describe("Interpreter tests", function() + describe("Arithmetic tests", function() + it("can add two numbers, and leave the sum on the stack", function() + for i=1, 1000 do + local a, b = twoRandom() + local expected_sum = a + b + local testString = a.." "..b.." +" + local testEnv = buildEnvironment(testString) + interpreters.start(testEnv) + assert.are.same(testEnv.activeDataStack.contents[1], expected_sum) + end + end) + it("can subtract two numbers, and leave the difference on the stack", function() + for i=1, 1000 do + local a, b = twoRandom() + local expected_diff = a -b + local testString = a.." "..b.." -" + local testEnv = buildEnvironment(testString) + interpreters.start(testEnv) + assert.are.same(testEnv.activeDataStack.contents[1], expected_diff) + end + end) + it("can multiply two numbers, leaving product on the stack", function() + + for i = 1, 1000 do + local a, b = twoRandom() + local expected_prod = a*b + local testString = a.." "..b.." *" + local testEnv = buildEnvironment(testString) + interpreters.start(testEnv) + assert.are.same(testEnv.activeDataStack.contents[1], expected_prod) + end + end) + it("can divide two numbers, leaving the quotient on the stack", function() + + for i = 1, 1000 do + local a, b = twoRandom() + local expected_quo = a/b + local testString = a.." "..b.." /" + local testEnv = buildEnvironment(testString) + interpreters.start(testEnv) + assert.are.same(testEnv.activeDataStack.contents[1], expected_quo) + end + end) + it("can evaluate a arithmetic expression with multiple operations", function() + for i = 1, 1000 do + operands = {math.random(100000), math.random(100000), math.random(100000), math.random(100000)} + local operations = {math.random(4), math.random(4), math.random(4)} + local testString = ""..operands[1] + local expectedResult = operands[1] + for j, op in ipairs(operations) do + if j == 1 then + testString = testString.." "..operands[j + 1].." +" + expectedResult = expectedResult + operands[j+1] + elseif j == 2 then + testString = testString.." "..operands[j + 1].." -" + expectedResult = expectedResult - operands[j+1] + elseif j == 3 then + testString = testString.." "..operands[j + 1].." *" + expectedResult = expectedResult * operands[j+1] + else + testString = testString.." "..operands[j + 1].." /" + expectedResult = expectedResult / operands[j+1] + end + end + local testEnv = buildEnvironment(testString) + interpreters.start(testEnv) + assert.are.same(expectedResult,testEnv.activeDataStack.contents[1]) + end + end) + end) +end) + + +