/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Forth.h"
#include "SkString.h"
class Reporter {
public:
int fFailureCount;
Reporter() : fFailureCount(0) {}
void reportFailure(const char expression[], const char file[], int line);
void reportFailure(const char msg[]);
};
typedef void (*ForthWordTestProc)(ForthWord*, ForthEngine*, Reporter*);
#define FORTH_ASSERT(reporter, expression) \
do { \
if (!(expression)) { \
reporter->reportFailure(#expression, __FILE__, __LINE__); \
} \
} while (0)
static void drop_test0(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(-17);
word->exec(fe);
FORTH_ASSERT(reporter, 0 == fe->depth());
}
static void drop_test1(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(-17);
fe->push(93);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -17 == fe->peek(0));
}
static void dup_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(-17);
word->exec(fe);
FORTH_ASSERT(reporter, 2 == fe->depth());
FORTH_ASSERT(reporter, -17 == fe->peek(0));
FORTH_ASSERT(reporter, -17 == fe->peek(1));
}
static void swap_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(-17);
fe->push(42);
word->exec(fe);
FORTH_ASSERT(reporter, 2 == fe->depth());
FORTH_ASSERT(reporter, -17 == fe->peek(0));
FORTH_ASSERT(reporter, 42 == fe->peek(1));
}
static void over_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
word->exec(fe);
FORTH_ASSERT(reporter, 3 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->peek(0));
FORTH_ASSERT(reporter, 2 == fe->peek(1));
FORTH_ASSERT(reporter, 1 == fe->peek(2));
}
static void rot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 3 == fe->depth());
FORTH_ASSERT(reporter, 2 == fe->peek(2));
FORTH_ASSERT(reporter, 3 == fe->peek(1));
FORTH_ASSERT(reporter, 1 == fe->peek(0));
}
static void rrot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 3 == fe->depth());
FORTH_ASSERT(reporter, 2 == fe->peek(0));
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 3 == fe->peek(2));
}
static void swap2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
fe->push(3);
fe->push(4);
word->exec(fe);
FORTH_ASSERT(reporter, 4 == fe->depth());
FORTH_ASSERT(reporter, 2 == fe->peek(0));
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 4 == fe->peek(2));
FORTH_ASSERT(reporter, 3 == fe->peek(3));
}
static void dup2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
word->exec(fe);
FORTH_ASSERT(reporter, 4 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->peek(3));
FORTH_ASSERT(reporter, 2 == fe->peek(2));
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 2 == fe->peek(0));
}
static void over2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
fe->push(3);
fe->push(4);
word->exec(fe);
FORTH_ASSERT(reporter, 6 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->peek(5));
FORTH_ASSERT(reporter, 2 == fe->peek(4));
FORTH_ASSERT(reporter, 3 == fe->peek(3));
FORTH_ASSERT(reporter, 4 == fe->peek(2));
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 2 == fe->peek(0));
}
static void drop2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
fe->push(3);
fe->push(4);
word->exec(fe);
FORTH_ASSERT(reporter, 2 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 2 == fe->peek(0));
}
//////////////////////////////////////////////////////////////////////////////
static void iadd_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(35);
fe->push(99);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 134 == fe->top());
fe->push(-135);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -1 == fe->top());
}
static void isub_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(35);
fe->push(99);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 35-99 == fe->top());
}
static void imul_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(15);
fe->push(-20);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -300 == fe->top());
fe->push(0);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 0 == fe->top());
}
static void idiv_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(100);
fe->push(25);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 4 == fe->top());
fe->setTop(10);
fe->push(-3);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -3 == fe->top());
fe->setTop(-1);
fe->push(-1);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->top());
}
static void imod_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->top());
fe->push(5);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->top());
}
static void idivmod_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 2 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->peek(1));
FORTH_ASSERT(reporter, 3 == fe->peek(0));
}
static void idot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(1);
fe->push(2);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 1 == fe->top());
}
static void iabs_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->top());
fe->setTop(-10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->top());
}
static void inegate_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -10 == fe->top());
fe->setTop(-10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->top());
}
static void imin_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 3 == fe->top());
fe->push(-10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, -10 == fe->top());
}
static void imax_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
fe->push(3);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->top());
fe->push(-10);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->top());
}
///////////////////////////////////////////////////////////////////////////////
static void logical_and_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
const static int data[] = {
0, 0, 0,
2, 0, 0,
0, -1, 0,
1, 5, -1
};
for (size_t i = 0; i < SK_ARRAY_COUNT(data)/3; i++) {
fe->push(data[i*3 + 0]);
fe->push(data[i*3 + 1]);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, data[i*3 + 2] == fe->top());
fe->pop();
}
}
static void logical_or_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
const static int data[] = {
0, 0, 0,
2, 0, -1,
0, -1, -1,
1, 5, -1
};
for (size_t i = 0; i < SK_ARRAY_COUNT(data)/3; i++) {
fe->push(data[i*3 + 0]);
fe->push(data[i*3 + 1]);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, data[i*3 + 2] == fe->top());
fe->pop();
}
}
static void logical_not_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
const static int data[] = {
0, -1,
5, 0,
-1, 0
};
for (size_t i = 0; i < SK_ARRAY_COUNT(data)/2; i++) {
fe->push(data[i*2 + 0]);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, data[i*2 + 1] == fe->top());
fe->pop();
}
}
static void if_dup_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
fe->push(10);
word->exec(fe);
FORTH_ASSERT(reporter, 2 == fe->depth());
FORTH_ASSERT(reporter, 10 == fe->peek(1));
FORTH_ASSERT(reporter, 10 == fe->peek(0));
fe->pop();
fe->pop();
fe->push(0);
word->exec(fe);
FORTH_ASSERT(reporter, 1 == fe->depth());
FORTH_ASSERT(reporter, 0 == fe->top());
}
static const struct {
const char* fName;
ForthWordTestProc fProc;
} gRecs[] = {
{ "DROP", drop_test0 }, { "DROP", drop_test1 },
{ "DUP", dup_test },
{ "SWAP", swap_test },
{ "OVER", over_test },
{ "ROT", rot_test },
{ "-ROT", rrot_test },
{ "2SWAP", swap2_test },
{ "2DUP", dup2_test },
{ "2OVER", over2_test },
{ "2DROP", drop2_test },
{ "+", iadd_test },
{ "-", isub_test },
{ "*", imul_test },
{ "/", idiv_test },
{ "MOD", imod_test },
{ "/MOD", idivmod_test },
// { ".", idot_test },
{ "ABS", iabs_test },
{ "NEGATE", inegate_test },
{ "MIN", imin_test },
{ "MAX", imax_test },
{ "AND", logical_and_test },
{ "OR", logical_or_test },
{ "0=", logical_not_test },
{ "?DUP", if_dup_test },
};
///////////////////////////////////////////////////////////////////////////////
void Reporter::reportFailure(const char expression[], const char file[],
int line) {
SkDebugf("failed %s:%d: %s\n", file, line, expression);
fFailureCount += 1;
}
void Reporter::reportFailure(const char msg[]) {
SkDebugf("%s\n");
fFailureCount += 1;
}
void Forth_test_stdwords(bool verbose);
void Forth_test_stdwords(bool verbose) {
ForthEnv env;
Reporter reporter;
for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
ForthEngine engine(NULL);
ForthWord* word = env.findWord(gRecs[i].fName);
if (NULL == word) {
SkString str;
str.printf("--- can't find stdword %d", gRecs[i].fName);
reporter.reportFailure(str.c_str());
} else {
if (verbose) {
SkDebugf("--- testing %s %p\n", gRecs[i].fName, word);
}
gRecs[i].fProc(word, &engine, &reporter);
}
}
if (0 == reporter.fFailureCount) {
SkDebugf("--- success!\n");
} else {
SkDebugf("--- %d failures\n", reporter.fFailureCount);
}
}