|  |  | 
|  | /* | 
|  | * 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); | 
|  | } | 
|  | } |