It appears that you have JavaScript disabled. Click here to find out what you're missing on this site.

BitWorking Theories on software development

by Joe Gregorio

And So Forth

Theory can leave questions unanswered, but practice has to come up with something.
-- Mason Cooley

The goal is to write a simple implementation of Forth. One that can handle simple mathematical expressions. Once we have that down then we will add functionality as needed for our application. Extreme Programming encourages writing the test case first. I am creating a class Forth that can parse statements and manipulate a stack. So I would like to be able to write code:

	Forth f;
	f.execute("1 2 +");
Create an instance of the Forth class and then use it to execute a Forth statement. I should be able to test the size of the stack and be able to test if the element on the top of the stack is equal to 3. If UnitTest is a class for tracking unit tests and has a member function score() that keeps track of the test results than our first test case should look like:
int main() {
        UnitTest test;
	Forth f;
	f.execute("1 2 +");
	f.stackSize() == 1; 
	test.score(f.stackSize() == 1, "StackSize");
	test.score(f.top() == 3, "Addition");

test.results(cout); return 0; }

Compiling this code fails, as expected, because we lack the UnitTest class, and the Forth class. We are also missing cout.

Here is a first pass at the class Forth.

class Forth {
  private:
	map dictionary_;
	deque<int> stack_;
  public:
	Forth();
	Forth(const Forth & rhs);
	~Forth();
	operator=(const Forth & rhs);
	int top();
	int stackSize();
	execute(const string & command);
};
Note that top() and stackSize() are functions that are already implemented in deque<int>. So if we go forward with this implementation top() and stackSize() will be just wrappers around deque<int>. That is a waste of time for now so move stack_ into the public space. This gives us:
class Forth {
  private:
	map dictionary_;
  public:
	deque stack_;
	Forth();
	Forth(const Forth & rhs);
	~Forth();
	operator=(const Forth & rhs);
	execute(const string & command);
};

Now our code still needs a UnitTest class. The test cases indicate only two functions, score() and results().

class UnitTest {
  private:
	map testResults_;
  public:
	UnitTest();
	UnitTest(const UnitTest & rhs);
	~UnitTest();
	operator=(const UnitTest & rhs);
	void score(bool result, const string & unitTestName);
	void results(ostream & o);
};

The member testResults_ will hold all the test results, either pass or fail. The results() member function will be responsible for tabulating the scores.

Because we are exporting stack_ we need to change our initial test case to use the member functions of stack_:

int main() {
    UnitTest test;
	Forth f;
	f.execute("1 2 +");
	test.score(f.stack_.size() == 1, "StackSize");
	test.score(f.stack_.front() == 3, "Addition");
	test.results(cout);

return 0; }

Filling in the definitions of our member functions we get:

#include <iostream>
#include <map>
#include <deque>
#include <string.h>
using namespace std;

class UnitTest { private: map testResults_; public: UnitTest() {} UnitTest(const UnitTest & rhs) { testResults_ = rhs.testResults_; } ~UnitTest() {} UnitTest & operator=(UnitTest & rhs) { testResults_ = rhs.testResults_; return *this; }

void score(bool result, const string & unitTestName) { testResults_[unitTestName] = result; } void results(ostream & o) { map<string, bool>::iterator i; int totalTests = 0; int totalPassed = 0; for(i = testResults_.begin(); i != testResults_.end(); i++) { o << (*i).first << " " << ((*i).second ? "Passed" : "Failed") << endl; totalTests++; totalPassed += ((*i).second ? 1 : 0); } o << endl; o << "Total Test Cases: " << totalTests << endl; o << "Total Test Cases Passed: " << totalPassed << endl; if (totalTests != totalPassed) { o << "100% of the test cases must pass before release!" << endl; } } }; class Forth { public: deque stack_;

Forth() {} Forth(const Forth & rhs) { stack_ = rhs.stack_; } ~Forth() {} Forth & operator=(Forth & rhs) { stack_ = rhs.stack_; return *this; }

bool execute(const string & command) { bool returnValue = true; char * s = strdup(command.c_str()); char * token = strtok(s, " "); while (token) { if (string("+") == token) { int num1 = stack_.front(); stack_.pop_front(); int num2 = stack_.front(); stack_.pop_front(); stack_.push_front(num1 + num2); } else { stack_.push_front(atoi(token)); } token = strtok(NULL, " "); } return returnValue; } };

int main() { UnitTest test; Forth f; f.execute("1 2 +"); test.score(f.stack_.size() == 1, "StackSize"); test.score(f.stack_.front() == 3, "Addition");

test.results(cout); return 0; }

Which passes our tests and produces the output:


Addition   Passed
StackSize   Passed

Total Test Cases: 2 Total Test Cases Passed: 2

Inspecting the code reveals that execute() is defined too narrowly. To add new functions such as - and * to our parser would require adding new if test cases, which is very sloppy. Also, the test cases we have written are pretty scant, the copy contructor and assignment operator for the class Forth are untested as of now. The Forth and UnitTest classes should be broken out into their own modules. Wow, there's a lot of work to do...

The source code for the article can be found here.

Postscript

The missing piece for this project is the Jamfile:

Main forth : forth.cpp : ;