[cl-interpol-devel] Support for Perl $1 and Makefile-like special variables
Edi Weitz
edi at agharta.de
Mon Dec 21 16:38:42 UTC 2009
I'm not sure if I really like this, especially as I think this is a
very specific addition only to be Perl-compatible. I'd be more happy
with something like a very thin layer atop cl-interpol that enables
users to extend it (for example in the way you want) but doesn't
prescribe an actual syntax.
But I'm pretty sure I don't want to change CL-PPCRE for this goal.
Anyway, I won't have more time to think about this before the next
year begins. Too busy, sorry.
Thanks and Happy Holidays,
Edi.
On Thu, Dec 17, 2009 at 8:58 PM, Evgeniy Zhemchugov <jini.zh at gmail.com> wrote:
>> I'm not sure about things like $1 and so on. That'd require tight integration with CL-PPCRE, wouldn't it?
>
> I was thinking about something like this:
>
> (defvar *groups*)
>
> (defmacro =~ (scanner string &body body)
> `(let (($_ ,string))
> (multiple-value-bind ($- $+ @- @+) (cl-ppcre:scan ,scanner $_)
> (let ((*groups* (map 'vector
> (lambda (start end)
> (subseq $_ start end))
> @-
> @+)))
> , at body))))
>
> ; Just as in cl-interpol
> (defvar *readtable-copy* (copy-readtable))
>
> ; Readtable modification for $1, $2, etc
> (set-macro-character #\$
> (lambda (stream char)
> (if (digit-char-p (peek-char nil stream nil #\newline t))
> ; Doesn't work well with symbols like $1:a. Also, interns unnecessary
> ; symbols. The alternative is to parse the stream until a terminating
> ; character occur (with some check for package designators), but
> ; how to determine whether a given character is a terminating one?
> (let ((symbol (read stream nil nil t)))
> (if (subtypep (type-of symbol) 'integer)
> `($ ,symbol)
> (car
> (multiple-value-list
> (intern (concatenate 'string "$" (symbol-name symbol)))))))
> (let ((*readtable* *readtable-copy*))
> (read (make-concatenated-stream
> (make-string-input-stream "$")
> stream)
> t
> nil
> t)))))
>
> ; Accessor for *groups*
> (defun $ (index)
> (when (<= index (length *groups*))
> (aref *groups* (1- index))))
>
> I haven't thought well the expansion of the #\$ macro character yet. Anyway,
> it's an userspace code. As for cl-interpol, I want $1 to be expanded into ($ 1)
> in interpolated strings as well. I see several possible approaches here. The one
> with the most features seems to be an ad-hoc implementation of something similar
> to Lisp readtable. The idea is to provide a function via *inner-delimiters*
> variable that will do the reading and expansion for cl-interpol in the same way
> as functions set via set-macro-character do that for the Lisp reader. If we
> redefine the read-form as follows:
>
> (defun read-form ()
> (let* ((start-delimiter (peek-char*))
> (end-delimiter (get-end-delimiter start-delimiter *inner-delimiters*)))
> (if (consp end-delimiter)
> (progn
> (read-char*)
> (funcall (cadr end-delimiter)
> *stream*
> start-delimiter
> (car end-delimiter)))
> (cond ((null end-delimiter)
> (if *optional-delimiters-p*
> (read-optional-delimited)
> nil))
> (t
> `(progn
> ,@(progn
> (read-char*)
> (let ((*readtable* (copy-readtable*)))
> ;; temporarily change the readtable
> (set-syntax-from-char end-delimiter #\))
> (read-delimited-list end-delimiter *stream* t)))))))))
>
> then I can do what I want to do in this way:
>
> (defun digit-reader (stream start end)
> (loop for d = start then (read-char stream nil nil t)
> while (and d (digit-char-p d))
> collect d into r
> finally (progn
> (when d (unread-char d stream))
> (return `($ ,(parse-integer (coerce r 'string)))))))
>
> (setf *inner-delimiters*
> (list
> ; $(A B) should call the function A with argument B
> (list #\( #\) (lambda (stream start end)
> (read-delimited-list #\) stream)))
> ; ${a} should expand to the value of variable a (it's for cases like
> ; "ab${cd}e")
> (list #\{ #\} (lambda (stream start end)
> (let ((*readtable* (copy-readtable*)))
> (set-syntax-from-char #\} #\))
> (prog1
> (read stream)
> (when (char/= (read-char stream) #\})
> (error "Interpolation error"))))))
>
> (loop for d from 1 to 9
> do (push (list (digit-char d) nil #'digit-reader)
> *inner-delimiters*))
>
> This solution seems to be backward compatible.
>
> If you want to incorporate this or similar machinery in cl-ppcre then we can
> create even better support for Perl variables by declaring dynamic variables
> (or #\$ character macro expansions) $_, $`, $&, $', $+, $-, @+, @-, etc and
> binding them on every call to cl-ppcre:scan if some flag is set. I'm not sure
> that Perl approach to regular expressions will work well with Lisp conceptions
> though. What do you think of it?
>
> _______________________________________________
> cl-interpol-devel site list
> cl-interpol-devel at common-lisp.net
> http://common-lisp.net/mailman/listinfo/cl-interpol-devel
>
>
More information about the cl-interpol-devel
mailing list