rename std words to be UPPER CASE (ugh)
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 1 Sep 2009 21:00:44 +0000 (21:00 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 1 Sep 2009 21:00:44 +0000 (21:00 +0000)
add initial unittests for std words (more to do)

git-svn-id: http://skia.googlecode.com/svn/trunk@347 2bbb7eff-a529-9590-31e7-b0007b416f81

forth/Forth.h
forth/ForthTests.cpp [new file with mode: 0644]
forth/SampleForth.cpp
forth/StdWords.cpp

index a68b219..0985c6a 100644 (file)
@@ -1,3 +1,4 @@
+
 #ifndef Forth_DEFINED
 #define Forth_DEFINED
 
diff --git a/forth/ForthTests.cpp b/forth/ForthTests.cpp
new file mode 100644 (file)
index 0000000..e918329
--- /dev/null
@@ -0,0 +1,188 @@
+#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 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 },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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();
+void Forth_test_stdwords() {
+    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 {
+            gRecs[i].fProc(word, &engine, &reporter);
+        }
+    }
+    
+    if (0 == reporter.fFailureCount) {
+        SkDebugf("--- success!\n");
+    } else {
+        SkDebugf("--- %d failures\n", reporter.fFailureCount);
+    }
+}
+
index 3de2fad..2877fb6 100644 (file)
@@ -150,6 +150,8 @@ static void load_words(ForthEnv* env, SkForthCtx* ctx) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
+void Forth_test_stdwords();
+
 class ForthView : public SkView {
     ForthEnv    fEnv;
     ForthWord*  fOnClickWord;
@@ -158,6 +160,8 @@ class ForthView : public SkView {
     SkForthCtx  fContext;
 public:
        ForthView() {
+        Forth_test_stdwords();
+
         load_words(&fEnv, &fContext);
 
         fBM.setConfig(SkBitmap::kARGB_8888_Config, 640, 480);
@@ -165,7 +169,7 @@ public:
         fBM.eraseColor(0);
         fContext.init(fBM);
 
-        fEnv.parse(": mycolor ( x. y. -- x. y. ) rot rot f< IF #FFFF0000 ELSE #FF0000FF THEN setColor ;");
+        fEnv.parse(": mycolor ( x. y. -- x. y. ) OVER OVER f< IF #FFFF0000 ELSE #FF0000FF THEN setColor ;");
         fEnv.parse(": view.onClick ( x. y. -- ) mycolor 10. drawCircle ;");
         fOnClickWord = fEnv.findWord("view.onClick");
 #if 0
index 4216e61..98e39c0 100644 (file)
 #include "ForthParser.h"
 #include "SkString.h"
 
-class drop_ForthWord : public ForthWord {
-public:
-    virtual void exec(ForthEngine* fe) {
-        (void)fe->pop();
-    }
-};
-
-class clearStack_ForthWord : public ForthWord {
-public:
-    virtual void exec(ForthEngine* fe) {
-        fe->clearStack();
-    }
-};
+#define BEGIN_WORD(name)   \
+    class name##_ForthWord : public ForthWord { \
+    public:                                     \
+        virtual void exec(ForthEngine* fe)
 
-class dup_ForthWord : public ForthWord {
-public:
-    virtual void exec(ForthEngine* fe) {
-        fe->push(fe->top());
-    }
-};
+#define END_WORD };
 
-class swap_ForthWord : public ForthWord {
-public:
-    virtual void exec(ForthEngine* fe) {
-        int32_t a = fe->pop();
-        int32_t b = fe->top();
-        fe->setTop(a);
-        fe->push(b);
-    }
-};
+///////////////////////////////////////////////////////////////////////////////
 
-class rot_ForthWord : public ForthWord {
-public:
-    virtual void exec(ForthEngine* fe) {
-        fe->push(fe->peek(1));
-    }
-};
+BEGIN_WORD(drop) {
+    (void)fe->pop();
+} END_WORD
+
+BEGIN_WORD(over) {
+    fe->push(fe->peek(1));
+} END_WORD
+
+BEGIN_WORD(dup) {
+    fe->push(fe->top());
+} END_WORD
+
+BEGIN_WORD(swap) {
+    intptr_t a = fe->pop();
+    intptr_t b = fe->top();
+    fe->setTop(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(rot) {
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(b);
+    fe->push(c);
+    fe->push(a);
+} END_WORD
+
+BEGIN_WORD(rrot) {
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(c);
+    fe->push(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(swap2) {
+    intptr_t d = fe->pop();
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(c);
+    fe->push(d);
+    fe->push(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(dup2) {
+    fe->push(fe->peek(1));
+    fe->push(fe->peek(1));
+} END_WORD
+
+BEGIN_WORD(over2) {
+    fe->push(fe->peek(3));
+    fe->push(fe->peek(3));
+} END_WORD
+
+BEGIN_WORD(drop2) {
+    (void)fe->pop();
+    (void)fe->pop();
+} END_WORD
+
+///////////////// logicals
+
+BEGIN_WORD(logical_and) {
+    fe->push(-(fe->pop() && fe->pop()));
+} END_WORD
+
+BEGIN_WORD(logical_or) {
+    fe->push(-(fe->pop() || fe->pop()));
+} END_WORD
+
+BEGIN_WORD(logical_not) {
+    fe->setTop(-(!fe->top()));
+} END_WORD
+
+BEGIN_WORD(if_dup) {
+    intptr_t tmp = fe->top();
+    if (tmp) {
+        fe->push(tmp);
+    }
+} END_WORD
 
 ///////////////// ints
 
-class add_ForthWord : public ForthWord {
-public:
+class add_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
         fe->setTop(fe->top() + tmp);
-    }
-};
+    }};
 
-class sub_ForthWord : public ForthWord {
-public:
+class sub_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
         fe->setTop(fe->top() - tmp);
-    }
-};
+    }};
 
-class mul_ForthWord : public ForthWord {
-public:
+class mul_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
         fe->setTop(fe->top() * tmp);
-    }
-};
+    }};
 
-class div_ForthWord : public ForthWord {
-public:
+class div_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
         fe->setTop(fe->top() / tmp);
-    }
-};
+    }};
 
-class dot_ForthWord : public ForthWord {
-public:
+class mod_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() % tmp);
+    }};
+
+class divmod_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t denom = fe->pop();
+        intptr_t numer = fe->pop();
+        fe->push(numer % denom);
+        fe->push(numer / denom);
+    }};
+
+class dot_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         SkString str;
         str.printf("%d ", fe->pop());
         fe->sendOutput(str.c_str());
-    }
-};
+    }};
 
-class abs_ForthWord : public ForthWord {
-public:
+class abs_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         int32_t value = fe->top();
         if (value < 0) {
             fe->setTop(-value);
         }
-    }
-};
+    }};
 
-class min_ForthWord : public ForthWord {
-public:
+class negate_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->setTop(-fe->top());
+    }};
+
+class min_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         int32_t value = fe->pop();
         if (value < fe->top()) {
             fe->setTop(value);
         }
-    }
-};
+    }};
 
 class max_ForthWord : public ForthWord {
 public:
@@ -221,106 +281,143 @@ public:
     }
 };
 
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////// int compares
 
 class eq_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
-        fe->push(fe->pop() == fe->pop());
+        fe->push(-(fe->pop() == fe->pop()));
     }
 };
 
 class neq_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
-        fe->push(fe->pop() != fe->pop());
+        fe->push(-(fe->pop() != fe->pop()));
     }
 };
 
 class lt_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
-        fe->setTop(fe->top() < tmp);
+        fe->setTop(-(fe->top() < tmp));
     }
 };
 
 class le_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
-        fe->setTop(fe->top() <= tmp);
+        fe->setTop(-(fe->top() <= tmp));
     }
 };
 
 class gt_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
-        fe->setTop(fe->top() > tmp);
+        fe->setTop(-(fe->top() > tmp));
     }
 };
 
 class ge_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         intptr_t tmp = fe->pop();
-        fe->setTop(fe->top() >= tmp);
+        fe->setTop(-(fe->top() >= tmp));
     }
 };
 
+BEGIN_WORD(lt0) {
+    fe->setTop(fe->top() >> 31);
+} END_WORD
+
+BEGIN_WORD(ge0) {
+    fe->setTop(~(fe->top() >> 31));
+} END_WORD
+
+BEGIN_WORD(gt0) {
+    fe->setTop(-(fe->top() > 0));
+} END_WORD
+
+BEGIN_WORD(le0) {
+    fe->setTop(-(fe->top() <= 0));
+} END_WORD
+
+/////////////////////////////// float compares
+
+/*  negative zero is our nemesis, otherwise we could use = and <> from ints */
+
 class feq_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
-        fe->push(fe->fpop() == fe->fpop());
+        fe->push(-(fe->fpop() == fe->fpop()));
     }
 };
 
 class fneq_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
-        fe->push(fe->fpop() != fe->fpop());
+        fe->push(-(fe->fpop() != fe->fpop()));
     }
 };
 
 class flt_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         float tmp = fe->fpop();
-        fe->setTop(fe->ftop() < tmp);
+        fe->setTop(-(fe->ftop() < tmp));
     }
 };
 
 class fle_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         float tmp = fe->fpop();
-        fe->setTop(fe->ftop() <= tmp);
+        fe->setTop(-(fe->ftop() <= tmp));
     }
 };
 
 class fgt_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         float tmp = fe->fpop();
-        fe->setTop(fe->ftop() > tmp);
+        fe->setTop(-(fe->ftop() > tmp));
     }
 };
 
 class fge_ForthWord : public ForthWord { public:
     virtual void exec(ForthEngine* fe) {
         float tmp = fe->fpop();
-        fe->setTop(fe->ftop() >= tmp);
+        fe->setTop(-(fe->ftop() >= tmp));
     }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#define ADD_LITERAL_WORD(sym, name) \
+    this->add(sym, sizeof(sym)-1, new name##_ForthWord)
+
 void ForthParser::addStdWords() {
-    this->add("clr", 3, new clearStack_ForthWord);
-    this->add("drop", 4, new drop_ForthWord);
-    this->add("dup", 3, new dup_ForthWord);
-    this->add("swap", 4, new swap_ForthWord);
-    this->add("rot", 3, new rot_ForthWord);
+    ADD_LITERAL_WORD("DROP", drop);
+    ADD_LITERAL_WORD("DUP", dup);
+    ADD_LITERAL_WORD("SWAP", swap);
+    ADD_LITERAL_WORD("OVER", over);
+    ADD_LITERAL_WORD("ROT", rot);
+    ADD_LITERAL_WORD("-ROT", rrot);
+    ADD_LITERAL_WORD("2SWAP", swap2);
+    ADD_LITERAL_WORD("2DUP", dup2);
+    ADD_LITERAL_WORD("2OVER", over2);
+    ADD_LITERAL_WORD("2DROP", drop2);
     
     this->add("+", 1, new add_ForthWord);
     this->add("-", 1, new sub_ForthWord);
     this->add("*", 1, new mul_ForthWord);
     this->add("/", 1, new div_ForthWord);
+    this->add("MOD", 1, new mod_ForthWord);
+    this->add("/MOD", 1, new divmod_ForthWord);
+
     this->add(".", 1, new dot_ForthWord);
-    this->add("abs", 3, new abs_ForthWord);
-    this->add("min", 3, new min_ForthWord);
-    this->add("max", 3, new max_ForthWord);
-    
+    this->add("ABS", 3, new abs_ForthWord);
+    this->add("NEGATE", 3, new negate_ForthWord);
+    this->add("MIN", 3, new min_ForthWord);
+    this->add("MAX", 3, new max_ForthWord);
+
+    ADD_LITERAL_WORD("AND", logical_and);
+    ADD_LITERAL_WORD("OR", logical_or);
+    ADD_LITERAL_WORD("0=", logical_not);
+    ADD_LITERAL_WORD("?DUP", if_dup);
+
     this->add("f+", 2, new fadd_ForthWord);
     this->add("f-", 2, new fsub_ForthWord);
     this->add("f*", 2, new fmul_ForthWord);
@@ -342,7 +439,11 @@ void ForthParser::addStdWords() {
     this->add("<=", 2, new le_ForthWord);
     this->add(">", 1, new gt_ForthWord);
     this->add(">=", 2, new ge_ForthWord);
-
+    ADD_LITERAL_WORD("0<", lt0);
+    ADD_LITERAL_WORD("0>", gt0);
+    ADD_LITERAL_WORD("0<=", le0);
+    ADD_LITERAL_WORD("0>=", ge0);
+    
     this->add("f=", 2, new feq_ForthWord);
     this->add("f<>", 3, new fneq_ForthWord);
     this->add("f<", 2, new flt_ForthWord);