Add arithmetic tests for the Forth interpreter.

This commit is contained in:
Starfflame 2021-05-23 06:59:36 -05:00
parent 59b7c5a028
commit 09e42c69d5
8 changed files with 126 additions and 33 deletions

View File

@ -11,7 +11,7 @@ function CoreHelpers.defineWord(dict: Dictionary, str: string, func: function(En
end end
function CoreHelpers.standardInputRefill(): string function CoreHelpers.standardInputRefill(): string
local input = io.read().."\n\n" local input = io.read().."\n"
return input return input
end end
@ -73,7 +73,8 @@ end
function CoreHelpers.skipWhitespace(state: Environment) function CoreHelpers.skipWhitespace(state: Environment)
local chr = state.activeInputStream:curr() local chr = state.activeInputStream:curr()
while (CoreHelpers.isWhitespace(chr)) do while (CoreHelpers.isWhitespace(chr)) do
chr = state.activeInputStream:next() state.activeInputStream:next()
chr = state.activeInputStream:curr()
end end
end end
@ -85,11 +86,12 @@ function CoreHelpers.popTwoOperands(state: Environment): any, any
end end
function CoreHelpers.parseToken(state: Environment): string function CoreHelpers.parseToken(state: Environment): string | nil
local chr = "" local token: string | nil = ""
local token = "" local chr: string | nil = ""
while(not CoreHelpers.isWhitespace(chr)) do if not state.activeInputStream:curr() then return nil end
token = token..chr while(not CoreHelpers.isWhitespace(chr) and chr ~= nil) do
token = token..(chr as string)
chr = state.activeInputStream:next() chr = state.activeInputStream:next()
end end
return token return token

View File

@ -78,7 +78,7 @@ end
-- I/O operations -- I/O operations
local function dot(state: Environment) local function dot(state: Environment)
local out = state.activeDataStack:pop() local out = state.activeDataStack:pop()
print(out) io.write(out as string.."\n")
end end
local function twoDup(state: Environment) local function twoDup(state: Environment)
over(state) over(state)

View File

@ -43,8 +43,9 @@ local type DataStructures = record
addDataStack: function(Environment, Stack) addDataStack: function(Environment, Stack)
changeCompilerStack: function(Environment, Stack) changeCompilerStack: function(Environment, Stack)
changeActiveDataStack: function(Environment, number) changeActiveDataStack: function(Environment, number)
recognizers: {function(Environment)}
instructionPointer: Pointer instructionPointer: Pointer
running: boolean
end end
end end
@ -58,12 +59,17 @@ end
local state_mt = {__index = Environment} local state_mt = {__index = Environment}
function Environment:new(): Environment function Environment:new(input: InputStream, dict: {Dictionary} ): Environment
local dicts = dict or {}
return setmetatable( return setmetatable(
{ {
dataStacks = {}, activeDataStack= Stack:new(),
compilerStack = Stack:new(), compilerStack = Stack:new(),
dictionaries = {} returnStack = Stack:new(),
dictionaries = dicts,
activeInputStream = input,
running = true
} as Environment, } as Environment,
state_mt) state_mt)
end end

View File

@ -2,14 +2,19 @@ local istream = require("InputStream")
local helpers = require("CoreHelpers") local helpers = require("CoreHelpers")
local interpreters = require("Interpreters") local interpreters = require("Interpreters")
local standardInputRefill = helpers.standardInputRefill local standardInputRefill = helpers.standardInputRefill
local CoreWords = require("CoreWords")
local oneReadInputStream = helpers.oneReadInputStream local oneReadInputStream = helpers.oneReadInputStream
local fileStream = helpers.fileStream -- local fileStream = helpers.fileStream
local Environment = require("DataStructures").Environment
--interpreters.start(fileStream("codetest.txt")) --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))

View File

@ -20,7 +20,6 @@ function InputStream:_manageBuffer()
else else
length = -1 length = -1
end end
print("self.str = ",self.str, self.offset,length)
if not self.str then if not self.str then
self.str = self.refill() self.str = self.refill()
self.offset = 1 self.offset = 1
@ -55,7 +54,6 @@ end
-- setters/getters -- setters/getters
function InputStream:__setRefill(func: function(): string) function InputStream:__setRefill(func: function(): string)
self.refill = func self.refill = func
print("setrefill")
self:_manageBuffer() self:_manageBuffer()
end end
@ -71,7 +69,6 @@ function InputStream:curr(): string | nil
end end
function InputStream:next(): string | nil function InputStream:next(): string | nil
print("next")
if self.offset > #self.str then if self.offset > #self.str then
self:_manageBuffer() self:_manageBuffer()
end end

View File

@ -10,27 +10,26 @@ local type Interpreters = record
outer: function(Environment) outer: function(Environment)
end end
function Interpreters.start(input: istream) function Interpreters.start(env: Environment)
local machineEnvironment = Environment:new() table.insert(env.dictionaries, CoreWords)
machineEnvironment.activeInputStream = input
machineEnvironment.activeDataStack = Stack:new()
machineEnvironment.returnStack = Stack:new()
table.insert(machineEnvironment.dictionaries, CoreWords)
local interpreterInstructions = {} local interpreterInstructions = {}
table.insert(interpreterInstructions, Interpreters.outer) table.insert(interpreterInstructions, Interpreters.outer)
table.insert(interpreterInstructions, helpers.reset) table.insert(interpreterInstructions, helpers.reset)
local startPointer = Pointer:new() local startPointer = Pointer:new()
startPointer.referant = interpreterInstructions startPointer.referant = interpreterInstructions
machineEnvironment.instructionPointer = startPointer env.instructionPointer = startPointer
Interpreters.inner(machineEnvironment) Interpreters.inner(env)
end end
function Interpreters.outer(env: Environment) function Interpreters.outer(env: Environment)
skipWhitespace(env) 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 if isNumber(token) then
env.activeDataStack:push(tonumber(token)) env.activeDataStack:push(tonumber(token))
else else
@ -47,7 +46,7 @@ function Interpreters.outer(env: Environment)
end end
function Interpreters.inner(env: Environment) function Interpreters.inner(env: Environment)
while(true) do while(env.running) do
env.instructionPointer:deref() as function(Environment)(env) env.instructionPointer:deref() as function(Environment)(env)
env.instructionPointer:inc() env.instructionPointer:inc()
end end

View File

@ -4,4 +4,4 @@
10 DUP * . 10 DUP * .
78 1 - 5 * . 78 1 - 5 * .
11 1 - .

View File

@ -2,7 +2,8 @@ local istream = require("InputStream")
local ds = require("DataStructures") local ds = require("DataStructures")
local Stack, Dictionary, WordInfo, Environment = ds.Stack, ds.Dictionary, ds.WordInfo, ds.Environment local Stack, Dictionary, WordInfo, Environment = ds.Stack, ds.Dictionary, ds.WordInfo, ds.Environment
local CoreHelpers = require("CoreHelpers") local CoreHelpers = require("CoreHelpers")
local CoreWords = require("CoreWords")
local interpreters = require("Interpreters")
function pushThree(s) function pushThree(s)
s:push(1) s:push(1)
s:push(2) s:push(2)
@ -131,4 +132,87 @@ describe("Dictionary tests", function()
end) 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)