Add arithmetic tests for the Forth interpreter.
This commit is contained in:
parent
59b7c5a028
commit
09e42c69d5
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
10 DUP * .
|
||||
78 1 - 5 * .
|
||||
|
||||
|
||||
11 1 - .
|
||||
|
|
86
tests.lua
86
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue