-
Notifications
You must be signed in to change notification settings - Fork 3
/
readtable.lisp
76 lines (70 loc) · 3.56 KB
/
readtable.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
(in-package :objc-runtime)
#+ccl
(defgeneric send-message (object message &rest args)
(:method ((object ccl:macptr) (message (eql 'alloc)) &rest args)
(apply #'objc-msg-send object (ensure-selector "alloc") args)))
(defun read-until (test symbol-prefix &optional stop-before-chars)
"Read from a string until"
(lambda (s c b)
(declare (ignore c b))
(let ((class-name (coerce (loop for next-char = (peek-char nil s nil nil t)
while next-char
until (funcall test next-char)
collect (read-char s t nil t)
finally (when (and (not (member next-char
stop-before-chars))
(funcall test next-char))
(read-char s t nil t)))
'string)))
`(,symbol-prefix ,class-name))))
(defmacro objc-send (obj message return &rest args)
(let* ((return-t (case return
(:nsstring :pointer)
(t return)))
(result `(cffi:foreign-funcall "objc_msgSend"
:pointer ,obj
:pointer ,message
,@args
,return-t)))
(case return
(:nsstring `(objc-send ,result
(ensure-selector "UTF8String")
:string))
(t result))))
(defun read-objc-form (s char)
(declare (ignore char))
(let* ((info (read-delimited-list #\] s t))
(safe-p (when (eql #\? (peek-char nil s nil #\p t))
(read-char s t nil t)))
(return-t (case (peek-char nil s nil #\p t)
(#\# (read-char s t nil t) :int)
(#\& (read-char s t nil t) :pointer)
(#\@ (read-char s t nil t) :nsstring)
(#\b (read-char s t nil t) :bool)
(#\s (read-char s t nil t) :string)
(t :pointer))))
(when info
(destructuring-bind (obj message . args) info
(if safe-p
`(safe-objc-msg-send ,return-t ,obj ,message ,@args)
`(objc-send ,obj ,message ,return-t ,@args))))))
(named-readtables:defreadtable :objc-readtable
(:merge :standard)
(:syntax-from :standard #\) #\])
(:macro-char #\[ 'read-objc-form nil)
(:dispatch-macro-char #\# #\@
(lambda (s c b)
c b
(let ((class-name (coerce (loop for c = (peek-char nil s nil nil t)
until (or (null c)
(serapeum:whitespacep c)
(member c
'(#\) #\( #\[ #\])))
collect (read-char s t nil t))
'string)))
`(ensure-class ,class-name))))
(:macro-char #\@ :dispatch t)
(:dispatch-macro-char #\@ #\( (read-until (serapeum:op (char= _ #\)))
'ensure-selector))
(:dispatch-macro-char #\@ #\" (read-until (serapeum:op (char= _ #\"))
'make-nsstring)))