help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

rcd-template-eval - was Re: How to tame compiler?


From: Jean Louis
Subject: rcd-template-eval - was Re: How to tame compiler?
Date: Fri, 30 Apr 2021 22:38:30 +0300
User-agent: Mutt/2.0.6 (2021-03-06)

* Jorge P. de Morais Neto <jorge+list@disroot.org> [2021-04-30 17:29]:
> Hi all!
> 
> Em [2021-04-22 qui 10:46:59-0400], Stefan Monnier escreveu:
> 
> >> Is there a way to avoid these warnings?
> >
> > Yes: don't abuse `eval` ;-)
> 
> Jean Louis, could you provide a little more detail on what are you using
> ~eval~ for?  Some tasks accomplished by eval can be done more safely by
> other means.  For example, if you just want symbol indirection, you can
> use ~symbol-value~ (there is also ~symbol-function~).  If you want to
> apply a function object to a sequence of arguments, you can use ~apply~
> or ~funcall~.
> 
> I know little about Elisp; more experienced hackers may know about other
> mechanisms that avoid the need for ~eval~.

Gladly. I use it to expand some values in templates. That is very
handy and I use it already for many years. Normally it is for HTML and
WWW publishing. But I may use it for local reports on the fly. It is
very common beyond Emacs. It is similar to the nature of M4
pre-processing, which I have used back in past to create bunch of WWW
pages. It is similar to various server side templates with embedded
Perl or other programming languages, where there is more text and less
programming code.

(defvar rcd-template-delimiter-open "⟦")
(defvar rcd-template-delimiter-close "⟧")

(defun insert-rcd-lisp-brackets ()
  (interactive)
  (let ((open "⟦")
        (close "⟧"))
    (insert (format "%s () %s" open close))
    (backward-char 3)))

;; I have it in my key settings, when I press `C-c r i` I get this:
⟦ () ⟧ and then I can immediately write some expression like:
⟦ (+ 2 2) ⟧

if you now run the below function rcd-template-buffer-eval it will
show you 4 on that place. 

;; (define-key rcd-map "i" #'insert-rcd-lisp-brackets)

(defun rcd-template-eval (string &optional delimiters)
  "Evaluates Emacs Lisp enclosed by `rcd-template-delimiter-open' and 
`rcd-template-delimiter-close'.

Optional DELIMITERS list may be provided to change default
delimiters, first list member has to be the opening delimiter and
second the closing delimiter.

Space or new line has to follow `rcd-template-delimiter-open' and
precede `rcd-template-delimiter-close' for evaluation to get
invoked."
  (let* ((delimiters (or delimiters (list rcd-template-delimiter-open 
rcd-template-delimiter-close)))
         (open (car delimiters))
         (close (cadr delimiters)))
    (with-temp-buffer
      (insert string)
      (goto-char 0)
      (while (re-search-forward (rx (literal open)
                                    (one-or-more (or blank "\n"))
                                    (group (minimal-match (one-or-more 
anything)))
                                    (one-or-more (or blank "\n"))
                                    (literal close))
                                nil t)
        ;;(message "Found match")
        (let* ((lisp (car (read-from-string
                           (buffer-substring-no-properties
                            (1+ (match-beginning 0)) (1- (match-end 0))))))
               (value (condition-case nil
                          (eval lisp)
                        (error "")))
               (value (string-or-empty-string value)))
          ;; (message "HELLO: %s" (eval (format "%s" hello-name)))
          (delete-region (match-beginning 0) (match-end 0))
          (insert (format "%s" value))))
      (buffer-string))))

(defun rcd-template-buffer-eval ()
  (interactive)
  (let* ((buffer (buffer-string))
         (mode major-mode)
         (point (point)))
    (pop-to-buffer-same-window "Preview")
    (insert (rcd-template-eval buffer))
    (goto-char point)
    (funcall mode)
    ;;(espeak "Preview")
    ))

I have been using various templating systems before, and Perl was very
fast, then I switched to Common Lisp and used CL-EMB, and tried to
emulate it in Emacs Lisp, but syntax was kind of dirty like:

<% (something here) %>

or

<% @var some-variable %>

which I find easier like this:

⟦ some-variable ⟧

One can use any delimiters, I have chosen those ⟦ () ⟧
to input: type "C-x 8 RET 27e7" or "C-x 8 RET MATHEMATICAL RIGHT WHITE SQUARE 
BRACKET"

In general, this function will be silent with error "" and thus in
case that variable `some-variable' does not exist it will be empty.

Practical use cases:

Mass mailing lists are sent, and I wish to include recipient's
names in the template: ⟦ to-name ⟧ would expand into their full
name.

⟦ (gold-price-kg 1) ⟧ would expand to current gold price, there is
no need for me to change the body text of the email. Prices are
updated automatically, same for EUR/USD/GBP and other currencies.

⟦ (usd (gold-price-kg)) ⟧ would expand into US $57191

⟦ to-email ⟧ would expand into their email address

⟦ unsubscribe-url ⟧ would expand into the per recipient customized
URL to unsubscribe

Time estimates can be caluclated and sent by email automatically
that use current dates expanded in a template, not static dates.

Similar for HTML and WWW publishing, static pages may be generated on
the fly, expanded into Lisp values and transferred to web servers.

Normally in Common Lisp, and CL-EMB templating package I would need to
submit something like variables or list of variables, list alists to
get variables expanded. But I prefer that it works with any variables.

Instead of:
(rcd-template-eval "Template ⟦ variable ⟧" '(variable "VALUE")) 

right now I prefer it this way:

(let* ((variable "VALUE")
       (template "Template ⟦ variable ⟧")
       (expanded (rcd-template-eval template)))
  expanded) ⇒ "Template VALUE"

However, there is problem as my programs are with lexical-binding
⇒ t, so then I have to watch to do following:

(let* ((variable "VALUE")
       (template "Template ⟦ variable ⟧")
       (lexical-binding nil)
       (expanded (rcd-template-eval template)))
  expanded) ⇒ "Template VALUE"

as otherwise it would not necessarily expand, depending where the
function is defined.

In the end this approach allows me to write plain text, Markdown,
Org mode, just anything, and get the Emacs Lisp programs expanded
before as pre-processing before the Markdown, Org mode or
Asciidoc, Restructured Text or other type of processing. It of
course works automatic.

-- 
Jean

Take action in Free Software Foundation campaigns:
https://www.fsf.org/campaigns

Sign an open letter in support of Richard M. Stallman
https://stallmansupport.org/
https://rms-support-letter.github.io/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]