reader
2020-12-20
A utility library intended at providing configurable reader macros for common tasks such as accessors, hash-tables, sets, uiop:run-program, arrays and a few others.
Installation
Download from the Releases section.
I'll promote v0.10.0 to v1.0.0 if there are no significant changes over the next year.
Getting Started
(reader:enable-reader-syntax &rest reader-macro-identifiers) ;; OR (reader+swank:enable-package-local-reader-syntax &rest reader-macro-identifiers) ;; (describe 'reader:enable-reader-syntax) ; for a list of identifiers ;; Complementory macros ;; (reader:disable-reader-syntax) ; is complementary ;; OR ;; (reader+swank-disable-package-local-reader-syntax)
Examples
CL-USER> #[[1 2 3 4 5 6] [3 2 1 7 8 9]] #3A(((1 2 3) (4 5 6)) ((3 2 1) (7 8 9))) ; cleaner syntax for arrays CL-USER> (gethash 'a {eq 'a 1 'b 2}) ; hash-tables 1 T CL-USER> (hash-set:hs-memberp #{"a" "b" "c"} "c") ; hash-set - can change in future T CL-USER> [#2A((1 2 3) (4 5 6)) 1 0] ; accessors 4 CL-USER> (setf reader:*get-val-array-function* 'select:select) SLCT:SELECT CL-USER> (let ((arr #2A((1 2 3) (4 5 6)))) [arr t 0]) #(1 4) CL-USER> [{"a" 1 "b" 2} "a"] 1 T CL-USER> [(cl-json:decode-json-from-string "{\"a\":1, \"b\":2}") :b] ; works with alists 2 CL-USER> (-> {"a" "apple" "b" "ball"} ["b"] [1]); accessors can be chained using arrows:-> #\a CL-USER> #!echo -n hello world hello world ; Captures until the end of line NIL ; Should be possible to capture into a string NIL ; but not ideal for speed 0 CL-USER> (let ((a t)) !a) ; not NIL CL-USER> (let ((a 5.0d0)) $a) ; write-to-string "5.0d0" ; should this be "ensure-string"? raise an issue! CL-USER> ?reader:enable-reader-syntax ; variable use not intended READER:ENABLE-READER-SYNTAX [symbol] ENABLE-READER-SYNTAX names a macro: Lambda-list: (&REST READER::READER-MACRO-IDENTIFIERS) Documentation: READER-MACRO-IDENTIFIERS are any of the following symbols: GET-VAL, HASH-TABLE, NOT, STRING, DESCRIBE, ARRAY, SET, RUN-PROGRAM Source file: /home/user/quicklisp/local-projects/reader/reader.lisp
Notes
The functions used for constructing arrays, hash-tables, sets, accessing array elements or accessors in general can be specified by
*array-function*
*hash-table-function*
*set-function*
*get-val-array-function*
*get-val-function*
This should allow users to use fset or access or other libraries for the respective functions.
By default, alists and plists are treated distinct from lists whenever possible (see the get-val
method specialized on lists). To disable this behavior, set each of these to T
:
*alists-are-lists*
*plists-are-lists*
Hash Tables
{"a" 1 "b" 2}
- hash tables use'equalp
as the default test. The intended test function can be specified as the first element of the literal syntax. Besides providing with a choice, this also gets the indentation correct (see Modification for emacs.
Setting alists and plists
(setf [] ...)
does not work with empty alists and plists. This seems to require setf-expanders withget-val
generic-function.
array vs get-val
[...]
(get-val
) does not work inside#[...]
(array
) syntax. I do not have a plan or haven't figured out how to combine the two; and I find that okay since thearray
syntax is meant for cleaner representation in works involving matrices.
Notes for existing users upgrading to v0.10.0 from v0.9.1
Lambda
lambda
with the following usage has been removed.
CL-USER> (mapcar λ(write-to-string -) '(2 3 4)) ; lambdas ("2" "3" "4")
This begins to feel like line-noise after a while and is not ideal for readability; a better equivalent is the simple macro
CL-USER> (defmacro lm (&rest var-body) `(lambda ,(butlast var-body) ,@(last var-body))) LM CL-USER> (mapcar (lm o (write-to-string o)) '(2 3 4)) ("2" "3" "4")
Hash-table
Since the function used for constructing hash-tables is now configurable (via reader:*hash-table-function*
), the first input to the hash-table may be interpreted as the equality function. Thus, hash-table syntax can take an even (with default test cl:equalp
) or odd (with the first symbol being interpreted as the equality function) number of arguments.
Setting alists and plists
Now works for non-empty lists.
Testing
(ql:quickload "reader") (5am:run :reader) ;; OR (asdf:test-system "reader")
Emacs Indentation Support
(modify-syntax-entry ?\[ "(]" lisp-mode-syntax-table) (modify-syntax-entry ?\] ")[" lisp-mode-syntax-table) (modify-syntax-entry ?\{ "(}" lisp-mode-syntax-table) (modify-syntax-entry ?\} "){" lisp-mode-syntax-table) (define-key paredit-mode-map (kbd "{") 'paredit-open-curly) (define-key paredit-mode-map (kbd "}") 'paredit-close-curly) (global-set-key (kbd "C-S-l") (lambda () ; for inserting lambda (interactive) (insert-char (aref (symbol-name 'λ) 0)))) (setq paredit-space-for-delimiter-predicates (list (lambda (endp delimiter) (not (and (eql ?\( delimiter) (member (char-before (point)) '(?\@ ?λ)))))))) (set-language-environment "UTF-8")
Motivation: paredit curly brace matching in swank-clojure repl - Stack OverFlow
Known Issues
Known issues include aligning hash-tables without first-element-as-key:
{"hello" 1 "world" 2}
A work-around is to specify the test as the first element, and let that default exist for one-liners:
{equal "hello" 1 "world" 2} {"hello" 1 "world" 2}
Comments
cl-interpol
is a prominent library providing perl / shell-like string interpolation facilities.cl-json
can be used for parsing JSON.