Re: Scoped reader macros
Hello Erik.
PS: I'm replying to the list, I hope you don't mind.
I thought so too at first glance.
But the implementation of this idea is full of subtleties and problems
to solve. The first is that you don't know if the form is supposed to
be a literal form, e.g:
(defvar my-symbols '(with-named-readtable foo bar))
How would this macro character know if it should handle the quoted
form? You'd probably have to install more reader macros for every
literal syntax, such as #\' (quote), #\# (sharpsign) and #\`
(backquote). Worse, you'd have to identify pre-emptively where forms
are expected.
Supposing you solve this problem, the implementation would have to
keep the thus far read characters themselves in case it wants to
(perhaps conditionally) chain to the previously installed #\( reader
macro function, e.g. by means of make-string-input-stream with a
string of the thus far read characters, and make-concatenated-stream
with this string input stream and the actual stream.
Both Pascals have resumed their experience in their answers, perhaps a
bit too much, such that their points are a bit opaque to people with
less reader macro experience without these gory^W expository details.
They are right though, you can't achieve this without at least one
other reader macro, because that's the level where you'd have to
handle this, or that file-local named readtables are quite enough,
such that you'd simply split your code in multiple files in case a
piece of code requires the standard readtable and another piece of
code requires a named readtable (for instance).
An alternative that would fit better with the reader algorithm and
syntax would be to use a dispatch macro character, such as e.g.
#n(:named-readtable)... that would temporarily bind *readtable* to a
named readtable and use a recursive read. It puts the burden on the
user (the consuming programmer), where the use of #n is explicit about
the intention to use another readtable, no matter if inside a quoted
or literal form.
Best regards,
Paulo Madeira
2015-03-26 20:55 GMT+00:00 Erik Ronström <erik.ronstrom@doremir.com>:
> Wow, that's clever!
>
> Still, it assumes no other package is also installs a reader macro for #\( (?) On the other hand, that is probably not very likely.
>
> Erik
>
>
> 26 mar 2015 kl. 19:16 skrev Paulo Madeira:
>
>>
>> Another possibility is to install a reader macro for #\( that checks
>> if the first symbol is e.g. with-readtable or with-named-readtable and
>> process the form itself in such case.
>>
>> Best regards,
>>
>> Paulo Madeira
>>
>>
>> 2015-03-26 10:42 GMT+00:00 Erik Ronström <erik.ronstrom@doremir.com>:
>>>
>>> Yeah, named-readtables is probably a good idea.
>>>
>>> I wrote a reader macro a few years ago to make objective C integration easier in LW (always calling (objc:invoke ...) gets tedious). It works quite nicely:
>>>
>>> (let ((color [NSColor colorWithDeviceRed: 1.0 green: 0.5 blue: 0.5 alpha: 0.8]))
>>> [color redComponent])
>>> => 1.0
>>>
>>> The problem is that some packages (like e.g. parenscript) make use of [] as a symbol name, so using the reader macro breaks those packages. So I'll put my hope to named-readtables! :)
>>>
>>> Erik
>>>
>>>
>>>
>>> For those interested:
>>>
>>> The macro is based on some similar macros I found on the web, but IMHO this version is better, as it automatically differentiates between instance methods and class methods (based on the case of the first character of the object, so it is not foolproof, but in practice it does what you want).
>>>
>>> It does not support dot notation, that would be a nice addition!
>>>
>>> (set-macro-character #\[
>>> (lambda (str char)
>>> (declare (ignore char))
>>> (let ((*readtable* (copy-readtable *readtable* nil))
>>> (keep-going t)
>>> (eof (gensym)))
>>> (flet ((read-one-token (input &optional colon-as-delimiter)
>>> (with-output-to-string (output)
>>> (loop with has-seen-content = nil do
>>> (let ((char (read-char input nil 'eof)))
>>> (if (whitespace-char-p char)
>>> (when has-seen-content (return))
>>> (case char
>>> ('eof (setf keep-going nil) (return))
>>> (#\: (write-char char output) (if colon-as-delimiter (return) (setf has-seen-content t)))
>>> (#\] (unread-char #\] input) (setf keep-going nil) (return))
>>> (t (write-char char output)
>>> (setf has-seen-content t))))))))
>>> (peek ()
>>> (let ((ch (read-char str)))
>>> (unread-char ch str)
>>> ch)))
>>> (set-macro-character #\] (lambda (stream char)
>>> (declare (ignore char stream))
>>> (setf keep-going nil)
>>> eof))
>>> (let ((object (if (upper-case-p (peek)) (read-one-token str) (read str)))
>>> (method "")
>>> (args nil))
>>> (loop while keep-going
>>> for key = (read-one-token str t)
>>> for value = (read str nil eof t) do
>>> (cond
>>> ((and (zerop (length method)) (eq value eof))
>>> (setf method key))
>>> ((and (plusp (length key)) (not (eq value eof)))
>>> (setf method (concatenate 'string method key))
>>> (push value args))))
>>> (append (list 'objc:invoke object method) (nreverse args)))))))
>>>
>>>
>>>
>>>
>>> 26 mar 2015 kl. 11:23 skrev Costanza, Pascal:
>>>
>>>> Hi,
>>>>
>>>> It’s some time ago that I looked into this, but what I remember is that s-expression-local reader macros are problematic and probably not reliable enough.
>>>>
>>>> However, I have had very good experiences with named-readtables, a library you can find in quicklisp. This allows you to define file-local reader macros, which may be good enough for your purposes.
>>>>
>>>> Pascal
>>>>
>>>>> On 26 Mar 2015, at 09:46, Erik Ronström <erik.ronstrom@doremir..com> wrote:
>>>>>
>>>>>
>>>>> Hi,
>>>>>
>>>>> Is it possible to create local reader macros?
>>>>>
>>>>> For example
>>>>>
>>>>> (with-special-syntax
>>>>> (setf a[0] b))
>>>>>
>>>>> => (setf (aref a 0) b)
>>>>>
>>>>>
>>>>> I came across this file: https://github.com/scketches/lisp/blob/master/read-table-dispatch.lisp
>>>>>
>>>>> However I'm curios to whether it is possible to achieve the same effect *without* using another macro character!
>>>>>
>>>>> Regards
>>>>> Erik
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Lisp Hug - the mailing list for LispWorks users
>>>>> lisp-hug@lispworks.com
>>>>> http://www.lispworks.com/support/lisp-hug.html
>>>>>
>>>>
>>>> --
>>>> Pascal Costanza
>>>> The views expressed in this email are my own, and not those of my employer.
>>>>
>>>>
>>>>
>>>> Intel Corporation NV/SA
>>>> Kings Square, Veldkant 31
>>>> 2550 Kontich
>>>> RPM (Bruxelles) 0415.497.718.
>>>> Citibank, Brussels, account 570/1031255/09
>>>>
>>>> This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.
>>>
>>>
>>> _______________________________________________
>>> Lisp Hug - the mailing list for LispWorks users
>>> lisp-hug@lispworks.com
>>> http://www.lispworks.com/support/lisp-hug.html
>>>
>>
>> _______________________________________________
>> Lisp Hug - the mailing list for LispWorks users
>> lisp-hug@lispworks.com
>> http://www.lispworks.com/support/lisp-hug.html
>>
>
_______________________________________________
Lisp Hug - the mailing list for LispWorks users
lisp-hug@lispworks.com
http://www.lispworks.com/support/lisp-hug.html