Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

998 regels
32 KiB

4 jaren geleden
  1. ;;; swank-jolt.k --- Swank server for Jolt -*- goo -*-
  2. ;;
  3. ;; Copyright (C) 2008 Helmut Eller
  4. ;;
  5. ;; This file is licensed under the terms of the GNU General Public
  6. ;; License as distributed with Emacs (press C-h C-c for details).
  7. ;;; Commentary:
  8. ;;
  9. ;; Jolt/Coke is a Lisp-like language wich operates at the semantic level of
  10. ;; C, i.e. most objects are machine words and memory pointers. The
  11. ;; standard boot files define an interface to Id Smalltalk. So we can
  12. ;; also pretend to do OOP, but we must be careful to pass properly
  13. ;; tagged pointers to Smalltalk.
  14. ;;
  15. ;; This file only implements a minimum of SLIME's functionality. We
  16. ;; install a handler with atexit(3) to invoke the debugger. This way
  17. ;; we can stop Jolt from terminating the process on every error.
  18. ;; Unfortunately, the backtrace doesn't contain much information and
  19. ;; we also have no error message (other than the exit code). Jolt
  20. ;; usually prints some message to stdout before calling exit, so you
  21. ;; have to look in the *inferior-lisp* buffer for hints. We do
  22. ;; nothing (yet) to recover from SIGSEGV.
  23. ;;; Installation
  24. ;;
  25. ;; 1. Download and build cola. See <http://piumarta.com/software/cola/>.
  26. ;; I used the svn version:
  27. ;; svn co http://piumarta.com/svn2/idst/trunk idst
  28. ;; 2. Add something like this to your .emacs:
  29. ;;
  30. ;; (add-to-list 'slime-lisp-implementations
  31. ;; '(jolt (".../idst/function/jolt-burg/main"
  32. ;; "boot.k" ".../swank-jolt.k" "-") ; note the "-"
  33. ;; :init jolt-slime-init
  34. ;; :init-function slime-redirect-inferior-output)
  35. ;; (defun jolt-slime-init (file _) (format "%S\n" `(start-swank ,file)))
  36. ;; (defun jolt () (interactive) (slime 'jolt))
  37. ;;
  38. ;; 3. Use `M-x jolt' to start it.
  39. ;;
  40. ;;; Code
  41. ;; In this file I use 2-3 letters for often used names, like DF or
  42. ;; VEC, even if those names are abbreviations. I think that after a
  43. ;; little getting used to, this style is just as readable as the more
  44. ;; traditional DEFUN and VECTOR. Shorter names make it easier to
  45. ;; write terse code, in particular 1-line definitions.
  46. ;; `df' is like `defun' in a traditional lisp
  47. (syntax df
  48. (lambda (form compiler)
  49. (printf "df %s ...\n" [[[form second] asString] _stringValue])
  50. `(define ,[form second] (lambda ,@[form copyFrom: '2]))))
  51. ;; (! args ...) is the same as [args ...] but easier to edit.
  52. (syntax !
  53. (lambda (form compiler)
  54. (cond ((== [form size] '3)
  55. (if [[form third] isSymbol]
  56. `(send ',[form third] ,[form second])
  57. [compiler errorSyntax: [form third]]))
  58. ((and [[form size] > '3]
  59. (== [[form size] \\ '2] '0))
  60. (let ((args [OrderedCollection new])
  61. (keys [OrderedCollection new])
  62. (i '2) (len [form size]))
  63. (while (< i len)
  64. (let ((key [form at: i]))
  65. (if (or [key isKeyword]
  66. (and (== i '2) [key isSymbol])) ; for [X + Y]
  67. [keys addLast: [key asString]]
  68. [compiler errorSyntax: key]))
  69. [args addLast: [form at: [i + '1]]]
  70. (set i [i + '2]))
  71. `(send ',[[keys concatenated] asSymbol] ,[form second] ,@args)))
  72. (1 [compiler errorArgumentCount: form]))))
  73. (define Integer (import "Integer"))
  74. (define Symbol (import "Symbol")) ;; aka. _selector
  75. (define StaticBlockClosure (import "StaticBlockClosure"))
  76. (define BlockClosure (import "BlockClosure"))
  77. (define SequenceableCollection (import "SequenceableCollection"))
  78. (define _vtable (import "_vtable"))
  79. (define ByteArray (import "ByteArray"))
  80. (define CodeGenerator (import "CodeGenerator"))
  81. (define TheGlobalEnvironment (import "TheGlobalEnvironment"))
  82. (df error (msg) (! Object error: msg))
  83. (df print-to-string (obj)
  84. (let ((len '200)
  85. (stream (! WriteStream on: (! String new: len))))
  86. (! stream print: obj)
  87. (! stream contents)))
  88. (df assertion-failed (exp)
  89. (error (! '"Assertion failed: " , (print-to-string exp))))
  90. (syntax assert
  91. (lambda (form)
  92. `(if (not ,(! form second))
  93. (assertion-failed ',(! form second)))))
  94. (df isa? (obj type) (! obj isKindOf: type))
  95. (df equal (o1 o2) (! o1 = o2))
  96. (define nil 0)
  97. (define false 0)
  98. (define true (! Object notNil))
  99. (df bool? (obj) (or (== obj false) (== obj true)))
  100. (df int? (obj) (isa? obj Integer))
  101. ;; In this file the convention X>Y is used for operations that convert
  102. ;; X-to-Y. And _ means "machine word". So _>int is the operator that
  103. ;; converts a machine word to an Integer.
  104. (df _>int (word) (! Integer value_: word))
  105. (df int>_ (i) (! i _integerValue))
  106. ;; Fixnum operators. Manual tagging/untagging would probably be more
  107. ;; efficent than invoking methods.
  108. (df fix? (obj) (& obj 1))
  109. (df _>fix (n) (! SmallInteger value_: n))
  110. (df fix>_ (i) (! i _integerValue))
  111. (df fx+ (fx1 fx2) (! fx1 + fx2))
  112. (df fx* (fx1 fx2) (! fx1 * fx2))
  113. (df fx1+ (fx) (! fx + '1))
  114. (df fx1- (fx) (! fx - '1))
  115. (df str? (obj) (isa? obj String))
  116. (df >str (o) (! o asString))
  117. (df str>_ (s) (! s _stringValue))
  118. (df _>str (s) (! String value_: s))
  119. (df sym? (obj) (isa? obj Symbol))
  120. (df seq? (obj) (isa? obj SequenceableCollection))
  121. (df array? (obj) (isa? obj Array))
  122. (df len (obj) (! obj size))
  123. (df len_ (obj) (! (! obj size) _integerValue))
  124. (df ref (obj idx) (! obj at: idx))
  125. (df set-ref (obj idx elt) (! obj at: idx put: elt))
  126. (df first (obj) (! obj first))
  127. (df second (obj) (! obj second))
  128. (df puts (string stream) (! stream nextPutAll: string))
  129. (define _GC_base (dlsym "GC_base"))
  130. ;; Is ADDR a pointer to a heap allocated object? The Boehm GC nows
  131. ;; such things. This is useful for debugging, because we can quite
  132. ;; safely (i.e. without provoking SIGSEGV) access such addresses.
  133. (df valid-pointer? (addr)
  134. (let ((ptr (& addr (~ 1))))
  135. (and (_GC_base ptr)
  136. (_GC_base (long@ ptr -1)))))
  137. ;; Print OBJ as a Lisp printer would do.
  138. (df prin1 (obj stream)
  139. (cond ((fix? obj) (! stream print: obj))
  140. ((== obj nil) (puts '"nil" stream))
  141. ((== obj false) (puts '"#f" stream))
  142. ((== obj true) (puts '"#t" stream))
  143. ((not (valid-pointer? obj))
  144. (begin (puts '"#<w " stream)
  145. (prin1 (_>int obj) stream)
  146. (puts '">" stream)))
  147. ((int? obj) (! stream print: obj))
  148. ((sym? obj) (puts (>str obj) stream))
  149. ((isa? obj StaticBlockClosure)
  150. (begin (puts '"#<fun /" stream)
  151. (! stream print: (! obj arity))
  152. (puts '"#>" stream)))
  153. ((and (str? obj) (len obj))
  154. (! obj printEscapedOn: stream delimited: (ref '"\"" '0)))
  155. ((and (array? obj) (len obj))
  156. (begin (puts '"(" stream)
  157. (let ((max (- (len_ obj) 1)))
  158. (for (i 0 1 max)
  159. (prin1 (ref obj (_>fix i)) stream)
  160. (if (!= i max)
  161. (puts '" " stream))))
  162. (puts '")" stream)))
  163. ((and (isa? obj OrderedCollection) (len obj))
  164. (begin (puts '"#[" stream)
  165. (let ((max (- (len_ obj) 1)))
  166. (for (i 0 1 max)
  167. (prin1 (ref obj (_>fix i)) stream)
  168. (if (!= i max)
  169. (puts '" " stream))))
  170. (puts '"]" stream)))
  171. (true
  172. (begin (puts '"#<" stream)
  173. (puts (! obj debugName) stream)
  174. (puts '">" stream))))
  175. obj)
  176. (df print (obj)
  177. (prin1 obj StdOut)
  178. (puts '"\n" StdOut))
  179. (df prin1-to-string (obj)
  180. (let ((len '100)
  181. (stream (! WriteStream on: (! String new: len))))
  182. (prin1 obj stream)
  183. (! stream contents)))
  184. ;;(df %vable-tally (_vtable) (long@ _vtable))
  185. (df cr () (printf "\n"))
  186. (df print-object-selectors (obj)
  187. (let ((vtable (! obj _vtable))
  188. (tally (long@ vtable 0))
  189. (bindings (long@ vtable 1)))
  190. (for (i 1 1 tally)
  191. (print (long@ (long@ bindings i)))
  192. (cr))))
  193. (df print-object-slots (obj)
  194. (let ((size (! obj _sizeof))
  195. (end (+ obj size)))
  196. (while (< obj end)
  197. (print (long@ obj))
  198. (cr)
  199. (incr obj 4))))
  200. (df intern (string) (! Symbol intern: string))
  201. ;; Jolt doesn't seem to have an equivalent for gensym, but it's damn
  202. ;; hard to write macros without it. So here we adopt the conventions
  203. ;; that symbols which look like ".[0-9]+" are reserved for gensym and
  204. ;; shouldn't be used for "user visible variables".
  205. (define gensym-counter 0)
  206. (df gensym ()
  207. (set gensym-counter (+ gensym-counter 1))
  208. (intern (! '"." , (>str (_>fix gensym-counter)))))
  209. ;; Surprisingly, SequenceableCollection doesn't have a indexOf method.
  210. ;; So we even need to implement such mundane things.
  211. (df index-of (seq elt)
  212. (let ((max (len seq))
  213. (i '0))
  214. (while (! i < max)
  215. (if (equal (ref seq i) elt)
  216. (return i)
  217. (set i (! i + '1))))
  218. nil))
  219. (df find-dot (array) (index-of array '.))
  220. ;; What followes is the implementation of the pattern matching macro MIF.
  221. ;; The syntax is (mif (PATTERN EXP) THEN ELSE).
  222. ;; The THEN-branch is executed if PATTERN matches the value produced by EXP.
  223. ;; ELSE gets only executed if the match failes.
  224. ;; A pattern can be
  225. ;; 1) a symbol, which matches all values, but also binds the variable to the
  226. ;; value
  227. ;; 2) (quote LITERAL), matches if the value is `equal' to LITERAL.
  228. ;; 3) (PS ...) matches sequences, if the elements match PS.
  229. ;; 4) (P1 ... Pn . Ptail) matches if P1 ... Pn match the respective elements
  230. ;; at indices 1..n and if Ptail matches the rest
  231. ;; of the sequence
  232. ;; Examples:
  233. ;; (mif (x 10) x 'else) => 10
  234. ;; (mif ('a 'a) 'then 'else) => then
  235. ;; (mif ('a 'b) 'then 'else) => else
  236. ;; (mif ((a b) '(1 2)) b 'else) => 2
  237. ;; (mif ((a . b) '(1 2)) b 'else) => '(2)
  238. ;; (mif ((. x) '(1 2)) x 'else) => '(1 2)
  239. (define mif% 0) ;; defer
  240. (df mif%array (compiler pattern i value then fail)
  241. ;;(print `(mif%array ,pattern ,i ,value))
  242. (cond ((== i (len_ pattern)) then)
  243. ((== (ref pattern (_>fix i)) '.)
  244. (begin
  245. (if (!= (- (len_ pattern) 2) i)
  246. (begin
  247. (print pattern)
  248. (! compiler error: (! '"dot in strange position: "
  249. , (>str (_>fix i))))))
  250. (mif% compiler
  251. (ref pattern (_>fix (+ i 1)))
  252. `(! ,value copyFrom: ',(_>fix i))
  253. then fail)))
  254. (true
  255. (mif% compiler
  256. (ref pattern (_>fix i))
  257. `(ref ,value ',(_>fix i))
  258. (mif%array compiler pattern (+ i 1) value then fail)
  259. fail))))
  260. (df mif% (compiler pattern value then fail)
  261. ;;(print `(mif% ,pattern ,value ,then))
  262. (cond ((== pattern '_) then)
  263. ((== pattern '.) (! compiler errorSyntax: pattern))
  264. ((sym? pattern)
  265. `(let ((,pattern ,value)) ,then))
  266. ((seq? pattern)
  267. (cond ((== (len_ pattern) 0)
  268. `(if (== (len_ ,value) 0) ,then (goto ,fail)))
  269. ((== (first pattern) 'quote)
  270. (begin
  271. (if (not (== (len_ pattern) 2))
  272. (! compiler errorSyntax: pattern))
  273. `(if (equal ,value ,pattern) ,then (goto ,fail))))
  274. (true
  275. (let ((tmp (gensym)) (tmp2 (gensym))
  276. (pos (find-dot pattern)))
  277. `(let ((,tmp2 ,value)
  278. (,tmp ,tmp2))
  279. (if (and (seq? ,tmp)
  280. ,(if (find-dot pattern)
  281. `(>= (len ,tmp)
  282. ',(_>fix (- (len_ pattern) 2)))
  283. `(== (len ,tmp) ',(len pattern))))
  284. ,(mif%array compiler pattern 0 tmp then fail)
  285. (goto ,fail)))))))
  286. (true (! compiler errorSyntax: pattern))))
  287. (syntax mif
  288. (lambda (node compiler)
  289. ;;(print `(mif ,node))
  290. (if (not (or (== (len_ node) 4)
  291. (== (len_ node) 3)))
  292. (! compiler errorArgumentCount: node))
  293. (if (not (and (array? (ref node '1))
  294. (== (len_ (ref node '1)) 2)))
  295. (! compiler errorSyntax: (ref node '1)))
  296. (let ((pattern (first (ref node '1)))
  297. (value (second (ref node '1)))
  298. (then (ref node '2))
  299. (else (if (== (len_ node) 4)
  300. (ref node '3)
  301. `(error "mif failed")))
  302. (destination (gensym))
  303. (fail (! compiler newLabel))
  304. (success (! compiler newLabel)))
  305. `(let ((,destination 0))
  306. ,(mif% compiler pattern value
  307. `(begin (set ,destination ,then)
  308. (goto ,success))
  309. fail)
  310. (label ,fail)
  311. (set ,destination ,else)
  312. (label ,success)
  313. ,destination))))
  314. ;; (define *catch-stack* nil)
  315. ;;
  316. (df bar (o) (mif ('a o) 'yes 'no))
  317. (assert (== (bar 'a) 'yes))
  318. (assert (== (bar 'b) 'no))
  319. (df foo (o) (mif (('a) o) 'yes 'no))
  320. (assert (== (foo '(a)) 'yes))
  321. (assert (== (foo '(b)) 'no))
  322. (df baz (o) (mif (('a 'b) o) 'yes 'no))
  323. (assert (== (baz '(a b)) 'yes))
  324. (assert (== (baz '(a c)) 'no))
  325. (assert (== (baz '(b c)) 'no))
  326. (assert (== (baz 'a) 'no))
  327. (df mifvar (o) (mif (y o) y 'no))
  328. (assert (== (mifvar 'foo) 'foo))
  329. (df mifvec (o) (mif ((y) o) y 'no))
  330. (assert (== (mifvec '(a)) 'a))
  331. (assert (== (mifvec 'x) 'no))
  332. (df mifvec2 (o) (mif (('a y) o) y 'no))
  333. (assert (== (mifvec2 '(a b)) 'b))
  334. (assert (== (mifvec2 '(b c)) 'no))
  335. (assert (== (mif ((x) '(a)) x 'no) 'a))
  336. (assert (== (mif ((x . y) '(a b)) x 'no) 'a))
  337. (assert (== (mif ((x y . z) '(a b)) y 'no) 'b))
  338. (assert (equal (mif ((x . y) '(a b)) y 'no) '(b)))
  339. (assert (equal (mif ((. x) '(a b)) x 'no) '(a b)))
  340. (assert (equal (mif (((. x)) '((a b))) x 'no) '(a b)))
  341. (assert (equal (mif (((. x) . y) '((a b) c)) y 'no) '(c)))
  342. (assert (== (mif (() '()) 'yes 'no) 'yes))
  343. (assert (== (mif (() '(a)) 'yes 'no) 'no))
  344. ;; Now that we have a somewhat convenient pattern matcher we can write
  345. ;; a more convenient macro defining macro:
  346. (syntax defmacro
  347. (lambda (node compiler)
  348. (mif (('defmacro name (. args) . body) node)
  349. (begin
  350. (printf "defmacro %s ...\n" (str>_ (>str name)))
  351. `(syntax ,name
  352. (lambda (node compiler)
  353. (mif ((',name ,@args) node)
  354. (begin ,@body)
  355. (! compiler errorSyntax: node)))))
  356. (! compiler errorSyntax: node))))
  357. ;; and an even more convenient pattern matcher:
  358. (defmacro mcase (value . clauses)
  359. (let ((tmp (gensym)))
  360. `(let ((,tmp ,value))
  361. ,(mif (() clauses)
  362. `(begin (print ,tmp)
  363. (error "mcase failed"))
  364. (mif (((pattern . body) . more) clauses)
  365. `(mif (,pattern ,tmp)
  366. (begin ,@(mif (() body) '(0) body))
  367. (mcase ,tmp ,@more))
  368. (! compiler errorSyntax: clauses))))))
  369. ;; and some traditional macros
  370. (defmacro when (test . body) `(if ,test (begin ,@body)))
  371. (defmacro unless (test . body) `(if ,test 0 (begin ,@body)))
  372. (defmacro or (. args) ; the built in OR returns 1 on success.
  373. (mcase args
  374. (() 0)
  375. ((e) e)
  376. ((e1 . more)
  377. (let ((tmp (gensym)))
  378. `(let ((,tmp ,e1))
  379. (if ,tmp ,tmp (or ,@more)))))))
  380. (defmacro dotimes_ ((var n) . body)
  381. (let ((tmp (gensym)))
  382. `(let ((,tmp ,n)
  383. (,var 0))
  384. (while (< ,var ,tmp)
  385. ,@body
  386. (set ,var (+ ,var 1))))))
  387. (defmacro dotimes ((var n) . body)
  388. (let ((tmp (gensym)))
  389. `(let ((,tmp ,n)
  390. (,var '0))
  391. (while (< ,var ,tmp)
  392. ,@body
  393. (set ,var (fx1+ ,var))))))
  394. ;; DOVEC is like the traditional DOLIST but works on "vectors"
  395. ;; i.e. sequences which can be indexed efficently.
  396. (defmacro dovec ((var seq) . body)
  397. (let ((i (gensym))
  398. (max (gensym))
  399. (tmp (gensym)))
  400. `(let ((,i 0)
  401. (,tmp ,seq)
  402. (,max (len_ ,tmp)))
  403. (while (< ,i ,max)
  404. (let ((,var (! ,tmp at: (_>fix ,i))))
  405. ,@body
  406. (set ,i (+ ,i 1)))))))
  407. ;; "Packing" is what Lispers usually call "collecting".
  408. ;; The Lisp idiom (let ((result '())) .. (push x result) .. (nreverse result))
  409. ;; translates to (packing (result) .. (pack x result))
  410. (defmacro packing ((var) . body)
  411. `(let ((,var (! OrderedCollection new)))
  412. ,@body
  413. (! ,var asArray)))
  414. (df pack (elt packer) (! packer addLast: elt))
  415. (assert (equal (packing (p) (dotimes_ (i 2) (pack (_>fix i) p)))
  416. '(0 1)))
  417. (assert (equal (packing (p) (dovec (e '(2 3)) (pack e p)))
  418. '(2 3)))
  419. (assert (equal (packing (p)
  420. (let ((a '(2 3)))
  421. (dotimes (i (len a))
  422. (pack (ref a i) p))))
  423. '(2 3)))
  424. ;; MAPCAR (more or less)
  425. (df map (fun col)
  426. (packing (r)
  427. (dovec (e col)
  428. (pack (fun e) r))))
  429. ;; VEC allocates and initializes a new array.
  430. ;; The macro translates (vec x y z) to `(,x ,y ,z).
  431. (defmacro vec (. args)
  432. `(quasiquote
  433. (,@(map (lambda (arg) `(,'unquote ,arg))
  434. args))))
  435. (assert (equal (vec '0 '1) '(0 1)))
  436. (assert (equal (vec) '()))
  437. (assert (== (len (vec 0 1 2 3 4)) '5))
  438. ;; Concatenate.
  439. (defmacro cat (. args) `(! (vec '"" ,@args) concatenated))
  440. (assert (equal (cat '"a" '"b" '"c") '"abc"))
  441. ;; Take a vector of bytes and copy the bytes to a continuous
  442. ;; block of memory
  443. (df assemble_ (col) (! (! ByteArray withAll: col) _bytes))
  444. ;; Jolt doesn't seem to have catch/throw or something equivalent.
  445. ;; Here I use a pair of assembly routines as substitue.
  446. ;; (catch% FUN) calls FUN with the current stack pointer.
  447. ;; (throw% VALUE K) unwinds the stack to K and then returns VALUE.
  448. ;; catch% is a bit like call/cc.
  449. ;;
  450. ;; [Would setjmp/longjmp work from Jolt? or does setjmp require
  451. ;; C-compiler magic?]
  452. ;; [I figure Smalltalk has a way to do non-local-exits but, I don't know
  453. ;; how to use that in Jolt.]
  454. ;;
  455. (define catch%
  456. (assemble_
  457. '(0x55 ; push %ebp
  458. 0x89 0xe5 ; mov %esp,%ebp
  459. 0x54 ; push %esp
  460. 0x8b 0x45 0x08 ; mov 0x8(%ebp),%eax
  461. 0xff 0xd0 ; call *%eax
  462. 0xc9 ; leave
  463. 0xc3 ; ret
  464. )))
  465. (define throw%
  466. (assemble_
  467. `(,@'()
  468. 0x8b 0x44 0x24 0x04 ; mov 0x4(%esp),%eax
  469. 0x8b 0x6c 0x24 0x08 ; mov 0x8(%esp),%ebp
  470. 0xc9 ; leave
  471. 0xc3 ; ret
  472. )))
  473. (df bar (i k)
  474. (if (== i 0)
  475. (throw% 100 k)
  476. (begin
  477. (printf "bar %d\n" i)
  478. (bar (- i 1) k))))
  479. (df foo (k)
  480. (printf "foo.1\n")
  481. (printf "foo.2 %d\n" (bar 10 k)))
  482. ;; Our way to produce closures: we compile a new little function which
  483. ;; hardcodes the addresses of the code resp. the data-vector. The
  484. ;; nice thing is that such closures can be used called C function
  485. ;; pointers. It's probably slow to invoke the compiler for such
  486. ;; things, so use with care.
  487. (df make-closure (addr state)
  488. (int>_
  489. (! `(lambda (a b c d)
  490. (,(_>int addr) ,(_>int state) a b c d))
  491. eval)))
  492. ;; Return a closure which calls FUN with ARGS and the arguments
  493. ;; that the closure was called with.
  494. ;; Example: ((curry printf "%d\n") 10)
  495. (defmacro curry (fun . args)
  496. `(make-closure
  497. (lambda (state a b c d)
  498. ((ref state '0)
  499. ,@(packing (sv)
  500. (dotimes (i (len args))
  501. (pack `(ref state ',(fx1+ i)) sv)))
  502. a b c d))
  503. (vec ,fun ,@args)))
  504. (df parse-closure-arglist (vars)
  505. (let ((pos (or (index-of vars '|)
  506. (return nil)))
  507. (cvars (! vars copyFrom: '0 to: (fx1- pos)))
  508. (lvars (! vars copyFrom: (fx1+ pos))))
  509. (vec cvars lvars)))
  510. ;; Create a closure, to-be-closed-over variables must enumerated
  511. ;; explicitly.
  512. ;; Example: ((let ((x 1)) (closure (x | y) (+ x y))) 3) => 4.
  513. ;; The variables before the "|" are captured by the closure.
  514. (defmacro closure ((. vars) . body)
  515. (mif ((cvars lvars) (parse-closure-arglist vars))
  516. `(curry (lambda (,@cvars ,@lvars) ,@body)
  517. ,@cvars)
  518. (! compiler errorSyntax: vars)))
  519. ;; The analog for Smalltalkish "blocks".
  520. (defmacro block ((. vars) . body)
  521. (mif ((cvars lvars) (parse-closure-arglist vars))
  522. `(! StaticBlockClosure
  523. function_: (curry (lambda (,@cvars _closure _self ,@lvars) ,@body)
  524. ,@cvars)
  525. arity_: ,(len lvars))
  526. (! compiler errorSyntax: vars)))
  527. (define %mkstemp (dlsym "mkstemp"))
  528. (df make-temp-file ()
  529. (let ((name (! '"/tmp/jolt-tmp.XXXXXX" copy))
  530. (fd (%mkstemp (! name _stringValue))))
  531. (if (== fd -1)
  532. (error "mkstemp failed"))
  533. `(,fd ,name)))
  534. (define %unlink (dlsym "unlink"))
  535. (df unlink (filename) (%unlink (! filename _stringValue)))
  536. (define write (dlsym "write"))
  537. (df write-bytes (addr count fd)
  538. (let ((written (write fd addr count)))
  539. (if (!= written count)
  540. (begin
  541. (printf "write failed %p %d %d => %d" addr count fd written)
  542. (error '"write failed")))))
  543. (define system (dlsym "system"))
  544. (define main (dlsym "main"))
  545. ;; Starting at address ADDR, disassemble COUNT bytes.
  546. ;; This is implemented by writing the memory region to a file
  547. ;; and call ndisasm on it.
  548. (df disas (addr count)
  549. (let ((fd+name (make-temp-file)))
  550. (write-bytes addr count (first fd+name))
  551. (let ((cmd (str>_ (cat '"ndisasm -u -o "
  552. (>str (_>fix addr))
  553. '" " (second fd+name)))))
  554. (printf "Running: %s\n" cmd)
  555. (system cmd))
  556. (unlink (second fd+name))))
  557. (df rep ()
  558. (let ((result (! (! CokeScanner read: StdIn) eval)))
  559. (puts '"=> " StdOut)
  560. (print result)
  561. (puts '"\n" StdOut)))
  562. ;; Perhaps we could use setcontext/getcontext to return from signal
  563. ;; handlers (or not).
  564. (define +ucontext-size+ 350)
  565. (define _getcontext (dlsym "getcontext"))
  566. (define _setcontext (dlsym "setcontext"))
  567. (df getcontext ()
  568. (let ((context (malloc 350)))
  569. (_getcontext context)
  570. context))
  571. (define on_exit (dlsym "on_exit")) ; "atexit" doesn't work. why?
  572. (define *top-level-restart* 0)
  573. (define *top-level-context* 0)
  574. (define *debugger-hook* 0)
  575. ;; Jolt's error handling strategy is charmingly simple: call exit.
  576. ;; We invoke the SLIME debugger from an exit handler.
  577. ;; (The handler is registered with atexit, that's a libc function.)
  578. (df exit-handler (reason arg)
  579. (printf "exit-handler 0x%x\n" reason)
  580. ;;(backtrace)
  581. (on_exit exit-handler nil)
  582. (when *debugger-hook*
  583. (*debugger-hook* `(exit ,reason)))
  584. (cond (*top-level-context*
  585. (_setcontext *top-level-context*))
  586. (*top-level-restart*
  587. (throw% reason *top-level-restart*))))
  588. (df repl ()
  589. (set *top-level-context* (getcontext))
  590. (while (not (! (! StdIn readStream) atEnd))
  591. (printf "top-level\n")
  592. (catch%
  593. (lambda (k)
  594. (set *top-level-restart* k)
  595. (printf "repl\n")
  596. (while 1
  597. (rep)))))
  598. (printf "EOF\n"))
  599. ;; (repl)
  600. ;;; Socket code. (How boring. Duh, should have used netcat instead.)
  601. (define strerror (dlsym "strerror"))
  602. (df check-os-code (value)
  603. (if (== value -1)
  604. (error (_>str (strerror (fix>_ (! OS errno)))))
  605. value))
  606. ;; For now just hard-code constants which usually reside in header
  607. ;; files (just like a Forth guy would do).
  608. (define PF_INET 2)
  609. (define SOCK_STREAM 1)
  610. (define SOL_SOCKET 1)
  611. (define SO_REUSEADDR 2)
  612. (define socket (dlsym "socket"))
  613. (define setsockopt (dlsym "setsockopt"))
  614. (df set-reuse-address (sock value)
  615. (let ((word-size 4)
  616. (val (! Object _balloc: (_>fix word-size))))
  617. (set-int@ val value)
  618. (check-os-code
  619. (setsockopt sock SOL_SOCKET SO_REUSEADDR val word-size))))
  620. (define sockaddr_in/size 16)
  621. (define sockaddr_in/sin_family 0)
  622. (define sockaddr_in/sin_port 2)
  623. (define sockaddr_in/sin_addr 4)
  624. (define INADDR_ANY 0)
  625. (define AF_INET 2)
  626. (define htons (dlsym "htons"))
  627. (define bind (dlsym "bind"))
  628. (df bind-socket (sock port)
  629. (let ((addr (! OS _balloc: (_>fix sockaddr_in/size))))
  630. (set-short@ (+ addr sockaddr_in/sin_family) AF_INET)
  631. (set-short@ (+ addr sockaddr_in/sin_port) (htons port))
  632. (set-int@ (+ addr sockaddr_in/sin_addr) INADDR_ANY)
  633. (check-os-code
  634. (bind sock addr sockaddr_in/size))))
  635. (define listen (dlsym "listen"))
  636. (df create-socket (port)
  637. (let ((sock (check-os-code (socket PF_INET SOCK_STREAM 0))))
  638. (set-reuse-address sock 1)
  639. (bind-socket sock port)
  640. (check-os-code (listen sock 1))
  641. sock))
  642. (define accept% (dlsym "accept"))
  643. (df accept (sock)
  644. (let ((addr (! OS _balloc: (_>fix sockaddr_in/size)))
  645. (len (! OS _balloc: 4)))
  646. (set-int@ len sockaddr_in/size)
  647. (check-os-code (accept% sock addr len))))
  648. (define getsockname (dlsym "getsockname"))
  649. (define ntohs (dlsym "ntohs"))
  650. (df local-port (sock)
  651. (let ((addr (! OS _balloc: (_>fix sockaddr_in/size)))
  652. (len (! OS _balloc: 4)))
  653. (set-int@ len sockaddr_in/size)
  654. (check-os-code
  655. (getsockname sock addr len))
  656. (ntohs (short@ (+ addr sockaddr_in/sin_port)))))
  657. (define close (dlsym "close"))
  658. (define _read (dlsym "read"))
  659. ;; Now, after 2/3 of the file we can begin with the actual Swank
  660. ;; server.
  661. (df read-string (fd count)
  662. (let ((buffer (! String new: count))
  663. (buffer_ (str>_ buffer))
  664. (count_ (int>_ count))
  665. (start 0))
  666. (while (> (- count_ start) 0)
  667. (let ((rcount (check-os-code (_read fd
  668. (+ buffer_ start)
  669. (- count_ start)))))
  670. (set start (+ start rcount))))
  671. buffer))
  672. ;; Read and parse a message from the wire.
  673. (df read-packet (fd)
  674. (let ((header (read-string fd '6))
  675. (length (! Integer fromString: header base: '16))
  676. (payload (read-string fd length)))
  677. (! CokeScanner read: payload)))
  678. ;; Print a messag to the wire.
  679. (df send-to-emacs (event fd)
  680. (let ((stream (! WriteStream on: (! String new: '100))))
  681. (! stream position: '6)
  682. (prin1 event stream)
  683. (let ((len (! stream position)))
  684. (! stream position: '0)
  685. (! (fx+ len '-6) printOn: stream base: '16 width: '6)
  686. (write-bytes (str>_ (! stream collection)) (int>_ len) fd))))
  687. (df add-quotes (form)
  688. (mcase form
  689. ((fun . args)
  690. `(,fun ,@(packing (s)
  691. (dovec (e args)
  692. (pack `(quote ,e) s)))))))
  693. (define sldb 0) ;defer
  694. (df eval-for-emacs (form id fd abort)
  695. (let ((old-hook *debugger-hook*))
  696. (mcase (catch%
  697. (closure (form fd | k)
  698. (set *debugger-hook* (curry sldb fd k))
  699. `(ok ,(int>_ (! (add-quotes form) eval)))))
  700. (('ok value)
  701. (set *debugger-hook* old-hook)
  702. (send-to-emacs `(:return (:ok ,value) ,id) fd)
  703. 'ok)
  704. (arg
  705. (set *debugger-hook* old-hook)
  706. (send-to-emacs `(:return (:abort) ,id) fd)
  707. (throw% arg abort)))))
  708. (df process-events (fd)
  709. (on_exit exit-handler nil)
  710. (let ((done nil))
  711. (while (not done)
  712. (mcase (read-packet fd)
  713. ((':emacs-rex form package thread id)
  714. (mcase (catch% (closure (form id fd | abort)
  715. (eval-for-emacs form id fd abort)))
  716. ('ok)
  717. ;;('abort nil)
  718. ('top-level)
  719. (other
  720. ;;(return other) ; compiler breaks with return
  721. (set done 1))))))))
  722. (df next-frame (fp)
  723. (let ((next (get-caller-fp fp)))
  724. (if (and (!= next fp)
  725. (<= next %top-level-fp))
  726. next
  727. nil)))
  728. (df nth-frame (n top)
  729. (let ((fp top)
  730. (i 0))
  731. (while fp
  732. (if (== i n) (return fp))
  733. (set fp (next-frame fp))
  734. (set i (+ i 1)))
  735. nil))
  736. (define Dl_info/size 16)
  737. (define Dl_info/dli_fname 0)
  738. (define Dl_info/dli_sname 8)
  739. (df get-dl-sym-name (addr)
  740. (let ((info (! OS _balloc: (_>fix Dl_info/size))))
  741. (when (== (dladdr addr info) 0)
  742. (return nil))
  743. (let ((sname (long@ (+ info Dl_info/dli_sname)) )
  744. (fname (long@ (+ info Dl_info/dli_fname))))
  745. (cond ((and sname fname)
  746. (cat (_>str sname) '" in " (_>str fname)))
  747. (sname (_>str fname))
  748. (fname (cat '"<??> " (_>str fname)))
  749. (true nil)))))
  750. ;;(get-dl-sym-name printf)
  751. (df guess-function-name (ip)
  752. (let ((fname (get-function-name ip)))
  753. (if fname
  754. (_>str fname)
  755. (get-dl-sym-name ip))))
  756. (df backtrace>el (top_ from_ to_)
  757. (let ((fp (nth-frame from_ top_))
  758. (i from_))
  759. (packing (bt)
  760. (while (and fp (< i to_))
  761. (let ((ip (get-frame-ip fp)))
  762. (pack (vec (_>int i)
  763. (cat (or (guess-function-name ip) '"(no-name)")
  764. '" " ;;(>str (_>int ip))
  765. ))
  766. bt))
  767. (set i (+ i 1))
  768. (set fp (next-frame fp))))))
  769. (df debugger-info (fp msg)
  770. (vec `(,(prin1-to-string msg) " [type ...]" ())
  771. '(("quit" "Return to top level"))
  772. (backtrace>el fp 0 20)
  773. '()))
  774. (define *top-frame* 0)
  775. (define *sldb-quit* 0)
  776. (df debugger-loop (fd args abort)
  777. (let ((fp (get-current-fp)))
  778. (set *top-frame* fp)
  779. (send-to-emacs `(:debug 0 1 ,@(debugger-info fp args)) fd)
  780. (while 1
  781. (mcase (read-packet fd)
  782. ((':emacs-rex form package thread id)
  783. (mcase (catch% (closure (form id fd | k)
  784. (set *sldb-quit* k)
  785. (eval-for-emacs form id fd k)
  786. 'ok))
  787. ('ok nil)
  788. (other
  789. (send-to-emacs `(:return (:abort) ,id) fd)
  790. (throw% other abort))))))))
  791. (df sldb (fd abort args)
  792. (let ((old-top-frame *top-frame*)
  793. (old-sldb-quit *sldb-quit*))
  794. (mcase (catch% (curry debugger-loop fd args))
  795. (value
  796. (set *top-frame* old-top-frame)
  797. (set *sldb-quit* old-sldb-quit)
  798. (send-to-emacs `(:debug-return 0 1 nil) fd)
  799. (throw% value abort)))))
  800. (df swank:backtrace (start end)
  801. (backtrace>el *top-frame* (int>_ start) (int>_ end)))
  802. (df sldb-quit ()
  803. (assert *sldb-quit*)
  804. (throw% 'top-level *sldb-quit*))
  805. (df swank:invoke-nth-restart-for-emacs (...) (sldb-quit))
  806. (df swank:throw-to-toplevel (...) (sldb-quit))
  807. (df setup-server (port announce)
  808. (let ((sock (create-socket port)))
  809. (announce sock)
  810. (let ((client (accept sock)))
  811. (process-events client)
  812. (close client))
  813. (printf "Closing socket: %d %d\n" sock (local-port sock))
  814. (close sock)))
  815. (df announce-port (sock)
  816. (printf "Listening on port: %d\n" (local-port sock)))
  817. (df create-server (port) (setup-server port announce-port))
  818. (df write-port-file (filename sock)
  819. (let ((f (! File create: filename)))
  820. (! f write: (print-to-string (_>int (local-port sock))))
  821. (! f close)))
  822. (df start-swank (port-file)
  823. (setup-server 0 (curry write-port-file (_>str port-file))))
  824. (define getpid (dlsym "getpid"))
  825. (df swank:connection-info ()
  826. `(,@'()
  827. :pid ,(_>int (getpid))
  828. :style nil
  829. :lisp-implementation (,@'()
  830. :type "Coke"
  831. :name "jolt"
  832. :version ,(! CodeGenerator versionString))
  833. :machine (:instance "" :type ,(! OS architecture) :version "")
  834. :features ()
  835. :package (:name "jolt" :prompt "jolt")))
  836. (df swank:listener-eval (string)
  837. (let ((result (! (! CokeScanner read: string) eval)))
  838. `(:values ,(prin1-to-string (if (or (fix? result)
  839. (and (valid-pointer? result)
  840. (int? result)))
  841. (int>_ result)
  842. result))
  843. ,(prin1-to-string result))))
  844. (df swank:interactive-eval (string)
  845. (let ((result (! (! CokeScanner read: string) eval)))
  846. (cat '"=> " (prin1-to-string (if (or (fix? result)
  847. (and (valid-pointer? result)
  848. (int? result)))
  849. (int>_ result)
  850. result))
  851. '", " (prin1-to-string result))))
  852. (df swank:operator-arglist () nil)
  853. (df swank:buffer-first-change () nil)
  854. (df swank:create-repl (_) '("jolt" "jolt"))
  855. (df min (x y) (if (<= x y) x y))
  856. (df common-prefix2 (e1 e2)
  857. (let ((i '0)
  858. (max (min (len e1) (len e2))))
  859. (while (and (< i max)
  860. (== (ref e1 i) (ref e2 i)))
  861. (set i (fx1+ i)))
  862. (! e1 copyFrom: '0 to: (fx1- i))))
  863. (df common-prefix (seq)
  864. (mcase seq
  865. (() nil)
  866. (_
  867. (let ((prefix (ref seq '0)))
  868. (dovec (e seq)
  869. (set prefix (common-prefix2 prefix e)))
  870. prefix))))
  871. (df swank:simple-completions (prefix _package)
  872. (let ((matches (packing (s)
  873. (dovec (e (! TheGlobalEnvironment keys))
  874. (let ((name (>str e)))
  875. (when (! name beginsWith: prefix)
  876. (pack name s)))))))
  877. (vec matches (or (common-prefix matches) prefix))))
  878. ;; swank-jolt.k ends here