[HN Gopher] Reader Macros in Common Lisp (2014)
___________________________________________________________________
Reader Macros in Common Lisp (2014)
Author : susam
Score : 86 points
Date : 2022-11-20 10:22 UTC (12 hours ago)
(HTM) web link (lisper.in)
(TXT) w3m dump (lisper.in)
| timonoko wrote:
| Apropos. Is there reader macro definitions for "(" and ")" ?
|
| Or is Godel rolling in his grave for such insolent heresy?
| sph wrote:
| It would be a noop. A reader macro that takes a s-expr bounded
| by parentheses, and returns a s-expr, literally is just the
| identity function^Hmacro.
| stassats wrote:
| It's not. Reader macros don't take s-expressions, they take
| characters.
| stassats wrote:
| Sure, what's so special about it?
|
| (get-macro-character #\\() => SB-IMPL::READ-LIST
|
| (get-macro-character #\\)) => SB-IMPL::READ-RIGHT-PAREN (that
| one just signals an error, the read-list one picks up the
| closing #\\))
| timonoko wrote:
| In other words : You cannot redefine ")" because it cannot be
| undefined while reading the new definition.
| aidenn0 wrote:
| No, it can totally be redefined. Reading happens before
| evaluation
| creepycrawler wrote:
| Sure you can. CL-USER> (set-macro-
| character #\) (get-macro-character #\()) T
| CL-USER> )format t "Hello") Hello NIL
| thelopa wrote:
| That's completely incorrect. The parent comment has it
| right. In normal usage, the meaning of #\\) is entirely
| determined by the implementation for the #\\( reader macro.
| This happens because the reader macro for #\\( keeps
| consuming characters until it has located AND consumed the
| matching #\\). The reader would only run the #\\) reader
| macro if it encountered a #\\) that didn't have a
| corresponding #\\(. That's what they meant when they said
| "it signals an error". Specifically, it signals that there
| are unbalanced parentheses.
|
| If you redefine the #\\) reader macro, you could use it in
| a top-level context. That's probably not a good idea since
| it's easy to accidentally have too many #\\) when closing a
| deeply nested expression and thus accidentally invoke the
| macro.
|
| Aside from signaling an error, the #\\) reader macro is
| also useful because it changes the rules when reading
| symbols. Basically, if you write (+ foo bar), the existence
| of the #\\) macro helps the reader know that you're
| referencing the "bar" symbol rather than the "bar)" symbol.
|
| Generally, when people define new balanced-pair syntax for
| Common Lisp, (such as a #\\{ macro for hash tables) they
| will follow the same pattern and define a corresponding
| reader macro for the closing side that always signals an
| error for all the same reasons.
|
| Edit: also, as others have pointed out, you seem to be
| mistakenly assuming that the redefinition takes affect mid-
| way through reading the expression. That's not how CL
| works. CL cleanly separates the process of executing code
| into a few distinct phases. First, the reader reads an
| entire expression ("form" in lisp terms). Then, that form
| is macroexpanded (traditional macros, not reader macros!)
| as needed before (optionally) being compiled and then
| executed.
|
| The change to the read table would happen during the
| execution phase -- well ordered after the original
| characters for that form are out of the picture.
|
| You COULD force a change to the readtable mid-way through
| reading a form using the #. reader macro, but that
| definitely gets into chainsaw-juggling territory.
| timonoko wrote:
| Ok. I understood my error in about 5.3 second but let it
| stay, because it was funny, and would trigger somebody to
| generate a wall-chart of explanations.
|
| Anyways already in 1982 I was diabolically opposed to
| this kind of shit. Read and Print should be as simple as
| possible and always one-to-one. When you print something
| to disk, that is what you get when reading, no additional
| adjustment needed.
|
| If you want reading macros, for example, you make your
| own Read. Better Read could even be in standard package
| "Additional-macros-for-common-lisp".
| TeMPOraL wrote:
| > _If you want reading macros, for example, you make your
| own Read. Better Read could even be in standard package
| "Additional-macros-for-common-lisp"._
|
| It makes little sense to reimplement (and maintain over
| time) the whole Read if you only care about changing some
| small aspect of it. Instead, you can consider standard
| Read mechanism from CL to be _extensible_ - reader macros
| are plugins /hooks/customization points/whatever you want
| to call it. You can maintain 1:1 Read/Print compatibility
| easily by defining/overriding a matching printer method,
| which is the Print side plugin/hook/customization
| point/whatever.
|
| The only thing that could be simpler than this would be
| some magic that lets you automatically derive a read
| macro and a printing method from a simple declaration,
| but for that you'd have to sacrifice Turing completeness.
|
| In my experience, reader macros are just freaking people
| out for unconscious and irrational reasons. I can tell
| because they still freak _me_ out a little, even though I
| 'm conceptually fine with them.
| creepycrawler wrote:
| > Aside from signaling an error, the #\\) reader macro is
| also useful because it changes the rules when reading
| symbols.
|
| This is wrong. See http://www.lispworks.com/documentation
| /lw50/CLHS/Body/02_ad....
| thelopa wrote:
| > A macro character is either terminating or non-
| terminating. The difference between terminating and non-
| terminating macro characters lies in what happens when
| such characters occur in the middle of a token. If a non-
| terminating macro character occurs in the middle of a
| token, the function associated with the non-terminating
| macro character is not called, and the non-terminating
| macro character does not terminate the token's name; it
| becomes part of the name as if the macro character were
| really a constituent character. A terminating macro
| character terminates any token, and its associated reader
| macro function is called no matter where the character
| appears. The only non-terminating macro character in
| standard syntax is sharpsign.
|
| http://www.lispworks.com/documentation/lw50/CLHS/Body/02_
| add...
|
| That is the system I was alluding to. It's been a few
| years since I've done anything with CL and so it seems my
| memory was slightly off.
| sph wrote:
| Great timing. It's my turn to write a Lisp, and I was just
| thinking about implementing reader macros this morning :-)
|
| My goal is to create the barest-metal Lisp OS (basically Lisp
| REPL with full ring-0 access, the OS) with an asm reader macro to
| write low level assembly opcodes you can jmp into.
| (defn add1 (x) (declare x int64) #asm(
| mov rax, %x add rax, 1 ))
|
| (Just thought of this syntax on the spot, I'm a few dozen hours
| away from that still.)
| moonchild wrote:
| Why a reader macro? Just use s-expressions, like sbcl and ccl.
| guenthert wrote:
| Indeed. That would ease making other tools, e.g. a super-
| optimizer, which generate or modify assembly code.
| User23 wrote:
| I'm guessing #ASM does the assembly at read or load time?
|
| Does it desugar to something like this?
| (assemble '((mov rax %x) (add rax 1)))
|
| Anyhow I think this is a great project. I hope you share it!
| I've often thought bootstrapping an assembler with lisp macros
| would be an interesting way to build a compiler from the very
| ground up. Assembler macros are nothing new, but I've never
| heard of an homoiconic implementation.
| rst wrote:
| The original LISP's "Lisp Assembly Program" was rather like
| this -- see Appendix C in the Lisp 1.5 manual, from 1962: htt
| ps://www.softwarepreservation.org/projects/LISP/book/LISP...
| timonoko wrote:
| 1970's calling. Been there. Done that:
| https://news.ycombinator.com/item?id=33517092#33517797
| sph wrote:
| Thanks for the inspiration. The fact that you've already done
| it won't stop me.
|
| Got more details/links to study?
| timonoko wrote:
| https://github.com/timonoko/nokolisp_addons/blob/master/ass
| e...
|
| Keyword is the line (dos-eval '(debug <
| TO.DEB > NUL))
|
| ("mapc" and "map" had their parameters in wrong order,
| which annoys me even today)
| macoovacany wrote:
| https://movitz.common-lisp.dev/
| jlarocco wrote:
| Out of curiosity, why not write a Scheme or Common Lisp
| compiler? Or just a subset?
| vitiral wrote:
| Okay let me get this straight...
|
| Are you saying that I don't need to use parenthesis as much in
| lisp? Instead I could define a few "reader macros" and then
| immediately have a different syntax?
|
| Say if I wanted to change (if (test-clause)
| (action1) (action2))
|
| To if (test-clause) do (action1) else
| (action2)
|
| Is this actually possible inline in lisp, meaning I just need to
| import a library that implements the if-reader-macro?
| dgan wrote:
| so I started learning common lisp lately. It's insane how one can
| combine very high level language, with down-to-metal compilation.
| I think that's the only language allowing it? Absolutely
| impressive. Feels like writing python but compiling to native
| krastanov wrote:
| Julia has similar capabilities, probably because it was heavily
| inspired by lisps. You can even modify Julia's compiler from
| within Julia. I often write code at a python level of
| abstraction and then use julia introspection to check the
| machine code that was generated.
| vitiral wrote:
| Forth is even more "down to the metal" than lisp, but yes I
| agree it is quite impressive.
___________________________________________________________________
(page generated 2022-11-20 23:01 UTC)