mirth.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* Mirth -- Minimalist Joy language interpreter.
  2. Copyright © 2016, 2017 Eric Bavier <bavier@member.fsf.org>
  3. This is free software licensed under the terms of the GNU GPL
  4. version 3, or at your option, any later version. */
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #define HEAP_SIZE 5000
  8. #define STACK_SIZE 100
  9. #define CONTINUATION_SIZE 100
  10. #define VARS_SIZE 128
  11. struct var {
  12. int immediate;
  13. void * val;
  14. };
  15. /* TODO: Maintain the stack and continuation from the same chunk of
  16. memory, where the two move in from each end. This would work both
  17. for programs that need a large stack but small continuation and
  18. programs that need a large continuation but relatively small
  19. stack. */
  20. static void * stack_base[STACK_SIZE];
  21. static void * continuation_base[CONTINUATION_SIZE];
  22. static void ** stack = stack_base - 1;
  23. static void ** continuation = continuation_base - 1;
  24. static struct var vars[VARS_SIZE];
  25. #define push(s,e) *(++s) = ((void *)(e))
  26. #define pop(v) v = (typeof(v))*(stack--)
  27. #define pop2(t,s) do{ t = (typeof(t))*(stack--); s = (typeof(s))*(stack--); } while(0)
  28. #define popi(i) i = unpack_int(*(stack--))
  29. /* Only two types in Mirth: 1) quotes, and 2) immediate integers.
  30. They are distinguished by the least-significant bit (lsb). Quotes
  31. will always have 0 in the lsb, and immediate integers will always
  32. have 1. Quotes are allocated 4-byte-aligned, so the next bit can
  33. be used for marking during garbage-collection. */
  34. #define quote_p(v) (!((int)(v) & 1))
  35. #define pack_int(c) ((void *)(((c)<<1)|1))
  36. #define unpack_int(v) ((int)(v)>>1)
  37. /* This should look like a quote and behave as expected for "123[]+++" */
  38. #define null 0
  39. typedef struct cell_s * cell_t;
  40. struct cell_s {
  41. void * car;
  42. cell_t cdr;
  43. };
  44. /* The heap is where all HEAP_SIZE cells are stored. */
  45. static struct cell_s heap[HEAP_SIZE];
  46. static cell_t free_list = heap;
  47. void heap_init() {
  48. /* Link all heap cells. Assumes the compiler inits memory to 0. */
  49. cell_t cell = heap;
  50. while (cell < heap + HEAP_SIZE)
  51. cell = cell->cdr = cell+1;
  52. }
  53. int gc()
  54. {
  55. }
  56. cell_t alloc_cell() {
  57. if (free_list == null) {
  58. gc();
  59. if (free_list == null) {
  60. fprintf (stderr, "error: out of heap space\n");
  61. return NULL;
  62. }
  63. }
  64. cell_t ret = free_list;
  65. free_list = ret->cdr;
  66. ret->cdr = null;
  67. return ret;
  68. }
  69. #define free_cell(c) c->cdr = free_list; free_list = c
  70. cell_t cons (void * head, void * tail) {
  71. cell_t c = alloc_cell();
  72. c->car = head; c->cdr = tail;
  73. return c;
  74. }
  75. int length (cell_t lst) {
  76. if (lst == null) return 0;
  77. else return 1 + length(lst->cdr);
  78. }
  79. /* The convention is that the "head" of the list becomes the top of
  80. the stack. */
  81. #define do_unstack(lst,base) \
  82. do { \
  83. int _l = length(lst); \
  84. for (base = base + _l; lst != null; lst=lst->cdr) \
  85. *base-- = lst->car ; \
  86. base += _l; \
  87. } while(0)
  88. cell_t listify_stack (void ** top, void ** bottom) {
  89. cell_t c = null;
  90. while (bottom <= top)
  91. c = cons (*bottom++, c);
  92. return c;
  93. }
  94. int next_char (FILE * stream) {
  95. int c;
  96. c = getc (stream);
  97. if (c == '{') {
  98. /* Skip to the next '}'. No nesting! */
  99. while (c != '}') c = getc (stream);
  100. c = getc (stream);
  101. }
  102. return c;
  103. }
  104. cell_t parse_quote (FILE * stream) {
  105. int c;
  106. cell_t ret, run, next;
  107. ret = next = alloc_cell();
  108. while (1) {
  109. c = next_char (stream);
  110. if (c == ']') {
  111. if (ret == next) ret = null;
  112. else run->cdr = null;
  113. free_cell(next);
  114. return ret;
  115. } else {
  116. run = next;
  117. if (c == '[') run->car = (void *)parse_quote(stream);
  118. else run->car = pack_int(c);
  119. next = run->cdr = alloc_cell();
  120. }
  121. }
  122. }
  123. /* Get the next token from input and put it on the continuation stack.
  124. Return non-zero for end-of-input, otherwise 0. The parsed token is
  125. returned in RET. */
  126. int parse_next (FILE * stream, void ** ret) {
  127. int c;
  128. c = next_char (stream);
  129. if (c == EOF) return 1;
  130. if (c == '[')
  131. *ret = parse_quote(stream);
  132. else
  133. *ret = pack_int(c);
  134. return 0;
  135. }
  136. #ifdef DEBUG
  137. void print_quote(cell_t);
  138. void print_items(cell_t l) {
  139. if (l != null) {
  140. if (quote_p(l->car))
  141. print_quote ((cell_t)l->car);
  142. else
  143. printf ("%d ", unpack_int(l->car));
  144. print_items ((cell_t)l->cdr);
  145. }
  146. }
  147. void print_quote(cell_t l) {
  148. printf ("["); print_items (l); printf ("] ");
  149. }
  150. void print_stack() {
  151. void ** ptr = stack_base;
  152. printf ("stack: ");
  153. while (ptr <= stack)
  154. {
  155. if (quote_p(*ptr)) print_quote ((cell_t)*ptr);
  156. else printf ("%d ", unpack_int(*ptr));
  157. ++ptr;
  158. }
  159. printf ("\n");
  160. }
  161. #endif
  162. /* If input is a primitive operator, perform its function and return
  163. 0, otherwise do nothing and return non-zero. */
  164. int maybe_do_primitive (int c) {
  165. void * top, * second;
  166. cell_t l, m;
  167. #define binary(op) \
  168. do{ \
  169. int _i, _j; \
  170. popi(_j); popi(_i); \
  171. push(stack, pack_int(_i op _j)); \
  172. } while(0)
  173. if (isspace (c)) return 0;
  174. switch (c) {
  175. case '$': top = *stack; push(stack,top); break; /* dup */
  176. case '>': second = *(stack-1); push(stack,second); break; /* over */
  177. case '%': --stack; break; /* pop */
  178. case '\\': /* swap */
  179. pop2(top,second);
  180. push(stack,top); push(stack,second);
  181. break;
  182. case '!': pop(l); do_unstack(l,continuation); break;
  183. case '_':
  184. pop2(l,second);
  185. push(continuation,second);
  186. do_unstack(l,continuation); break;
  187. case '?':
  188. pop2(l,second); if (second != pack_int(0)) do_unstack(l,continuation);
  189. break;
  190. case ':':
  191. if (quote_p(*stack)) { /* define immediate */
  192. pop(l); c = unpack_int((int)l->car);
  193. pop(vars[c].val); vars[c].immediate = 1;
  194. } else { /* define variable */
  195. popi(c);
  196. pop(vars[c].val); vars[c].immediate = 0;
  197. }
  198. break;
  199. case ';': popi(c); push(stack,vars[c].val); break; /* load */
  200. case '^': push(stack,pack_int(getc(stdin))); break; /* read */
  201. case '.': popi(c); printf("%d", c); break; /* write int */
  202. case ',': /* write char/string */
  203. if (quote_p(*stack))
  204. for (pop(l); l != null; l = l->cdr)
  205. printf("%c", unpack_int(l->car));
  206. else printf("%c", popi(c));
  207. break;
  208. case '`': push(stack,pack_int(quote_p(*stack) ? -1 : 0)); break; /* quote? */
  209. case '+':
  210. if (quote_p(*stack)) { /* cons */
  211. pop2(top,second);
  212. push(stack,cons(second,top));
  213. } else binary(+); /* addition */
  214. break;
  215. case '-':
  216. if (quote_p(*stack)) { /* uncons */
  217. pop(l);
  218. push(stack,l->car); push(stack,l->cdr);
  219. } else binary(-); /* subtraction */
  220. break;
  221. case '*':
  222. if (quote_p(*stack)) { /* concat */
  223. pop(top);
  224. for (pop(l); l != null; l = l->cdr){
  225. push(stack,l->car);
  226. push(continuation,pack_int('+'));
  227. }
  228. push(stack,top);
  229. } else binary(*); /* multiplication */
  230. break;
  231. case '/':
  232. if (quote_p(*stack)) {
  233. /* TODO: take and drop */
  234. }
  235. else binary(/);
  236. break;
  237. case '(': /* stack */
  238. l = listify_stack(stack, stack_base);
  239. push(stack,l); break;
  240. case ')': /* unstack */
  241. pop(l);
  242. stack = stack_base - 1; /* TODO: collect garbage */
  243. do_unstack(l,stack); break;
  244. case '<': /* lesser? */
  245. pop2(top,second);
  246. push(stack,pack_int(top > second ? -1 : 0)); break;
  247. case '=': /* eq? */
  248. pop2(top,second);
  249. push(stack,pack_int(top == second ? -1 : 0)); break;
  250. case '~': pop(top); push(stack, pack_int(~(int)top)); break;
  251. case '|':
  252. if (quote_p(*stack)) { /* reverse */
  253. for (pop(l), m = null; l != null; l = l->cdr)
  254. m = cons(l->car,m);
  255. push(stack,m);
  256. } else binary(|); /* bitwise or */
  257. break;
  258. case '&': binary(&); break; /* bitwise and */
  259. case '@': /* shuffle */
  260. {
  261. /* Shuffle 0-indexed (i.e. 0 is top, 1 is second, etc.) stack
  262. elements with list of indices, where the left-hand-side is
  263. the top. e.g. "swap" == "[10]@" and "elho[13220]@,,,,," =>
  264. hello */
  265. int max = 0, len = 0;
  266. for (pop(l); l != null; l = l->cdr, ++len) {
  267. c = unpack_int(l->car) - 48;
  268. push(continuation,*(stack-c));
  269. max = (c > max) ? c : max;
  270. }
  271. stack -= ++max; /* adjust stack based on largest index */
  272. for (; len; --len) /* shunt shuffled elements to stack */
  273. push(stack,*continuation--);
  274. break;
  275. }
  276. case '\'': pop(top); push(stack,cons(top,null)); break; /* unit */
  277. case '0' ... '9': push(stack,pack_int(c-48)); break;
  278. default: return 1;
  279. }
  280. #undef binary
  281. return 0;
  282. }
  283. /* Immediate variables are assumed to be quotes. They can be bound to
  284. any of the characters [a-zA-Z]. When a character that has been
  285. defined as an immediate is encountered, its quote is immediately
  286. executed. E.g. "[[hello],][H]:H" prints "hello" to stdout.
  287. Primitives may not be redefined as immediates; their primitive
  288. definitions always take precendence. */
  289. int maybe_do_immediate (int c) {
  290. if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
  291. if (vars[c].immediate) {
  292. cell_t v = vars[c].val;
  293. do_unstack(v,continuation);
  294. return 0;
  295. }
  296. return 1;
  297. }
  298. int eval (FILE * stream) {
  299. #ifdef DEBUG
  300. print_stack();
  301. #endif
  302. void * c;
  303. if (continuation < continuation_base) { /* empty? */
  304. if (parse_next (stream, &c))
  305. return 0; /* EOF */
  306. } else c = *continuation--;
  307. if (quote_p(c)) push(stack,c);
  308. else {
  309. if (maybe_do_primitive(unpack_int(c))
  310. && maybe_do_immediate(unpack_int(c)))
  311. push(stack,c);
  312. }
  313. eval (stream); /* loop */
  314. }
  315. int main (int argc, char ** argv) {
  316. FILE * stream;
  317. heap_init();
  318. stream = fopen ("prelude.mrth","r");
  319. eval (stream);
  320. fclose (stream);
  321. if (argc > 1) stream = fopen (argv[1],"r");
  322. else stream = stdin;
  323. eval (stream);
  324. fclose (stream);
  325. return 0;
  326. }