From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1 ([2001:41d0:2:4a6f::]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by ms0.migadu.com with LMTPS id mH3NF3Yvc2CNMgAAgWs5BA (envelope-from ) for ; Sun, 11 Apr 2021 19:18:46 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1 with LMTPS id dYqXEXYvc2DeAwAAbx9fmQ (envelope-from ) for ; Sun, 11 Apr 2021 17:18:46 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id A3D84A67F for ; Sun, 11 Apr 2021 19:18:45 +0200 (CEST) Received: from localhost ([::1]:58452 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lVdjQ-0007E3-LD for larch@yhetil.org; Sun, 11 Apr 2021 13:18:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:40144) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lVdik-0007Dt-Lx for emacs-orgmode@gnu.org; Sun, 11 Apr 2021 13:18:03 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:49534) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lVdih-0005ES-2x for emacs-orgmode@gnu.org; Sun, 11 Apr 2021 13:18:01 -0400 Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 941CE1001FE; Sun, 11 Apr 2021 13:17:57 -0400 (EDT) Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 6A5DD100068; Sun, 11 Apr 2021 13:17:55 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1618161475; bh=mBTV42BttHBenOgAcDIB/RvtsL8Um/L8Ua8SWtUEPkE=; h=From:To:Subject:Date:From; b=O9eoO+dKPS6W4sXsqXLKaP+Fy30cBFQcg3UId9QeNGJw4Nc4E9b5n5ku+CgU3i7Rl PhZvZ0kIuil6vgMfI7mb9gV2AW/NKsxfbnxo1ZyACoa1Aavmk9oAvCEKewbSTruAjC j1/Odm3vzN8rvt0eSNpBIjEKYHaWCA0UgYTo1ohLzh2kPk1wUBpMH8sQs6T4gby+Zp WG3EOwBjpjz+C2GrJ6Ai5OgsHi0PA4xIFWdBXZtrFNuM7UcPJlh20Nvawawmolf9Nz 2IxpvNz2tUgBgGtGLdN1z81BsHh4Syafv5Gx0INHLC/Jx3eKN9wPJb4AwrRSq7eVrV L8HSMT7Lw75vQ== Received: from alfajor (104-222-126-84.cpe.teksavvy.com [104.222.126.84]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 3AE331206EB; Sun, 11 Apr 2021 13:17:55 -0400 (EDT) From: Stefan Monnier To: emacs-orgmode@gnu.org Subject: Improving org-macro.el Message-ID: Date: Sun, 11 Apr 2021 13:17:54 -0400 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL 0.003 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain Received-SPF: pass client-ip=132.204.25.50; envelope-from=monnier@iro.umontreal.ca; helo=mailscanner.iro.umontreal.ca X-Spam_score_int: -42 X-Spam_score: -4.3 X-Spam_bar: ---- X-Spam_report: (-4.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: "Emacs-orgmode" X-Migadu-Flow: FLOW_IN ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1618161525; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=lkg1vYhJrVv4bJnKRPomCIeEfmDnSqJIe29xmzZCMMw=; b=saxiMNF9nb9ZxEDSifzPV+8VT02TvQvUalHDLH5ZFutmt6DTYPiYndw5qwPI8lBQH5FUeC SqrRYxVmZT9vIg1zedhABYysYSKNuwCi2IMWTsFQkaAgchroIkL+QniEiNlVCQLEI2MLiy rknhjgOAAZxWYwpf8MqTTaa7FGqP4du0IBB+OqsW8exExNz0TGqiHTPhan3H+cz4fDBLz1 Ft7G6nJlsO+IIDwv63mSJcqqZtOFSoAaLwEQOfbeLzN1hVPsIS7bSf2UnQd4Zq7QzMCQii Jvag1iocF6jT3/dGlN00h2ohpcqp23YA1HU9/WftAOZZKzqZa8wsAIUrhDnLuQ== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1618161525; a=rsa-sha256; cv=none; b=JbiXQVnC6PJdOaUynU75QUOJk8qQIeUx+5WDaDe1PQQ8WmA6Hp1CamfH3X08dJf6+pok04 xvBlt+Ahqo6ZErUuWxCoO6VLkt6fLnIgjtVeaQLQd8cg/0K3+Qhu9sTfe72cCEE1e1bGQS 7BJAZ0la4S0iwHNaE7lPdiNFysm+YrTL3WsN9JO5F75gIR4RYN43+Ns/CLdriJEndvjJnq ubKjop6uTt5fEzTo2gC/oRAewwiGLxHCkAqKRUMonSD+RWTRz20k54rOWRTKo9tbUDcL4F 4hDqA9MdFbg2oMfzW2i0IFyNAiDZsedliGTkACSy3crZ8Xz/+4P3btC+GsRijQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=iro.umontreal.ca header.s=mail header.b=O9eoO+dK; dmarc=pass (policy=none) header.from=iro.umontreal.ca; spf=pass (aspmx1.migadu.com: domain of emacs-orgmode-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=emacs-orgmode-bounces@gnu.org X-Migadu-Spam-Score: -0.63 Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=iro.umontreal.ca header.s=mail header.b=O9eoO+dK; dmarc=pass (policy=none) header.from=iro.umontreal.ca; spf=pass (aspmx1.migadu.com: domain of emacs-orgmode-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=emacs-orgmode-bounces@gnu.org X-Migadu-Queue-Id: A3D84A67F X-Spam-Score: -0.63 X-Migadu-Scanner: scn0.migadu.com X-TUID: QJUiHgyeecPo In the course of trying to get the Org package to work with the (then) new GNU ELPA scripts, I bumped into the org-macro.el monster (mostly because it has changed incompatibly between Emacs-26 and Emacs-27, IIRC). In any case, the code struck me as quite inefficient since it reparses the macro definition every time the macro is called. I came up with the tentative patch below. It seems to work on Org's own manual, but other than that I haven't gone out of my way to test it. It clearly changes the semantics of Org macros to some extent: - It skips the call to `eval`, which caused a double evaluation. This only makes a difference for those macros defined with #+macro: (eval (expression-which-does-not-return-a-string)) so I think this is a safe change. - It also changes the behavior when $N appears elsewhere than an "expression context". E.g.: #+macro: (eval (let (($1 foo)) (bar))) or #+macro: (eval (mapconcat #'foo '($1 $2 $3) "")) or #+macro: (eval (fun-with "code $1")) I don't think it requires changes to the manual because the semantics described in the manual is sufficiently incomplete that both the old and the new semantics satisfy it. WDYT? Stefan diff --git a/lisp/org/org-macro.el b/lisp/org/org-macro.el index f914a33d61..1508a2f647 100644 --- a/lisp/org/org-macro.el +++ b/lisp/org/org-macro.el @@ -90,6 +90,17 @@ org-macro--set-template previous one, unless VALUE is nil. TEMPLATES is the list of templates. Return the updated list." (let ((old-definition (assoc name templates))) + (when (and value (string-match-p "\\`(eval\\>" value)) + ;; Pre-process the evaluation form for faster macro expansion. + (let* ((args (org-macro--makeargs value)) + (body (condition-case nil + ;; `value' is of the form "(eval ...)" but we don't want + ;; this to mean to pass the result to `eval' (which + ;; would cause double evaluation), so we strip the + ;; `eval' away with `cadr'. + (cadr (read value)) + (error (debug))))) + (setq value (eval (macroexpand-all `(lambda ,args ,body)) t)))) (cond ((and value old-definition) (setcdr old-definition value)) (old-definition) (t (push (cons name (or value "")) templates)))) @@ -138,21 +149,33 @@ org-macro-initialize-templates (list `("input-file" . ,(file-name-nondirectory visited-file)) `("modification-time" . - ,(format "(eval -\(format-time-string $1 - (or (and (org-string-nw-p $2) - (org-macro--vc-modified-time %s)) - '%s)))" - (prin1-to-string visited-file) - (prin1-to-string - (file-attribute-modification-time - (file-attributes visited-file)))))))) + ,(let ((modtime (file-attribute-modification-time + (file-attributes visited-file)))) + (lambda (arg1 arg2 &rest _) + (format-time-string + arg1 + (or (and (org-string-nw-p arg2) + (org-macro--vc-modified-time visited-file)) + modtime)))))))) ;; Install generic macros. (list - '("n" . "(eval (org-macro--counter-increment $1 $2))") - '("keyword" . "(eval (org-macro--find-keyword-value $1))") - '("time" . "(eval (format-time-string $1))") - '("property" . "(eval (org-macro--get-property $1 $2))"))))) + `("n" . org-macro--counter-increment) + `("keyword" . ,(lambda (name) + (org-macro--find-keyword-value name))) + `("time" . ,(lambda (format) (format-time-string format))) + `("property" . org-macro--get-property))))) + +(defun org-macro--makeargs (template) + "Compute the formal arglist to use for TEMPLATE." + (let ((max 0) (i 0)) + (while (string-match "\\$\\([0-9]+\\)" template i) + (setq i (match-end 0)) + (setq max (max max (string-to-number (match-string 1 template))))) + (let ((args '(&rest _))) + (while (> i 0) + (push (intern (format "$%d" i)) args) + (setq i (1- i))) + (cons '&optional args)))) (defun org-macro-expand (macro templates) "Return expanded MACRO, as a string. @@ -164,21 +187,17 @@ org-macro-expand ;; Macro names are case-insensitive. (cdr (assoc-string (org-element-property :key macro) templates t)))) (when template - (let* ((eval? (string-match-p "\\`(eval\\>" template)) - (value - (replace-regexp-in-string - "\\$[0-9]+" - (lambda (m) - (let ((arg (or (nth (1- (string-to-number (substring m 1))) - (org-element-property :args macro)) - ;; No argument: remove place-holder. - ""))) - ;; `eval' implies arguments are strings. - (if eval? (format "%S" arg) arg))) - template nil 'literal))) - (when eval? - (setq value (eval (condition-case nil (read value) - (error (debug)))))) + (let* ((value + (if (functionp template) + (apply template (org-element-property :args macro)) + (replace-regexp-in-string + "\\$[0-9]+" + (lambda (m) + (or (nth (1- (string-to-number (substring m 1))) + (org-element-property :args macro)) + ;; No argument: remove place-holder. + "")) + template nil 'literal)))) ;; Force return value to be a string. (format "%s" (or value ""))))))