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
|
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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -43,10 +43,11 @@ 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
|
||||||
|
|
||||||
local Stack, Environment, Dictionary, WordInfo = DataStructures.Stack, DataStructures.Environment, DataStructures.Dictionary, DataStructures.WordInfo
|
local Stack, Environment, Dictionary, WordInfo = DataStructures.Stack, DataStructures.Environment, DataStructures.Dictionary, DataStructures.WordInfo
|
||||||
|
@ -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
|
||||||
|
@ -79,7 +85,7 @@ end
|
||||||
-- assert(stackIndex <= #self.dataStacks and stackIndex > 0)
|
-- assert(stackIndex <= #self.dataStacks and stackIndex > 0)
|
||||||
-- self.activeDataStack = self.dataStacks[stackIndex]
|
-- self.activeDataStack = self.dataStacks[stackIndex]
|
||||||
--end
|
--end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
10 DUP * .
|
10 DUP * .
|
||||||
78 1 - 5 * .
|
78 1 - 5 * .
|
||||||
|
|
||||||
|
11 1 - .
|
||||||
|
|
86
tests.lua
86
tests.lua
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue