experimental hack to write a forth engine to drive skia
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Sat, 29 Aug 2009 21:30:25 +0000 (21:30 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Sat, 29 Aug 2009 21:30:25 +0000 (21:30 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@343 2bbb7eff-a529-9590-31e7-b0007b416f81

forth/Forth.cpp [new file with mode: 0644]
forth/Forth.h [new file with mode: 0644]
forth/SampleForth.cpp [new file with mode: 0644]
xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj

diff --git a/forth/Forth.cpp b/forth/Forth.cpp
new file mode 100644 (file)
index 0000000..722ed08
--- /dev/null
@@ -0,0 +1,682 @@
+#include "Forth.h"
+#include "SkTDArray.h"
+#include "SkTDict.h"
+#include "SkString.h"
+
+ForthEngine::ForthEngine(ForthOutput* output) : fOutput(output) {
+    size_t size = 32 * sizeof(intptr_t);
+    fStackBase = reinterpret_cast<intptr_t*>(sk_malloc_throw(size));
+    fStackStop = fStackBase + size/sizeof(intptr_t);
+    fStackCurr = fStackStop;
+}
+
+ForthEngine::~ForthEngine() {
+    sk_free(fStackBase);
+}
+
+void ForthEngine::sendOutput(const char text[]) {
+    if (fOutput) {
+        fOutput->show(text);
+    } else {
+        SkDebugf("%s", text);
+    }
+}
+
+///////////////// ints
+
+void ForthEngine::push(intptr_t value) {
+    if (fStackCurr > fStackBase) {
+        SkASSERT(fStackCurr <= fStackStop && fStackCurr > fStackBase);
+        *--fStackCurr = value;
+    } else {
+        this->signal_error("overflow");
+    }
+}
+
+intptr_t ForthEngine::peek(size_t index) const {
+    SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+    if (fStackCurr + index < fStackStop) {
+        return fStackCurr[index];
+    } else {
+        this->signal_error("peek out of range");
+        return 0x80000001;
+    }
+}
+
+void ForthEngine::setTop(intptr_t value) {
+    if (fStackCurr < fStackStop) {
+        SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+        *fStackCurr = value;
+    } else {
+        this->signal_error("underflow");
+    }
+}
+
+intptr_t ForthEngine::pop() {
+    if (fStackCurr < fStackStop) {
+        SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+        return *fStackCurr++;
+    } else {
+        this->signal_error("underflow");
+        return 0x80000001;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ForthWord::call(ForthCallBlock* block) {
+    ForthEngine engine(NULL);
+    if (block) {
+        // walk the array backwards, so that the top of the stack is data[0]
+        for (size_t i = 0; i < block->in_count; i++) {
+            engine.push(block->in_data[i]);
+        }
+    }
+    this->exec(&engine);
+    if (block) {
+        size_t n = engine.depth();
+        block->out_depth = n;
+        if (n > block->out_count) {
+            n = block->out_count;
+        }
+        for (size_t i = 0; i < n; i++) {
+            block->out_data[i] = engine.peek(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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();
+    }
+};
+
+class dup_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->push(fe->top());
+    }
+};
+
+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);
+    }
+};
+
+///////////////// ints
+
+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:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() - tmp);
+    }
+};
+
+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:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() / tmp);
+    }
+};
+
+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:
+    virtual void exec(ForthEngine* fe) {
+        int32_t value = fe->top();
+        if (value < 0) {
+            fe->setTop(-value);
+        }
+    }
+};
+
+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:
+    virtual void exec(ForthEngine* fe) {
+        int32_t value = fe->pop();
+        if (value > fe->top()) {
+            fe->setTop(value);
+        }
+    }
+};
+
+///////////////// floats
+
+class fadd_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() + tmp);
+    }
+};
+
+class fsub_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() - tmp);
+    }
+};
+
+class fmul_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() * tmp);
+    }
+};
+
+class fdiv_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() / tmp);
+    }
+};
+
+class fdot_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkString str;
+        str.printf("%g ", fe->fpop());
+        fe->sendOutput(str.c_str());
+    }
+};
+
+class fabs_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->ftop();
+        if (value < 0) {
+            fe->fsetTop(-value);
+        }
+    }
+};
+
+class fmin_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->fpop();
+        if (value < fe->ftop()) {
+            fe->fsetTop(value);
+        }
+    }
+};
+
+class fmax_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->fpop();
+        if (value > fe->ftop()) {
+            fe->fsetTop(value);
+        }
+    }
+};
+
+class floor_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(floorf(fe->ftop()));
+    }
+};
+
+class ceil_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(ceilf(fe->ftop()));
+    }
+};
+
+class round_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(floorf(fe->ftop() + 0.5f));
+    }
+};
+
+class f2i_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->setTop((int)fe->ftop());
+    }
+};
+
+class i2f_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop((float)fe->top());
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+    reading an initial 32bit value from the code stream:
+    xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx00
+    Those last two bits are always 0 for a word, so we set those bits for other
+    opcodes
+    00 -- execute this word
+    01 -- push (value & ~3) on the data stack
+    10 -- push value >> 2 on the data stack (sign extended)
+    11 -- switch (value >>> 2) for Code
+ */
+
+class FCode {
+public:
+    enum Bits {
+        kWord_Bits          = 0,    // must be zero for function address
+        kDataClear2_Bits    = 1,
+        kDataShift2_Bits    = 2,
+        kCodeShift2_Bits    = 3
+    };
+    enum Code {
+        kPushInt_Code,
+        kDone_Code
+    };
+
+    void appendInt(int32_t);
+    void appendWord(ForthWord*);
+    void appendIF() {}
+    bool appendELSE() { return false; }
+    bool appendTHEN() { return false; }
+    void done();
+
+    intptr_t* detach() {
+        this->done();
+        return fData.detach();
+    }
+    intptr_t* begin() {
+        this->done();
+        return fData.begin();
+    }
+    
+    static void Exec(const intptr_t*, ForthEngine*);
+
+private:
+    SkTDArray<intptr_t> fData;
+};
+
+void FCode::appendInt(int32_t value) {
+    if ((value & 3) == 0) {
+        *fData.append() = value | kDataClear2_Bits;
+    } else if ((value >> 2 << 2) == value) {
+        *fData.append() = value | kDataShift2_Bits;
+    } else {
+        intptr_t* p = fData.append(2);
+        *p++ = (kPushInt_Code << 2) | kCodeShift2_Bits;
+        *p++ = value;
+    }
+}
+
+void FCode::appendWord(ForthWord* word) {
+    SkASSERT((reinterpret_cast<intptr_t>(word) & 3) == 0);
+    *fData.append() = reinterpret_cast<intptr_t>(word);
+}
+
+void FCode::done() {
+    *fData.append() = (kDone_Code << 2) | kCodeShift2_Bits;
+}
+
+void FCode::Exec(const intptr_t* curr, ForthEngine* engine) {
+    for (;;) {
+        intptr_t c = *curr++;
+        switch (c & 3) {
+            case kWord_Bits:
+                reinterpret_cast<ForthWord*>(c)->exec(engine);
+                break;
+            case kDataClear2_Bits:
+                engine->push(c & ~3);
+                break;
+            case kDataShift2_Bits:
+                engine->push(c >> 2);
+                break;
+            case kCodeShift2_Bits:
+                switch ((uint32_t)c >> 2) {
+                    case kPushInt_Code:
+                        engine->push(*curr++);
+                        break;
+                    case kDone_Code:
+                        return;
+                }
+                break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class CustomWord : public ForthWord {
+public:
+    // we assume ownership of code[]
+    CustomWord(intptr_t code[]) : fCode(code) {}
+    virtual ~CustomWord() { sk_free(fCode); }
+
+    virtual void exec(ForthEngine* engine) {
+        FCode::Exec(fCode, engine);
+    }
+
+private:
+    intptr_t* fCode;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ForthParser {
+public:
+    ForthParser() : fDict(4096) {
+        this->addStdWords();
+    }
+
+    const char* parse(const char text[], FCode*);
+
+    void addWord(const char name[], ForthWord* word) {
+        this->add(name, strlen(name), word);
+    }
+
+    ForthWord* find(const char name[], size_t len) const {
+        ForthWord* word;
+        return fDict.find(name, len, &word) ? word : NULL;
+    }
+    
+private:
+    void add(const char name[], size_t len, ForthWord* word) {
+        (void)fDict.set(name, len, word);
+    }
+
+    void 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("+", 1, new add_ForthWord);
+        this->add("-", 1, new sub_ForthWord);
+        this->add("*", 1, new mul_ForthWord);
+        this->add("/", 1, new div_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("f+", 2, new fadd_ForthWord);
+        this->add("f-", 2, new fsub_ForthWord);
+        this->add("f*", 2, new fmul_ForthWord);
+        this->add("f/", 2, new fdiv_ForthWord);
+        this->add("f.", 2, new fdot_ForthWord);
+        this->add("fabs", 4, new fabs_ForthWord);
+        this->add("fmin", 4, new fmin_ForthWord);
+        this->add("fmax", 4, new fmax_ForthWord);
+        this->add("fmax", 4, new fmax_ForthWord);
+        this->add("floor", 5, new floor_ForthWord);
+        this->add("ceil", 4, new ceil_ForthWord);
+        this->add("round", 5, new round_ForthWord);
+        this->add("f>i", 3, new f2i_ForthWord);
+        this->add("i>f", 3, new i2f_ForthWord);
+    }
+    
+    SkTDict<ForthWord*> fDict;
+};
+
+static const char* parse_error(const char msg[]) {
+    SkDebugf("-- parser error: %s\n", msg);
+    return NULL;
+}
+
+/** returns true if c is whitespace, including null
+ */
+static bool is_ws(int c) {
+    return c <= ' ';
+}
+
+static const char* parse_token(const char** text, size_t* len) {
+    const char* s = *text;
+    while (is_ws(*s)) {
+        if (0 == *s) {
+            return NULL;
+        }
+        s++;
+    }
+    const char* token = s++;
+    while (!is_ws(*s)) {
+        s++;
+    }
+    *text = s;
+    *len = s - token;
+    return token;
+}
+
+static bool is_digit(int c) { return (unsigned)(c - '0') <= 9; }
+static int hex_val(int c) {
+    if (is_digit(c)) {
+        return c - '0';
+    } else {
+        if (c <= 'Z') {
+            return 10 + c - 'A';
+        } else {
+            return 10 + c - 'a';
+        }
+    }
+}
+
+static bool parse_num(const char str[], size_t len, int32_t* numBits) {
+    if (1 == len && !is_digit(*str)) {
+        return false;
+    }
+    const char* start = str;
+    int32_t num = 0;
+    bool neg = false;
+    if (*str == '-') {
+        neg = true;
+        str += 1;
+    } else if (*str == '#') {
+        str++;
+        while (str - start < len) {
+            num = num*16 + hex_val(*str);
+            str += 1;
+        }
+        *numBits = num;
+        return true;
+    }
+
+    while (is_digit(*str)) {
+        num = 10*num + *str - '0';
+        str += 1;
+    }
+    SkASSERT(str - start <= len);
+    if (str - start == len) {
+        if (neg) {
+            num = -num;
+        }
+        *numBits = num;
+        return true;
+    }
+    // if we're not done with the token then the next char must be a decimal
+    if (*str != '.') {
+        return false;
+    }
+    str += 1;
+    float x = num;
+    float denom = 1;
+    while (str - start < len && is_digit(*str)) {
+        x = 10*x + *str - '0';
+        denom *= 10;
+        str += 1;
+    }
+    x /= denom;
+    if (str - start == len) {
+        if (neg) {
+            x = -x;
+        }
+        *numBits = f2i_bits(x);
+        return true;
+    }
+    return false;
+}
+
+static const char* parse_comment(const char text[]) {
+    SkASSERT(*text == '(');
+    while (')' != *++text) {
+        if (0 == *text) {
+            return NULL;
+        }
+    }
+    return text + 1;    // skip past the closing ')'
+}
+
+const char* ForthParser::parse(const char text[], FCode* code) {
+    for (;;) {
+        size_t len;
+        const char* token = parse_token(&text, &len);
+        if (NULL == token) {
+            break;
+        }
+        if (1 == len) {
+            if ('(' == *token) {
+                text = parse_comment(token);
+                if (NULL == text) {
+                    return NULL;
+                }
+                continue;
+            }
+            if (';' == *token) {
+                break;
+            }
+            if (':' == *token) {
+                token = parse_token(&text, &len);
+                if (NULL == token) {
+                    return parse_error("missing name after ':'");
+                }
+                FCode subCode;
+                text = this->parse(text, &subCode);
+                if (NULL == text) {
+                    return NULL;
+                }
+                this->add(token, len, new CustomWord(subCode.detach()));
+                continue;
+            }
+        }
+        int32_t num;
+        if (parse_num(token, len, &num)) {
+            // note that num is just the bit representation of the float
+            code->appendInt(num);
+        } else if (2 == len && memcmp(token, "IF", 2) == 0) {
+            code->appendIF();
+        } else if (2 == len && memcmp(token, "ELSE", 4) == 0) {
+            if (!code->appendELSE()) {
+                return parse_error("ELSE with no matching IF");
+            }
+        } else if (2 == len && memcmp(token, "THEN", 4) == 0) {
+            if (!code->appendTHEN()) {
+                return parse_error("THEN with no matching IF");
+            }
+        } else{
+            ForthWord* word = this->find(token, len);
+            if (NULL == word) {
+                SkString str(token, len);
+                str.prepend("unknown word ");
+                return parse_error(str.c_str());
+            }
+            code->appendWord(word);
+        }
+    }
+    return text;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ForthEnv::Impl {
+public:
+    ForthParser fParser;
+    FCode       fBuilder;
+};
+
+ForthEnv::ForthEnv() {
+    fImpl = new Impl;
+}
+
+ForthEnv::~ForthEnv() {
+    delete fImpl;
+}
+
+void ForthEnv::addWord(const char name[], ForthWord* word) {
+    fImpl->fParser.addWord(name, word);
+}
+
+void ForthEnv::parse(const char text[]) {
+    fImpl->fParser.parse(text, &fImpl->fBuilder);
+}
+
+ForthWord* ForthEnv::findWord(const char name[]) {
+    return fImpl->fParser.find(name, strlen(name));
+}
+
+void ForthEnv::run(ForthOutput* output) {
+    ForthEngine engine(output);
+    FCode::Exec(fImpl->fBuilder.begin(), &engine);
+}
+
+#if 0
+void ForthEnv::run(const char text[], ForthOutput* output) {
+    FCode builder;
+
+    if (fImpl->fParser.parse(text, &builder)) {
+        ForthEngine engine(output);
+        FCode::Exec(builder.begin(), &engine);
+    }
+}
+#endif
+
diff --git a/forth/Forth.h b/forth/Forth.h
new file mode 100644 (file)
index 0000000..a68b219
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef Forth_DEFINED
+#define Forth_DEFINED
+
+#include "SkTypes.h"
+
+class ForthOutput {
+public:
+    virtual void show(const char output[]) = 0;
+};
+
+union FloatIntDual {
+    int32_t fInt;
+    float   fFloat;
+};
+
+static inline int32_t f2i_bits(float x) {
+    FloatIntDual d;
+    d.fFloat = x;
+    return d.fInt;
+}
+
+static inline float i2f_bits(int32_t x) {
+    FloatIntDual d;
+    d.fInt = x;
+    return d.fFloat;
+}
+
+class ForthEngine {
+public:
+    ForthEngine(ForthOutput*);
+    ~ForthEngine();
+
+    int         depth() const { return fStackStop - fStackCurr; }
+    void        clearStack() { fStackCurr = fStackStop; }
+
+    void        push(intptr_t value);
+    intptr_t    top() const { return this->peek(0); }
+    intptr_t    peek(size_t index) const;
+    void        setTop(intptr_t value);
+    intptr_t    pop();
+
+    void        fpush(float value) { this->push(f2i_bits(value)); }
+    float       fpeek(size_t i) const { return i2f_bits(this->fpeek(i)); }
+    float       ftop() const { return i2f_bits(this->top()); }
+    void        fsetTop(float value) { this->setTop(f2i_bits(value)); }
+    float       fpop() { return i2f_bits(this->pop()); }
+
+    void sendOutput(const char text[]);
+
+private:
+    ForthOutput* fOutput;
+    intptr_t*   fStackBase;
+    intptr_t*   fStackCurr;
+    intptr_t*   fStackStop;
+
+    void signal_error(const char msg[]) const {
+        SkDebugf("ForthEngine error: %s\n", msg);
+    }
+};
+
+struct ForthCallBlock {
+    const intptr_t* in_data;
+    size_t          in_count;
+    intptr_t*       out_data;
+    size_t          out_count;
+    size_t          out_depth;
+};
+
+class ForthWord {
+public:
+    virtual ~ForthWord() {}
+    virtual void exec(ForthEngine*) = 0;
+
+    // todo: return error state of the engine
+    void call(ForthCallBlock*);
+};
+
+class ForthEnv {
+public:
+    ForthEnv();
+    ~ForthEnv();
+
+
+    void addWord(const char name[], ForthWord*);
+
+    void parse(const char code[]);
+
+    ForthWord* findWord(const char name[]);
+
+    void run(ForthOutput* = NULL);
+
+private:
+    class Impl;
+    Impl* fImpl;
+};
+
+#endif
+
diff --git a/forth/SampleForth.cpp b/forth/SampleForth.cpp
new file mode 100644 (file)
index 0000000..d207cb8
--- /dev/null
@@ -0,0 +1,248 @@
+#include "SampleCode.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+#include "Forth.h"
+
+class MyOutput : public ForthOutput {
+public:
+    SkString fStr;
+
+    virtual void show(const char text[]) {
+        fStr.set(text);
+    }
+};
+
+class SkForthCtx {
+public:
+    SkCanvas    fCanvas;
+    SkPaint     fPaint;
+
+    void init(const SkBitmap& bm) {
+        fCanvas.setBitmapDevice(bm);
+        fPaint.setAntiAlias(true);
+    }
+};
+
+class SkForthCtx_FW : public ForthWord {
+public:
+    SkForthCtx_FW() : fCtx(NULL) {}
+    
+    void setCtx(SkForthCtx* ctx) { fCtx = ctx; }
+    
+    SkCanvas* canvas() const { return &fCtx->fCanvas; }
+    SkPaint* paint() const { return &fCtx->fPaint; }
+
+private:
+    SkForthCtx* fCtx;
+};
+
+class setColor_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        paint()->setColor(fe->pop());
+    }
+    
+    static SkForthCtx_FW* New() { return new setColor_FW; }
+};
+
+class setStyle_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        paint()->setStyle((SkPaint::Style)fe->pop());
+    }
+    
+    static SkForthCtx_FW* New() { return new setStyle_FW; }
+};
+
+class setStrokeWidth_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        paint()->setStrokeWidth(fe->fpop());
+    }
+    
+    static SkForthCtx_FW* New() { return new setStrokeWidth_FW; }
+};
+
+class translate_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkScalar dy = fe->fpop();
+        SkScalar dx = fe->fpop();
+        canvas()->translate(dx, dy);
+    }
+    
+    static SkForthCtx_FW* New() { return new translate_FW; }
+};
+
+class drawColor_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        canvas()->drawColor(fe->pop());
+    }
+    
+    static SkForthCtx_FW* New() { return new drawColor_FW; }
+};
+
+class drawRect_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkRect r;
+        r.fBottom = fe->fpop();
+        r.fRight = fe->fpop();
+        r.fTop = fe->fpop();
+        r.fLeft = fe->fpop();
+        canvas()->drawRect(r, *paint());
+    }
+    
+    static SkForthCtx_FW* New() { return new drawRect_FW; }
+};
+
+class drawCircle_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkScalar radius = fe->fpop();
+        SkScalar y = fe->fpop();
+        SkScalar x = fe->fpop();
+        canvas()->drawCircle(x, y, radius, *paint());
+    }
+    
+    static SkForthCtx_FW* New() { return new drawCircle_FW; }
+};
+
+class drawLine_FW : public SkForthCtx_FW {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkScalar x0, y0, x1, y1;
+        y1 = fe->fpop();
+        x1 = fe->fpop();
+        y0 = fe->fpop();
+        x0 = fe->fpop();
+        canvas()->drawLine(x0, y0, x1, y1, *paint());
+    }
+    
+    static SkForthCtx_FW* New() { return new drawLine_FW; }
+};
+
+static const struct {
+    const char* fName;
+    SkForthCtx_FW* (*fProc)();
+} gWordRecs[] = {
+    { "setColor",       setColor_FW::New },
+    { "setStyle",       setStyle_FW::New },
+    { "setStrokeWidth", setStrokeWidth_FW::New },
+    { "translate",      translate_FW::New },
+    { "drawColor",      drawColor_FW::New },
+    { "drawRect",       drawRect_FW::New },
+    { "drawCircle",     drawCircle_FW::New },
+    { "drawLine",       drawLine_FW::New },
+};
+
+static void load_words(ForthEnv* env, SkForthCtx* ctx) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gWordRecs); i++) {
+        SkForthCtx_FW* word = gWordRecs[i].fProc();
+        word->setCtx(ctx);
+        env->addWord(gWordRecs[i].fName, word);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ForthView : public SkView {
+    ForthEnv    fEnv;
+    ForthWord*  fOnClickWord;
+
+    SkBitmap    fBM;
+    SkForthCtx  fContext;
+public:
+       ForthView() {
+        load_words(&fEnv, &fContext);
+
+        fBM.setConfig(SkBitmap::kARGB_8888_Config, 640, 480);
+        fBM.allocPixels();
+        fBM.eraseColor(0);
+        fContext.init(fBM);
+
+        fEnv.parse(": view.onClick ( x y -- ) 10. drawCircle ;");
+        fOnClickWord = fEnv.findWord("view.onClick");
+#if 0
+        env.parse(
+                  ": draw1 "
+                  "10. setStrokeWidth 1 setStyle #FF000000 setColor "
+                  "10. 20. 200. 100. drawLine "
+                  "0 setStyle #80FF0000 setColor "
+                  "50. 50. 250. 150. drawRect "
+                  ";");
+        env.parse("#FF0000FF drawColor "
+                  "draw1 "
+                  "150. 120. translate "
+                  "draw1 "
+                  );
+#endif
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Forth");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    void test_onClick(ForthEnv* env) {
+        ForthWord* word = env->findWord("view.onClick");
+        if (word) {
+            const intptr_t idata[2] = { 40, 2 };
+            intptr_t odata[1] = { -1 };
+            ForthCallBlock block;
+            block.in_data = idata;
+            block.in_count = 2;
+            block.out_data = odata;
+            block.out_count = 1;
+            word->call(&block);
+            SkDebugf("after view.onClick depth %d count %d top %d\n",
+                     block.out_depth, block.out_count, odata[0]);
+        } else {
+            SkDebugf("------ view.onClick not found\n");
+        }
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        drawBG(canvas);
+        canvas->drawBitmap(fBM, 0, 0, NULL);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return fOnClickWord ? new Click(this) : NULL;
+    }
+    
+    virtual bool onClick(Click* click) {
+        intptr_t idata[2] = {
+            f2i_bits(click->fCurr.fX), f2i_bits(click->fCurr.fY)
+        };
+        ForthCallBlock block;
+        block.in_data = idata;
+        block.in_count = 2;
+        block.out_count = 0;
+        fOnClickWord->call(&block);
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ForthView; }
+static SkViewRegister reg(MyFactory);
+
index 3edf0e4..f3cf4e7 100644 (file)
@@ -24,6 +24,7 @@
                00003CA10EFC233F000FF73A /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */; };
                00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */; };
                000A99820FD97526007E45BD /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; };
+               001B871E1042184D00C84ED4 /* Forth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 001B871D1042184D00C84ED4 /* Forth.cpp */; };
                0028847B0EFAB46A0083E387 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884510EFAA35C0083E387 /* libcore.a */; };
                002884BD0EFAB6A30083E387 /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884BC0EFAB69F0083E387 /* libmaccore.a */; };
                0041CDDB0F00975E00695E8C /* SampleImageDir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDDA0F00975E00695E8C /* SampleImageDir.cpp */; };
@@ -71,6 +72,7 @@
                00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A729630FD93ED600D5051F /* SampleTestGL.cpp */; };
                00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C640EFC22A8000FF73A /* SamplePath.cpp */; };
                00AF9B18103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */; };
+               00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00BB289A104781D00057BF7E /* SampleForth.cpp */; };
                00C1B809103857A400FA5948 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; };
                00F53F480FFCFC4D003FA70A /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; };
                00FF39140FC6ED2C00915187 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */; };
                00003C9A0EFC233F000FF73A /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDOM.cpp; path = ../../src/xml/SkDOM.cpp; sourceTree = SOURCE_ROOT; };
                00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser.cpp; path = ../../src/xml/SkXMLParser.cpp; sourceTree = SOURCE_ROOT; };
                00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser_empty.cpp; path = ../../src/ports/SkXMLParser_empty.cpp; sourceTree = SOURCE_ROOT; };
+               001B871D1042184D00C84ED4 /* Forth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Forth.cpp; path = ../../forth/Forth.cpp; sourceTree = SOURCE_ROOT; };
                002884490EFAA35C0083E387 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
                002884B40EFAB69F0083E387 /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; };
                003145310FB9B48F00B10956 /* SampleShapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleShapes.cpp; path = ../../samplecode/SampleShapes.cpp; sourceTree = SOURCE_ROOT; };
                00A7282E0FD43D3700D5051F /* SkMovie_gif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMovie_gif.cpp; path = ../../src/images/SkMovie_gif.cpp; sourceTree = SOURCE_ROOT; };
                00A729630FD93ED600D5051F /* SampleTestGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleTestGL.cpp; path = ../../samplecode/SampleTestGL.cpp; sourceTree = SOURCE_ROOT; };
                00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleDitherBitmap.cpp; path = ../../samplecode/SampleDitherBitmap.cpp; sourceTree = SOURCE_ROOT; };
+               00BB289A104781D00057BF7E /* SampleForth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleForth.cpp; path = ../../forth/SampleForth.cpp; sourceTree = SOURCE_ROOT; };
+               00BB289D1047826E0057BF7E /* Forth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Forth.h; path = ../../forth/Forth.h; sourceTree = SOURCE_ROOT; };
                00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleGradients.cpp; path = ../../samplecode/SampleGradients.cpp; sourceTree = SOURCE_ROOT; };
                00D6B5CB0F72DC4300C466B9 /* SampleFuzz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleFuzz.cpp; path = ../../samplecode/SampleFuzz.cpp; sourceTree = SOURCE_ROOT; };
                00FF39130FC6ED2C00915187 /* SampleEffects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleEffects.cpp; path = ../../samplecode/SampleEffects.cpp; sourceTree = SOURCE_ROOT; };
                                0041CE260F00A12400695E8C /* SampleEncode.cpp */,
                                0041CE270F00A12400695E8C /* SampleFillType.cpp */,
                                00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */,
+                               00BB289D1047826E0057BF7E /* Forth.h */,
+                               00BB289A104781D00057BF7E /* SampleForth.cpp */,
                                0041CE280F00A12400695E8C /* SampleFilter.cpp */,
                                0041CE290F00A12400695E8C /* SampleFilter2.cpp */,
                                0041CE2A0F00A12400695E8C /* SampleFontCache.cpp */,
                20286C29FDCF999611CA2CEA /* CICarbonSample */ = {
                        isa = PBXGroup;
                        children = (
+                               001B871D1042184D00C84ED4 /* Forth.cpp */,
                                2762F66A0FCCCAA2002BD8B4 /* images */,
                                00003C6A0EFC22AD000FF73A /* views */,
                                00003C610EFC2287000FF73A /* samples */,
                                27005D5F10095B2B00E275B6 /* SampleCircle.cpp in Sources */,
                                00C1B809103857A400FA5948 /* SampleFillType.cpp in Sources */,
                                00AF9B18103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp in Sources */,
+                               001B871E1042184D00C84ED4 /* Forth.cpp in Sources */,
+                               00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };