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
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!