mirth.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /* Mirth -- Minimalist Joy language interpreter.
  2. Copyright © 2016 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. cell_t parse_quote (FILE * stream) {
  95. int c;
  96. cell_t ret, run, next;
  97. ret = next = alloc_cell();
  98. while (1) {
  99. c = getc(stream);
  100. if (c == ']') {
  101. if (ret == next) ret = null;
  102. else run->cdr = null;
  103. free_cell(next);
  104. return ret;
  105. } else {
  106. run = next;
  107. if (c == '[') run->car = (void *)parse_quote(stream);
  108. else run->car = pack_int(c);
  109. next = run->cdr = alloc_cell();
  110. }
  111. }
  112. }
  113. /* Get the next token from input and put it on the continuation stack.
  114. Return NULL for end-of-input, the parsed token otherwise. */
  115. void * parse_next (FILE * stream) {
  116. int c;
  117. c = getc (stream);
  118. if (c == '{') {
  119. /* Skip to the next '}'. No nesting! */
  120. while (c != '}') c = getc (stream);
  121. c = getc (stream);
  122. }
  123. if (c == EOF) return NULL;
  124. if (isspace(c)) {
  125. return parse_next (stream);
  126. } else if (c == '[') {
  127. void * q = parse_quote (stream);
  128. if (q == NULL)
  129. fprintf (stderr, "error: failed to read quotation\n");
  130. else return q;
  131. } else {
  132. return pack_int(c);
  133. }
  134. }
  135. #ifdef DEBUG
  136. void print_quote(cell_t);
  137. void print_items(cell_t l) {
  138. if (l != null) {
  139. if (quote_p(l->car))
  140. print_quote ((cell_t)l->car);
  141. else
  142. printf ("%d ", unpack_int(l->car));
  143. print_items ((cell_t)l->cdr);
  144. }
  145. }
  146. void print_quote(cell_t l) {
  147. printf ("["); print_items (l); printf ("] ");
  148. }
  149. void print_stack() {
  150. void ** ptr = stack_base;
  151. printf ("stack: ");
  152. while (ptr <= stack)
  153. {
  154. if (quote_p(*ptr)) print_quote ((cell_t)*ptr);
  155. else printf ("%d ", unpack_int(*ptr));
  156. ++ptr;
  157. }
  158. printf ("\n");
  159. }
  160. #endif
  161. /* If input is a primitive operator, perform its function and return
  162. 0, otherwise do nothing and return non-zero. */
  163. int maybe_do_primitive (int c) {
  164. void * top, * second;
  165. cell_t l, m;
  166. #define binary(op) \
  167. do{ \
  168. int _i, _j; \
  169. popi(_j); popi(_i); \
  170. push(stack, pack_int(_i op _j)); \
  171. } while(0)
  172. switch (c) {
  173. case '$': top = *stack; push(stack,top); break; /* dup */
  174. case '>': second = *(stack-1); push(stack,second); break; /* over */
  175. case '%': --stack; break; /* pop */
  176. case '\\': /* swap */
  177. pop2(top,second);
  178. push(stack,top); push(stack,second);
  179. break;
  180. case '!': pop(l); do_unstack(l,continuation); break;
  181. case '_':
  182. pop2(l,top);
  183. push(continuation,top);
  184. do_unstack(l,continuation); break;
  185. case '?':
  186. pop2(l,top); if (top != pack_int(0)) do_unstack(l,continuation);
  187. break;
  188. case ':':
  189. if (quote_p(*stack)) { /* define immediate */
  190. pop(l); c = unpack_int((int)l->car);
  191. pop(vars[c].val); vars[c].immediate = 1;
  192. } else { /* define variable */
  193. popi(c);
  194. pop(vars[c].val); vars[c].immediate = 0;
  195. }
  196. break;
  197. case ';': popi(c); push(stack,vars[c].val); break; /* load */
  198. case '^': push(stack,pack_int(getc(stdin))); break; /* read */
  199. case '.': popi(c); printf("%d", c); break; /* write int */
  200. case ',': /* write char/string */
  201. if (quote_p(*stack))
  202. for (pop(l); l != null; l = l->cdr)
  203. printf("%c", unpack_int(l->car));
  204. else printf("%c", popi(c));
  205. break;
  206. case '`': push(stack,pack_int(quote_p(*stack) ? -1 : 0)); break; /* quote? */
  207. case '+':
  208. if (quote_p(*stack)) { /* cons */
  209. pop2(top,second);
  210. push(stack,cons(second,top));
  211. } else binary(+); /* addition */
  212. break;
  213. case '-':
  214. if (quote_p(*stack)) { /* uncons */
  215. pop(l);
  216. push(stack,l->car); push(stack,l->cdr);
  217. } else binary(-); /* subtraction */
  218. break;
  219. case '*':
  220. if (quote_p(*stack)) { /* concat */
  221. pop(top);
  222. for (pop(l); l != null; l = l->cdr){
  223. push(stack,l->car);
  224. push(continuation,pack_int('+'));
  225. }
  226. push(stack,top);
  227. } else binary(*); /* multiplication */
  228. break;
  229. case '/':
  230. if (quote_p(*stack)) {
  231. /* TODO: take and drop */
  232. }
  233. else binary(/);
  234. break;
  235. case '(': /* stack */
  236. l = listify_stack(stack, stack_base);
  237. push(stack,l); break;
  238. case ')': /* unstack */
  239. pop(l);
  240. stack = stack_base - 1; /* TODO: collect garbage */
  241. do_unstack(l,stack); break;
  242. case '<': /* lesser? */
  243. pop2(top,second);
  244. push(stack,pack_int(top > second ? -1 : 0)); break;
  245. case '=': /* eq? */
  246. pop2(top,second);
  247. push(stack,pack_int(top == second ? -1 : 0)); break;
  248. case '~': pop(top); push(stack, pack_int(~(int)top)); break;
  249. case '|':
  250. if (quote_p(*stack)) { /* reverse */
  251. for (pop(l), m = null; l != null; l = l->cdr)
  252. m = cons(l->car,m);
  253. push(stack,m);
  254. } else binary(|); /* bitwise or */
  255. break;
  256. case '&': binary(&); break; /* bitwise and */
  257. case '@': /* shuffle */
  258. {
  259. /* Shuffle 0-indexed (i.e. 0 is top, 1 is second, etc.) stack
  260. elements with list of indices, where the left-hand-side is
  261. the top. e.g. "swap" == "[10]@" and "elho[13220]@,,,,," =>
  262. hello */
  263. int max = 0, len = 0;
  264. for (pop(l); l != null; l = l->cdr, ++len) {
  265. c = unpack_int(l->car) - 48;
  266. push(continuation,*(stack-c));
  267. max = (c > max) ? c : max;
  268. }
  269. stack -= ++max; /* adjust stack based on largest index */
  270. for (; len; --len) /* shunt shuffled elements to stack */
  271. push(stack,*continuation--);
  272. break;
  273. }
  274. case '\'': pop(top); push(stack,cons(top,null)); break; /* unit */
  275. case '0' ... '9': push(stack,pack_int(c-48)); break;
  276. default: return 1;
  277. }
  278. #undef binary
  279. return 0;
  280. }
  281. /* Immediate variables are assumed to be quotes. They can be bound to
  282. any of the characters [a-zA-Z]. When a character that has been
  283. defined as an immediate is encountered, its quote is immediately
  284. executed. E.g. "[[hello],][H]:H" prints "hello" to stdout.
  285. Primitives may not be redefined as immediates; their primitive
  286. definitions always take precendence. */
  287. int maybe_do_immediate (int c) {
  288. if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
  289. if (vars[c].immediate) {
  290. cell_t v = vars[c].val;
  291. do_unstack(v,continuation);
  292. return 0;
  293. }
  294. return 1;
  295. }
  296. int eval (FILE * stream) {
  297. #ifdef DEBUG
  298. print_stack();
  299. #endif
  300. void * c;
  301. if (continuation < continuation_base) { /* empty? */
  302. if ((c = parse_next (stream)) == NULL)
  303. return 0; /* EOF */
  304. } else c = *continuation--;
  305. if (quote_p(c)) push(stack,c);
  306. else {
  307. if (maybe_do_primitive(unpack_int(c))
  308. && maybe_do_immediate(unpack_int(c)))
  309. push(stack,c);
  310. }
  311. eval (stream); /* loop */
  312. }
  313. int main (int argc, char ** argv) {
  314. FILE * stream;
  315. heap_init();
  316. stream = fopen ("prelude.mrth","r");
  317. eval (stream);
  318. fclose (stream);
  319. if (argc > 1) stream = fopen (argv[1],"r");
  320. else stream = stdin;
  321. eval (stream);
  322. fclose (stream);
  323. return 0;
  324. }