Home | History | Annotate | Download | only in forth
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "Forth.h"
      9 #include "ForthParser.h"
     10 #include "SkTDArray.h"
     11 #include "SkString.h"
     12 #include "SkTDStack.h"
     13 
     14 ForthEngine::ForthEngine(ForthOutput* output) : fOutput(output) {
     15     size_t size = 32 * sizeof(intptr_t);
     16     fStackBase = reinterpret_cast<intptr_t*>(sk_malloc_throw(size));
     17     fStackStop = fStackBase + size/sizeof(intptr_t);
     18     fStackCurr = fStackStop;
     19 }
     20 
     21 ForthEngine::~ForthEngine() {
     22     sk_free(fStackBase);
     23 }
     24 
     25 void ForthEngine::sendOutput(const char text[]) {
     26     if (fOutput) {
     27         fOutput->show(text);
     28     } else {
     29         SkDebugf("%s", text);
     30     }
     31 }
     32 
     33 void ForthEngine::push(intptr_t value) {
     34     if (fStackCurr > fStackBase) {
     35         SkASSERT(fStackCurr <= fStackStop && fStackCurr > fStackBase);
     36         *--fStackCurr = value;
     37     } else {
     38         this->signal_error("overflow");
     39     }
     40 }
     41 
     42 intptr_t ForthEngine::peek(size_t index) const {
     43     SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
     44     if (fStackCurr + index < fStackStop) {
     45         return fStackCurr[index];
     46     } else {
     47         this->signal_error("peek out of range");
     48         return 0x80000001;
     49     }
     50 }
     51 
     52 void ForthEngine::setTop(intptr_t value) {
     53     if (fStackCurr < fStackStop) {
     54         SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
     55         *fStackCurr = value;
     56     } else {
     57         this->signal_error("underflow");
     58     }
     59 }
     60 
     61 intptr_t ForthEngine::pop() {
     62     if (fStackCurr < fStackStop) {
     63         SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
     64         return *fStackCurr++;
     65     } else {
     66         this->signal_error("underflow");
     67         return 0x80000001;
     68     }
     69 }
     70 
     71 ///////////////////////////////////////////////////////////////////////////////
     72 
     73 void ForthWord::call(ForthCallBlock* block) {
     74     ForthEngine engine(NULL);
     75 
     76     // setup the initial stack with the callers input data
     77     if (block) {
     78         // walk the array backwards, so that the top of the stack is data[0]
     79         for (size_t i = 0; i < block->in_count; i++) {
     80             engine.push(block->in_data[i]);
     81         }
     82     }
     83 
     84     // now invoke the word
     85     this->exec(&engine);
     86 
     87     // now copy back the stack into the caller's output data
     88     if (block) {
     89         size_t n = engine.depth();
     90         block->out_depth = n;
     91         if (n > block->out_count) {
     92             n = block->out_count;
     93         }
     94         for (size_t i = 0; i < n; i++) {
     95             block->out_data[i] = engine.peek(i);
     96         }
     97     }
     98 }
     99 
    100 ///////////////////////////////////////////////////////////////////////////////
    101 
    102 /*
    103     reading an initial 32bit value from the code stream:
    104 
    105     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx00
    106 
    107     Those last two bits are always 0 for a word, so we set those bits for other
    108     opcodes
    109 
    110     00 -- execute this word
    111     01 -- push (value & ~3) on the data stack
    112     10 -- push value >> 2 on the data stack (sign extended)
    113     11 -- switch (value >>> 2) for Code
    114  */
    115 
    116 class FCode {
    117 public:
    118     enum {
    119         kCodeShift  = 2,
    120         kCodeMask   = 7,
    121         kCodeDataShift  = 5
    122     };
    123     static unsigned GetCode(intptr_t c) {
    124         return ((uint32_t)c >> kCodeShift) & kCodeMask;
    125     }
    126     static unsigned GetCodeData(intptr_t c) {
    127         return (uint32_t)c >> kCodeDataShift;
    128     }
    129 
    130     enum Bits {
    131         kWord_Bits          = 0,    // must be zero for function address
    132         kDataClear2_Bits    = 1,
    133         kDataShift2_Bits    = 2,
    134         kCodeShift2_Bits    = 3
    135     };
    136 
    137     enum Code {
    138         kPushInt_Code,  // for data that needs more than 30 bits
    139         kIF_Code,
    140         kELSE_Code,
    141         kDone_Code
    142     };
    143     static unsigned MakeCode(Code code) {
    144         return (code << kCodeShift) | kCodeShift2_Bits;
    145     }
    146 
    147     void appendInt(int32_t);
    148     void appendWord(ForthWord*);
    149     void appendIF();
    150     bool appendELSE();
    151     bool appendTHEN();
    152     void done();
    153 
    154     intptr_t* detach() {
    155         this->done();
    156         return fData.detach();
    157     }
    158     intptr_t* begin() {
    159         this->done();
    160         return fData.begin();
    161     }
    162 
    163     static void Exec(const intptr_t*, ForthEngine*);
    164 
    165 private:
    166     SkTDArray<intptr_t> fData;
    167     SkTDStack<size_t>   fIfStack;
    168 };
    169 
    170 void FCode::appendInt(int32_t value) {
    171     if ((value & 3) == 0) {
    172         *fData.append() = value | kDataClear2_Bits;
    173     } else if ((value << 2 >> 2) == value) {
    174         *fData.append() = (value << 2) | kDataShift2_Bits;
    175     } else {
    176         intptr_t* p = fData.append(2);
    177         *p++ = (kPushInt_Code << 2) | kCodeShift2_Bits;
    178         *p++ = value;
    179     }
    180 }
    181 
    182 void FCode::appendWord(ForthWord* word) {
    183     SkASSERT((reinterpret_cast<intptr_t>(word) & 3) == 0);
    184     *fData.append() = reinterpret_cast<intptr_t>(word);
    185 }
    186 
    187 void FCode::appendIF() {
    188     size_t ifIndex = fData.count();
    189     fIfStack.push(ifIndex);
    190     *fData.append() = MakeCode(kIF_Code);
    191 }
    192 
    193 bool FCode::appendELSE() {
    194     if (fIfStack.empty()) {
    195         return false;
    196     }
    197 
    198     size_t elseIndex = fData.count();
    199     *fData.append() = MakeCode(kELSE_Code);
    200 
    201     size_t ifIndex = fIfStack.top();
    202     // record the offset in the data part of the if-code
    203     fData[ifIndex] |= (elseIndex - ifIndex) << kCodeDataShift;
    204 
    205     // now reuse this IfStack entry to track the else
    206     fIfStack.top() = elseIndex;
    207     return true;
    208 }
    209 
    210 bool FCode::appendTHEN() {
    211     if (fIfStack.empty()) {
    212         return false;
    213     }
    214 
    215     // this is either an IF or an ELSE
    216     size_t index = fIfStack.top();
    217     // record the offset in the data part of the code
    218     fData[index] |= (fData.count() - index - 1) << kCodeDataShift;
    219 
    220     fIfStack.pop();
    221     return true;
    222 }
    223 
    224 void FCode::done() {
    225     *fData.append() = (kDone_Code << 2) | kCodeShift2_Bits;
    226 }
    227 
    228 void FCode::Exec(const intptr_t* curr, ForthEngine* engine) {
    229     for (;;) {
    230         intptr_t c = *curr++;
    231         switch (c & 3) {
    232             case kWord_Bits:
    233                 reinterpret_cast<ForthWord*>(c)->exec(engine);
    234                 break;
    235             case kDataClear2_Bits:
    236                 engine->push(c & ~3);
    237                 break;
    238             case kDataShift2_Bits:
    239                 engine->push(c >> 2);
    240                 break;
    241             case kCodeShift2_Bits:
    242                 switch (GetCode(c)) {
    243                     case kPushInt_Code:
    244                         engine->push(*curr++);
    245                         break;
    246                     case kIF_Code:
    247                         if (!engine->pop()) {
    248                             // takes us past the ELSE or THEN
    249                             curr += GetCodeData(c);
    250                         }
    251                         break;
    252                     case kELSE_Code:
    253                         // takes us past the THEN
    254                         curr += GetCodeData(c);
    255                         break;
    256                     case kDone_Code:
    257                         return;
    258                 }
    259                 break;
    260         }
    261     }
    262 }
    263 
    264 ///////////////////////////////////////////////////////////////////////////////
    265 
    266 class CustomWord : public ForthWord {
    267 public:
    268     // we assume ownership of code[]
    269     CustomWord(intptr_t code[]) : fCode(code) {}
    270     virtual ~CustomWord() { sk_free(fCode); }
    271 
    272     virtual void exec(ForthEngine* engine) {
    273         FCode::Exec(fCode, engine);
    274     }
    275 
    276 private:
    277     intptr_t* fCode;
    278 };
    279 
    280 ///////////////////////////////////////////////////////////////////////////////
    281 
    282 ForthParser::ForthParser() : fDict(4096) {
    283     this->addStdWords();
    284 }
    285 
    286 ForthParser::~ForthParser() {
    287     SkTDict<ForthWord*>::Iter iter(fDict);
    288     ForthWord* word;
    289     while (iter.next(&word)) {
    290         delete word;
    291     }
    292 }
    293 
    294 static const char* parse_error(const char msg[]) {
    295     SkDebugf("-- parser error: %s\n", msg);
    296     return NULL;
    297 }
    298 
    299 /** returns true if c is whitespace, including null
    300  */
    301 static bool is_ws(int c) {
    302     return c <= ' ';
    303 }
    304 
    305 static const char* parse_token(const char** text, size_t* len) {
    306     const char* s = *text;
    307     while (is_ws(*s)) {
    308         if (0 == *s) {
    309             return NULL;
    310         }
    311         s++;
    312     }
    313     const char* token = s++;
    314     while (!is_ws(*s)) {
    315         s++;
    316     }
    317     *text = s;
    318     *len = s - token;
    319     return token;
    320 }
    321 
    322 static bool is_digit(int c) { return (unsigned)(c - '0') <= 9; }
    323 static int hex_val(int c) {
    324     if (is_digit(c)) {
    325         return c - '0';
    326     } else {
    327         if (c <= 'Z') {
    328             return 10 + c - 'A';
    329         } else {
    330             return 10 + c - 'a';
    331         }
    332     }
    333 }
    334 
    335 static bool parse_num(const char str[], size_t len, int32_t* numBits) {
    336     if (1 == len && !is_digit(*str)) {
    337         return false;
    338     }
    339     const char* start = str;
    340     int32_t num = 0;
    341     bool neg = false;
    342     if (*str == '-') {
    343         neg = true;
    344         str += 1;
    345     } else if (*str == '#') {
    346         str++;
    347         while (str - start < len) {
    348             num = num*16 + hex_val(*str);
    349             str += 1;
    350         }
    351         *numBits = num;
    352         return true;
    353     }
    354 
    355     while (is_digit(*str)) {
    356         num = 10*num + *str - '0';
    357         str += 1;
    358     }
    359     SkASSERT(str - start <= len);
    360     if (str - start == len) {
    361         if (neg) {
    362             num = -num;
    363         }
    364         *numBits = num;
    365         return true;
    366     }
    367     // if we're not done with the token then the next char must be a decimal
    368     if (*str != '.') {
    369         return false;
    370     }
    371     str += 1;
    372     float x = num;
    373     float denom = 1;
    374     while (str - start < len && is_digit(*str)) {
    375         x = 10*x + *str - '0';
    376         denom *= 10;
    377         str += 1;
    378     }
    379     x /= denom;
    380     if (str - start == len) {
    381         if (neg) {
    382             x = -x;
    383         }
    384         *numBits = f2i_bits(x);
    385         return true;
    386     }
    387     return false;
    388 }
    389 
    390 static const char* parse_comment(const char text[]) {
    391     SkASSERT(*text == '(');
    392     while (')' != *++text) {
    393         if (0 == *text) {
    394             return NULL;
    395         }
    396     }
    397     return text + 1;    // skip past the closing ')'
    398 }
    399 
    400 const char* ForthParser::parse(const char text[], FCode* code) {
    401     for (;;) {
    402         size_t len;
    403         const char* token = parse_token(&text, &len);
    404         if (NULL == token) {
    405             break;
    406         }
    407         if (1 == len) {
    408             if ('(' == *token) {
    409                 text = parse_comment(token);
    410                 if (NULL == text) {
    411                     return NULL;
    412                 }
    413                 continue;
    414             }
    415             if (';' == *token) {
    416                 break;
    417             }
    418             if (':' == *token) {
    419                 token = parse_token(&text, &len);
    420                 if (NULL == token) {
    421                     return parse_error("missing name after ':'");
    422                 }
    423                 FCode subCode;
    424                 text = this->parse(text, &subCode);
    425                 if (NULL == text) {
    426                     return NULL;
    427                 }
    428                 this->add(token, len, new CustomWord(subCode.detach()));
    429                 continue;
    430             }
    431         }
    432         int32_t num;
    433         if (parse_num(token, len, &num)) {
    434             // note that num is just the bit representation of the float
    435             code->appendInt(num);
    436         } else if (2 == len && memcmp(token, "IF", 2) == 0) {
    437             code->appendIF();
    438         } else if (4 == len && memcmp(token, "ELSE", 4) == 0) {
    439             if (!code->appendELSE()) {
    440                 return parse_error("ELSE with no matching IF");
    441             }
    442         } else if (4 == len && memcmp(token, "THEN", 4) == 0) {
    443             if (!code->appendTHEN()) {
    444                 return parse_error("THEN with no matching IF");
    445             }
    446         } else{
    447             ForthWord* word = this->find(token, len);
    448             if (NULL == word) {
    449                 SkString str(token, len);
    450                 str.prepend("unknown word ");
    451                 return parse_error(str.c_str());
    452             }
    453             code->appendWord(word);
    454         }
    455     }
    456     return text;
    457 }
    458 
    459 ///////////////////////////////////////////////////////////////////////////////
    460 
    461 class ForthEnv::Impl {
    462 public:
    463     ForthParser fParser;
    464     FCode       fBuilder;
    465 };
    466 
    467 ForthEnv::ForthEnv() {
    468     fImpl = new Impl;
    469 }
    470 
    471 ForthEnv::~ForthEnv() {
    472     delete fImpl;
    473 }
    474 
    475 void ForthEnv::addWord(const char name[], ForthWord* word) {
    476     fImpl->fParser.addWord(name, word);
    477 }
    478 
    479 void ForthEnv::parse(const char text[]) {
    480     fImpl->fParser.parse(text, &fImpl->fBuilder);
    481 }
    482 
    483 ForthWord* ForthEnv::findWord(const char name[]) {
    484     return fImpl->fParser.find(name, strlen(name));
    485 }
    486 
    487 void ForthEnv::run(ForthOutput* output) {
    488     ForthEngine engine(output);
    489     FCode::Exec(fImpl->fBuilder.begin(), &engine);
    490 }
    491 
    492 #if 0
    493 void ForthEnv::run(const char text[], ForthOutput* output) {
    494     FCode builder;
    495 
    496     if (fImpl->fParser.parse(text, &builder)) {
    497         ForthEngine engine(output);
    498         FCode::Exec(builder.begin(), &engine);
    499     }
    500 }
    501 #endif
    502