lisp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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 TRACE 0 // print eval input output
  21. #define RETRO 1 // auto capitalize input
  22. #define DELETE 1 // allow backspace to rub out symbol
  23. #define QUOTES 1 // allow 'X shorthand (QUOTE X)
  24. #define PROMPT 1 // show repl prompt
  25. #define WORD short
  26. #define WORDS 8192
  27. /*───────────────────────────────────────────────────────────────────────────│─╗
  28. │ The LISP Challenge § LISP Machine ─╬─│┼
  29. ╚────────────────────────────────────────────────────────────────────────────│*/
  30. #define ATOM 0
  31. #define CONS 1
  32. #define NIL 0
  33. #define UNDEFINED 8
  34. #define ATOM_T 30
  35. #define ATOM_QUOTE 34
  36. #define ATOM_ATOM 46
  37. #define ATOM_EQ 56
  38. #define ATOM_COND 62
  39. #define ATOM_CAR 72
  40. #define ATOM_CDR 80
  41. #define ATOM_CONS 88
  42. #define ATOM_LAMBDA 98
  43. #define BOOL(x) ((x) ? ATOM_T : NIL)
  44. #define VALUE(x) ((x) >> 1)
  45. #define PTR(i) ((i) << 1 | CONS)
  46. struct Lisp {
  47. WORD mem[WORDS];
  48. unsigned char syntax[256];
  49. WORD look;
  50. WORD globals;
  51. WORD index;
  52. char token[128];
  53. char str[WORDS];
  54. };
  55. _Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600,
  56. "LISP Machine too large for real mode");
  57. _Alignas(char) const char kSymbols[] = "NIL\0"
  58. "*UNDEFINED\0"
  59. "T\0"
  60. "QUOTE\0"
  61. "ATOM\0"
  62. "EQ\0"
  63. "COND\0"
  64. "CAR\0"
  65. "CDR\0"
  66. "CONS\0"
  67. "LAMBDA";
  68. #ifdef __REAL_MODE__
  69. static struct Lisp *const q;
  70. #else
  71. static struct Lisp q[1];
  72. #endif
  73. static void Print(long);
  74. static WORD GetList(void);
  75. static WORD GetObject(void);
  76. static void PrintObject(long);
  77. static WORD Eval(long, long);
  78. static void SetupSyntax(void) {
  79. unsigned char *syntax = q->syntax;
  80. asm("" : "+bSD"(syntax));
  81. syntax[' '] = ' ';
  82. syntax['\r'] = ' ';
  83. syntax['\n'] = ' ';
  84. syntax['('] = '(';
  85. syntax[')'] = ')';
  86. syntax['.'] = '.';
  87. #if QUOTES
  88. syntax['\''] = '\'';
  89. #endif
  90. }
  91. static void SetupBuiltins(void) {
  92. CopyMemory(q->str, kSymbols, sizeof(kSymbols));
  93. }
  94. static inline WORD Car(long x) {
  95. return PEEK_ARRAY(q, mem, VALUE(x), 0);
  96. }
  97. static inline WORD Cdr(long x) {
  98. return PEEK_ARRAY(q, mem, VALUE(x), 1);
  99. }
  100. static WORD Set(long i, long k, long v) {
  101. POKE_ARRAY(q, mem, VALUE(i), 0, k);
  102. POKE_ARRAY(q, mem, VALUE(i), 1, v);
  103. return i;
  104. }
  105. static WORD Cons(WORD car, WORD cdr) {
  106. int i, cell;
  107. i = q->index;
  108. POKE_ARRAY(q, mem, i, 0, car);
  109. POKE_ARRAY(q, mem, i, 1, cdr);
  110. q->index = i + 2;
  111. cell = OBJECT(CONS, i);
  112. return cell;
  113. }
  114. static char *StpCpy(char *d, char *s) {
  115. char c;
  116. do {
  117. c = LODS(s); // a.k.a. c = *s++
  118. STOS(d, c); // a.k.a. *d++ = c
  119. } while (c);
  120. return d;
  121. }
  122. static WORD Intern(char *s) {
  123. int j, cx;
  124. char c, *z, *t;
  125. z = q->str;
  126. c = LODS(z);
  127. while (c) {
  128. for (j = 0;; ++j) {
  129. if (c != PEEK(s, j, 0)) {
  130. break;
  131. }
  132. if (!c) {
  133. return OBJECT(ATOM, z - q->str - j - 1);
  134. }
  135. c = LODS(z);
  136. }
  137. while (c) c = LODS(z);
  138. c = LODS(z);
  139. }
  140. --z;
  141. StpCpy(z, s);
  142. return OBJECT(ATOM, SUB((long)z, q->str));
  143. }
  144. static unsigned char XlatSyntax(unsigned char b) {
  145. return PEEK_ARRAY(q, syntax, b, 0);
  146. }
  147. static void PrintString(char *s) {
  148. char c;
  149. for (;;) {
  150. if (!(c = PEEK(s, 0, 0))) break;
  151. PrintChar(c);
  152. ++s;
  153. }
  154. }
  155. static int GetChar(void) {
  156. int c;
  157. c = ReadChar();
  158. #if RETRO
  159. if (c >= 'a') {
  160. CompilerBarrier();
  161. if (c <= 'z') c -= 'a' - 'A';
  162. }
  163. #endif
  164. #if DELETE
  165. if (c == '\b') return c;
  166. #endif
  167. PrintChar(c);
  168. if (c == '\r') PrintChar('\n');
  169. return c;
  170. }
  171. static void GetToken(void) {
  172. char *t;
  173. unsigned char b, x;
  174. b = q->look;
  175. t = q->token;
  176. for (;;) {
  177. x = XlatSyntax(b);
  178. if (x != ' ') break;
  179. b = GetChar();
  180. }
  181. if (x) {
  182. STOS(t, b);
  183. b = GetChar();
  184. } else {
  185. while (b && !x) {
  186. if (!DELETE || b != '\b') {
  187. STOS(t, b);
  188. } else if (t > q->token) {
  189. PrintString("\b \b");
  190. if (t > q->token) --t;
  191. }
  192. b = GetChar();
  193. x = XlatSyntax(b);
  194. }
  195. }
  196. STOS(t, 0);
  197. q->look = b;
  198. }
  199. static WORD ConsumeObject(void) {
  200. GetToken();
  201. return GetObject();
  202. }
  203. static WORD Cadr(long x) {
  204. return Car(Cdr(x)); // ((A B C D) (E F G) H I) → (E F G)
  205. }
  206. static WORD List(long x, long y) {
  207. return Cons(x, Cons(y, NIL));
  208. }
  209. static WORD Quote(long x) {
  210. return List(ATOM_QUOTE, x);
  211. }
  212. static WORD GetQuote(void) {
  213. return Quote(ConsumeObject());
  214. }
  215. static WORD AddList(WORD x) {
  216. return Cons(x, GetList());
  217. }
  218. static WORD GetList(void) {
  219. GetToken();
  220. switch (*q->token & 0xFF) {
  221. default:
  222. return AddList(GetObject());
  223. case ')':
  224. return NIL;
  225. case '.':
  226. return ConsumeObject();
  227. #if QUOTES
  228. case '\'':
  229. return AddList(GetQuote());
  230. #endif
  231. }
  232. }
  233. static WORD GetObject(void) {
  234. switch (*q->token & 0xFF) {
  235. default:
  236. return Intern(q->token);
  237. case '(':
  238. return GetList();
  239. #if QUOTES
  240. case '\'':
  241. return GetQuote();
  242. #endif
  243. }
  244. }
  245. static WORD ReadObject(void) {
  246. q->look = GetChar();
  247. GetToken();
  248. return GetObject();
  249. }
  250. static WORD Read(void) {
  251. return ReadObject();
  252. }
  253. static void PrintAtom(long x) {
  254. PrintString(q->str + VALUE(x));
  255. }
  256. static void PrintList(long x) {
  257. #if QUOTES
  258. if (Car(x) == ATOM_QUOTE) {
  259. PrintChar('\'');
  260. PrintObject(Cadr(x));
  261. return;
  262. }
  263. #endif
  264. PrintChar('(');
  265. PrintObject(Car(x));
  266. while ((x = Cdr(x))) {
  267. if (!ISATOM(x)) {
  268. PrintChar(' ');
  269. PrintObject(Car(x));
  270. } else {
  271. PrintString(" . ");
  272. PrintObject(x);
  273. break;
  274. }
  275. }
  276. PrintChar(')');
  277. }
  278. static void PrintObject(long x) {
  279. if (ISATOM(x)) {
  280. PrintAtom(x);
  281. } else {
  282. PrintList(x);
  283. }
  284. }
  285. static void Print(long i) {
  286. PrintObject(i);
  287. PrintString("\r\n");
  288. }
  289. /*───────────────────────────────────────────────────────────────────────────│─╗
  290. │ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼
  291. ╚────────────────────────────────────────────────────────────────────────────│*/
  292. static WORD Atom(long x) {
  293. return BOOL(ISATOM(x));
  294. }
  295. static WORD Eq(long x, long y) {
  296. return BOOL(x == y);
  297. }
  298. static WORD Caar(long x) {
  299. return Car(Car(x)); // ((A B C D) (E F G) H I) → A
  300. }
  301. static WORD Cdar(long x) {
  302. return Cdr(Car(x)); // ((A B C D) (E F G) H I) → (B C D)
  303. }
  304. static WORD Cadar(long x) {
  305. return Cadr(Car(x)); // ((A B C D) (E F G) H I) → B
  306. }
  307. static WORD Caddr(long x) {
  308. return Cadr(Cdr(x)); // ((A B C D) (E F G) H I) → H
  309. }
  310. static WORD Caddar(long x) {
  311. return Caddr(Car(x)); // ((A B C D) (E F G) H I) → C
  312. }
  313. static WORD Arg1(long e, long a) {
  314. return Eval(Cadr(e), a);
  315. }
  316. static WORD Arg2(long e, long a) {
  317. return Eval(Caddr(e), a);
  318. }
  319. static WORD Append(long x, long y) {
  320. return x ? Cons(Car(x), Append(Cdr(x), y)) : y;
  321. }
  322. static WORD Evcon(long c, long a) {
  323. return Eval(Caar(c), a) ? Eval(Cadar(c), a) : Evcon(Cdr(c), a);
  324. }
  325. static WORD Bind(long v, long a, long e) { // evlis + pair w/ dot notation
  326. return v ? Cons(Cons(Car(v), Eval(Car(a), e)), Bind(Cdr(v), Cdr(a), e)) : e;
  327. }
  328. static WORD Assoc(long x, long y) {
  329. return y ? Eq(Caar(y), x) ? Cdar(y) : Assoc(x, Cdr(y)) : NIL;
  330. }
  331. static WORD Evaluate(long e, long a) {
  332. if (Atom(e)) {
  333. return Assoc(e, a);
  334. } else if (Atom(Car(e))) {
  335. switch (Car(e)) {
  336. case NIL:
  337. return UNDEFINED;
  338. case ATOM_QUOTE:
  339. return Cadr(e);
  340. case ATOM_ATOM:
  341. return Atom(Arg1(e, a));
  342. case ATOM_EQ:
  343. return Eq(Arg1(e, a), Arg2(e, a));
  344. case ATOM_COND:
  345. return Evcon(Cdr(e), a);
  346. case ATOM_CAR:
  347. return Car(Arg1(e, a));
  348. case ATOM_CDR:
  349. return Cdr(Arg1(e, a));
  350. case ATOM_CONS:
  351. return Cons(Arg1(e, a), Arg2(e, a));
  352. default:
  353. return Eval(Cons(Assoc(Car(e), a), Cdr(e)), a);
  354. }
  355. } else if (Eq(Caar(e), ATOM_LAMBDA)) {
  356. return Eval(Caddar(e), Bind(Cadar(e), Cdr(e), a));
  357. } else {
  358. return UNDEFINED;
  359. }
  360. }
  361. static WORD Eval(long e, long a) {
  362. WORD r;
  363. #if TRACE
  364. PrintString("->");
  365. Print(e);
  366. PrintString(" ");
  367. Print(a);
  368. #endif
  369. e = Evaluate(e, a);
  370. #if TRACE
  371. PrintString("<-");
  372. Print(e);
  373. #endif
  374. return e;
  375. }
  376. /*───────────────────────────────────────────────────────────────────────────│─╗
  377. │ The LISP Challenge § User Interface ─╬─│┼
  378. ╚────────────────────────────────────────────────────────────────────────────│*/
  379. void Repl(void) {
  380. for (;;) {
  381. #if PROMPT
  382. PrintString("* ");
  383. #endif
  384. Print(Eval(Read(), q->globals));
  385. }
  386. }
  387. int main(int argc, char *argv[]) {
  388. RawMode();
  389. SetupSyntax();
  390. SetupBuiltins();
  391. #if PROMPT
  392. PrintString("THE LISP CHALLENGE V1\r\n"
  393. "VISIT GITHUB.COM/JART\r\n");
  394. #endif
  395. Repl();
  396. return 0;
  397. }