How to define characters that will work as (and) with a character macro?

I am trying to define the characters a and b as follows

a + 1 1 b 2 

I am trying to do this using define-symbol-macro

 (define-symbol-macro a '( ) (define-symbol-macro b ') ) 

but this method does not work.

+4
source share
3 answers

What Lisp does with source code

Generic Lisp is an incredibly flexible language, in part because its source code can be easily represented using the same data structures that are used in that language. The most common form of macrodegradation converts these structures to other structures. These are the macros that you can define with define-symbol-macro , define-compiler-macro , defmacro and macrolet . However, before any of these macro definitions can be executed, the system must first read the source from the input stream (usually a file or an interactive prompt). This is the responsibility of the reader. The reader is also capable of performing some special actions when it encounters certain characters, such as ( and ' . What you are trying to do should probably happen at the reader level if you want, for example, (read-from-string "a + 1 1 b") return the list (+ 1 1) that you need if you want (eval (read-from-string "a + 1 1 b")) return 2 However, you can also define a special custom language (e.g. loop ), where a and b handled specially.

Use set-macro-character , not define-symbol-macro

This is not what you do using macro characters, but rather with macro characters. You can set macro characters using the aptly named set-macro-character . For example, in the following, I set the macro symbol for % as a function that reads a list using read-delimited-list , which should end in ^ . (Using the characters a and b here will be very difficult, because after that you won’t be able to write things like (set-macro-character ...) , it will be like writing (set-m(cro-ch(r(cter ...) , which is not very good.)

 CL-USER> (set-macro-character #\% (lambda (stream ignore) (declare (ignore ignore)) (read-delimited-list #\^ stream))) T CL-USER> % + 1 1 ^ 2 

Associated set-syntax-from-char

There is a related function that almost does what you want here, set-syntax-from-char . You can use it so that one character behaves like another. For example, you can make % behave like (

 CL-USER> (set-syntax-from-char #\% #\() T CL-USER> % + 1 1 ) 2 

However, since the macro character associated with ( does not look for a character that has the same syntax as ) , but the actual character ) , you cannot just replace ) with ^ in the same way:

 CL-USER> (set-syntax-from-char #\^ #\)) T CL-USER> % + 1 1 ^ ; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "unmatched close parenthesis" {1002C66031}>. 

set-syntax-from-char more useful when there is an existing character that in itself does what you want to emulate. For example, if you want to do it ! additional quote symbol:

 CL-USER> (set-syntax-from-char #\! #\') T CL-USER> (list !a !(1 2 3)) (A (1 2 3)) 

or make % a comment symbol, as in LaTeX:

 CL-USER> (set-syntax-from-char #\% #\;) T CL-USER> (list 1 2 % 3 4 5 6) (1 2 5 6) 

But think about why you do this at all ...

Now, although you can do all this, it seems like something completely unexpected for everyone who came across it. (Perhaps you are entering a confusing coding contest ?;)). For the reasons shown above, doing this with commonly used characters like a and b will also make it difficult to create any other source code. This is probably the best bet for defining a completely new reading table that does what you want, or even write a new parser. although (Common) Lisp allows you to redefine the language, there are still things that probably make sense to leave alone.

+21
source

A symbolic macro is a symbol that denotes a different shape . It looks like you want to see reader macros.

I would repeat Rainer's comment, but what are you trying to do?

+3
source

Okay, so I adore your comment for this reason, and now I know that it is for “Just because it is lisp”, then I'm completely on board!

Well, that’s why you are right about lisp, which is great for creating new languages ​​because we only need to “compile” the actual lisp code and it will run. Therefore, although we cannot use the regular compiler to convert the characters “a” and “b” into brackets, we can write this ourselves.

So let's get started!

 (defun symbol-name-equal (ab) (and (symbolp a) (symbolp b) (equal (symbol-name a) (symbol-name b)))) (defun find-matching-weird (start-pos open-symbol close-symbol code) (unless (symbol-name-equal open-symbol (nth start-pos code)) (error "start-pos does not point to a weird open-symbol")) (let ((nest-index 0)) (loop :for item :in (nthcdr start-pos code) :for i :from start-pos :do (cond ((symbol-name-equal item open-symbol) (incf nest-index 1)) ((symbol-name-equal item close-symbol) (incf nest-index -1))) (when (eql nest-index 0) (return i)) :finally (return nil)))) (defun weird-forms (open-symbol close-symbol body) (cond ((null body) nil) ((listp body) (let ((open-pos (position open-symbol body :test #'symbol-name-equal))) (if open-pos (let ((close-pos (find-matching-weird open-pos open-symbol close-symbol body))) (if close-pos (weird-forms open-symbol close-symbol `(,@(subseq body 0 open-pos) (,@(subseq body (1+ open-pos) close-pos)) ,@(subseq body (1+ close-pos)))) (error "unmatched weird brackets"))) (if (find close-symbol body :test #'symbol-name-equal) (error "unmatched weird brackets") (loop for item in body collect (weird-forms open-symbol close-symbol item)))))) (t body))) (defmacro with-weird-forms ((open-symbol close-symbol) &body body) `(progn ,@(weird-forms open-symbol close-symbol body))) 

So there are several parts to this.

First we have (symbol-name-equal), this is a helper function, because now we use the symbols and symbols that belong to the packages. symbol-name-equal gives us the opportunity to check whether the symbols have the same name, ignoring which package they are in.

Secondly, we have (find-matching-weird). This is a function that puts a list and index into an open weird bracket and returns the index to a closing weird bracket. This ensures that we get the right bracket even when nested.

Next we have (weird forms). This is a juicy bit, and what it does is to recursively go through the list passed as the "body" argument and do the following:

  • If the body is an empty list, just return it
  • if the body is a list, then
    • Find the positions of our public and private symbols.
    • if only one of them is found, then we have inconsistent brackets.
    • if we find both characters, then create a new list with a bit between the start and end positions inside the nested list.
    • then we invoke strange forms on this result if there are more strange characters inside it.
  • there are no strange characters, and then just iterate over the items in the list and call the strange shape on them to continue the search.

OK, so the function converts the list. For example, try:

 (weird-forms 'a 'b '(1 2 3 a 4 5 b 6 7)) 

But we want this to be the correct lisp code that runs, so we need to use a simple macro. (with-weird-forms) is a macro that calls the weird-forms function calls and puts the result in our source code for compilation using lisp. Therefore, if we have this:

 (with-weird-forms (ab) (+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23)) 

Then it goes into the macro:

 (PROGN (+ 1 2 3 (- (+ 1 2 3) 10 5) 11 23)) 

Which is fully valid lisp code, so it will work!

 CL-USER> (with-weird-forms (ab) (+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23)) 31 

Finally, if you set the brackets 'a' and 'b', you can write another small macro:

 (defmacro ab-lang (&rest code) `(with-weird-forms (ab) ,@code)) 

Now try the following:

 (ab-lang a let* aad 1 baea * d 5 bbba format t "this stupid test gives: ~a" ebb) 

Hi friend, it was great to write. Sorry for dismissing the problem earlier.

This kind of coding is very important, because in the end it is a tiny compiler for our weird language where characters can be punctuation. Compilers are awesome, and no language makes them as easy to write as lisp.

Peace!

+2
source

Source: https://habr.com/ru/post/981079/


All Articles