lisp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
  2. │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
  3. ╞══════════════════════════════════════════════════════════════════════════════╡
  4. │ Copyright 2020 Justine Alexandra Roberts Tunney │
  5. │ │
  6. │ Permission to use, copy, modify, and/or distribute this software for │
  7. │ any purpose with or without fee is hereby granted, provided that the │
  8. │ above copyright notice and this permission notice appear in all copies. │
  9. │ │
  10. │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
  11. │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
  12. │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
  13. │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
  14. │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
  15. │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
  16. │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
  17. │ PERFORMANCE OF THIS SOFTWARE. │
  18. ╚─────────────────────────────────────────────────────────────────────────────*/
  19. #include "lisp.h"
  20. #define RETRO 1 // auto capitalize input
  21. #define DELETE 1 // allow backspace to rub out symbol
  22. #define QUOTES 1 // allow 'X shorthand (QUOTE X)
  23. #define PROMPT 1 // show repl prompt
  24. #define WORD short
  25. #define WORDS 8192
  26. /*───────────────────────────────────────────────────────────────────────────│─╗
  27. │ The LISP Challenge § LISP Machine ─╬─│┼
  28. ╚────────────────────────────────────────────────────────────────────────────│*/
  29. #define ATOM 1
  30. #define CONS 0
  31. #define NIL (ATOM | 0)
  32. #define UNDEFINED (ATOM | 8)
  33. #define ATOM_T (ATOM | 30)
  34. #define ATOM_QUOTE (ATOM | 34)
  35. #define ATOM_COND (ATOM | 46)
  36. #define ATOM_ATOM (ATOM | 56)
  37. #define ATOM_CAR (ATOM | 66)
  38. #define ATOM_CDR (ATOM | 74)
  39. #define ATOM_CONS (ATOM | 82)
  40. #define ATOM_EQ (ATOM | 92)
  41. #define ATOM_LAMBDA (ATOM | 98)
  42. #define VALUE(x) ((x) >> 1)
  43. struct Lisp {
  44. WORD mem[WORDS];
  45. unsigned char syntax[256];
  46. WORD look;
  47. WORD globals;
  48. WORD index;
  49. char token[128];
  50. char str[WORDS];
  51. };
  52. _Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600,
  53. "LISP Machine too large for real mode");
  54. _Alignas(char) const char kSymbols[] = "NIL\0"
  55. "*UNDEFINED\0"
  56. "T\0"
  57. "QUOTE\0"
  58. "COND\0"
  59. "ATOM\0"
  60. "CAR\0"
  61. "CDR\0"
  62. "CONS\0"
  63. "EQ\0"
  64. "LAMBDA";
  65. #ifdef __REAL_MODE__
  66. static struct Lisp *const q;
  67. #else
  68. static struct Lisp q[1];
  69. #endif
  70. static void Print(long);
  71. static WORD GetList(void);
  72. static WORD GetObject(void);
  73. static void PrintObject(long);
  74. static WORD Eval(WORD, WORD);
  75. static void SetupSyntax(void) {
  76. unsigned char *syntax = q->syntax;
  77. asm("" : "+bSD"(syntax));
  78. syntax[' '] = ' ';
  79. syntax['\r'] = ' ';
  80. syntax['\n'] = ' ';
  81. syntax['('] = '(';
  82. syntax[')'] = ')';
  83. syntax['.'] = '.';
  84. #if QUOTES
  85. syntax['\''] = '\'';
  86. #endif
  87. }
  88. static void SetupBuiltins(void) {
  89. CopyMemory(q->str, kSymbols, sizeof(kSymbols));
  90. }
  91. static inline WORD Car(long x) {
  92. return PEEK_ARRAY(q, mem, VALUE(x), 0);
  93. }
  94. static inline WORD Cdr(long x) {
  95. return PEEK_ARRAY(q, mem, VALUE(x), 1);
  96. }
  97. static WORD Set(long i, long k, long v) {
  98. POKE_ARRAY(q, mem, VALUE(i), 0, k);
  99. POKE_ARRAY(q, mem, VALUE(i), 1, v);
  100. return i;
  101. }
  102. static WORD Cons(WORD car, WORD cdr) {
  103. int i, cell;
  104. i = q->index;
  105. POKE_ARRAY(q, mem, i, 0, car);
  106. POKE_ARRAY(q, mem, i, 1, cdr);
  107. q->index = i + 2;
  108. cell = OBJECT(CONS, i);
  109. return cell;
  110. }
  111. static char *StpCpy(char *d, char *s) {
  112. char c;
  113. do {
  114. c = LODS(s); // a.k.a. c = *s++
  115. STOS(d, c); // a.k.a. *d++ = c
  116. } while (c);
  117. return d;
  118. }
  119. static WORD Intern(char *s) {
  120. int j, cx;
  121. char c, *z, *t;
  122. z = q->str;
  123. c = LODS(z);
  124. while (c) {
  125. for (j = 0;; ++j) {
  126. if (c != PEEK(s, j, 0)) {
  127. break;
  128. }
  129. if (!c) {
  130. return OBJECT(ATOM, z - q->str - j - 1);
  131. }
  132. c = LODS(z);
  133. }
  134. while (c) c = LODS(z);
  135. c = LODS(z);
  136. }
  137. --z;
  138. StpCpy(z, s);
  139. return OBJECT(ATOM, SUB((long)z, q->str));
  140. }
  141. static unsigned char XlatSyntax(unsigned char b) {
  142. return PEEK_ARRAY(q, syntax, b, 0);
  143. }
  144. static void PrintString(char *s) {
  145. char c;
  146. for (;;) {
  147. if (!(c = PEEK(s, 0, 0))) break;
  148. PrintChar(c);
  149. ++s;
  150. }
  151. }
  152. static int GetChar(void) {
  153. int c;
  154. c = ReadChar();
  155. #if RETRO
  156. if (c >= 'a') {
  157. CompilerBarrier();
  158. if (c <= 'z') c -= 'a' - 'A';
  159. }
  160. #endif
  161. #if DELETE
  162. if (c == '\b') return c;
  163. #endif
  164. PrintChar(c);
  165. if (c == '\r') PrintChar('\n');
  166. return c;
  167. }
  168. static void GetToken(void) {
  169. char *t;
  170. unsigned char b, x;
  171. b = q->look;
  172. t = q->token;
  173. for (;;) {
  174. x = XlatSyntax(b);
  175. if (x != ' ') break;
  176. b = GetChar();
  177. }
  178. if (x) {
  179. STOS(t, b);
  180. b = GetChar();
  181. } else {
  182. while (b && !x) {
  183. if (!DELETE || b != '\b') {
  184. STOS(t, b);
  185. } else if (t > q->token) {
  186. PrintString("\b \b");
  187. if (t > q->token) --t;
  188. }
  189. b = GetChar();
  190. x = XlatSyntax(b);
  191. }
  192. }
  193. STOS(t, 0);
  194. q->look = b;
  195. }
  196. static WORD ConsumeObject(void) {
  197. GetToken();
  198. return GetObject();
  199. }
  200. static WORD Cadr(long x) {
  201. return Car(Cdr(x)); // ((A B C D) (E F G) H I) → (E F G)
  202. }
  203. static WORD List(long x, long y) {
  204. return Cons(x, Cons(y, NIL));
  205. }
  206. static WORD Quote(long x) {
  207. return List(ATOM_QUOTE, x);
  208. }
  209. static WORD GetQuote(void) {
  210. return Quote(ConsumeObject());
  211. }
  212. static WORD AddList(WORD x) {
  213. return Cons(x, GetList());
  214. }
  215. static WORD GetList(void) {
  216. GetToken();
  217. switch (*q->token & 0xFF) {
  218. default:
  219. return AddList(GetObject());
  220. case ')':
  221. return NIL;
  222. case '.':
  223. return ConsumeObject();
  224. #if QUOTES
  225. case '\'':
  226. return AddList(GetQuote());
  227. #endif
  228. }
  229. }
  230. static WORD GetObject(void) {
  231. switch (*q->token & 0xFF) {
  232. default:
  233. return Intern(q->token);
  234. case '(':
  235. return GetList();
  236. #if QUOTES
  237. case '\'':
  238. return GetQuote();
  239. #endif
  240. }
  241. }
  242. static WORD ReadObject(void) {
  243. q->look = GetChar();
  244. GetToken();
  245. return GetObject();
  246. }
  247. static WORD Read(void) {
  248. return ReadObject();
  249. }
  250. static void PrintAtom(long x) {
  251. PrintString(q->str + VALUE(x));
  252. }
  253. static void PrintList(long x) {
  254. #if QUOTES
  255. if (Car(x) == ATOM_QUOTE) {
  256. PrintChar('\'');
  257. PrintObject(Cadr(x));
  258. return;
  259. }
  260. #endif
  261. PrintChar('(');
  262. PrintObject(Car(x));
  263. while ((x = Cdr(x))) {
  264. if (!ISATOM(x)) {
  265. PrintChar(' ');
  266. PrintObject(Car(x));
  267. } else {
  268. PrintString(" . ");
  269. PrintObject(x);
  270. break;
  271. }
  272. }
  273. PrintChar(')');
  274. }
  275. static void PrintObject(long x) {
  276. if (ISATOM(x)) {
  277. PrintAtom(x);
  278. } else {
  279. PrintList(x);
  280. }
  281. }
  282. static void Print(long i) {
  283. PrintObject(i);
  284. PrintString("\r\n");
  285. }
  286. /*───────────────────────────────────────────────────────────────────────────│─╗
  287. │ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼
  288. ╚────────────────────────────────────────────────────────────────────────────│*/
  289. static WORD Caar(long x) {
  290. return Car(Car(x)); // ((A B C D) (E F G) H I) → A
  291. }
  292. static WORD Cdar(long x) {
  293. return Cdr(Car(x)); // ((A B C D) (E F G) H I) → (B C D)
  294. }
  295. static WORD Cadar(long x) {
  296. return Cadr(Car(x)); // ((A B C D) (E F G) H I) → B
  297. }
  298. static WORD Caddr(long x) {
  299. return Cadr(Cdr(x)); // ((A B C D) (E F G) H I) → H
  300. }
  301. static WORD Caddar(long x) {
  302. return Caddr(Car(x)); // ((A B C D) (E F G) H I) → C
  303. }
  304. static WORD Evcon(long c, long a) {
  305. return Eval(Caar(c), a) != NIL ? Eval(Cadar(c), a) : Evcon(Cdr(c), a);
  306. }
  307. static WORD Assoc(long x, long a) {
  308. return a != NIL ? Caar(a) == x ? Cdar(a) : Assoc(x, Cdr(a)) : NIL;
  309. }
  310. static WORD Pairlis(WORD x, WORD y, WORD a) {
  311. if (x == NIL)
  312. return a;
  313. WORD di = Cons(Car(x), Car(y));
  314. WORD si = Pairlis(Cdr(x), Cdr(y), a);
  315. return Cons(di, si); // Tail-Modulo-Cons
  316. }
  317. static WORD Evlis(WORD m, WORD a) {
  318. if (m == NIL)
  319. return NIL;
  320. WORD di = Eval(Car(m), a);
  321. WORD si = Evlis(Cdr(m), a);
  322. return Cons(di, si);
  323. }
  324. static WORD Apply(WORD fn, WORD x, WORD a) {
  325. if (ISATOM(fn)) {
  326. switch (fn) {
  327. case NIL:
  328. return UNDEFINED;
  329. case ATOM_CAR:
  330. return Caar(x);
  331. case ATOM_CDR:
  332. return Cdar(x);
  333. case ATOM_ATOM:
  334. return ISATOM(Car(x)) ? ATOM_T : NIL;
  335. case ATOM_CONS:
  336. return Cons(Car(x), Cadr(x));
  337. case ATOM_EQ:
  338. return Car(x) == Cadr(x) ? ATOM_T : NIL;
  339. default:
  340. return Apply(Eval(fn, a), x, a);
  341. }
  342. }
  343. if (Car(fn) == ATOM_LAMBDA) {
  344. WORD t1 = Cdr(fn);
  345. WORD si = Pairlis(Car(t1), x, a);
  346. WORD ax = Cadr(t1);
  347. return Eval(ax, si);
  348. }
  349. return UNDEFINED;
  350. }
  351. static WORD Eval(WORD e, WORD a) {
  352. if (ISATOM(e))
  353. return Assoc(e, a);
  354. WORD ax = Car(e);
  355. if (ISATOM(ax)) {
  356. if (ax == ATOM_QUOTE)
  357. return Cadr(e);
  358. if (ax == ATOM_COND)
  359. return Evcon(Cdr(e), a);
  360. if (ax == ATOM_LAMBDA)
  361. return e;
  362. }
  363. return Apply(ax, Evlis(Cdr(e), a), a);
  364. }
  365. /*───────────────────────────────────────────────────────────────────────────│─╗
  366. │ The LISP Challenge § User Interface ─╬─│┼
  367. ╚────────────────────────────────────────────────────────────────────────────│*/
  368. void Repl(void) {
  369. for (;;) {
  370. #if PROMPT
  371. PrintString("* ");
  372. #endif
  373. Print(Eval(Read(), q->globals));
  374. }
  375. }
  376. int main(int argc, char *argv[]) {
  377. RawMode();
  378. SetupSyntax();
  379. SetupBuiltins();
  380. #if PROMPT
  381. PrintString("THE LISP CHALLENGE V1\r\n"
  382. "VISIT GITHUB.COM/JART\r\n");
  383. #endif
  384. Repl();
  385. return 0;
  386. }