git.fiddlerwoaroof.com
Browse code

add beginnings of expositions of the extractor framework

Ed Langley authored on 23/09/2018 09:47:55
Showing 1 changed files
... ...
@@ -110,220 +110,232 @@ From demo-app.lisp:
110 110
 
111 111
 ** Reading List to Org-file converter
112 112
 
113
- #+name: r-l-r-main
114
- #+begin_src lisp :tangle no :results no :noweb yes
115
-   (defun main ()
116
-     <<disable-sbcl-debugger>>
117
-     (make-org-file *standard-output*
118
-                    (translate-plist (get-bookmark-filename))))
119
- #+end_src
120
-
121
- #+name: translate-plist
122
- #+begin_src lisp :tangle no :results no
123
-   (defparameter *reading-list-location* "Library/Safari/Bookmarks.plist")
124
-   (defun get-bookmark-filename ()
125
-     (merge-pathnames *reading-list-location*
126
-                      (truename "~/")))
127
-
128
-   (defun translate-plist (fn)
129
-     (objc-runtime.data-extractors:extract-from-objc
130
-      (objc-runtime.data-extractors:get-plist fn)))
131
- #+end_src
132
-
133
- #+name: make-org-file
134
- #+begin_src lisp :tangle no :results no
135
-   (defun make-org-file (s bookmarks)
136
-     (format s "~&* Safari Reading List~%")
137
-     (serapeum:mapply (serapeum:partial 'make-org-entry s)
113
+   The entry-point is fairly unremarkable: it delegates most of the work to other functions and disables the debugger so
114
+   that this doesn't blow up when an error occurs in non-interactive mode.
115
+
116
+   #+name: r-l-r-main
117
+   #+begin_src lisp :tangle no :results no :noweb yes
118
+     (defun main ()
119
+       <<disable-sbcl-debugger>>
120
+       (make-org-file *standard-output*
121
+                      (translate-plist 
122
+                       (get-bookmark-filename))
138 123
                       (get-readinglist-info bookmarks)))
139
-   (defun make-org-entry (s title url preview tag)
140
-     (format s "~&** ~a :~{~a:~}~% ~a~2% ~{~<~% ~1,80:;~a~> ~}~%"
141
-             title (alexandria:ensure-list tag)
142
-             url
143
-             (serapeum:tokens preview)))
144
- #+end_src
145
-
146
- #+name: translate-data
147
- #+begin_src lisp :tangle no :results no
148
-   (defun get-readinglist-info (bookmarks)
149
-     (mapcar 'extract-link-info
150
-             (gethash "Children"
151
-                      (car
152
-                       (select-child bookmarks
153
-                                     "com.apple.ReadingList")))))
154
-
155
-   (defun extract-link-info (link)
156
-     (list (fw.lu:pick '("URIDictionary" "title") link)
157
-           (fw.lu:pick '("URLString") link)
158
-           (plump:decode-entities (coerce (fw.lu:pick '("ReadingList" "PreviewText") link) 'simple-string) t)
159
-           (fw.lu:may (slugify (fw.lu:pick '("ReadingListNonSync" "siteName") link)))))
160
-
161
-   (defun slugify (s)
162
-     (cl-ppcre:regex-replace-all "\\s+"
163
-                                 (string-downcase s)
164
-                                 "_"))
165
-
166
-   (defun select-child (d title)
167
-     (flet ((get-title (h)
168
-              (equal (gethash "Title" h)
169
-                     title)))
170
-       (fw.lu:let-each (:be *)
171
-         (gethash "Children" d)
172
-         (remove-if-not #'get-title *))))
173
- #+end_src
124
+   #+end_src
125
+   
126
+   This pair of functions builds an org file from data extracted from the Safari bookmark file.
127
+
128
+   #+name: make-org-file
129
+   #+begin_src lisp :tangle no :results no
130
+     (defun make-org-file (s bookmarks reading-list-info)
131
+       (format s "~&* Safari Reading List~%")
132
+       (serapeum:mapply (serapeum:partial 'make-org-entry s)
133
+                        reading-list-info))
134
+
135
+     (defun make-org-entry (s title url preview tag)
136
+       (format s "~&** ~a :~{~a:~}~% ~a~2% ~{~<~% ~1,80:;~a~> ~}~%"
137
+               title (alexandria:ensure-list tag)
138
+               url
139
+               (serapeum:tokens preview)))
140
+   #+end_src
141
+
142
+   #+name: translate-plist
143
+   #+begin_src lisp :tangle no :results no
144
+     (defparameter *reading-list-location* "Library/Safari/Bookmarks.plist")
145
+     (defun get-bookmark-filename ()
146
+       (merge-pathnames *reading-list-location*
147
+                        (truename "~/")))
148
+
149
+     (defun translate-plist (fn)
150
+       (objc-runtime.data-extractors:extract-from-objc
151
+        (objc-runtime.data-extractors:get-plist fn)))
152
+   #+end_src
153
+
154
+   #+name: translate-data
155
+   #+begin_src lisp :tangle no :results no
156
+     (defun get-readinglist-info (bookmarks)
157
+       (mapcar 'extract-link-info
158
+               (gethash "Children"
159
+                        (car
160
+                         (select-child bookmarks
161
+                                       "com.apple.ReadingList")))))
162
+
163
+     (defun extract-link-info (link)
164
+       (list (fw.lu:pick '("URIDictionary" "title") link)
165
+             (fw.lu:pick '("URLString") link)
166
+             (plump:decode-entities (coerce (fw.lu:pick '("ReadingList" "PreviewText") link) 'simple-string) t)
167
+             (fw.lu:may (slugify (fw.lu:pick '("ReadingListNonSync" "siteName") link)))))
168
+
169
+     (defun slugify (s)
170
+       (cl-ppcre:regex-replace-all "\\s+"
171
+                                   (string-downcase s)
172
+                                   "_"))
173
+
174
+     (defun select-child (d title)
175
+       (flet ((get-title (h)
176
+                (equal (gethash "Title" h)
177
+                       title)))
178
+         (fw.lu:let-each (:be *)
179
+           (gethash "Children" d)
180
+           (remove-if-not #'get-title *))))
181
+   #+end_src
174 182
 
175 183
 ** Appendices
176 184
   
177 185
 *** objc-data-extractor.lisp
178 186
 
179
- #+begin_src lisp :tangle objc-data-extractors.lisp :noweb yes
180
-   (defpackage :objc-runtime.data-extractors
181
-     (:use :cl )
182
-     (:export
183
-      #:extract-from-objc
184
-      #:define-extractor
185
-      #:clear-extractors
186
-      #:add-extractor
187
-      #:get-plist))
188
-
189
-   (in-package :objc-runtime.data-extractors)
190
-   (named-readtables:in-readtable :objc-readtable)
191
-
192
-   (defun get-plist (file)
193
-     [#@NSDictionary @(dictionaryWithContentsOfFile:)
194
-                     :pointer (objc-runtime::make-nsstring file)])
195
-
196
-   (defun objc-subclass-p (sub super)
197
-     (unless (or (cffi:null-pointer-p sub)
198
-                 (cffi:null-pointer-p super))
199
-       (or (eql sub super)
200
-           (= [sub @(isSubclassOfClass:) :pointer [super @(class)]]#
201
-              1))))
202
-
203
-   (defun order-objc-classes (classes &rest r &key key)
204
-     (declare (ignore key))
205
-     (apply 'stable-sort
206
-            (copy-seq classes)
207
-            'objc-subclass-p
208
-            r))
209
-
210
-   (defun objc-isa (obj class)
211
-     (unless (or (cffi:null-pointer-p obj)
212
-                 (cffi:null-pointer-p class))
213
-       (= [obj @(isKindOfClass:) :pointer class]#
214
-          1)))
215
-
216
-   (defun objc-pick-by-type (obj pairs)
217
-     (assoc obj
218
-            (order-objc-classes pairs :key 'car)
219
-            :test 'objc-isa))
220
-
221
-   (serapeum:eval-always
222
-     (defun make-cases (cases obj)
223
-       (mapcar (serapeum:op
224
-                 `(if (objc-isa ,obj ,(car _1))
225
-                      (progn ,@(cdr _1))))
226
-                      cases)))
227
-
228
-   (defmacro objc-typecase (form &body ((case-type &body case-handler) &rest cases))
229
-     (alexandria:once-only (form)
230
-       (let* ((initial-cases `((,case-type ,@case-handler) ,@(butlast cases)))
231
-              (cases (fw.lu:rollup-list (make-cases initial-cases form)
232
-                                        (if (eql t (caar (last cases)))
233
-                                            `((progn ,@(cdar (last cases))))
234
-                                            (make-cases (last cases) form)))))
235
-         cases)))
236
-
237
-   (defun map-nsarray (fn arr)
238
-     (unless (and (cffi:pointerp arr)
239
-                  (objc-isa arr #@NSArray))
240
-       (error "must provide a NSArray pointer"))
241
-     (loop for x below [arr @(count)]#
242
-        collect (funcall fn [arr @(objectAtIndex:) :int x])))
243
-
244
-   (defun nsarray-contents (arr)
245
-     (unless (and (cffi:pointerp arr)
246
-                  (objc-isa arr #@NSArray))
247
-       (error "must provide a NSArray pointer"))
248
-     (dotimes (n [arr @(count)]#)
249
-       (let ((obj [arr @(objectAtIndex:) :int n ]))
250
-         (objc-typecase obj
251
-           (#@NSString (format t "~&string~%"))
252
-           (#@NSArray (format t "~&array~%"))
253
-           (#@NSDictionary (format t "~&dictionary~%"))
254
-           (t (format t "~&other... ~s~%" (objc-runtime::objc-class-get-name
255
-                                           (objc-runtime::object-get-class obj))))))))
256
-                                          
257
-   (defmacro funcall-some (fun &rest args)
258
-     (alexandria:once-only (fun)
259
-       `(if ,fun
260
-            (funcall ,fun ,@args))))
261
-
262
-   <<extractor-framework>>
263
- #+end_src
187
+    #+begin_src lisp :tangle objc-data-extractors.lisp :noweb yes
188
+      (defpackage :objc-runtime.data-extractors
189
+        (:use :cl )
190
+        (:export
191
+         #:extract-from-objc
192
+         #:define-extractor
193
+         #:clear-extractors
194
+         #:add-extractor
195
+         #:get-plist))
196
+
197
+      (in-package :objc-runtime.data-extractors)
198
+      (named-readtables:in-readtable :objc-readtable)
199
+
200
+      (defun get-plist (file)
201
+        [#@NSDictionary @(dictionaryWithContentsOfFile:)
202
+                        :pointer (objc-runtime::make-nsstring file)])
203
+
204
+      (defun objc-subclass-p (sub super)
205
+        (unless (or (cffi:null-pointer-p sub)
206
+                    (cffi:null-pointer-p super))
207
+          (or (eql sub super)
208
+              (= [sub @(isSubclassOfClass:) :pointer [super @(class)]]#
209
+                 1))))
210
+
211
+      (defun order-objc-classes (classes &rest r &key key)
212
+        (declare (ignore key))
213
+        (apply 'stable-sort
214
+               (copy-seq classes)
215
+               'objc-subclass-p
216
+               r))
217
+
218
+      (defun objc-isa (obj class)
219
+        (unless (or (cffi:null-pointer-p obj)
220
+                    (cffi:null-pointer-p class))
221
+          (= [obj @(isKindOfClass:) :pointer class]#
222
+             1)))
223
+
224
+      (defun objc-pick-by-type (obj pairs)
225
+        (assoc obj
226
+               (order-objc-classes pairs :key 'car)
227
+               :test 'objc-isa))
228
+
229
+      (serapeum:eval-always
230
+        (defun make-cases (cases obj)
231
+          (mapcar (serapeum:op
232
+                    `(if (objc-isa ,obj ,(car _1))
233
+                         (progn ,@(cdr _1))))
234
+                         cases)))
235
+
236
+      (defmacro objc-typecase (form &body ((case-type &body case-handler) &rest cases))
237
+        (alexandria:once-only (form)
238
+          (let* ((initial-cases `((,case-type ,@case-handler) ,@(butlast cases)))
239
+                 (cases (fw.lu:rollup-list (make-cases initial-cases form)
240
+                                           (if (eql t (caar (last cases)))
241
+                                               `((progn ,@(cdar (last cases))))
242
+                                               (make-cases (last cases) form)))))
243
+            cases)))
244
+
245
+      (defun map-nsarray (fn arr)
246
+        (unless (and (cffi:pointerp arr)
247
+                     (objc-isa arr #@NSArray))
248
+          (error "must provide a NSArray pointer"))
249
+        (loop for x below [arr @(count)]#
250
+           collect (funcall fn [arr @(objectAtIndex:) :int x])))
251
+
252
+      (defun nsarray-contents (arr)
253
+        (unless (and (cffi:pointerp arr)
254
+                     (objc-isa arr #@NSArray))
255
+          (error "must provide a NSArray pointer"))
256
+        (dotimes (n [arr @(count)]#)
257
+          (let ((obj [arr @(objectAtIndex:) :int n ]))
258
+            (objc-typecase obj
259
+              (#@NSString (format t "~&string~%"))
260
+              (#@NSArray (format t "~&array~%"))
261
+              (#@NSDictionary (format t "~&dictionary~%"))
262
+              (t (format t "~&other... ~s~%" (objc-runtime::objc-class-get-name
263
+                                              (objc-runtime::object-get-class obj))))))))
264
+
265
+      (defmacro funcall-some (fun &rest args)
266
+        (alexandria:once-only (fun)
267
+          `(if ,fun
268
+               (funcall ,fun ,@args))))
269
+
270
+      <<extractor-framework>>
271
+    #+end_src
264 272
 
265 273
 *** build-reading-list-reader.sh
266 274
    
267
- #+begin_src sh :tangle build-reading-list-reader.sh
268
-   #!/usr/bin/env bash
269
-   set -eu -x -o pipefail
270
-
271
-   cd "$(dirname $0)"
272
-   mkdir -p dist
273
-
274
-   pushd dist
275
-   rm -rf fwoar.lisputils
276
-   git clone https://github.com/fiddlerwoaroof/fwoar.lisputils.git
277
-   popd
278
-
279
-   export CL_SOURCE_REGISTRY="$PWD/dist//"
280
-   sbcl --no-userinit \
281
-        --load ~/quicklisp/setup.lisp \
282
-        --load build.lisp
283
- #+end_src
275
+    #+begin_src sh :tangle build-reading-list-reader.sh
276
+      #!/usr/bin/env bash
277
+      set -eu -x -o pipefail
284 278
 
285
-*** build.lisp
279
+      cd "$(dirname $0)"
280
+      mkdir -p dist
286 281
 
287
- #+begin_src lisp :mkdirp yes :results no :noweb yes :tangle build.lisp
288
-   (eval-when (:compile-toplevel :load-toplevel :execute)
289
-     (setf *default-pathname-defaults* (truename "~/git_repos/objc-lisp-bridge/"))
290
-     (load (compile-file "objc-runtime.asd")))
282
+      pushd dist
283
+      rm -rf fwoar.lisputils
284
+      git clone https://github.com/fiddlerwoaroof/fwoar.lisputils.git
285
+      popd
291 286
 
292
-   (eval-when (:compile-toplevel :load-toplevel :execute)
293
-     (ql:quickload '(:objc-runtime :yason :plump :cl-ppcre)))
287
+      export CL_SOURCE_REGISTRY="$PWD/dist//"
288
+      sbcl --no-userinit \
289
+           --load ~/quicklisp/setup.lisp \
290
+           --load build.lisp
291
+    #+end_src
294 292
 
295
-   (load "reading-list-reader.lisp")
293
+*** build.lisp
296 294
 
297
-   (eval-when (:compile-toplevel :load-toplevel :execute)
298
-     (sb-ext:save-lisp-and-die "reading-list2org"
299
-                               :toplevel (intern "MAIN"
300
-                                                 "READING-LIST-READER")
301
-                               :executable t))
302
- #+end_src
295
+    #+begin_src lisp :mkdirp yes :results no :noweb yes :tangle build.lisp
296
+      (eval-when (:compile-toplevel :load-toplevel :execute)
297
+        (setf *default-pathname-defaults* (truename "~/git_repos/objc-lisp-bridge/"))
298
+        (load (compile-file "objc-runtime.asd")))
303 299
 
304
-*** reading-list-reader.lisp
300
+      (eval-when (:compile-toplevel :load-toplevel :execute)
301
+        (ql:quickload '(:objc-runtime :yason :plump :cl-ppcre)))
305 302
 
306
- #+begin_src lisp :mkdirp yes :results no :noweb yes :tangle reading-list-reader.lisp 
307
-   (defpackage :reading-list-reader
308
-     (:use :cl )
309
-     (:export ))
310
-   (in-package :reading-list-reader)
303
+      (load "reading-list-reader.lisp")
311 304
 
312
-   (serapeum:eval-always
313
-     (named-readtables:in-readtable :objc-readtable))
305
+      (eval-when (:compile-toplevel :load-toplevel :execute)
306
+        (sb-ext:save-lisp-and-die "reading-list2org"
307
+                                  :toplevel (intern "MAIN"
308
+                                                    "READING-LIST-READER")
309
+                                  :executable t))
310
+    #+end_src
314 311
 
315
-   <<r-l-r-main>>
316
-   <<translage-plist>>
317
-   <<make-org-file>>
318
-   <<translate-data>>
319
- #+end_src
312
+*** reading-list-reader.lisp
320 313
 
321
- #+name: disable-sbcl-debugger
322
- #+begin_src lisp :tangle no
323
-   ,#+(and build sbcl)
324
-   (progn (sb-ext:disable-debugger)
325
-          (sb-alien:alien-funcall
326
-           (sb-alien:extern-alien "disable_lossage_handler"
327
-                                  (function sb-alien:void))))
328
- #+end_src
314
+    #+begin_src lisp :mkdirp yes :results no :noweb yes :tangle reading-list-reader.lisp 
315
+      (defpackage :reading-list-reader
316
+        (:use :cl )
317
+        (:export ))
318
+      (in-package :reading-list-reader)
319
+
320
+      (serapeum:eval-always
321
+        (named-readtables:in-readtable :objc-readtable))
322
+
323
+      <<r-l-r-main>>
324
+      <<translage-plist>>
325
+      <<make-org-file>>
326
+      <<translate-data>>
327
+    #+end_src
328
+
329
+    #+name: disable-sbcl-debugger
330
+    #+begin_src lisp :tangle no
331
+      ,#+(and build sbcl)
332
+      (progn (sb-ext:disable-debugger)
333
+             (sb-alien:alien-funcall
334
+              (sb-alien:extern-alien "disable_lossage_handler"
335
+                                     (function sb-alien:void))))
336
+    #+end_src
329 337
  
338
+
339
+# Local Variables:
340
+# fill-column: 120 :
341
+# End: