blob: 1dd4c73e4a9ee1753c30211015e356c88c1df6a0 [file] [log] [blame]
/*
* 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