From: Adam Warner
Subject: Reimplementing the left-parenthesis
Date: 
Message-ID: <883732104.629169@nntp.win.co.nz>
Hi all,

I'm tacking a problem with new syntax and I have realised that if I can
see the Lisp code that implements the standard macro character "(" that
I should be able to modify it to solve my problem:
http://www.xanalys.com/software_tools/reference/HyperSpec/Body/02_da.htm

Another way to put this is how would you implement the macro character [
to copy the functionality of the standard ( macro character?

The point I am stuck at is that even though it's not difficult to
create a function to manipulate the reader input, all I am returning is a
string that is not evaluated. I want whatever manipulations I perform on
the input syntax using a macro character to be transparently evaluated in
the current context.

My aim is to turn this type of syntax using a { macro character:
{princ this is a C:\dos\path\ and demo of escaped "double quotation"
marks}

Into this standard Lisp syntax:
(princ "this is a C:\\dos\\path\\ and demo of escaped \"double
quotation\" marks")

It's easy to read and modify the stream to create the above output. But
if I return this string from the function that the macro character calls,
all I get is the above output unevaluated.

Note that "princ" can be any function that takes a string as its sole input.

Thanks for your help,
Adam

From: Thomas F. Burdick
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <xcv8z4xonze.fsf@apocalypse.OCF.Berkeley.EDU>
Adam Warner <······@consulting.net.nz> writes:

> I'm tacking a problem with new syntax and I have realised that if I can
> see the Lisp code that implements the standard macro character "(" that
> I should be able to modify it to solve my problem:
> http://www.xanalys.com/software_tools/reference/HyperSpec/Body/02_da.htm

You've already got the answer to your specific question, but for the
general form, "I wonder what the implementation of <X> looks like", it
is nice to have the source around.  You can get the source to CMUCL or
SBCL for free.  If you're on a Unix system, you can set up ILISP to
find the source of a function for you with a single keystroke.  Even
if you're not on Unix or aren't using CMUCL, you can still get it to
see how one could implement some piece of functionality.  Or, if you
buy Corman Lisp, you get the source to that.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afll10$fd30d$1@ID-105510.news.dfncis.de>
Hi Thomas F. Burdick,

> If you're on a Unix system, you can set up ILISP to find the source of a
> function for you with a single keystroke.

Thanks for the tip to just read the source code of the specific
implementation Thomas! ;-) Could you please expand on this comment above?
I'm finding it difficult to locate a command that will expand a function
to its source (I only have macroexpand so far).

(get-macro-character #\() tells me that the character is assigned to
SYSTEM-FUNCTION SYSTEM::LPAR-READER

So how do I view the source of LPAR-READER?

BTW looking at the source code to clisp-2.28 I obtain:

$ grep -r -i 'LPAR-READER' *
src/compiler.lisp:          (system::lpar-reader 2 0 nil nil nil)
src/constsym.d:LISPSYM(lpar_reader,"LPAR-READER",system)

Which is unhelpful. I found the correct place by grepping for LPAR and
discovering that it is referred to as LPAR_READER within in the source:

$ grep -r -i 'LPAR_READER' *  #note underscore this time
src/ChangeLog:	   lpar_reader, line_comment_reader, comment_reader, char_reader,
src/constsym.d:LISPSYM(lpar_reader,"LPAR-READER",system)
src/io.d:    table['('] = L(lpar_reader);
src/io.d:LISPFUNN(lpar_reader,2) { # reads (
                                     ^^^^^^^
src/subr.d:LISPFUNN(lpar_reader,2)

And viewing the context around the location in io.d we see:

# (set-macro-character #\(
#   #'(lambda (stream char)
#       (read-delimited-list #\) stream t :dot-allowed t)
# )   )
LISPFUNN(lpar_reader,2) { # reads (
  var object* stream_ = test_stream_arg(STACK_1);
  # read List after '(' until ')', Dot allowed:
  value1 = read_delimited_list(stream_,ascii_char(')'),dot_value);
  mv_count=1;
  skipSTACK(2);
}

Even though this implementation is not in Lisp (which explains why you
did not tell me to look at the source of clisp!) the authors have still
been kind enough to comment the Lisp way to implement "("!

But it would be nice to be able to find the source to a function/read
macro in an easier way like you indicate (at least with CMUCL).

Regards,
Adam
From: Thomas F. Burdick
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <xcvd6u97dj4.fsf@famine.OCF.Berkeley.EDU>
Adam Warner <······@consulting.net.nz> writes:

> Hi Thomas F. Burdick,
> 
> > If you're on a Unix system, you can set up ILISP to find the source of a
> > function for you with a single keystroke.
> 
> Thanks for the tip to just read the source code of the specific
> implementation Thomas! ;-) Could you please expand on this comment above?
> I'm finding it difficult to locate a command that will expand a function
> to its source (I only have macroexpand so far).

 [ ... snip grep'ing through sources ... ]

> But it would be nice to be able to find the source to a function/read
> macro in an easier way like you indicate (at least with CMUCL).

ILISP has a command edit-definitions-lisp, bound by default to "M-.".
CMUCL keeps track of which source file a function comes from, so ILISP
can bring you to a function or variable or macro definition.  In the
case of CMUCL's own source, the remembered location looks like:

  target:code/hash-new.lisp

for example, the source file that the GETHASH definition comes from.
This uses CMUCL's search-list extension, so you need to set the
"target:" search-list, unless you happen to have the source in the
same place as the person who built your CMUCL:

  * (search-list "target:")
  (#p"/net/dukas/l1/emarsden/CMUCL-18d-src/build/"
   #p"/net/dukas/l1/emarsden/CMUCL-18d-src/src/")
  * (setf (search-list "target:")
          (list #p"/opt/local/packages/cmucl-18d/src/"))
  (#p"/opt/local/packages/cmucl-18d/src/")

Now you can type "(gethash", then M-. and get brought to the
definition for GETHASH.  Very cool.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afltgb$fdqnr$1@ID-105510.news.dfncis.de>
Hi Thomas F. Burdick,

Note: I have tried to post this message a number of times without
success. The packet loss and lag on my connection is so bad that
even posting a newsgroup message is a tribulation. Sorry if you see any
duplicates. I'll now give up until things have fixed/improve.

> ILISP has a command edit-definitions-lisp, bound by default to "M-.".
> CMUCL keeps track of which source file a function comes from, so ILISP
> can bring you to a function or variable or macro definition.  In the
> case of CMUCL's own source, the remembered location looks like:
> 
>   target:code/hash-new.lisp
> 
> for example, the source file that the GETHASH definition comes from.
> This uses CMUCL's search-list extension, so you need to set the
> "target:" search-list, unless you happen to have the source in the
> same place as the person who built your CMUCL:
> 
>   * (search-list "target:")
>   (#p"/net/dukas/l1/emarsden/CMUCL-18d-src/build/"
>    #p"/net/dukas/l1/emarsden/CMUCL-18d-src/src/")
>   * (setf (search-list "target:")
>           (list #p"/opt/local/packages/cmucl-18d/src/"))
>   (#p"/opt/local/packages/cmucl-18d/src/")

Thanks Thomas. The CMUCL source is already nicely set up in Debian:
$ dpkg -l cmucl-source
...
||/ Name           Version        Description
+++-==============-==============-============================================
ii  cmucl-source   3.0.12         The CMUCL lisp sources

* (search-list "target:")
(#p"/usr/share/common-lisp/source/cmucl/")

> Now you can type "(gethash", then M-. and get brought to the
> definition for GETHASH.  Very cool.

Unfortunately edit-definitions-lisp does not work for me. I am
getting the error message "cannot open load file: ilisp-src".

I haven't figured out what's broken yet since ilisp-src.el does exist at
/usr/share/emacs/site-lisp/ilisp/ilisp-src.el

I'm still running ilisp 5.11.1 though. If 5.12.0 is not packaged soon I may have
a go at creating the package myself. The official version might still be
a little way off because the debian directory in the CVS tree is yet to
be completed for ilisp 5.12.0:
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/ilisp/ILISP/debian/

(for example note that the changelog doesn't yet mention ilisp 5.12.0)

Regards,
Adam
From: Thomas F. Burdick
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <xcvwusgelhm.fsf@conquest.OCF.Berkeley.EDU>
Adam Warner <······@consulting.net.nz> writes:

> Unfortunately edit-definitions-lisp does not work for me. I am
> getting the error message "cannot open load file: ilisp-src".
> 
> I haven't figured out what's broken yet since ilisp-src.el does exist at
> /usr/share/emacs/site-lisp/ilisp/ilisp-src.el

Huh, you should probably ask on the ILISP list, or report a Debian
bug, since it should work.  I don't use the packages for Emacs or Lisp
stuff (so I can keep a consistent environment across Debian and
Solaris), but I installed normally, and it Just Worked.  Emacs 20.7
and ILISP 5.11.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Kaz Kylheku
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afkl5i$o3v$3@luna.vcn.bc.ca>
In article <················@nntp.win.co.nz>, Adam Warner wrote:
> Hi all,
> 
> I'm tacking a problem with new syntax and I have realised that if I can
> see the Lisp code that implements the standard macro character "(" that
> I should be able to modify it to solve my problem:
> http://www.xanalys.com/software_tools/reference/HyperSpec/Body/02_da.htm
> 
> Another way to put this is how would you implement the macro character [
> to copy the functionality of the standard ( macro character?

Easy. You do a (read-delimited-list #\) ...) . The read-delimited-list
function is designed specifically for use in reader macros.

> The point I am stuck at is that even though it's not difficult to
> create a function to manipulate the reader input, all I am returning is a
> string that is not evaluated. I want whatever manipulations I perform on
> the input syntax using a macro character to be transparently evaluated in
> the current context.

But if you are workign on the reader, evaluation is simply not your
business. Your job is to return a Lisp object, or else to use (values)
to indicate that no object is returned (useful in implementing
constructs similar to #- and #+).

So for instance to obtain a list notation that uses curly braces,
it's quite simply:

(set-macro-character #\{ #'(lambda (stream ch) (declare (ignore ch))
                              (read-delimited-list #\} stream)))

> My aim is to turn this type of syntax using a { macro character:
> {princ this is a C:\dos\path\ and demo of escaped "double quotation"
> marks}

Okay, so we change it to:

(set-macro-character #\{ #'(lambda (stream ch) (declare (ignore ch))
                             (let ((name (read stream)))
                                   (string [snip])
                               `(,name ,string))))

The [snip] part represents code which will read characters from the
stream, collect them to a string until it sees a } character,
and skip leading and trailing whitespace, etc. You probably want
an escape mechanism so you can type a } character into the string.

Exercise for the reader. ;)
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afk3jr$f24o8$1@ID-105510.news.dfncis.de>
NB: Many thanks to Paul for the email response and a solution. I'll
discuss the solution when I fully understand it.

Regards,
Adam
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afk3ns$f1jsh$1@ID-105510.news.dfncis.de>
NB: Many thanks to Paul for the email response and a solution. I'll
discuss the solution when I fully understand it.

Regards,
Adam
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afmo9k$fgs7g$1@ID-105510.news.dfncis.de>
I wrote:

> My aim is to turn this type of syntax using a { macro character:
> {princ this is a C:\dos\path\ and demo of escaped "double quotation"
> marks}
> 
> Into this standard Lisp syntax:
> (princ "this is a C:\\dos\\path\\ and demo of escaped \"double
> quotation\" marks")

LOL. I did parsing that looked something like this:

    (cond ((eq char #\}) (return))
          ((eq char #\") (setf str (concatenate 'string str "\\\"")))
          ((eq char #\\) (setf str (concatenate 'string str "\\\\")))
          (t (setf str (concatenate 'string str (string char)))))

Until I realised that I was adding code to the string that didn't even
need to be there! You don't need to add escape characters to a string
representation. I even added double quotations to the string before I
realised it was all unnecessary.

This is all the code that is required to implement the above aim:


(set-macro-character #\{ #'(lambda (stream macrochar)
			     (declare (ignore macrochar))
			     (let ((function-name (read stream t nil t)) (str ""))
			       (loop for char = (read-char stream t nil t) until (char= char #\}) do
				     (setf str (concatenate 'string str (string char))))
			       `(,function-name ,str))))


To show it works:
[26]> '{princ this is a C:\dos\path\ and demo of escaped "double quotation" marks}
(PRINC "this is a C:\\dos\\path\\ and demo of escaped \"double quotation\" marks")

Thanks for the loop tip Paul. I suspect you used arrays and
vector-push-extend(s) for speed. I'll have to study the approach further.

And thanks for the backquote tip Kaz.

My first attempt was a failure because I returned everything as a string
from the function instead of returning a list. The backquote notation
nicely reconstructs the list.

There's one command I don't understand: why (declare (ignore macrochar))?
If I comment it out it seems to make no difference (note that I called the
variable macrochar just because it contains the macro character).

I'm impressed at the simplicity in being able to construct new syntax.
In what other languages can you achieve this?

Regards,
Adam
From: Vijay L
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <1eaf81aa.0206300941.62ce0eca@posting.google.com>
Adam Warner wrote:
> There's one command I don't understand: why (declare (ignore macrochar))?
> If I comment it out it seems to make no difference (note that I called the
> variable macrochar just because it contains the macro character).

To avoid compiler warnings.

Vijay L
If at first you do not succeed, destroy all evidence that you tried
If at first you DO succeed, try not to look surprised
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afo3b8$g7p65$1@ID-105510.news.dfncis.de>
Hi Vijay L,

> Adam Warner wrote:
>> There's one command I don't understand: why (declare (ignore
>> macrochar))? If I comment it out it seems to make no difference (note
>> that I called the variable macrochar just because it contains the macro
>> character).
> 
> To avoid compiler warnings.

Thanks. I was able to parsed that better than: "An ignore declaration
specifies that for-value references to the indicated bindings will not
occur within the scope of the declaration. Within the scope of such a
declaration, it is desirable for a compiler to issue a warning about the
presence of either a for-value reference to any var or fn, or a special
declaration for any var. "

Regards,
Adam
From: Adam Warner
Subject: Re: Reimplementing the left-parenthesis
Date: 
Message-ID: <afp397$g2hcb$1@ID-105510.news.dfncis.de>
Again I wrote:

> I wrote:
> 
>> My aim is to turn this type of syntax using a { macro character:
>> {princ this is a C:\dos\path\ and demo of escaped "double quotation"
>> marks}
>> 
>> Into this standard Lisp syntax:
>> (princ "this is a C:\\dos\\path\\ and demo of escaped \"double
>> quotation\" marks")

> (set-macro-character #\{ #'(lambda (stream macrochar)
> 			     (declare (ignore macrochar))
> 			     (let ((function-name (read stream t nil t)) (str ""))
> 			       (loop for char = (read-char stream t nil t) until (char= char #\}) do
> 				     (setf str (concatenate 'string str (string char))))
> 			       `(,function-name ,str))))

Here's a better implementation that is still easy to understand:

(set-macro-character #\{ #'(lambda (stream macrochar)
			     (declare (ignore macrochar))
			     (let ((function-name (read stream t nil t))
				   (strstream (make-string-output-stream)))
			       (loop for char = (read-char stream t nil t) until (char= char #\}) do
				     (write-char char strstream))
			       `(,function-name ,(get-output-stream-string strstream)))))

This eliminates the continual copying of the string as it is concatenated
at each character. Instead a string stream is written to that is then retrieved
when constructing the list.

Regards,
Adam