Package: guix-patches;
Reported by: David Wilson <david <at> daviwil.com>
Date: Thu, 12 Jan 2023 14:12:02 UTC
Severity: normal
Tags: patch
Message #47 received at 60753 <at> debbugs.gnu.org (full text, mbox):
From: Jelle Licht <jlicht <at> posteo.net> To: Andrew Tropin <andrew <at> trop.in>, Ludovic Courtès <ludo <at> gnu.org> Cc: "\(" <paren <at> disroot.org>, David Wilson <david <at> daviwil.com>, 60753 <at> debbugs.gnu.org Subject: Re: [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. Date: Wed, 01 Feb 2023 12:59:42 +0000
Andrew Tropin <andrew <at> trop.in> writes: > On 2023-01-23 11:18, Ludovic Courtès wrote: > >> Hi, >> >> Andrew Tropin <andrew <at> trop.in> skribis: >> >>> On 2023-01-17 10:02, Ludovic Courtès wrote: >>> >>>> Hi, >>>> >>>> Andrew Tropin <andrew <at> trop.in> 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 <local-file> 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: --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)))) (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)) (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
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.