blob: 722ed08728b25c0e652404c6295baae928e202fa [file] [log] [blame]
reed@android.comf56e2952009-08-29 21:30:25 +00001#include "Forth.h"
2#include "SkTDArray.h"
3#include "SkTDict.h"
4#include "SkString.h"
5
6ForthEngine::ForthEngine(ForthOutput* output) : fOutput(output) {
7 size_t size = 32 * sizeof(intptr_t);
8 fStackBase = reinterpret_cast<intptr_t*>(sk_malloc_throw(size));
9 fStackStop = fStackBase + size/sizeof(intptr_t);
10 fStackCurr = fStackStop;
11}
12
13ForthEngine::~ForthEngine() {
14 sk_free(fStackBase);
15}
16
17void ForthEngine::sendOutput(const char text[]) {
18 if (fOutput) {
19 fOutput->show(text);
20 } else {
21 SkDebugf("%s", text);
22 }
23}
24
25///////////////// ints
26
27void ForthEngine::push(intptr_t value) {
28 if (fStackCurr > fStackBase) {
29 SkASSERT(fStackCurr <= fStackStop && fStackCurr > fStackBase);
30 *--fStackCurr = value;
31 } else {
32 this->signal_error("overflow");
33 }
34}
35
36intptr_t ForthEngine::peek(size_t index) const {
37 SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
38 if (fStackCurr + index < fStackStop) {
39 return fStackCurr[index];
40 } else {
41 this->signal_error("peek out of range");
42 return 0x80000001;
43 }
44}
45
46void ForthEngine::setTop(intptr_t value) {
47 if (fStackCurr < fStackStop) {
48 SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
49 *fStackCurr = value;
50 } else {
51 this->signal_error("underflow");
52 }
53}
54
55intptr_t ForthEngine::pop() {
56 if (fStackCurr < fStackStop) {
57 SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
58 return *fStackCurr++;
59 } else {
60 this->signal_error("underflow");
61 return 0x80000001;
62 }
63}
64
65///////////////////////////////////////////////////////////////////////////////
66
67void ForthWord::call(ForthCallBlock* block) {
68 ForthEngine engine(NULL);
69 if (block) {
70 // walk the array backwards, so that the top of the stack is data[0]
71 for (size_t i = 0; i < block->in_count; i++) {
72 engine.push(block->in_data[i]);
73 }
74 }
75 this->exec(&engine);
76 if (block) {
77 size_t n = engine.depth();
78 block->out_depth = n;
79 if (n > block->out_count) {
80 n = block->out_count;
81 }
82 for (size_t i = 0; i < n; i++) {
83 block->out_data[i] = engine.peek(i);
84 }
85 }
86}
87
88///////////////////////////////////////////////////////////////////////////////
89
90class drop_ForthWord : public ForthWord {
91public:
92 virtual void exec(ForthEngine* fe) {
93 (void)fe->pop();
94 }
95};
96
97class clearStack_ForthWord : public ForthWord {
98public:
99 virtual void exec(ForthEngine* fe) {
100 fe->clearStack();
101 }
102};
103
104class dup_ForthWord : public ForthWord {
105public:
106 virtual void exec(ForthEngine* fe) {
107 fe->push(fe->top());
108 }
109};
110
111class swap_ForthWord : public ForthWord {
112public:
113 virtual void exec(ForthEngine* fe) {
114 int32_t a = fe->pop();
115 int32_t b = fe->top();
116 fe->setTop(a);
117 fe->push(b);
118 }
119};
120
121///////////////// ints
122
123class add_ForthWord : public ForthWord {
124public:
125 virtual void exec(ForthEngine* fe) {
126 intptr_t tmp = fe->pop();
127 fe->setTop(fe->top() + tmp);
128 }
129};
130
131class sub_ForthWord : public ForthWord {
132public:
133 virtual void exec(ForthEngine* fe) {
134 intptr_t tmp = fe->pop();
135 fe->setTop(fe->top() - tmp);
136 }
137};
138
139class mul_ForthWord : public ForthWord {
140public:
141 virtual void exec(ForthEngine* fe) {
142 intptr_t tmp = fe->pop();
143 fe->setTop(fe->top() * tmp);
144 }
145};
146
147class div_ForthWord : public ForthWord {
148public:
149 virtual void exec(ForthEngine* fe) {
150 intptr_t tmp = fe->pop();
151 fe->setTop(fe->top() / tmp);
152 }
153};
154
155class dot_ForthWord : public ForthWord {
156public:
157 virtual void exec(ForthEngine* fe) {
158 SkString str;
159 str.printf("%d ", fe->pop());
160 fe->sendOutput(str.c_str());
161 }
162};
163
164class abs_ForthWord : public ForthWord {
165public:
166 virtual void exec(ForthEngine* fe) {
167 int32_t value = fe->top();
168 if (value < 0) {
169 fe->setTop(-value);
170 }
171 }
172};
173
174class min_ForthWord : public ForthWord {
175public:
176 virtual void exec(ForthEngine* fe) {
177 int32_t value = fe->pop();
178 if (value < fe->top()) {
179 fe->setTop(value);
180 }
181 }
182};
183
184class max_ForthWord : public ForthWord {
185public:
186 virtual void exec(ForthEngine* fe) {
187 int32_t value = fe->pop();
188 if (value > fe->top()) {
189 fe->setTop(value);
190 }
191 }
192};
193
194///////////////// floats
195
196class fadd_ForthWord : public ForthWord {
197public:
198 virtual void exec(ForthEngine* fe) {
199 float tmp = fe->fpop();
200 fe->fsetTop(fe->ftop() + tmp);
201 }
202};
203
204class fsub_ForthWord : public ForthWord {
205public:
206 virtual void exec(ForthEngine* fe) {
207 float tmp = fe->fpop();
208 fe->fsetTop(fe->ftop() - tmp);
209 }
210};
211
212class fmul_ForthWord : public ForthWord {
213public:
214 virtual void exec(ForthEngine* fe) {
215 float tmp = fe->fpop();
216 fe->fsetTop(fe->ftop() * tmp);
217 }
218};
219
220class fdiv_ForthWord : public ForthWord {
221public:
222 virtual void exec(ForthEngine* fe) {
223 float tmp = fe->fpop();
224 fe->fsetTop(fe->ftop() / tmp);
225 }
226};
227
228class fdot_ForthWord : public ForthWord {
229public:
230 virtual void exec(ForthEngine* fe) {
231 SkString str;
232 str.printf("%g ", fe->fpop());
233 fe->sendOutput(str.c_str());
234 }
235};
236
237class fabs_ForthWord : public ForthWord {
238public:
239 virtual void exec(ForthEngine* fe) {
240 float value = fe->ftop();
241 if (value < 0) {
242 fe->fsetTop(-value);
243 }
244 }
245};
246
247class fmin_ForthWord : public ForthWord {
248public:
249 virtual void exec(ForthEngine* fe) {
250 float value = fe->fpop();
251 if (value < fe->ftop()) {
252 fe->fsetTop(value);
253 }
254 }
255};
256
257class fmax_ForthWord : public ForthWord {
258public:
259 virtual void exec(ForthEngine* fe) {
260 float value = fe->fpop();
261 if (value > fe->ftop()) {
262 fe->fsetTop(value);
263 }
264 }
265};
266
267class floor_ForthWord : public ForthWord {
268public:
269 virtual void exec(ForthEngine* fe) {
270 fe->fsetTop(floorf(fe->ftop()));
271 }
272};
273
274class ceil_ForthWord : public ForthWord {
275public:
276 virtual void exec(ForthEngine* fe) {
277 fe->fsetTop(ceilf(fe->ftop()));
278 }
279};
280
281class round_ForthWord : public ForthWord {
282public:
283 virtual void exec(ForthEngine* fe) {
284 fe->fsetTop(floorf(fe->ftop() + 0.5f));
285 }
286};
287
288class f2i_ForthWord : public ForthWord {
289public:
290 virtual void exec(ForthEngine* fe) {
291 fe->setTop((int)fe->ftop());
292 }
293};
294
295class i2f_ForthWord : public ForthWord {
296public:
297 virtual void exec(ForthEngine* fe) {
298 fe->fsetTop((float)fe->top());
299 }
300};
301
302///////////////////////////////////////////////////////////////////////////////
303
304/*
305 reading an initial 32bit value from the code stream:
306
307 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx00
308
309 Those last two bits are always 0 for a word, so we set those bits for other
310 opcodes
311
312 00 -- execute this word
313 01 -- push (value & ~3) on the data stack
314 10 -- push value >> 2 on the data stack (sign extended)
315 11 -- switch (value >>> 2) for Code
316 */
317
318class FCode {
319public:
320 enum Bits {
321 kWord_Bits = 0, // must be zero for function address
322 kDataClear2_Bits = 1,
323 kDataShift2_Bits = 2,
324 kCodeShift2_Bits = 3
325 };
326 enum Code {
327 kPushInt_Code,
328 kDone_Code
329 };
330
331 void appendInt(int32_t);
332 void appendWord(ForthWord*);
333 void appendIF() {}
334 bool appendELSE() { return false; }
335 bool appendTHEN() { return false; }
336 void done();
337
338 intptr_t* detach() {
339 this->done();
340 return fData.detach();
341 }
342 intptr_t* begin() {
343 this->done();
344 return fData.begin();
345 }
346
347 static void Exec(const intptr_t*, ForthEngine*);
348
349private:
350 SkTDArray<intptr_t> fData;
351};
352
353void FCode::appendInt(int32_t value) {
354 if ((value & 3) == 0) {
355 *fData.append() = value | kDataClear2_Bits;
356 } else if ((value >> 2 << 2) == value) {
357 *fData.append() = value | kDataShift2_Bits;
358 } else {
359 intptr_t* p = fData.append(2);
360 *p++ = (kPushInt_Code << 2) | kCodeShift2_Bits;
361 *p++ = value;
362 }
363}
364
365void FCode::appendWord(ForthWord* word) {
366 SkASSERT((reinterpret_cast<intptr_t>(word) & 3) == 0);
367 *fData.append() = reinterpret_cast<intptr_t>(word);
368}
369
370void FCode::done() {
371 *fData.append() = (kDone_Code << 2) | kCodeShift2_Bits;
372}
373
374void FCode::Exec(const intptr_t* curr, ForthEngine* engine) {
375 for (;;) {
376 intptr_t c = *curr++;
377 switch (c & 3) {
378 case kWord_Bits:
379 reinterpret_cast<ForthWord*>(c)->exec(engine);
380 break;
381 case kDataClear2_Bits:
382 engine->push(c & ~3);
383 break;
384 case kDataShift2_Bits:
385 engine->push(c >> 2);
386 break;
387 case kCodeShift2_Bits:
388 switch ((uint32_t)c >> 2) {
389 case kPushInt_Code:
390 engine->push(*curr++);
391 break;
392 case kDone_Code:
393 return;
394 }
395 break;
396 }
397 }
398}
399
400///////////////////////////////////////////////////////////////////////////////
401
402class CustomWord : public ForthWord {
403public:
404 // we assume ownership of code[]
405 CustomWord(intptr_t code[]) : fCode(code) {}
406 virtual ~CustomWord() { sk_free(fCode); }
407
408 virtual void exec(ForthEngine* engine) {
409 FCode::Exec(fCode, engine);
410 }
411
412private:
413 intptr_t* fCode;
414};
415
416///////////////////////////////////////////////////////////////////////////////
417
418class ForthParser {
419public:
420 ForthParser() : fDict(4096) {
421 this->addStdWords();
422 }
423
424 const char* parse(const char text[], FCode*);
425
426 void addWord(const char name[], ForthWord* word) {
427 this->add(name, strlen(name), word);
428 }
429
430 ForthWord* find(const char name[], size_t len) const {
431 ForthWord* word;
432 return fDict.find(name, len, &word) ? word : NULL;
433 }
434
435private:
436 void add(const char name[], size_t len, ForthWord* word) {
437 (void)fDict.set(name, len, word);
438 }
439
440 void addStdWords() {
441 this->add("clr", 3, new clearStack_ForthWord);
442 this->add("drop", 4, new drop_ForthWord);
443 this->add("dup", 3, new dup_ForthWord);
444 this->add("swap", 4, new swap_ForthWord);
445
446 this->add("+", 1, new add_ForthWord);
447 this->add("-", 1, new sub_ForthWord);
448 this->add("*", 1, new mul_ForthWord);
449 this->add("/", 1, new div_ForthWord);
450 this->add(".", 1, new dot_ForthWord);
451 this->add("abs", 3, new abs_ForthWord);
452 this->add("min", 3, new min_ForthWord);
453 this->add("max", 3, new max_ForthWord);
454
455 this->add("f+", 2, new fadd_ForthWord);
456 this->add("f-", 2, new fsub_ForthWord);
457 this->add("f*", 2, new fmul_ForthWord);
458 this->add("f/", 2, new fdiv_ForthWord);
459 this->add("f.", 2, new fdot_ForthWord);
460 this->add("fabs", 4, new fabs_ForthWord);
461 this->add("fmin", 4, new fmin_ForthWord);
462 this->add("fmax", 4, new fmax_ForthWord);
463 this->add("fmax", 4, new fmax_ForthWord);
464 this->add("floor", 5, new floor_ForthWord);
465 this->add("ceil", 4, new ceil_ForthWord);
466 this->add("round", 5, new round_ForthWord);
467 this->add("f>i", 3, new f2i_ForthWord);
468 this->add("i>f", 3, new i2f_ForthWord);
469 }
470
471 SkTDict<ForthWord*> fDict;
472};
473
474static const char* parse_error(const char msg[]) {
475 SkDebugf("-- parser error: %s\n", msg);
476 return NULL;
477}
478
479/** returns true if c is whitespace, including null
480 */
481static bool is_ws(int c) {
482 return c <= ' ';
483}
484
485static const char* parse_token(const char** text, size_t* len) {
486 const char* s = *text;
487 while (is_ws(*s)) {
488 if (0 == *s) {
489 return NULL;
490 }
491 s++;
492 }
493 const char* token = s++;
494 while (!is_ws(*s)) {
495 s++;
496 }
497 *text = s;
498 *len = s - token;
499 return token;
500}
501
502static bool is_digit(int c) { return (unsigned)(c - '0') <= 9; }
503static int hex_val(int c) {
504 if (is_digit(c)) {
505 return c - '0';
506 } else {
507 if (c <= 'Z') {
508 return 10 + c - 'A';
509 } else {
510 return 10 + c - 'a';
511 }
512 }
513}
514
515static bool parse_num(const char str[], size_t len, int32_t* numBits) {
516 if (1 == len && !is_digit(*str)) {
517 return false;
518 }
519 const char* start = str;
520 int32_t num = 0;
521 bool neg = false;
522 if (*str == '-') {
523 neg = true;
524 str += 1;
525 } else if (*str == '#') {
526 str++;
527 while (str - start < len) {
528 num = num*16 + hex_val(*str);
529 str += 1;
530 }
531 *numBits = num;
532 return true;
533 }
534
535 while (is_digit(*str)) {
536 num = 10*num + *str - '0';
537 str += 1;
538 }
539 SkASSERT(str - start <= len);
540 if (str - start == len) {
541 if (neg) {
542 num = -num;
543 }
544 *numBits = num;
545 return true;
546 }
547 // if we're not done with the token then the next char must be a decimal
548 if (*str != '.') {
549 return false;
550 }
551 str += 1;
552 float x = num;
553 float denom = 1;
554 while (str - start < len && is_digit(*str)) {
555 x = 10*x + *str - '0';
556 denom *= 10;
557 str += 1;
558 }
559 x /= denom;
560 if (str - start == len) {
561 if (neg) {
562 x = -x;
563 }
564 *numBits = f2i_bits(x);
565 return true;
566 }
567 return false;
568}
569
570static const char* parse_comment(const char text[]) {
571 SkASSERT(*text == '(');
572 while (')' != *++text) {
573 if (0 == *text) {
574 return NULL;
575 }
576 }
577 return text + 1; // skip past the closing ')'
578}
579
580const char* ForthParser::parse(const char text[], FCode* code) {
581 for (;;) {
582 size_t len;
583 const char* token = parse_token(&text, &len);
584 if (NULL == token) {
585 break;
586 }
587 if (1 == len) {
588 if ('(' == *token) {
589 text = parse_comment(token);
590 if (NULL == text) {
591 return NULL;
592 }
593 continue;
594 }
595 if (';' == *token) {
596 break;
597 }
598 if (':' == *token) {
599 token = parse_token(&text, &len);
600 if (NULL == token) {
601 return parse_error("missing name after ':'");
602 }
603 FCode subCode;
604 text = this->parse(text, &subCode);
605 if (NULL == text) {
606 return NULL;
607 }
608 this->add(token, len, new CustomWord(subCode.detach()));
609 continue;
610 }
611 }
612 int32_t num;
613 if (parse_num(token, len, &num)) {
614 // note that num is just the bit representation of the float
615 code->appendInt(num);
616 } else if (2 == len && memcmp(token, "IF", 2) == 0) {
617 code->appendIF();
618 } else if (2 == len && memcmp(token, "ELSE", 4) == 0) {
619 if (!code->appendELSE()) {
620 return parse_error("ELSE with no matching IF");
621 }
622 } else if (2 == len && memcmp(token, "THEN", 4) == 0) {
623 if (!code->appendTHEN()) {
624 return parse_error("THEN with no matching IF");
625 }
626 } else{
627 ForthWord* word = this->find(token, len);
628 if (NULL == word) {
629 SkString str(token, len);
630 str.prepend("unknown word ");
631 return parse_error(str.c_str());
632 }
633 code->appendWord(word);
634 }
635 }
636 return text;
637}
638
639///////////////////////////////////////////////////////////////////////////////
640
641class ForthEnv::Impl {
642public:
643 ForthParser fParser;
644 FCode fBuilder;
645};
646
647ForthEnv::ForthEnv() {
648 fImpl = new Impl;
649}
650
651ForthEnv::~ForthEnv() {
652 delete fImpl;
653}
654
655void ForthEnv::addWord(const char name[], ForthWord* word) {
656 fImpl->fParser.addWord(name, word);
657}
658
659void ForthEnv::parse(const char text[]) {
660 fImpl->fParser.parse(text, &fImpl->fBuilder);
661}
662
663ForthWord* ForthEnv::findWord(const char name[]) {
664 return fImpl->fParser.find(name, strlen(name));
665}
666
667void ForthEnv::run(ForthOutput* output) {
668 ForthEngine engine(output);
669 FCode::Exec(fImpl->fBuilder.begin(), &engine);
670}
671
672#if 0
673void ForthEnv::run(const char text[], ForthOutput* output) {
674 FCode builder;
675
676 if (fImpl->fParser.parse(text, &builder)) {
677 ForthEngine engine(output);
678 FCode::Exec(builder.begin(), &engine);
679 }
680}
681#endif
682