|  |  | 
|  | /* | 
|  | * Copyright 2011 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  | #include "Forth.h" | 
|  | #include "ForthParser.h" | 
|  | #include "SkTDArray.h" | 
|  | #include "SkString.h" | 
|  | #include "SkTDStack.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); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | // setup the initial stack with the callers input data | 
|  | 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]); | 
|  | } | 
|  | } | 
|  |  | 
|  | // now invoke the word | 
|  | this->exec(&engine); | 
|  |  | 
|  | // now copy back the stack into the caller's output data | 
|  | 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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | /* | 
|  | 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 { | 
|  | kCodeShift  = 2, | 
|  | kCodeMask   = 7, | 
|  | kCodeDataShift  = 5 | 
|  | }; | 
|  | static unsigned GetCode(intptr_t c) { | 
|  | return ((uint32_t)c >> kCodeShift) & kCodeMask; | 
|  | } | 
|  | static unsigned GetCodeData(intptr_t c) { | 
|  | return (uint32_t)c >> kCodeDataShift; | 
|  | } | 
|  |  | 
|  | enum Bits { | 
|  | kWord_Bits          = 0,    // must be zero for function address | 
|  | kDataClear2_Bits    = 1, | 
|  | kDataShift2_Bits    = 2, | 
|  | kCodeShift2_Bits    = 3 | 
|  | }; | 
|  |  | 
|  | enum Code { | 
|  | kPushInt_Code,  // for data that needs more than 30 bits | 
|  | kIF_Code, | 
|  | kELSE_Code, | 
|  | kDone_Code | 
|  | }; | 
|  | static unsigned MakeCode(Code code) { | 
|  | return (code << kCodeShift) | kCodeShift2_Bits; | 
|  | } | 
|  |  | 
|  | void appendInt(int32_t); | 
|  | void appendWord(ForthWord*); | 
|  | void appendIF(); | 
|  | bool appendELSE(); | 
|  | bool appendTHEN(); | 
|  | 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; | 
|  | SkTDStack<size_t>   fIfStack; | 
|  | }; | 
|  |  | 
|  | void FCode::appendInt(int32_t value) { | 
|  | if ((value & 3) == 0) { | 
|  | *fData.append() = value | kDataClear2_Bits; | 
|  | } else if ((value << 2 >> 2) == value) { | 
|  | *fData.append() = (value << 2) | 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::appendIF() { | 
|  | size_t ifIndex = fData.count(); | 
|  | fIfStack.push(ifIndex); | 
|  | *fData.append() = MakeCode(kIF_Code); | 
|  | } | 
|  |  | 
|  | bool FCode::appendELSE() { | 
|  | if (fIfStack.empty()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | size_t elseIndex = fData.count(); | 
|  | *fData.append() = MakeCode(kELSE_Code); | 
|  |  | 
|  | size_t ifIndex = fIfStack.top(); | 
|  | // record the offset in the data part of the if-code | 
|  | fData[ifIndex] |= (elseIndex - ifIndex) << kCodeDataShift; | 
|  |  | 
|  | // now reuse this IfStack entry to track the else | 
|  | fIfStack.top() = elseIndex; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool FCode::appendTHEN() { | 
|  | if (fIfStack.empty()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // this is either an IF or an ELSE | 
|  | size_t index = fIfStack.top(); | 
|  | // record the offset in the data part of the code | 
|  | fData[index] |= (fData.count() - index - 1) << kCodeDataShift; | 
|  |  | 
|  | fIfStack.pop(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | 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 (GetCode(c)) { | 
|  | case kPushInt_Code: | 
|  | engine->push(*curr++); | 
|  | break; | 
|  | case kIF_Code: | 
|  | if (!engine->pop()) { | 
|  | // takes us past the ELSE or THEN | 
|  | curr += GetCodeData(c); | 
|  | } | 
|  | break; | 
|  | case kELSE_Code: | 
|  | // takes us past the THEN | 
|  | curr += GetCodeData(c); | 
|  | 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; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | ForthParser::ForthParser() : fDict(4096) { | 
|  | this->addStdWords(); | 
|  | } | 
|  |  | 
|  | ForthParser::~ForthParser() { | 
|  | SkTDict<ForthWord*>::Iter iter(fDict); | 
|  | ForthWord* word; | 
|  | while (iter.next(&word)) { | 
|  | delete word; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 (4 == len && memcmp(token, "ELSE", 4) == 0) { | 
|  | if (!code->appendELSE()) { | 
|  | return parse_error("ELSE with no matching IF"); | 
|  | } | 
|  | } else if (4 == 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 |