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