/
toby_app.c
1395 lines (1139 loc) · 36.6 KB
1
2
3
4
5
6
7
8
/*
* Toby -- A programming language for learning.
* Copyright (C) 2007 Ryan C. Gordon.
*
* Please refer to LICENSE.txt in the root directory of the source
* distribution for licensing details.
*/
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
14
#include <ctype.h> /* !!! FIXME: lose this with tolower/toupper... */
15
16
#include "toby_app.h"
17
18
19
20
21
22
23
24
25
26
typedef enum TobyExecState
{
EXEC_STOPPED=0,
EXEC_STOPPING,
EXEC_RUNNING,
EXEC_STEPPING,
EXEC_PAUSED,
} TobyExecState;
27
/* TurtlesSpace state... */
28
29
static int currentTurtleIndex = -1;
static int totalTurtles = 0;
30
static Turtle *turtles = NULL;
31
static int fenceEnabled = 1;
32
static int halted = 0;
33
static TurtleRGB background = { 0, 0, 0 };
34
static TobyDebugInfo *callstack = NULL;
35
static int callstackCount = 0;
36
37
static TobyDebugInfo *varList = NULL;
static int varCount = 0;
38
static lua_State *luaState = NULL;
39
40
41
42
43
static int turtleSpaceIsDirty = 0;
static int executingLine = 0;
static TobyExecState execState = EXEC_STOPPED;
static long delayPerLine = 0;
static long nextPumpTicks = 0;
44
45
46
47
48
49
50
51
52
53
void TOBY_background(int *r, int *g, int *b)
{
*r = background.r;
*g = background.g;
*b = background.b;
} /* TOBY_background */
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static inline void throwError(lua_State *L, const char *err)
{
lua_pushstring(L, err);
lua_error(L);
} /* throwError */
static inline void haltProgram(lua_State *L)
{
halted = 1;
throwError(L, "program halted");
} /* haltProgram */
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
* All this math code had brilliant comments explaining it, but as proud as I
* was to figure it out in high school...really, it's fundamental
* mathematical wankery. Check the repository or Google it.
*/
static inline lua_Number degreesToRadians(lua_Number degrees)
{
/* !!! FIXME: fixed point... */
return(degrees * (M_PI / 180.0));
} /* degreesToRadians */
static inline lua_Number radiansToDegrees(lua_Number radians)
{
/* !!! FIXME: fixed point... */
return(radians * (180.0 / M_PI));
} /* degreesToRadians */
87
88
89
/* This can be optimized into one FPU operation on x86 chips. */
static inline void sinAndCos(lua_Number x, lua_Number *sval, lua_Number *cval)
{
90
91
/* !!! FIXME: x86 version */
/* !!! FIXME: fixed point? */
92
93
94
95
96
*sval = sin(x);
*cval = cos(x);
} /* sinAndCos */
97
98
99
100
static inline void calculateLine(lua_Number heading, lua_Number distance,
lua_Number startX, lua_Number startY,
lua_Number *endX, lua_Number *endY)
{
101
const lua_Number rad = degreesToRadians(heading);
102
lua_Number sinrad, cosrad;
103
104
/* !!! FIXME: fixed point... */
105
106
107
sinAndCos(rad, &sinrad, &cosrad);
*endY = (sinrad * distance) + startY;
*endX = (cosrad * distance) + startX;
108
109
110
} /* calculateLine */
111
#if 0 /* not used, yet. */
112
113
114
115
116
static inline lua_Number pythagorian(lua_Number s1, lua_Number hypotenuse)
{
/* !!! FIXME: fixed point... */
return(sqrt((hypotenuse * hypotenuse) - (s1 * s1)));
} /* pythagorian */
117
#endif
118
119
120
121
122
123
124
125
126
127
/* convert a lua_Number between 0.0 and 1.0 to 0 to 255. */
static inline int to8bit(lua_Number x)
{
/* !!! FIXME: fixed point... */
return (int) (x * N(255));
} /* to8bit */
128
129
130
131
132
static inline int secsToMs(lua_Number secs)
{
/* !!! FIXME: fixed point... */
return (int) (secs * N(1000));
} /* secsToMs */
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/* Liang-Barsky line clipping algorithm implementation. */
static int clip(lua_Number *minimum, lua_Number *maximum,
lua_Number directedProjection, lua_Number directedDistance)
{
if (directedProjection == N(0))
{
if (directedDistance < N(0))
return 0;
} /* if */
else
{
const lua_Number amount = directedDistance / directedProjection;
if (directedProjection < N(0)) {
if (amount > *maximum)
return 0;
else if (amount > *minimum)
*minimum = amount;
} /* if */
else
{
if (amount < *minimum)
return 0;
else if (amount < *maximum)
*maximum = amount;
} /* else */
} /* else */
return 1;
} /* clip */
int TOBY_clipLine(lua_Number *_x1, lua_Number *_y1,
lua_Number *_x2, lua_Number *_y2,
lua_Number w, lua_Number h)
{
const lua_Number x1 = *_x1;
const lua_Number y1 = *_y1;
const lua_Number x2 = *_x2;
const lua_Number y2 = *_y2;
const lua_Number px = x2 - x1;
const lua_Number py = y2 - y1;
lua_Number minimum = N(0);
lua_Number maximum = N(1);
if (clip(&minimum, &maximum, -px, x1 - N(0)))
{
if (clip(&minimum, &maximum, px, w - x1))
{
if (clip(&minimum, &maximum, -py, y1 - N(0)))
{
if (clip(&minimum, &maximum, py, h - y1))
{
if (maximum < N(1))
{
*_x2 = x1 + maximum * px;
*_y2 = y1 + maximum * py;
} /* if */
if (minimum > N(0))
{
*_x1 += minimum * px;
*_y1 += minimum * py;
} /* if */
return 1;
} /* if */
} /* if */
} /* if */
} /* if */
return 0;
} /* TOBY_clipLine */
207
208
209
210
static inline int checkWholeNum(lua_State *L, int idx)
{
const lua_Number num = luaL_checknumber(L, idx);
const int intnum = (int) num;
211
212
if ( ((lua_Number) intnum) != num )
throwError(L, "Expected whole number");
213
214
215
216
217
return intnum;
} /* checkWholeNum */
218
static int allocateTurtle(lua_State *L)
219
{
220
221
222
Turtle *ptr;
ptr = (Turtle *) realloc(turtles, sizeof (Turtle) * (totalTurtles + 1));
223
if (ptr == NULL)
224
throwError(L, "Out of memory");
225
226
227
turtles = ptr;
ptr = &turtles[totalTurtles];
228
229
230
231
memset(ptr, '\0', sizeof (Turtle));
ptr->pos.x = ptr->pos.y = N(500); /* center of turtlespace. */
ptr->width = ptr->height = N(20);
232
ptr->pen.r = ptr->pen.g = ptr->pen.b = 255; /* white. */
233
234
235
236
ptr->angle = 270; /* due north. */
ptr->penDown = 1;
ptr->recalcPoints = 1;
ptr->visible = 1;
237
238
239
return totalTurtles++;
} /* allocateTurtle */
240
241
242
static inline Turtle *getTurtle(lua_State *L)
243
{
244
if (currentTurtleIndex < 0)
245
throwError(L, "No current turtle");
246
return &turtles[currentTurtleIndex];
247
248
249
} /* getTurtle */
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
* See comments in Turtle::calcTriangle() for detailed discussion:
* http://svn.icculus.org/toby/branches/toby_rewrite_4/src/turtlespace/Turtle.cpp?view=markup
*/
static inline void calculateTurtleTriangle(Turtle *turtle)
{
if (turtle->recalcPoints)
{
const lua_Number w = turtle->width;
const lua_Number h = turtle->height;
const lua_Number angle = turtle->angle;
const lua_Number halfW = w / N(2);
TurtlePoint *pt = turtle->points;
lua_Number tailX, tailY;
calculateLine(angle + N(180), h / N(2), N(0), N(0), &tailX, &tailY);
calculateLine(angle, h, tailX, tailY, &pt[0].x, &pt[0].y);
calculateLine(angle + N(90), halfW, tailX, tailY, &pt[1].x, &pt[1].y);
calculateLine(angle - N(90), halfW, tailX, tailY, &pt[2].x, &pt[2].y);
pt[3].x = tailX;
pt[3].y = tailY;
turtle->recalcPoints = 0;
} /* if */
} /* calculateTurtleTriangle */
277
void TOBY_renderAllTurtles(void *udata)
278
279
{
int i;
280
int drewAtLeastOne = 0;
281
282
for (i = 0; i < totalTurtles; i++)
{
283
284
285
Turtle *turtle = &turtles[i];
calculateTurtleTriangle(turtle);
if (turtle->visible)
286
{
287
TOBY_drawTurtle(turtle, udata);
288
289
drewAtLeastOne = 1;
} /* if */
290
} /* for */
291
292
293
if ((drewAtLeastOne) && (udata == NULL))
turtleSpaceIsDirty = 1; /* put to backing store, must repaint... */
294
295
296
} /* TOBY_renderAllTurtles */
297
298
299
300
301
302
303
304
305
306
static inline void putToScreen(void)
{
if (turtleSpaceIsDirty)
{
TOBY_putToScreen();
turtleSpaceIsDirty = 0;
} /* if */
} /* putToScreen */
307
static void setTurtleAngle(lua_State *L, lua_Number angle)
308
{
309
Turtle *turtle = getTurtle(L);
310
311
if (angle != turtle->angle)
{
312
/* !!! FIXME: ugh, use modulus... */
313
while (angle >= N(360)) angle -= N(360);
314
315
while (angle < N(0)) angle += N(360);
turtle->angle = angle;
316
turtle->recalcPoints = 1;
317
318
if (turtle->visible)
turtleSpaceIsDirty = 1;
319
320
321
322
323
324
} /* if */
} /* setTurtleAngle */
static int luahook_setangle(lua_State *L)
{
325
setTurtleAngle(L, luaL_checknumber(L, 1));
326
327
328
329
return 0;
} /* luahook_setangle */
330
static inline void turnTurtle(lua_State *L, lua_Number degree)
331
{
332
Turtle *turtle = getTurtle(L);
333
if (degree != N(0))
334
setTurtleAngle(L, turtle->angle + degree);
335
336
337
338
339
} /* turnTurtle */
static int luahook_turnright(lua_State *L)
{
340
turnTurtle(L, luaL_checknumber(L, 1));
341
342
343
344
345
346
return 0;
} /* luahook_turnright */
static int luahook_turnleft(lua_State *L)
{
347
turnTurtle(L, -luaL_checknumber(L, 1));
348
349
350
351
return 0;
} /* luahook_turnleft */
352
static inline void testFence(lua_State *L, const Turtle *turtle)
353
{
354
355
356
357
358
359
360
361
362
363
const lua_Number x = turtle->pos.x;
const lua_Number y = turtle->pos.y;
if ( (x < N(0)) || (x > N(1000)) || (y < N(0)) || (y > N(1000)) )
throwError(L, "Turtle outside fence");
} /* testFence */
static void setTurtleXY(lua_State *L, lua_Number x, lua_Number y)
{
Turtle *turtle = getTurtle(L);
364
365
turtle->pos.x = x;
turtle->pos.y = y;
366
367
if (fenceEnabled)
368
testFence(L, turtle);
369
370
371
372
373
374
375
} /* setTurtleXY */
static int luahook_setturtlexy(lua_State *L)
{
const lua_Number x = luaL_checknumber(L, 1);
const lua_Number y = luaL_checknumber(L, 2);
376
setTurtleXY(L, x, y);
377
378
379
380
381
382
return 0;
} /* luahook_setturtlexy */
static int luahook_hometurtle(lua_State *L)
{
383
setTurtleXY(L, N(500), N(500));
384
385
return 0;
} /* luahook_hometurtle */
386
387
388
static void driveTurtle(lua_State *L, lua_Number distance)
389
{
390
Turtle *turtle = getTurtle(L);
391
392
if (distance != N(0))
{
393
394
lua_Number x1 = turtle->pos.x;
lua_Number y1 = turtle->pos.y;
395
lua_Number unclipped_x2, unclipped_y2;
396
397
398
lua_Number x2, y2;
calculateLine(turtle->angle, distance, x1, y1, &x2, &y2);
399
400
unclipped_x2 = x2;
unclipped_y2 = y2;
401
402
if (turtle->penDown) /* draw the line covering path turtle took? */
{
403
/* only draw if SOMETHING is inside TurtleSpace... */
404
if (TOBY_clipLine(&x1, &y1, &x2, &y2, N(999), N(999)))
405
{
406
407
const TurtleRGB *pen = &turtle->pen;
TOBY_drawLine(x1, y1, x2, y2, pen->r, pen->g, pen->b);
408
turtleSpaceIsDirty = 1;
409
} /* if */
410
} /* if */
411
412
setTurtleXY(L, unclipped_x2, unclipped_y2);
413
414
415
416
417
418
} /* if */
} /* driveTurtle */
static int luahook_goforward(lua_State *L)
{
419
driveTurtle(L, luaL_checknumber(L, 1));
420
421
422
423
424
425
return 0;
} /* luahook_goforward */
static int luahook_gobackward(lua_State *L)
{
426
driveTurtle(L, -luaL_checknumber(L, 1));
427
428
429
430
return 0;
} /* luahook_gobackward */
431
static int luahook_getturtlex(lua_State *L)
432
{
433
lua_pushnumber(L, getTurtle(L)->pos.x);
434
435
return 1;
} /* luahook_getturtlex */
436
437
438
439
static int luahook_getturtley(lua_State *L)
{
440
lua_pushnumber(L, getTurtle(L)->pos.y);
441
442
return 1;
} /* luahook_getturtley */
443
444
445
static int luahook_getturtlespacewidth(lua_State *L)
446
{
447
lua_pushnumber(L, N(1000)); /* !!! FIXME: allow this to change? */
448
449
return 1;
} /* luahook_getturtlespacewidth */
450
451
452
static int luahook_getturtlespaceheight(lua_State *L)
453
{
454
lua_pushnumber(L, N(1000)); /* !!! FIXME: allow this to change? */
455
456
457
458
459
460
return 1;
} /* luahook_getturtlespaceheight */
static int luahook_setpenup(lua_State *L)
{
461
getTurtle(L)->penDown = 0;
462
return 0;
463
} /* luahook_setpenup */
464
465
466
static int luahook_setpendown(lua_State *L)
467
{
468
getTurtle(L)->penDown = 1;
469
return 0;
470
} /* luahook_setpendown */
471
472
473
static inline void setPenColorRGB(lua_State *L, int r, int g, int b)
474
{
475
TurtleRGB *pen = &(getTurtle(L)->pen);
476
477
478
pen->r = r;
pen->g = g;
pen->b = b;
479
480
481
482
483
484
485
486
487
488
} /* setPenColorRGB */
static int luahook_setpencolorrgb(lua_State *L)
{
const lua_Number r = luaL_checknumber(L, 1);
const lua_Number g = luaL_checknumber(L, 2);
const lua_Number b = luaL_checknumber(L, 3);
if ( (r < N(0)) || (r > N(1)) )
489
throwError(L, "Red value is not between 0.0 and 1.0");
490
else if ( (g < N(0)) || (g > N(1)) )
491
throwError(L, "Green value is not between 0.0 and 1.0");
492
else if ( (b < N(0)) || (b > N(1)) )
493
throwError(L, "Blue value is not between 0.0 and 1.0");
494
else
495
setPenColorRGB(L, to8bit(r), to8bit(g), to8bit(b));
496
497
498
499
500
501
502
503
504
505
506
507
508
509
return 0;
} /* luahook_setpencolorrgb */
static int luahook_setpencolor(lua_State *L)
{
/*
* How I got these numbers:
* These color choices are ripped directly from QuickBasic's
* COLOR command in text mode. So, (1) is dark blue, (2) is
* dark green, etc...Then I used the GIMP color tool
* (http://www.gimp.org/), to find the equivalents in RGB format.
*/
510
static const TurtleRGB colors[] =
511
{
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
{ 0, 0, 0 }, /* black */
{ 0, 26, 196 }, /* dark blue */
{ 0, 153, 45 }, /* dark green */
{ 0, 144, 138 }, /* dark cyan */
{ 160, 0, 0 }, /* dark red */
{ 197, 0, 202 }, /* dark magenta */
{ 129, 90, 16 }, /* brown */
{ 200, 200, 200 }, /* gray */
{ 90, 90, 90 }, /* dark gray */
{ 0, 200, 255 }, /* bright blue */
{ 0, 90, 38 }, /* bright green */
{ 91, 110, 255 }, /* bright cyan */
{ 255, 0, 0 }, /* bright red */
{ 255, 0, 255 }, /* bright pink */
{ 255, 255, 0 }, /* bright yellow */
{ 255, 255, 255 }, /* bright white */
528
529
530
531
};
const int color = checkWholeNum(L, 1);
if ( (color < 0) || (color >= (STATICARRAYLEN(colors))) )
532
throwError(L, "Color value is not between 0 and 15");
533
else
534
setPenColorRGB(L, colors[color].r, colors[color].g, colors[color].b);
535
536
537
538
return 0;
} /* luahook_setpencolor */
539
540
static int luahook_showturtle(lua_State *L)
{
541
Turtle *turtle = getTurtle(L);
542
543
544
545
546
if (!turtle->visible)
{
turtle->visible = 1;
turtleSpaceIsDirty = 1;
} /* if */
547
548
549
550
551
552
return 0;
} /* luahook_showturtle */
static int luahook_hideturtle(lua_State *L)
{
553
Turtle *turtle = getTurtle(L);
554
555
556
557
558
if (turtle->visible)
{
turtle->visible = 0;
turtleSpaceIsDirty = 1;
} /* if */
559
560
561
562
563
564
return 0;
} /* luahook_hideturtle */
static int luahook_enablefence(lua_State *L)
{
565
566
int i;
567
fenceEnabled = 1;
568
569
for (i = 0; i < totalTurtles; i++)
570
testFence(L, &turtles[i]);
571
572
573
574
575
576
577
578
579
580
581
582
583
584
return 0;
} /* luahook_enablefence */
static int luahook_disablefence(lua_State *L)
{
fenceEnabled = 0;
return 0;
} /* luahook_disablefence */
static int luahook_cleanupturtlespace(lua_State *L)
{
585
/* !!! FIXME: let user choose color? */
586
TOBY_cleanup(background.r, background.g, background.b);
587
turtleSpaceIsDirty = 1;
588
589
590
591
return 0;
} /* luahook_getturtlespaceheight */
592
/* !!! FIXME: all must handle utf8.... */
593
594
595
static int luahook_stringlength(lua_State *L)
{
596
/* !!! FIXME: this is doing bytes, not chars... */
597
598
599
600
601
lua_pushinteger(L, (lua_Integer) strlen(luaL_checklstring(L, 1, NULL)));
return 1;
} /* luahook_stringlength */
602
603
604
605
606
607
608
static int luahook_leftstring(lua_State *L)
{
const char *str = luaL_checklstring(L, 1, NULL);
const size_t origLen = strlen(str);
int charCount = checkWholeNum(L, 2);
if (charCount < 0)
throwError(L, "Negative string length");
609
else if ( ((size_t) charCount) > origLen )
610
611
charCount = origLen;
612
lua_pushlstring(L, str, charCount);
613
614
615
616
617
618
619
620
621
622
623
return 1;
} /* luahook_leftstring */
static int luahook_rightstring(lua_State *L)
{
const char *str = luaL_checklstring(L, 1, NULL);
const size_t origLen = strlen(str);
int charCount = checkWholeNum(L, 2);
if (charCount < 0)
throwError(L, "Negative string length");
624
else if ( ((size_t) charCount) > origLen)
625
626
627
628
629
630
631
632
633
charCount = origLen;
lua_pushstring(L, str + (origLen - charCount));
return 1;
} /* luahook_rightstring */
static int luahook_joinstrings(lua_State *L)
{
634
/* !!! FIXME: this code sucks. */
635
636
637
638
639
640
const char *str1 = luaL_checklstring(L, 1, NULL);
const char *str2 = luaL_checklstring(L, 2, NULL);
char *buf = (char *) malloc(strlen(str1) + strlen(str2) + 1);
if (buf == NULL)
throwError(L, "Out of memory");
strcpy(buf, str1);
641
strcat(buf, str2);
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
lua_pushstring(L, buf);
free(buf);
return 1;
} /* luahook_joinstrings */
static int luahook_substring(lua_State *L)
{
const char *str = luaL_checklstring(L, 1, NULL);
size_t origLen = strlen(str);
const int pos = checkWholeNum(L, 2);
int charCount = checkWholeNum(L, 3);
if (charCount < 0)
throwError(L, "Negative string length");
if (pos < 0)
659
throwError(L, "Negative string position"); /* !!! FIXME: just clamp instead? */
660
661
662
663
664
665
origLen -= pos;
if (origLen <= 0)
lua_pushstring(L, "");
else
{
666
667
if ( ((size_t) charCount) > origLen)
charCount = (int) origLen;
668
lua_pushlstring(L, str+pos, charCount);
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
} /* else */
return 1;
} /* luahook_substring */
static int luahook_uppercasestring(lua_State *L)
{
const char *str = luaL_checklstring(L, 1, NULL);
char *buf = (char *) alloca(strlen(str) + 1);
while (*str)
*(buf++) = toupper(*(str++));
*buf = '\0';
lua_pushstring(L, buf);
return 1;
} /* luahook_uppercasestring */
static int luahook_lowercasestring(lua_State *L)
{
const char *str = luaL_checklstring(L, 1, NULL);
char *buf = (char *) alloca(strlen(str) + 1);
while (*str)
*(buf++) = tolower(*(str++));
*buf = '\0';
lua_pushstring(L, buf);
return 1;
} /* luahook_lowercasestring */
699
700
static int luahook_drawstring(lua_State *L)
{
701
702
const Turtle *turtle = getTurtle(L);
const char *utf8str = luaL_checklstring(L, 1, NULL);
703
704
705
706
707
708
if (!TOBY_drawString(turtle->pos.x, turtle->pos.y, utf8str, turtle->angle,
turtle->pen.r, turtle->pen.g, turtle->pen.b))
{
throwError(L, "Platform doesn't support string drawing");
} /* if */
709
turtleSpaceIsDirty = 1;
710
711
712
713
714
715
return 0;
} /* luahook_drawstring */
static int luahook_random(lua_State *L)
{
716
717
718
#ifdef _MSC_VER
const long val = rand(); /* !!! FIXME: seed this. */
#else
719
const long val = random(); /* !!! FIXME: seed this. */
720
#endif
721
722
/* !!! FIXME: fixed point */
/* !!! FIXME: nasty double code. */
723
724
const lua_Number flt = (lua_Number) (((double)val) / ((double)RAND_MAX));
lua_pushnumber(L, flt);
725
726
727
728
729
730
return 1;
} /* luahook_random */
static int luahook_round(lua_State *L)
{
731
/* !!! FIXME: fixed point. */
732
lua_pushinteger(L, (lua_Integer) (luaL_checknumber(L, 1) + 0.5));
733
734
735
736
return 1;
} /* luahook_setpendown */
737
738
739
740
int TOBY_delay(long ms)
{
long now = TOBY_getTicks();
const long end = now + ms;
741
do
742
{
743
TOBY_pumpEvents();
744
745
746
747
748
749
750
now = TOBY_getTicks();
if (now < end)
{
const long ticks = end - now;
TOBY_yieldCPU((ticks > 50) ? 50 : ticks);
now = TOBY_getTicks();
} /* if */
751
} while (now < end);
752
753
return (TOBY_isRunning() && !TOBY_isStopping());
754
755
756
} /* TOBY_delay */
757
758
759
static int luahook_pause(lua_State *L)
{
const lua_Number secs = luaL_checknumber(L, 1);
760
if (!TOBY_delay(secsToMs(secs)))
761
haltProgram(L);
762
763
764
765
766
767
return 0;
} /* luahook_pause */
static int luahook_addturtle(lua_State *L)
{
768
allocateTurtle(L);
769
770
771
772
773
774
return 0;
} /* luahook_addturtle */
static int luahook_useturtle(lua_State *L)
{
775
776
777
778
779
const int newidx = checkWholeNum(L, 1);
if ((newidx < 0) || (newidx >= totalTurtles))
throwError(L, "Not a valid turtle");
currentTurtleIndex = newidx;
780
781
782
783
return 0;
} /* luahook_useturtle */
784
785
786
787
788
789
790
static int luahook_print(lua_State *L)
{
printf("%s\n", luaL_checklstring(L, 1, NULL));
return 0;
} /* luahook_print */
791
792
793
794
795
static void add_toby_functions(lua_State *L)
{
#define SET_LUAHOOK(sym) \
lua_pushcfunction(L, luahook_##sym); \
lua_setglobal(L, #sym);
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
SET_LUAHOOK(showturtle);
SET_LUAHOOK(hideturtle);
SET_LUAHOOK(hometurtle);
SET_LUAHOOK(enablefence);
SET_LUAHOOK(disablefence);
SET_LUAHOOK(drawstring);
SET_LUAHOOK(random);
SET_LUAHOOK(getturtlespacewidth);
SET_LUAHOOK(getturtlespaceheight);
SET_LUAHOOK(getturtlex);
SET_LUAHOOK(getturtley);
SET_LUAHOOK(cleanupturtlespace);
SET_LUAHOOK(setpencolorrgb);
SET_LUAHOOK(setpenup);
SET_LUAHOOK(setpendown);
SET_LUAHOOK(addturtle);
SET_LUAHOOK(useturtle);
SET_LUAHOOK(round);
SET_LUAHOOK(stringlength);
815
816
817
818
819
820
SET_LUAHOOK(rightstring);
SET_LUAHOOK(leftstring);
SET_LUAHOOK(substring);
SET_LUAHOOK(uppercasestring);
SET_LUAHOOK(lowercasestring);
SET_LUAHOOK(joinstrings);
821
822
SET_LUAHOOK(pause);
SET_LUAHOOK(setangle);
823
824
825
826
827
828
SET_LUAHOOK(goforward);
SET_LUAHOOK(gobackward);
SET_LUAHOOK(turnright);
SET_LUAHOOK(turnleft);
SET_LUAHOOK(setpencolor);
SET_LUAHOOK(setturtlexy);
829
SET_LUAHOOK(print);
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
#undef SET_LUAHOOK
}
static int luahook_stackwalk(lua_State *L)
{
const char *errstr = lua_tostring(L, 1);
#if 0
lua_Debug ldbg;
int i = 0;
if (errstr != NULL)
logDebug("%s\n", errstr);
logDebug("Lua stack backtrace:");
847
/* start at 1 to skip this function. */
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
for (i = 1; lua_getstack(L, i, &ldbg); i++)
{
char *ptr = (char *) scratchbuf_128k;
size_t len = sizeof (scratchbuf_128k);
int bw = snprintfcat(&ptr, &len, "#%d", i-1);
const int maxspacing = 4;
int spacing = maxspacing - bw;
while (spacing-- > 0)
snprintfcat(&ptr, &len, " ");
if (!lua_getinfo(L, "nSl", &ldbg))
{
snprintfcat(&ptr, &len, "???\n");
logDebug((const char *) scratchbuf_128k);
continue;
863
} /* if */
864
865
866
867
868
869
870
871
872
873
874
875
876
877
if (ldbg.namewhat[0])
snprintfcat(&ptr, &len, "%s ", ldbg.namewhat);
if ((ldbg.name) && (ldbg.name[0]))
snprintfcat(&ptr, &len, "function %s ()", ldbg.name);
else
{
if (strcmp(ldbg.what, "main") == 0)
snprintfcat(&ptr, &len, "mainline of chunk");
else if (strcmp(ldbg.what, "tail") == 0)
snprintfcat(&ptr, &len, "tail call");
else
snprintfcat(&ptr, &len, "unidentifiable function");
878
} /* if */
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
logDebug((const char *) scratchbuf_128k);
ptr = (char *) scratchbuf_128k;
len = sizeof (scratchbuf_128k);
for (spacing = 0; spacing < maxspacing; spacing++)
snprintfcat(&ptr, &len, " ");
if (strcmp(ldbg.what, "C") == 0)
snprintfcat(&ptr, &len, "in native code");
else if (strcmp(ldbg.what, "tail") == 0)
snprintfcat(&ptr, &len, "in Lua code");
else if ( (strcmp(ldbg.source, "=?") == 0) && (ldbg.currentline == 0) )
snprintfcat(&ptr, &len, "in Lua code (debug info stripped)");
else
{
snprintfcat(&ptr, &len, "in Lua code at %s", ldbg.short_src);
if (ldbg.currentline != -1)
snprintfcat(&ptr, &len, ":%d", ldbg.currentline);
898
} /* else */
899
logDebug((const char *) scratchbuf_128k);
900
} /* for */
901
902
903
904
#endif
lua_pushstring(L, errstr ? errstr : "");
return 1;
905
} /* luahook_stackwalk */
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
static int *breakpointLines = NULL;
static int breakpointLineCount = 0;
void TOBY_clearAllBreakpoints(void)
{
free(breakpointLines);
breakpointLines = NULL;
breakpointLineCount = 0;
} /* TOBY_clearAllBreakpoints */
int TOBY_addBreakpointLine(int line)
{
const size_t newSize = sizeof (int) * (breakpointLineCount + 1);
void *ptr = realloc(breakpointLines, newSize);
if (ptr == NULL)
return -1;
/* !!! FIXME: sort into array, don't append. */
/* !!! FIXME: make sure it's not already in the array. */
breakpointLines = (int *) ptr;
breakpointLines[breakpointLineCount++] = line;
return breakpointLineCount-1;
} /* TOBY_addBreakpointLine */
static int isBreakpointLine(int line)
{
int retval = -1;
if (breakpointLines != NULL)
{
int i; /* !!! FIXME: don't do a linear search here... */
for (i = 0; i < breakpointLineCount; i++)
{
if (breakpointLines[i] == line)
return i;
} /* for */
} /* if */
return retval;
} /* isBreakpointLine */
951
952
static void luaDebugHook(lua_State *L, lua_Debug *ar)
{
953
const int hook = ar->event;
954
const int line = ar->currentline;
955
956
const long startTicks = TOBY_getTicks();
long pauseTicks = -1;
957
int breakpoint = -1;
958
959
960
961
962
963
964
965
int shouldRedraw = (startTicks >= nextPumpTicks);
/*
* Should only break inside this function, and should block here until
* breakpoint ends and program continues.
*/
assert(!TOBY_isPaused());
966
/* If we hit a new line, see if this is a breakpoint. Pause here if so. */
967
968
969
if (hook == LUA_HOOKLINE)
{
const long mustDelay = TOBY_getDelayTicksPerLine();
970
/*printf("Now on line #%d\n", line);*/
971
972
if (mustDelay > 0)
pauseTicks = startTicks + mustDelay;
973
if (TOBY_isStepping()) /* single stepping? Break here. */
974
execState = EXEC_PAUSED;
975
976
977
978
979
980
981
982
983
984
985
986
987
if ((breakpoint = isBreakpointLine(line)) != -1)
execState = EXEC_PAUSED;
} /* if */
/* !!! FIXME: maybe later. */
#if 0
if (hook == LUA_HOOKCALL)
{
if (breakpoint == -1)
{
if ((breakpoint = isBreakpointFunc(ar)) != -1)
execState = EXEC_PAUSED;
} /* if */
988
} /* if */
989
#endif
990
991
if ((TOBY_isPaused()) || (pauseTicks > 0))
992
TOBY_pauseReached(line, TOBY_isPaused(), breakpoint, pauseTicks);
993
994
995
996
997
while ( (TOBY_isPaused()) || (pauseTicks > 0) || (shouldRedraw) )
{
if (shouldRedraw)
{
998
999
1000
/*
* Force repaint here...this means we will be clamped to 20fps,
* but the overall execution of the program will be much faster,