1 /***********************************************************************
2
3 SPL, the Shakespeare Programming Language
4
5 Copyright (C) 2001 Karl Hasselstr�m and Jon �slund
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 USA.
21
22 ***********************************************************************/
23
24 #include <errno.h>
25 #include <limits.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #define GLOBAL
31 #include "spl.h"
32 #include "strutils.h"
33
34 /* debug functions */
35 #ifdef DEBUG
dump_cast_whereabouts(FILE * out)36 void dump_cast_whereabouts(FILE *out)
37 {
38 int i;
39
40 for (i = 0; i < num_cast; i++) {
41 fprintf(out, "%20s", cast[i]->name);
42 if (cast[i]->on_stage == 1) {
43 fprintf(out, " on stage");
44 }
45 fprintf(out, "\n");
46 }
47 }
48
dump_stack(FILE * out,CHARACTER * character)49 void dump_stack(FILE *out, CHARACTER *character)
50 {
51 STACKNODE *node;
52
53 fprintf(out, "%s's stack is now:", character->name);
54 for (node = character->stack; node != NULL; node = node->next) {
55 fprintf(out, " %d", node->value);
56 }
57 fprintf(out, "\n");
58 }
59 #endif /* DEBUG */
60
61 /* local function prototypes */
62
63 static void runtime_error(int line, char *msg);
64
65 /* function definitions */
66
activate_character(int line,CHARACTER * character)67 void activate_character(int line, CHARACTER *character)
68 {
69 int i;
70
71 /* Make sure the character is on stage */
72 if (character->on_stage == 0) {
73 runtime_error(line, cat2(newstr(character->name), newstr(" is not on stage, and thus cannot speak!\n")));
74 }
75
76 /* If there are exactly two people on stage, the other one should be
77 second person */
78 if (num_on_stage == 2) {
79 for (i = 0; i < num_cast; i++) {
80 if (cast[i]->on_stage == 1 && cast[i] != character) {
81 second_person = cast[i];
82 }
83 }
84 } else {
85 second_person = NULL;
86 }
87
88 /* Make this character the first person */
89 first_person = character;
90 }
91
assign(int line,CHARACTER * character,int value)92 void assign(int line, CHARACTER *character, int value)
93 {
94 /* Make sure the character isn't NULL */
95 if (character == NULL) {
96 if (num_on_stage == 1) {
97 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
98 } else {
99 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
100 }
101 }
102
103 #ifdef DEBUG
104 fprintf(stderr, "\n%s's previous value was %d.\n", character->name, character->value);
105 #endif
106
107 /* Assign the value */
108 character->value = value;
109
110 #ifdef DEBUG
111 fprintf(stderr, "%s's new value is %d.\n", character->name, character->value);
112 #endif
113 }
114
char_input(int line,CHARACTER * character)115 void char_input(int line, CHARACTER *character)
116 {
117 /* Make sure the character isn't NULL */
118 if (character == NULL) {
119 if (num_on_stage == 1) {
120 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
121 } else {
122 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
123 }
124 }
125
126 /* Read a character from stdin */
127 character->value = getc(stdin);
128
129 /* If EOF was encountered, record the value -1 */
130 if (character->value == EOF) {
131 character->value = -1;
132 }
133
134 #ifdef DEBUG
135 fprintf(stderr, "\n%s read the character %c (ascii code %d)\n", character->name, (char) character->value, character->value);
136 #endif
137 }
138
char_output(int line,CHARACTER * character)139 void char_output(int line, CHARACTER *character)
140 {
141 /* Make sure the character isn't NULL */
142 if (character == NULL) {
143 if (num_on_stage == 1) {
144 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
145 } else {
146 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
147 }
148 }
149
150 /* Make sure the value is in range */
151 if (character->value < 0 || character->value > UCHAR_MAX) {
152 runtime_error(line, cat3(newstr("The value "), int2str(character->value),
153 newstr(" does not correspond to a character.")));
154 }
155
156 /* Write a character to stdout */
157 putc(character->value, stdout);
158
159 #ifdef DEBUG
160 fprintf(stderr, "\n%s wrote the character %c (ascii code %d)\n", character->name, (char) character->value, character->value);
161 #endif
162 }
163
enter_scene(int line,CHARACTER * character)164 void enter_scene(int line, CHARACTER *character)
165 {
166 /* Make sure the character isn't already on stage */
167 if (character->on_stage == 1) {
168 runtime_error(line, cat2(newstr(character->name), newstr(" is already on stage, and thus cannot enter!\n")));
169 }
170
171 /* Set on_stage flag */
172 character->on_stage = 1;
173
174 /* Increase num_on_stage counter */
175 num_on_stage++;
176
177 #ifdef DEBUG
178 fprintf(stderr, "\n%s just entered\n", character->name);
179 dump_cast_whereabouts(stderr);
180 #endif
181 }
182
exit_scene(int line,CHARACTER * character)183 void exit_scene(int line, CHARACTER *character)
184 {
185 /* Make sure the character is on stage */
186 if (character->on_stage == 0) {
187 runtime_error(line, cat2(newstr(character->name), newstr(" is not on stage, and thus cannot exit!\n")));
188 }
189
190 /* Set on_stage flag */
191 character->on_stage = 0;
192
193 /* Decrease num_on_stage counter */
194 num_on_stage--;
195
196 #ifdef DEBUG
197 fprintf(stderr, "\n%s just exited\n", character->name);
198 dump_cast_whereabouts(stderr);
199 #endif
200 }
201
exit_scene_all(int line)202 void exit_scene_all(int line)
203 {
204 int i;
205
206 /* Kick everyone off stage! */
207 for (i = 0; i < num_cast; i++) {
208 cast[i]->on_stage = 0;
209 }
210
211 /* Reset num_on_stage counter */
212 num_on_stage = 0;
213
214 #ifdef DEBUG
215 fprintf(stderr, "\nEverybody just exeunted\n");
216 dump_cast_whereabouts(stderr);
217 #endif
218 }
219
global_initialize()220 void global_initialize()
221 {
222 cast = NULL;
223 first_person = NULL;
224 second_person = NULL;
225 truth_flag = 0;
226 num_on_stage = 0;
227 num_cast = 0;
228 }
229
initialize_character(char * name)230 CHARACTER *initialize_character(char *name)
231 {
232 CHARACTER *newchar;
233
234 newchar = (CHARACTER *) malloc(sizeof(CHARACTER));
235 newchar->name = newstr(name);
236 newchar->on_stage = 0;
237 newchar->stack = NULL;
238 newchar->value = 0;
239
240 num_cast++;
241 cast = (CHARACTER **) realloc(cast, num_cast*sizeof(CHARACTER *));
242 cast[num_cast - 1] = newchar;
243
244 return newchar;
245 }
246
int_add(int line,int a,int b)247 int int_add(int line, int a, int b)
248 {
249 #ifdef DEBUG
250 fprintf(stderr, "\nComputing int_add(%d, %d) = %d\n", a, b, a + b);
251 #endif
252
253 /* Compute the result */
254 return a + b;
255 }
256
int_cube(int line,int value)257 int int_cube(int line, int value)
258 {
259 #ifdef DEBUG
260 fprintf(stderr, "\nComputing int_cube(%d) = %d\n", value, value*value*value);
261 #endif
262
263 return value*value*value;
264 }
265
int_div(int line,int a,int b)266 int int_div(int line, int a, int b)
267 {
268 /* Make sure the denominator is nonzero */
269 if (b == 0) {
270 runtime_error(line, cat3(newstr("Unable to divide "), int2str(a), newstr(" by zero!")));
271 }
272
273 #ifdef DEBUG
274 fprintf(stderr, "\nComputing int_div(%d, %d) = %d\n", a, b, a/b);
275 #endif
276
277 /* Compute the result */
278 return a/b;
279 }
280
int_factorial(int line,int n)281 int int_factorial(int line, int n)
282 {
283 int i;
284
285 #ifdef DEBUG
286 int orig = n;
287 #endif
288
289 /* Make sure the argument is nonnegative */
290 if (n < 0) {
291 runtime_error(line, cat3(newstr("Unable to compute factorial of "), int2str(n), newstr(", since it is negative!")));
292 }
293
294 /* Compute the result */
295 if (n == 0) {
296 n = 1;
297 }
298 for (i = n - 1; i > 1; i--) {
299 n *= i;
300 }
301
302 #ifdef DEBUG
303 fprintf(stderr, "\nComputing int_factorial(%d) = %d\n", orig, n);
304 #endif
305
306 return n;
307 }
308
int_input(int line,CHARACTER * character)309 void int_input(int line, CHARACTER *character)
310 {
311 char buf[BUF_SIZE];
312 long lval;
313
314 /* Make sure the character isn't NULL */
315 if (character == NULL) {
316 if (num_on_stage == 1) {
317 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
318 } else {
319 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
320 }
321 }
322
323 /* Get a line of text */
324 fgets(buf, BUF_SIZE, stdin);
325
326 /* Try to parse that into an integer */
327 errno = 0;
328 lval = strtol(buf, NULL, 10);
329 if (lval == 0) {
330 switch (errno) {
331 case EINVAL:
332 runtime_error(line, cat2(newstr(character->name), newstr("'s heart whispered something that was not a valid integer.")));
333 break;
334 case ERANGE:
335 runtime_error(line, cat2(newstr(character->name), newstr("'s heart whispered an integer that was out of range.")));
336 break;
337 default:
338 /* No error, buf really contained the integer zero */
339 break;
340 }
341 }
342
343 /* Make sure it was not out of range */
344 if (lval < INT_MIN || lval > INT_MAX) {
345 runtime_error(line, cat2(newstr(character->name), newstr("'s heart whispered an integer that was out of range.")));
346 }
347
348 /* Store the value */
349 character->value = (int) lval;
350
351 #ifdef DEBUG
352 fprintf(stderr, "\n%s read the value %d\n", character->name, character->value);
353 #endif
354 }
355
int_mod(int line,int a,int b)356 int int_mod(int line, int a, int b)
357 {
358 /* Make sure the denominator is nonzero */
359 if (b == 0) {
360 runtime_error(line, cat3(newstr("Unable to divide "), int2str(a), newstr(" by zero!")));
361 }
362
363 #ifdef DEBUG
364 fprintf(stderr, "\nComputing int_mod(%d, %d) = %d\n", a, b, a%b);
365 #endif
366
367 /* Compute the result */
368 return a%b;
369 }
370
int_mul(int line,int a,int b)371 int int_mul(int line, int a, int b)
372 {
373 #ifdef DEBUG
374 fprintf(stderr, "\nComputing int_mul(%d, %d) = %d\n", a, b, a*b);
375 #endif
376
377 /* Compute the result */
378 return a*b;
379 }
380
int_output(int line,CHARACTER * character)381 void int_output(int line, CHARACTER *character)
382 {
383 /* Make sure the character isn't NULL */
384 if (character == NULL) {
385 if (num_on_stage == 1) {
386 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
387 } else {
388 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
389 }
390 }
391
392 /* Write the number to stdout */
393 printf("%d", character->value);
394
395 #ifdef DEBUG
396 fprintf(stderr, "\n%s wrote the value %d\n", character->name, character->value);
397 #endif
398 }
399
int_sqrt(int line,int value)400 int int_sqrt(int line, int value)
401 {
402 /* Make sure the number is positive */
403 if (value < 0) {
404 runtime_error(line, cat3(newstr("Unable to compute the square root of "), int2str(value),
405 newstr(", since it is negative.")));
406 }
407
408 #ifdef DEBUG
409 fprintf(stderr, "\nComputing int_sqrt(%d) = %d\n", value, (int) sqrt((double) value));
410 #endif
411
412 /* Compute the square root */
413 return (int) sqrt((double) value);
414 }
415
int_square(int line,int value)416 int int_square(int line, int value)
417 {
418 #ifdef DEBUG
419 fprintf(stderr, "\nComputing int_square(%d) = %d\n", value, value*value);
420 #endif
421
422 return value*value;
423 }
424
int_sub(int line,int a,int b)425 int int_sub(int line, int a, int b)
426 {
427 #ifdef DEBUG
428 fprintf(stderr, "\nComputing int_sub(%d, %d) = %d\n", a, b, a - b);
429 #endif
430
431 /* Compute the result */
432 return a - b;
433 }
434
int_twice(int line,int value)435 int int_twice(int line, int value)
436 {
437 #ifdef DEBUG
438 fprintf(stderr, "\nComputing int_twice(%d) = %d\n", value, 2*value);
439 #endif
440
441 return 2*value;
442 }
443
pop(int line,CHARACTER * character)444 void pop(int line, CHARACTER *character)
445 {
446 STACKNODE *node;
447
448 /* Make sure the character isn't NULL */
449 if (character == NULL) {
450 if (num_on_stage == 1) {
451 runtime_error(line, newstr("Erroneous use of imperative. There is only one character on stage!"));
452 } else {
453 runtime_error(line, newstr("Ambiguous use of imperative. There are more than two characters on stage!"));
454 }
455 }
456
457 /* Try to pop a value from the stack */
458 node = character->stack;
459 if (node == NULL) {
460 runtime_error(line, cat2(newstr(character->name), newstr(" is unable to recall anything.")));
461 } else {
462 character->value = node->value;
463 character->stack = node->next;
464 free(node);
465 }
466
467 #ifdef DEBUG
468 fprintf(stderr, "\nPopping %s, getting the value %d\n", character->name, character->value);
469 dump_stack(stderr, character);
470 #endif
471 }
472
push(int line,CHARACTER * character,int value)473 void push(int line, CHARACTER *character, int value)
474 {
475 STACKNODE *node;
476
477 /* Make sure the character isn't NULL */
478 if (character == NULL) {
479 if (num_on_stage == 1) {
480 runtime_error(line, newstr("Erroneous use of imperative. There is only one character on stage!"));
481 } else {
482 runtime_error(line, newstr("Ambiguous use of imperative. There are more than two characters on stage!"));
483 }
484 }
485
486 /* Push a value onto the stack */
487 node = (STACKNODE *) malloc(sizeof(STACKNODE));
488 node->value = value;
489 node->next = character->stack;
490 character->stack = node;
491
492 #ifdef DEBUG
493 fprintf(stderr, "\nPushing %d onto %s\n", value, character->name);
494 dump_stack(stderr, character);
495 #endif
496 }
497
runtime_error(int line,char * msg)498 void runtime_error(int line, char *msg)
499 {
500 fprintf(stderr, "\nRuntime error at line %d: %s\n", line, msg);
501 free(msg);
502 exit(1);
503 }
504
value_of(int line,CHARACTER * character)505 int value_of(int line, CHARACTER *character)
506 {
507 /* Make sure the character isn't NULL */
508 if (character == NULL) {
509 if (num_on_stage == 1) {
510 runtime_error(line, newstr("Erroneous use of second person pronoun. There is only one character on stage!"));
511 } else {
512 runtime_error(line, newstr("Ambiguous use of second person pronoun. There are more than two characters on stage!"));
513 }
514 }
515
516 /* Return the value */
517 return character->value;
518 }
519