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
ForthEngine(ForthOutput * output)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
~ForthEngine()21 ForthEngine::~ForthEngine() {
22 sk_free(fStackBase);
23 }
24
sendOutput(const char text[])25 void ForthEngine::sendOutput(const char text[]) {
26 if (fOutput) {
27 fOutput->show(text);
28 } else {
29 SkDebugf("%s", text);
30 }
31 }
32
push(intptr_t value)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
peek(size_t index) const42 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
setTop(intptr_t value)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
pop()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
call(ForthCallBlock * block)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 };
GetCode(intptr_t c)123 static unsigned GetCode(intptr_t c) {
124 return ((uint32_t)c >> kCodeShift) & kCodeMask;
125 }
GetCodeData(intptr_t c)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 };
MakeCode(Code code)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
detach()154 intptr_t* detach() {
155 this->done();
156 return fData.detach();
157 }
begin()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
appendInt(int32_t value)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
appendWord(ForthWord * word)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
appendIF()187 void FCode::appendIF() {
188 size_t ifIndex = fData.count();
189 fIfStack.push(ifIndex);
190 *fData.append() = MakeCode(kIF_Code);
191 }
192
appendELSE()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
appendTHEN()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
done()224 void FCode::done() {
225 *fData.append() = (kDone_Code << 2) | kCodeShift2_Bits;
226 }
227
Exec(const intptr_t * curr,ForthEngine * engine)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[]
CustomWord(intptr_t code[])269 CustomWord(intptr_t code[]) : fCode(code) {}
~CustomWord()270 virtual ~CustomWord() { sk_free(fCode); }
271
exec(ForthEngine * engine)272 virtual void exec(ForthEngine* engine) {
273 FCode::Exec(fCode, engine);
274 }
275
276 private:
277 intptr_t* fCode;
278 };
279
280 ///////////////////////////////////////////////////////////////////////////////
281
ForthParser()282 ForthParser::ForthParser() : fDict(4096) {
283 this->addStdWords();
284 }
285
~ForthParser()286 ForthParser::~ForthParser() {
287 SkTDict<ForthWord*>::Iter iter(fDict);
288 ForthWord* word;
289 while (iter.next(&word)) {
290 delete word;
291 }
292 }
293
parse_error(const char msg[])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 */
is_ws(int c)301 static bool is_ws(int c) {
302 return c <= ' ';
303 }
304
parse_token(const char ** text,size_t * len)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
is_digit(int c)322 static bool is_digit(int c) { return (unsigned)(c - '0') <= 9; }
hex_val(int c)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
parse_num(const char str[],size_t len,int32_t * numBits)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
parse_comment(const char text[])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
parse(const char text[],FCode * code)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
ForthEnv()467 ForthEnv::ForthEnv() {
468 fImpl = new Impl;
469 }
470
~ForthEnv()471 ForthEnv::~ForthEnv() {
472 delete fImpl;
473 }
474
addWord(const char name[],ForthWord * word)475 void ForthEnv::addWord(const char name[], ForthWord* word) {
476 fImpl->fParser.addWord(name, word);
477 }
478
parse(const char text[])479 void ForthEnv::parse(const char text[]) {
480 fImpl->fParser.parse(text, &fImpl->fBuilder);
481 }
482
findWord(const char name[])483 ForthWord* ForthEnv::findWord(const char name[]) {
484 return fImpl->fParser.find(name, strlen(name));
485 }
486
run(ForthOutput * output)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