lisp.js 9.0 KB

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