lisp.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*bin/echo '#-*- indent-tabs-mode:nil;js-indent-level:2;coding:utf-8 -*-
  2. SectorLISP v2.o (ISC License)
  3. Copyright 2021 Justine Tunney
  4. This file implements SectorLISP as a C / JavaScript polyglot and
  5. includes friendly branch features such as the undefined behavior
  6. exceptions handlers, optimized interning, and global definitions
  7. (aset standard-display-table #x2029 [?¶]) ;; emacs protip '>/dev/null
  8. curl -so bestline.c -z bestline.c https://justine.lol/sectorlisp2/bestline.c
  9. curl -so bestline.h -z bestline.h https://justine.lol/sectorlisp2/bestline.h
  10. [ lisp.js -nt lisp ] && cc -w -xc lisp.js bestline.c -o lisp
  11. exec ./lisp "$@"
  12. exit
  13. */
  14. //
`
  15. #include "bestline.h"
  16. #ifndef __COSMOPOLITAN__
  17. #include <stdio.h>
  18. #include <locale.h>
  19. #include <setjmp.h>
  20. #endif
  21. #define var int
  22. #define function
  23. #define Null 16384
  24. var M[Null * 2];
  25. jmp_buf undefined;
  26. //`
  27. var cx, dx, lo, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine;
  28. function Set(i, x) {
  29. M[Null + i] = x;
  30. }
  31. function Get(i) {
  32. return M[Null + i];
  33. }
  34. function Car(x) {
  35. if (x > 0) Throw(List(kCar, x));
  36. return x ? Get(x) : +0;
  37. }
  38. function Cdr(x) {
  39. if (x > 0) Throw(List(kCdr, x));
  40. return x ? Get(x + 1) : -0;
  41. }
  42. function Cons(car, cdr) {
  43. Set(--cx, cdr);
  44. Set(--cx, car);
  45. if (cx < lo) lo = cx;
  46. return cx;
  47. }
  48. function Hash(h, c) {
  49. return h + c * 2;
  50. }
  51. function Intern(x, y, i) {
  52. i &= Null - 1;
  53. if (x == Get(i) && y == Get(i + 1)) return i;
  54. if (Get(i)) return Intern(x, y, i + 2);
  55. Set(i, x);
  56. Set(i + 1, y);
  57. return i;
  58. }
  59. function ReadAtom(h) {
  60. var c = ReadChar();
  61. if (c <= Ord(' ')) return ReadAtom(h);
  62. return Intern(c, c > Ord(')') && dx > Ord(')') ?
  63. ReadAtom(Hash(h, c)) : 0,
  64. Hash(h, c) - Hash(0, Ord('N')));
  65. }
  66. function PrintAtom(x) {
  67. do PrintChar(Get(x));
  68. while ((x = Get(x + 1)));
  69. }
  70. function ReadList() {
  71. var x;
  72. if ((x = Read()) > 0) {
  73. if (Get(x) == Ord(')')) return -0;
  74. if (Get(x) == Ord('.') && !Get(x + 1)) {
  75. x = Read();
  76. ReadList();
  77. return x;
  78. }
  79. }
  80. return Cons(x, ReadList());
  81. }
  82. function ReadObject(t) {
  83. if (Get(t) != Ord('(')) return t;
  84. return ReadList();
  85. }
  86. function PrintList(x) {
  87. PrintChar(Ord('('));
  88. if (x < 0) {
  89. PrintObject(Car(x));
  90. while ((x = Cdr(x))) {
  91. if (x < 0) {
  92. PrintChar(Ord(' '));
  93. PrintObject(Car(x));
  94. } else {
  95. PrintChar(0x2219);
  96. PrintObject(x);
  97. break;
  98. }
  99. }
  100. }
  101. PrintChar(Ord(')'));
  102. }
  103. function PrintObject(x) {
  104. if (1./x < 0) {
  105. PrintList(x);
  106. } else {
  107. PrintAtom(x);
  108. }
  109. }
  110. function Print(e) {
  111. PrintObject(e);
  112. PrintChar(Ord('\n'));
  113. }
  114. function Read() {
  115. return ReadObject(ReadAtom(0));
  116. }
  117. function Remove(x, y) {
  118. if (!y) return y;
  119. if (x == Car(Car(y))) return Cdr(y);
  120. return Cons(Car(y), Remove(x, Cdr(y)));
  121. }
  122. function List(x, y) {
  123. return Cons(x, Cons(y, 0));
  124. }
  125. function Define(x, y) {
  126. return Cons(Cons(x, Read()), Remove(x, y));
  127. }
  128. function Gc(A, x) {
  129. var C, B = cx;
  130. x = Copy(x, A, A - B), C = cx;
  131. while (C < B) Set(--A, Get(--B));
  132. return cx = A, x;
  133. }
  134. function Copy(x, m, k) {
  135. return x < m ? Cons(Copy(Car(x), m, k),
  136. Copy(Cdr(x), m, k)) + k : x;
  137. }
  138. function Evlis(m, a) {
  139. return m ? Cons(Eval(Car(m), a),
  140. Evlis(Cdr(m), a)) : m;
  141. }
  142. function Pairlis(x, y, a) {
  143. if (!!x ^ !!y) Throw(List(x, y));
  144. return x ? Cons(Cons(Car(x), Car(y)),
  145. Pairlis(Cdr(x), Cdr(y), a)) : a;
  146. }
  147. function Assoc(x, y) {
  148. if (y >= 0) Throw(x);
  149. if (x == Car(Car(y))) return Cdr(Car(y));
  150. return Assoc(x, Cdr(y));
  151. }
  152. function Evcon(c, a) {
  153. if (Eval(Car(Car(c)), a)) {
  154. return Eval(Car(Cdr(Car(c))), a);
  155. } else if (Cdr(c)) {
  156. return Evcon(Cdr(c), a);
  157. } else {
  158. Throw(c);
  159. }
  160. }
  161. function Apply(f, x, a) {
  162. if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a));
  163. if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0;
  164. if (f == kCons) return Cons(Car(x), Car(Cdr(x)));
  165. if (f == kAtom) return Car(x) < 0 ? 0 : kT;
  166. if (f == kCar) return Car(Car(x));
  167. if (f == kCdr) return Cdr(Car(x));
  168. return Apply(Assoc(f, a), x, a);
  169. }
  170. function Eval(e, a) {
  171. var A = cx;
  172. if (!e) return e;
  173. if (e > 0) return Assoc(e, a);
  174. if (Car(e) == kQuote) return Car(Cdr(e));
  175. if (Car(e) == kCond) return Gc(A, Evcon(Cdr(e), a));
  176. return Gc(A, Apply(Car(e), Evlis(Cdr(e), a), a));
  177. }
  178. function LoadBuiltins() {
  179. ReadAtom(0);
  180. kT = ReadAtom(0);
  181. kEq = ReadAtom(0);
  182. kCar = ReadAtom(0);
  183. kCdr = ReadAtom(0);
  184. kAtom = ReadAtom(0);
  185. kCond = ReadAtom(0);
  186. kCons = ReadAtom(0);
  187. kQuote = ReadAtom(0);
  188. kDefine = ReadAtom(0);
  189. }
  190. //
`
  191. ////////////////////////////////////////////////////////////////////////////////
  192. // ANSI POSIX C Specific Code
  193. Ord(c) {
  194. return c;
  195. }
  196. Throw(x) {
  197. longjmp(undefined, ~x);
  198. }
  199. PrintChar(b) {
  200. fputwc(b, stdout);
  201. }
  202. SaveMachine(a) {
  203. }
  204. ReadChar() {
  205. int b, c, t;
  206. static char *freeme;
  207. static char *line = "NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE ";
  208. if (line || (line = freeme = bestlineWithHistory("* ", "sectorlisp"))) {
  209. if (*line) {
  210. c = *line++ & 0377;
  211. if (c >= 0300) {
  212. for (b = 0200; c & b; b >>= 1) c ^= b;
  213. while ((*line & 0300) == 0200) {
  214. c <<= 6;
  215. c |= *line++ & 0177;
  216. }
  217. }
  218. } else {
  219. free(freeme);
  220. freeme = 0;
  221. line = 0;
  222. c = '\n';
  223. }
  224. t = dx;
  225. dx = c;
  226. return t;
  227. } else {
  228. exit(0);
  229. }
  230. }
  231. main() {
  232. var x, a, A;
  233. setlocale(LC_ALL, "");
  234. bestlineSetXlatCallback(bestlineUppercase);
  235. LoadBuiltins();
  236. for (a = 0;;) {
  237. A = cx;
  238. if (!(x = setjmp(undefined))) {
  239. x = Read();
  240. if (x == kDefine) {
  241. a = Gc(0, Define(Read(), a));
  242. SaveMachine(a);
  243. continue;
  244. }
  245. x = Eval(x, a);
  246. } else {
  247. x = ~x;
  248. PrintChar('?');
  249. }
  250. Print(x);
  251. Gc(A, 0);
  252. }
  253. }
  254. #if 0
  255. //`
  256. ////////////////////////////////////////////////////////////////////////////////
  257. // JavaScript Specific Code for https://justine.lol/
  258. var a, code, index, output, M, Null;
  259. var eInput, eOutput, eSubmit, eReset, eLoad, ePrograms;
  260. function Throw(x) {
  261. throw x;
  262. }
  263. function Ord(s) {
  264. return s.charCodeAt(0);
  265. }
  266. function Reset() {
  267. var i;
  268. a = 0;
  269. cx = 0;
  270. lo = 0;
  271. Null = 16384;
  272. M = new Array(Null * 2);
  273. for (i = 0; i < M.length; ++i) {
  274. M[i] = 0; /* make json smaller */
  275. }
  276. Load("NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE ");
  277. LoadBuiltins()
  278. }
  279. function PrintChar(c) {
  280. output += String.fromCharCode(c);
  281. }
  282. function ReadChar() {
  283. var ax;
  284. if (code.length) {
  285. ax = dx;
  286. if (index < code.length) {
  287. dx = code.charCodeAt(index++);
  288. } else {
  289. code = "";
  290. dx = 0;
  291. }
  292. return ax;
  293. } else {
  294. Throw(0);
  295. }
  296. }
  297. function Lisp() {
  298. var x, A;
  299. lo = cx;
  300. output = '';
  301. while (dx) {
  302. if (dx <= Ord(' ')) {
  303. ReadChar();
  304. } else {
  305. A = cx;
  306. try {
  307. x = Read();
  308. if (x == kDefine) {
  309. a = Gc(0, Define(Read(), a));
  310. continue;
  311. }
  312. x = Eval(x, a);
  313. } catch (z) {
  314. PrintChar(Ord('?'));
  315. x = z;
  316. }
  317. Print(x);
  318. Gc(A, 0);
  319. }
  320. }
  321. eOutput.innerText = output;
  322. SaveMachine(a);
  323. SaveOutput();
  324. ReportUsage();
  325. }
  326. function Load(s) {
  327. code = s + "\n";
  328. dx = Ord(s);
  329. index = 1;
  330. }
  331. function OnSubmit() {
  332. Load(eInput.value.toUpperCase());
  333. Lisp();
  334. }
  335. function Dump(a) {
  336. if (!a) return;
  337. Dump(Cdr(a));
  338. output += "DEFINE ";
  339. PrintObject(Car(Car(a)));
  340. output += " ";
  341. PrintObject(Cdr(Car(a)));
  342. output += "\n";
  343. }
  344. function OnReset() {
  345. output = "";
  346. try {
  347. Dump(a);
  348. eOutput.innerText = output;
  349. Reset();
  350. } catch (e) {
  351. /* ignored */
  352. }
  353. localStorage.removeItem("sectorlisp.machine");
  354. SaveOutput();
  355. ReportUsage();
  356. }
  357. function OnLoad() {
  358. ePrograms.classList.toggle("show");
  359. }
  360. function OnWindowClick(event) {
  361. if (!event.target.matches("#load")) {
  362. ePrograms.classList.remove("show");
  363. }
  364. }
  365. function SaveMachine(a) {
  366. var machine;
  367. if (typeof localStorage != "undefined") {
  368. machine = [M, a, cx];
  369. localStorage.setItem("sectorlisp.machine", JSON.stringify(machine));
  370. }
  371. }
  372. function RestoreMachine() {
  373. var machine;
  374. if (typeof localStorage != "undefined" &&
  375. (machine = JSON.parse(localStorage.getItem("sectorlisp.machine")))) {
  376. M = machine[0];
  377. a = machine[1];
  378. cx = machine[2];
  379. lo = cx;
  380. }
  381. }
  382. function SaveOutput() {
  383. if (typeof localStorage != "undefined") {
  384. localStorage.setItem("input", document.getElementById("input").value);
  385. localStorage.setItem("output", eOutput.innerText);
  386. }
  387. }
  388. function Number(i) {
  389. return i.toLocaleString();
  390. }
  391. function ReportUsage() {
  392. var i, c;
  393. for (c = i = 0; i < Null; i += 2) {
  394. if (Get(i)) ++c;
  395. }
  396. document.getElementById("usage").innerText =
  397. Number((-cx >> 1) + c) + " / " +
  398. Number((-lo >> 1) + c) + " / " +
  399. Number(Null) + " doublewords";
  400. }
  401. function SetUp() {
  402. eLoad = document.getElementById("load");
  403. eInput = document.getElementById("input");
  404. eReset = document.getElementById("reset");
  405. eOutput = document.getElementById("output");
  406. eSubmit = document.getElementById("submit");
  407. ePrograms = document.getElementById("programs");
  408. window.onclick = OnWindowClick;
  409. eSubmit.onclick = OnSubmit;
  410. eReset.onclick = OnReset;
  411. eLoad.onclick = OnLoad;
  412. Reset();
  413. RestoreMachine();
  414. ReportUsage();
  415. }
  416. SetUp();
  417. //
`
  418. #endif
  419. //`