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