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