From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Madhu Newsgroups: gmane.emacs.help Subject: the (declare special) declaration with lexical scope. Date: Sun, 23 Apr 2023 10:37:36 +0530 Message-ID: Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="15447"; mail-complaints-to="usenet@ciao.gmane.io" To: help-gnu-emacs@gnu.org Cancel-Lock: sha1:nuDBkNSmzaOCEDGNwx5zzeU6vYI= Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Sun Apr 23 07:08:51 2023 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pqRxz-0003ve-JT for geh-help-gnu-emacs@m.gmane-mx.org; Sun, 23 Apr 2023 07:08:51 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pqRxM-00047D-Aj; Sun, 23 Apr 2023 01:08:12 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pqRx8-00045Z-Jf for help-gnu-emacs@gnu.org; Sun, 23 Apr 2023 01:08:00 -0400 Original-Received: from ciao.gmane.io ([116.202.254.214]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pqRx5-0008Da-N3 for help-gnu-emacs@gnu.org; Sun, 23 Apr 2023 01:07:57 -0400 Original-Received: from list by ciao.gmane.io with local (Exim 4.92) (envelope-from ) id 1pqRx1-0002oE-UM for help-gnu-emacs@gnu.org; Sun, 23 Apr 2023 07:07:51 +0200 X-Injected-Via-Gmane: http://gmane.org/ Received-SPF: pass client-ip=116.202.254.214; envelope-from=geh-help-gnu-emacs@m.gmane-mx.org; helo=ciao.gmane.io X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.25, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.help:143337 Archived-At: [Pardon me blog] So I wanted a quick templating system -- having heard the term "mustache" thrown around in this context, I decided to name this function after that. ``` (defun mustache-lookup-binding (string-key bindings) (cond ((null bindings) (eval (intern-soft string-key))) ((consp (car bindings)) (cdr (cl-assoc (if (stringp (car (car bindings))) string-key (intern-soft string-key)) bindings :test #'equal))) (t (cl-assert (symbolp (car bindings))) (plist-get bindings (intern-soft string-key))))) (cl-defun mustache (string &key readable bindings) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward "{{\\([^}{]+\\)}}" nil t) (let* ((string-key (match-string 1)) (val (mustache-lookup-binding string-key bindings)) (replacement (if readable (prin1-to-string val) ""))) (replace-match replacement nil nil nil 0) (unless readable (princ val (current-buffer))))) (buffer-substring (point-min) (point-max)))) ``` This would let me write stuff like ``` (mustache "foo = {{foo}} bar = {{barf}}" :readable nil :bindings '((foo . 10) (barf . (10 + 10)))) ;; "foo = 10 bar = (10 + 10)" ;; or (let ((foo 10) (barf '(10 + 10))) (mustache "foo = {{foo}} bar = {{barf}}")) ``` The important thing was to interpolate the values regular emacs variables, say bound during a function call, into the template string. This is all very good when using dynamic binding, but you can't reliably use dynamic binding with emacs lisp anymore. I'm sure stefan could come up with some rube goldberg constructs to avoid the call to eval in the mustache-lookup-binding function above. But in the absence of cl-symbol-value in elisp this is what I needed and I was resigned to resorting to -*- lexical-binding: nil -*- for using all this However it turns out elisp has a little-documented way to achieve (locally (declare (special)). It isn't documented in the doctsrings or in the code, only in an info node. The semantics are not discernable from reading the info node and look arbitrary instead being absed on reliable CL precedent... * (info "(elisp) Using Lexical Binding") ``` Using ‘defvar’ without a value, it is possible to bind a variable dynamically just in one file, or in just one part of a file while still binding it lexically elsewhere. For example: (let (_) (defvar x) ; Let-bindings of ‘x’ will be dynamic within this let. (let ((x -99)) ; This is a dynamic binding of ‘x’. (defun get-dynamic-x () x))) ``` With this I could write ``` (defmacro mustache-lexical (string) (let* ((keys-string (mustache-extract-keys string)) (keys-symbol (mapcar #'intern-soft keys-string))) `(let (_) ,@(cl-loop for key in keys-symbol collect `(defvar ,key)) (let (,@(cl-loop for key in keys-symbol collect (list key key))) (mustache ,string))))) ``` and this would I think works with lexical-binding:t I've lost the flexibililty of a function (with :binding and :readable keyword args to control the input and output) but I think I can call ``` (defun foobar (foo barf) (mustache-lexical "foo = {{foo}} bar = {{barf}}")) (foobar 10 20) ``` reliably in lexical elisp. Is the code above really reliable? How stable and well rounded is the ``` (let () (defvar x)) ''' feature?