--- /dev/null
+#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);
+ }
+}
+
#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:
}
};
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////// 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);
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);