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