On 2023-02-01 12:59, Jelle Licht wrote: > Andrew Tropin writes: > >> On 2023-01-23 11:18, Ludovic Courtès wrote: >> >>> Hi, >>> >>> Andrew Tropin skribis: >>> >>>> On 2023-01-17 10:02, Ludovic Courtès wrote: >>>> >>>>> Hi, >>>>> >>>>> Andrew Tropin skribis: >>>>> >>>>>>> What about accepting sexps (or gexps) instead of strings? As in: >>>>>>> >>>>>>> (init-file '((require 'whatever) (setq something t))) >>>>>> >>>>>> A quick minor note on this approach: it won't be possible to use >>>>>> #'elisp-function inside such configuration because it will be >>>>>> interpreted by guile reader, but actually rde lives without this >>>>>> functionality completely ok. >>>>> >>>>> Specifically: >>>>> >>>>> (write '#'x) >>>>> |= (syntax x) >>>>> >>>>> But we can use (guix read-print) and ensure that it prints #'. >>>>> >>>> >>>> Do you have any links to docs/sample implementations on the topic of >>>> extending guile reader, so we have an example to start with? >>> >>> It’s not the reader but rather the writer that we’d want to tweak. >> >> Right, it already can read #'x as (syntax x) and we can print it >> properly later, but AFAIK comments are ignored by the default reader. >> So I would expect to do something (very roughly) like this: >> >> --8<---------------cut here---------------start------------->8--- >> (parameterize (((@@ (guix gexp) read-procedure) read-with-comments)) >> #~(list 'hello ; Comment I would like to preserve during serialization >> 'guix)) >> --8<---------------cut here---------------end--------------->8--- >> >> Of course it doesn't work, but I hope demonstrates the idea. >> >>> >>> In (guix read-print), ‘pretty-print-with-comments’ already special >>> cases quasiquote etc. so that it prints ‘`’ (backtick) and not >>> ‘quasiquote'. We’d add clauses for ‘syntax’ and ‘quasisyntax’. >>> >> >> It seems ice-9 pretty-print also preserves backticks, but I see that >> pretty-print-with-comments also preserves gexps, which is cool. Adding >> syntax will make it even cooler. >> >>>> I think it will be cool to hook up a custom reader, ideally comment >>>> preserving, for emacs lisp inside scheme files. >>> >>> (guix read-print) is what you want. :-) >>> >> >> Can you give a hint on how to use it for preserving comments, please? >> >>>>>> Do we want something like this possible? >>>>>> >>>>>> (init-file `((require 'whatever) >>>>>> (setq something t) >>>>>> (load ,(local-file "old-init.el"))) >>>>> >>>>> It’d be nice. In that case, we’ll want it to be a gexp though: >>>>> >>>>> #~((require 'whatever) (load #$(local-file …))) >>>>> >>>> >>>> gexps are nice, but do we really need/want them here? Do you have any >>>> thoughts on what are the benifits over quasiquotes in this case? Maybe >>>> some examples? >>> >>> The benefit in the example above is that the gexp would actually work >>> whereas the sexp wouldn’t :-), unless there’s code somewhere to manually >>> traverse the sexp adn replace the record with its store >>> item (which is what gexps are about). >>> >>> I hope that makes sense! >> >> With this simple serializer we already achieved quite good results: >> https://git.sr.ht/~abcdw/rde/tree/388d3ad95e8607543df3dcdf26d058b610e77389/src/rde/serializers/lisp.scm#L35 >> >> For this input >> --8<---------------cut here---------------start------------->8--- >> `((load ,(local-file "./feature-lists.scm")) >> ,#~(format #f "hello") ; top level gexps are evaluated >> (list ,#~(format #f "hello")) ; nested gexps are not >> ,#~";; hacky comment" >> ;; comment, which is not preserved >> #'hi-fn ; incorrectly serialized, but fixable by alternative >> ; pretty-print >> ) >> --8<---------------cut here---------------end--------------->8--- >> >> it provides quite satisfying results: >> --8<---------------cut here---------------start------------->8--- >> (load "/gnu/store/xb6ma0mcgg1zzq645s63arvy3qskmbiz-feature-lists.scm") >> hello >> (list (format #f "hello")) >> ;; hacky comment >> (syntax hi-fn) >> --8<---------------cut here---------------end--------------->8--- >> >> It's a little incosistent (top level gexp are evaluated, but nested are >> not), comments are not preserved and #' serialized incorrectly, but >> other than that it works very good. >> >> WDYT about overall approach used here? or we can do it radically >> better? > > Not saying it's better in any particular way, but I have had this locally > for all my elisp-read-by-guile-written-back-to-elisp needs: I saw it in guix-home-manager and probably you've made the thread on rde-devel too. I tried some parts of this code, but didn't succeed to get a complete working out of it, now I have a little more knowledge and hope will get better results :) > > --8<---------------cut here---------------start------------->8--- > (define-module (jlicht build elisp-write) > #:use-module (ice-9 match) > #:use-module (srfi srfi-1) > #:export (elisp-write)) > > (define (elisp-write in-list? exp port) > "Stack-blowing implementation that writes guile's internal elisp > representation to something that can be parsed by Emacs." > ;; Definitions from (language elisp parser)'s quotation-symbols: > (define symbol-strings > '((#{`}# . "`") > (#{,}# . ",") > (#{,@}# . ",@"))) > (define (elisp-symbol? sym) > (assq sym symbol-strings)) > (define (write-elisp-symbol sym port) > (format port "~A" (assq-ref symbol-strings sym))) > > (match exp > (((? elisp-symbol? sym) rest) > (write-elisp-symbol sym port) > (elisp-write in-list? rest port)) > ;; Vector expression > (#(vs ...) > (format port "[") > (elisp-write #t vs port) > (format port "]")) > ;; Guile elisp implementation detail > ('(%set-lexical-binding-mode #f) 'skip) > ;; List walker > ((e ...) > (when (not in-list?) (format port "(")) > (unless (null? e) > (elisp-write #f (car e) port) > (for-each (lambda (v) > (format port " ") > (elisp-write #f v port)) (cdr e))) > (when (not in-list?) (format port ")"))) > ;; dotted pair > ((and (? pair?) (? dotted-list? l)) > (format port "(") > (elisp-write #t (drop-right l 0) port) > (format port " . ") > (elisp-write #t (take-right l 0) port) > (format port ")")) > ;; Print simple primitives > (_ (write exp port)))) > --8<---------------cut here---------------end--------------->8--- > > On the reader side I just use guile's elisp reader: > > --8<---------------cut here---------------start------------->8--- > (define-module (jlicht test elisp) > #:use-module (language elisp parser) > #:use-module (jlicht build elisp-write) > #:use-module (srfi srfi-26) > #:use-module (srfi srfi-64)) > > (eval-when (expand load eval) > (read-hash-extend #\e (lambda (chr port) (read-elisp port)))) That's what I was looking for. If you have any links related to the topic of the reader extension, please let me know. > > (set! test-log-to-file #f) > > (define (roundtrip expr) > (let ((written (call-with-output-string (cut elisp-write #f expr <>)))) > (call-with-input-string written read-elisp))) > > (define-syntax test-roundtrip-equals > (syntax-rules () > ((_ expr) > (let ((e1 (roundtrip expr))) > (test-equal e1 (roundtrip e1)))))) > > (define runner (test-runner-simple)) > > (test-with-runner runner > (test-begin "roundtrip-elisp-fixed-point") > (test-roundtrip-equals 12) > (test-roundtrip-equals "hello") > (test-roundtrip-equals '#e#'my-fn) > (test-roundtrip-equals '#e[a b c]) > (test-roundtrip-equals '#e`(+ 1 2 ,@(a b) ,c)) It would be cool to make elisp-unquote for #e, but I think I can take a look at ungexp to understand how to implement it. > (test-end "roundtrip-elisp-fixed-point")) > > (exit (test-runner-fail-count runner)) > --8<---------------cut here---------------end--------------->8--- > > I've also hooked it up in combination with a sequence of calls to > `scheme-file' -> `computed-file' called `elisp-file', but that's a bit > more hacky and less relevant to the current discussion. > > - Jelle Thank you very much! -- Best regards, Andrew Tropin