experimental hack to write a forth engine to drive skia
git-svn-id: http://skia.googlecode.com/svn/trunk@343 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
a03a70124d
commit
f56e295e88
682
forth/Forth.cpp
Normal file
682
forth/Forth.cpp
Normal file
@ -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
|
||||
|
98
forth/Forth.h
Normal file
98
forth/Forth.h
Normal file
@ -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
|
||||
|
248
forth/SampleForth.cpp
Normal file
248
forth/SampleForth.cpp
Normal file
@ -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);
|
||||
|
@ -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 */; };
|
||||
@ -149,6 +151,7 @@
|
||||
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; };
|
||||
@ -199,6 +202,8 @@
|
||||
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; };
|
||||
@ -280,6 +285,8 @@
|
||||
0041CE260F00A12400695E8C /* SampleEncode.cpp */,
|
||||
0041CE270F00A12400695E8C /* SampleFillType.cpp */,
|
||||
00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */,
|
||||
00BB289D1047826E0057BF7E /* Forth.h */,
|
||||
00BB289A104781D00057BF7E /* SampleForth.cpp */,
|
||||
0041CE280F00A12400695E8C /* SampleFilter.cpp */,
|
||||
0041CE290F00A12400695E8C /* SampleFilter2.cpp */,
|
||||
0041CE2A0F00A12400695E8C /* SampleFontCache.cpp */,
|
||||
@ -355,6 +362,7 @@
|
||||
20286C29FDCF999611CA2CEA /* CICarbonSample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
001B871D1042184D00C84ED4 /* Forth.cpp */,
|
||||
2762F66A0FCCCAA2002BD8B4 /* images */,
|
||||
00003C6A0EFC22AD000FF73A /* views */,
|
||||
00003C610EFC2287000FF73A /* samples */,
|
||||
@ -579,6 +587,8 @@
|
||||
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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user