emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [RFC] Changing internal representation of back-ends to defstructs
@ 2013-07-03 20:57 Nicolas Goaziou
  2013-07-03 21:48 ` Carsten Dominik
  0 siblings, 1 reply; 15+ messages in thread
From: Nicolas Goaziou @ 2013-07-03 20:57 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 2706 bytes --]

Hello,

Currently, a back-end is a symbol which may point to an entry in
`org-export-registered-backends' variable. Therefore a back-end must be
registered (with a unique name) before one can call it. Or, to put it
differently, it is impossible to use an anonymous and temporary
back-end.

This is not satisfying for developers, as there are situations when you
need to use a one-shot back-end but don't want to clutter registered
back-ends list. You can of course let-bind
`org-export-registered-backends' to something else, but it leads to
clunky code. The process should be abstracted a bit more.

The following (long) patches address this by defining back-ends as
structures (see `defstruct'), possibly anonymous and by separating
creation from registration process. It allows to quickly create and use
temporary back-ends. In the example below, we quickly export a string
using a temporary back-end:

  (org-export-string-as
   "* H1\n** H2\nSome string"
   (org-export-create-backend
    :transcoders
    '((headline . (lambda (h contents i)
                    (let ((m (make-string (org-export-get-relative-level h i) ?=)))
                      (concat m " " (org-element-property :raw-value h) " " m "\n"
                              contents))))
      ;; Contents only.
      (section . (lambda (e c i) c))
      (paragraph . (lambda (e c i) c)))))

It is also possible to create a temporary derived back-end. The
following export will use registered `latex' back-end, excepted for
`bold' type objects.

  (org-export-string-as
     "Some *bold* /string/"
     (org-export-create-backend
      :parent 'latex
      :transcoders '((italic . (lambda (o c i) (format "\\texit{%s}" c)))))
     'body-only)

Besides `org-export-create-backend', tools provided are:
 - `org-export-get-backend'
 - `org-export-register-backend'
 - `org-export-get-all-transcoders' (handles inheritance)
 - `org-export-get-all-options' (handles inheritance)
 - `org-export-get-all-filters' (handles inheritance)

At a higher level, `org-export-define-backend' and
`org-export-define-derived-backend' do not change (they are equivalent
to create and register in a row). So this change only matters for
back-end developers who used advanced features like
`org-export-with-translations' (which should now be
`org-export-with-backend' coupled with an anonymous back-end).

Also, it leads to a cleaner implementation as it removes the confusion
between a back-end and its name.

The next step after applying this patch will be to make
orgtbl-to-BACKEND functions use anonymous functions in order to
support :splice property, which is tedious with the new export
framework.

Feedback welcome.


Regards,

-- 
Nicolas Goaziou

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Change-back-ends-internal-representation-to-struc.patch --]
[-- Type: text/x-diff, Size: 87445 bytes --]

From 35bf951a1cd4c455f01863e128a899d36e76a76c Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <n.goaziou@gmail.com>
Date: Mon, 24 Jun 2013 20:52:10 +0200
Subject: [PATCH 1/2] ox: Change back-ends internal representation to
 structures

* lisp/ox.el (org-export--registered-backends): Renamed from
  `org-export-registered-backends'.
(org-export-invisible-backends): Removed variable.
(org-export-get-backend, org-export-get-all-transcoders
org-export-get-all-options, org-export-get-all-filters): New
functions.  It replaces `org-export-backend-translate-table'.
(org-export-barf-if-invalid-backend, org-export-derived-backend-p,
org-export-define-backend, org-export-define-derived-backend):
Rewrite functions using new representation.
(org-export-backend-translate-table): Remove function.
(org-export-get-environment): Use new function.
(org-export--get-subtree-options, org-export--parse-option-keyword,
org-export--get-inbuffer-options, org-export--get-global-options,
org-export-to-buffer org-export-to-file, org-export-string-as
org-export-replace-region-by): Update docstring.
(org-export-data-with-translations): Remove function.  Use
`org-export-data-with-backend' with a temporary back-end instead.
(org-export-data-with-backend, org-export-as): Reflect new definition
for back-ends.
(org-export--dispatch-action, org-export--dispatch-ui): Reflect new
definition for back-ends and variable removal.  Refactoring.
(org-export-filter-apply-functions): Call functions with
current back-end's name, not full back-end.
* lisp/org.el (org-export-backends, org-create-formula--latex-header):
Use new structure and variables.
* testing/lisp/test-ox.el: Update tests.

This patch separates back-end definition from its registration.  Thus,
it allows to use anonymous or unregistered back-ends.
---
 lisp/org.el             |  58 ++--
 lisp/ox.el              | 527 +++++++++++++++++---------------
 testing/lisp/test-ox.el | 781 +++++++++++++++++++++++++++---------------------
 3 files changed, 764 insertions(+), 602 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 6233972..89cc328 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -436,8 +436,9 @@ For export specific modules, see also `org-export-backends'."
 	(const :tag "C  wl:                Links to Wanderlust folders/messages" org-wl)
 	(repeat :tag "External packages" :inline t (symbol :tag "Package"))))
 
-(defvar org-export-registered-backends)	; From ox.el
+(defvar org-export--registered-backends) ; From ox.el.
 (declare-function org-export-derived-backend-p "ox" (backend &rest backends))
+(declare-function org-export-backend-name "ox" (backend))
 (defcustom org-export-backends '(ascii html icalendar latex)
   "List of export back-ends that should be always available.
 
@@ -451,30 +452,29 @@ needed.
 
 This variable needs to be set before org.el is loaded.  If you
 need to make a change while Emacs is running, use the customize
-interface or run the following code, where VALUE stands for the
-new value of the variable, after updating it:
+interface or run the following code, where VAL stands for the new
+value of the variable, after updating it:
 
   \(progn
-    \(setq org-export-registered-backends
+    \(setq org-export--registered-backends
           \(org-remove-if-not
            \(lambda (backend)
-             \(or (memq backend val)
-                 \(catch 'parentp
-                   \(mapc
-                    \(lambda (b)
-                      \(and (org-export-derived-backend-p b (car backend))
-                           \(throw 'parentp t)))
-                    val)
-                   nil)))
-           org-export-registered-backends))
-    \(let ((new-list (mapcar 'car org-export-registered-backends)))
+             \(let ((name (org-export-backend-name backend)))
+               \(or (memq name val)
+                   \(catch 'parentp
+                     \(dolist (b val)
+                       \(and (org-export-derived-backend-p b name)
+                            \(throw 'parentp t)))))))
+           org-export--registered-backends))
+    \(let ((new-list (mapcar 'org-export-backend-name
+                            org-export--registered-backends)))
       \(dolist (backend val)
         \(cond
          \((not (load (format \"ox-%s\" backend) t t))
           \(message \"Problems while trying to load export back-end `%s'\"
                    backend))
          \((not (memq backend new-list)) (push backend new-list))))
-      \(set-default var new-list)))
+      \(set-default 'org-export-backends new-list)))
 
 Adding a back-end to this list will also pull the back-end it
 depends on, if any."
@@ -488,21 +488,20 @@ depends on, if any."
 	   ;; Any back-end not required anymore (not present in VAL and not
 	   ;; a parent of any back-end in the new value) is removed from the
 	   ;; list of registered back-ends.
-	   (setq org-export-registered-backends
+	   (setq org-export--registered-backends
 		 (org-remove-if-not
 		  (lambda (backend)
-		    (or (memq backend val)
-			(catch 'parentp
-			  (mapc
-			   (lambda (b)
-			     (and (org-export-derived-backend-p b (car backend))
-				  (throw 'parentp t)))
-			   val)
-			  nil)))
-		  org-export-registered-backends))
+		    (let ((name (org-export-backend-name backend)))
+		      (or (memq name val)
+			  (catch 'parentp
+			    (dolist (b val)
+			      (and (org-export-derived-backend-p b name)
+				   (throw 'parentp t)))))))
+		  org-export--registered-backends))
 	   ;; Now build NEW-LIST of both new back-ends and required
 	   ;; parents.
-	   (let ((new-list (mapcar 'car org-export-registered-backends)))
+	   (let ((new-list (mapcar 'org-export-backend-name
+				   org-export--registered-backends)))
 	     (dolist (backend val)
 	       (cond
 		((not (load (format "ox-%s" backend) t t))
@@ -18494,14 +18493,17 @@ share a good deal of logic."
          "Invalid value of `org-latex-create-formula-image-program'")))
    string tofile options buffer))
 
+(declare-function org-export-get-backend "ox" (name))
 (declare-function org-export--get-global-options "ox" (&optional backend))
 (declare-function org-export--get-inbuffer-options "ox" (&optional backend))
 (declare-function org-latex-guess-inputenc "ox-latex" (header))
 (declare-function org-latex-guess-babel-language "ox-latex" (header info))
 (defun org-create-formula--latex-header ()
   "Return LaTeX header appropriate for previewing a LaTeX snippet."
-  (let ((info (org-combine-plists (org-export--get-global-options 'latex)
-				  (org-export--get-inbuffer-options 'latex))))
+  (let ((info (org-combine-plists (org-export--get-global-options
+				   (org-export-get-backend 'latex))
+				  (org-export--get-inbuffer-options
+				   (org-export-get-backend 'latex)))))
     (org-latex-guess-babel-language
      (org-latex-guess-inputenc
       (org-splice-latex-header
diff --git a/lisp/ox.el b/lisp/ox.el
index 92ad356..71435b7 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -47,15 +47,10 @@
 ;; The core function is `org-export-as'.  It returns the transcoded
 ;; buffer as a string.
 ;;
-;; An export back-end is defined with `org-export-define-backend',
-;; which defines one mandatory information: his translation table.
-;; Its value is an alist whose keys are elements and objects types and
-;; values translator functions.  See function's docstring for more
-;; information about translators.
-;;
-;; Optionally, `org-export-define-backend' can also support specific
-;; buffer keywords, OPTION keyword's items and filters.  Also refer to
-;; function documentation for more information.
+;; An export back-end is defined with `org-export-define-backend'.
+;; This function can also support specific buffer keywords, OPTION
+;; keyword's items and filters.  Refer to function's documentation for
+;; more information.
 ;;
 ;; If the new back-end shares most properties with another one,
 ;; `org-export-define-derived-backend' can be used to simplify the
@@ -280,14 +275,8 @@ containing the back-end used, as a symbol, and either a process
 or the time at which it finished.  It is used to build the menu
 from `org-export-stack'.")
 
-(defvar org-export-registered-backends nil
+(defvar org-export--registered-backends nil
   "List of backends currently available in the exporter.
-
-A backend is stored as a list where CAR is its name, as a symbol,
-and CDR is a plist with the following properties:
-`:filters-alist', `:menu-entry', `:options-alist' and
-`:translate-alist'.
-
 This variable is set with `org-export-define-backend' and
 `org-export-define-derived-backend' functions.")
 
@@ -830,20 +819,6 @@ process faster and the export more portable."
   :package-version '(Org . "8.0")
   :type '(file :must-match t))
 
-(defcustom org-export-invisible-backends nil
-  "List of back-ends that shouldn't appear in the dispatcher.
-
-Any back-end belonging to this list or derived from a back-end
-belonging to it will not appear in the dispatcher menu.
-
-Indeed, Org may require some export back-ends without notice.  If
-these modules are never to be used interactively, adding them
-here will avoid cluttering the dispatcher menu."
-  :group 'org-export-general
-  :version "24.4"
-  :package-version '(Org . "8.0")
-  :type '(repeat (symbol :tag "Back-End")))
-
 (defcustom org-export-dispatch-use-expert-ui nil
   "Non-nil means using a non-intrusive `org-export-dispatch'.
 In that case, no help buffer is displayed.  Though, an indicator
@@ -863,25 +838,147 @@ mode."
 \f
 ;;; Defining Back-ends
 ;;
-;; `org-export-define-backend' is the standard way to define an export
-;; back-end.  It allows to specify translators, filters, buffer
-;; options and a menu entry.  If the new back-end shares translators
-;; with another back-end, `org-export-define-derived-backend' may be
-;; used instead.
+;; An export back-end is a structure with `org-export-backend' type
+;; and `name', `parent', `transcoders', `options', `filters', `blocks'
+;; and `menu' slots.
+;;
+;; At the lowest level, a back-end is created with
+;; `org-export-create-backend' function.
+;;
+;; A named back-end can be registered with
+;; `org-export-register-backend' function.  A registered back-end can
+;; later be referred to by its name, with `org-export-get-backend'
+;; function.  Also, such a back-end can become the parent of a derived
+;; back-end from which slot values will be inherited by default.
+;; `org-export-derived-backend-p' can check if a given back-end is
+;; derived from a list of back-end names.
+;;
+;; `org-export-get-all-transcoders', `org-export-get-all-options' and
+;; `org-export-get-all-filters' return the full alist of transcoders,
+;; options and filters, including those inherited from ancestors.
 ;;
-;; Internally, a back-end is stored as a list, of which CAR is the
-;; name of the back-end, as a symbol, and CDR a plist.  Accessors to
-;; properties of a given back-end are: `org-export-backend-filters',
-;; `org-export-backend-menu', `org-export-backend-options' and
-;; `org-export-backend-translate-table'.
+;; At a higher level, `org-export-define-backend' is the standard way
+;; to define an export back-end.  If the new back-end is similar to
+;; a registered back-end, `org-export-define-derived-backend' may be
+;; used instead.
 ;;
 ;; Eventually `org-export-barf-if-invalid-backend' returns an error
 ;; when a given back-end hasn't been registered yet.
 
-(defun org-export-define-backend (backend translators &rest body)
+(defstruct (org-export-backend (:constructor org-export-create-backend)
+			       (:copier nil))
+  name parent transcoders options filters blocks menu)
+
+(defun org-export-get-backend (name)
+  "Return export back-end named after NAME.
+NAME is a symbol.  Return nil if no such back-end is found."
+  (catch 'found
+    (dolist (b org-export--registered-backends)
+      (when (eq (org-export-backend-name b) name)
+	(throw 'found b)))))
+
+(defun org-export-register-backend (backend)
+  "Register BACKEND as a known export back-end.
+BACKEND is a structure with `org-export-backend' type."
+  ;; Refuse to register an unnamed back-end.
+  (unless (org-export-backend-name backend)
+    (error "Cannot register a unnamed export back-end"))
+  ;; Refuse to register a back-end with an unknown parent.
+  (let ((parent (org-export-backend-parent backend)))
+    (when (and parent (not (org-export-get-backend parent)))
+      (error "Cannot use unknown \"%s\" back-end as a parent" parent)))
+  ;; Register dedicated export blocks in the parser.
+  (dolist (name (org-export-backend-blocks backend))
+    (add-to-list 'org-element-block-name-alist
+		 (cons name 'org-element-export-block-parser)))
+  ;; If a back-end with the same name as BACKEND is already
+  ;; registered, replace it with BACKEND.  Otherwise, simply add
+  ;; BACKEND to the list of registered back-ends.
+  (let ((old (org-export-get-backend (org-export-backend-name backend))))
+    (if old (setcar (memq old org-export--registered-backends) backend)
+      (push backend org-export--registered-backends))))
+
+(defun org-export-barf-if-invalid-backend (backend)
+  "Signal an error if BACKEND isn't defined."
+  (unless (org-export-backend-p backend)
+    (error "Unknown \"%s\" back-end: Aborting export" backend)))
+
+(defun org-export-derived-backend-p (backend &rest backends)
+  "Non-nil if BACKEND is derived from one of BACKENDS.
+BACKEND is an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.  BACKENDS is constituted of symbols."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (when backend
+    (catch 'exit
+      (while (org-export-backend-parent backend)
+	(when (memq (org-export-backend-name backend) backends)
+	  (throw 'exit t))
+	(setq backend
+	      (org-export-get-backend (org-export-backend-parent backend))))
+      (memq (org-export-backend-name backend) backends))))
+
+(defun org-export-get-all-transcoders (backend)
+  "Return full translation table for BACKEND.
+
+BACKEND is an export back-end, as return by, e.g,,
+`org-export-create-backend'.  Return value is an alist where
+keys are element or object types, as symbols, and values are
+transcoders.
+
+Unlike to `org-export-backend-transcoders', this function
+also returns transcoders inherited from parent back-ends,
+if any."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (when backend
+    (let ((transcoders (org-export-backend-transcoders backend))
+	  parent)
+      (while (setq parent (org-export-backend-parent backend))
+	(setq backend (org-export-get-backend parent))
+	(setq transcoders
+	      (append transcoders (org-export-backend-transcoders backend))))
+      transcoders)))
+
+(defun org-export-get-all-options (backend)
+  "Return export options for BACKEND.
+
+BACKEND is an export back-end, as return by, e.g,,
+`org-export-create-backend'.  See `org-export-options-alist'
+for the shape of the return value.
+
+Unlike to `org-export-backend-options', this function also
+returns options inherited from parent back-ends, if any."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (when backend
+    (let ((options (org-export-backend-options backend))
+	  parent)
+      (while (setq parent (org-export-backend-parent backend))
+	(setq backend (org-export-get-backend parent))
+	(setq options (append options (org-export-backend-options backend))))
+      options)))
+
+(defun org-export-get-all-filters (backend)
+  "Return complete list of filters for BACKEND.
+
+BACKEND is an export back-end, as return by, e.g,,
+`org-export-create-backend'.  Return value is an alist where
+keys are symbols and values lists of functions.
+
+Unlike to `org-export-backend-filters', this function also
+returns filters inherited from parent back-ends, if any."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (when backend
+    (let ((filters (org-export-backend-filters backend))
+	  parent)
+      (while (setq parent (org-export-backend-parent backend))
+	(setq backend (org-export-get-backend parent))
+	(setq filters (append filters (org-export-backend-filters backend))))
+      filters)))
+
+(defun org-export-define-backend (backend transcoders &rest body)
   "Define a new back-end BACKEND.
 
-TRANSLATORS is an alist between object or element types and
+TRANSCODERS is an alist between object or element types and
 functions handling them.
 
 These functions should return a string without any trailing
@@ -997,32 +1094,23 @@ keywords are understood:
     `org-export-options-alist' for more information about
     structure of the values."
   (declare (indent 1))
-  (let (export-block filters menu-entry options contents)
+  (let (blocks filters menu-entry options contents)
     (while (keywordp (car body))
       (case (pop body)
         (:export-block (let ((names (pop body)))
-			 (setq export-block
-			       (if (consp names) (mapcar 'upcase names)
-				 (list (upcase names))))))
+			 (setq blocks (if (consp names) (mapcar 'upcase names)
+					(list (upcase names))))))
 	(:filters-alist (setq filters (pop body)))
 	(:menu-entry (setq menu-entry (pop body)))
         (:options-alist (setq options (pop body)))
         (t (pop body))))
-    (setq contents (append (list :translate-alist translators)
-			   (and filters (list :filters-alist filters))
-			   (and options (list :options-alist options))
-			   (and menu-entry (list :menu-entry menu-entry))))
-    ;; Register back-end.
-    (let ((registeredp (assq backend org-export-registered-backends)))
-      (if registeredp (setcdr registeredp contents)
-	(push (cons backend contents) org-export-registered-backends)))
-    ;; Tell parser to not parse EXPORT-BLOCK blocks.
-    (when export-block
-      (mapc
-       (lambda (name)
-	 (add-to-list 'org-element-block-name-alist
-		      `(,name . org-element-export-block-parser)))
-       export-block))))
+    (org-export-register-backend
+     (org-export-create-backend :name backend
+				:transcoders transcoders
+				:options options
+				:filters filters
+				:blocks blocks
+				:menu menu-entry))))
 
 (defun org-export-define-derived-backend (child parent &rest body)
   "Create a new back-end as a variant of an existing one.
@@ -1077,75 +1165,25 @@ The back-end could then be called with, for example:
 
   \(org-export-to-buffer 'my-latex \"*Test my-latex*\")"
   (declare (indent 2))
-  (let (export-block filters menu-entry options translators contents)
+  (let (blocks filters menu-entry options transcoders contents)
     (while (keywordp (car body))
       (case (pop body)
 	(:export-block (let ((names (pop body)))
-			 (setq export-block
-			       (if (consp names) (mapcar 'upcase names)
-				 (list (upcase names))))))
+			 (setq blocks (if (consp names) (mapcar 'upcase names)
+					(list (upcase names))))))
         (:filters-alist (setq filters (pop body)))
 	(:menu-entry (setq menu-entry (pop body)))
         (:options-alist (setq options (pop body)))
-        (:translate-alist (setq translators (pop body)))
+        (:translate-alist (setq transcoders (pop body)))
         (t (pop body))))
-    (setq contents (append
-		    (list :parent parent)
-		    (let ((p-table (org-export-backend-translate-table parent)))
-		      (list :translate-alist (append translators p-table)))
-		    (let ((p-filters (org-export-backend-filters parent)))
-		      (list :filters-alist (append filters p-filters)))
-		    (let ((p-options (org-export-backend-options parent)))
-		      (list :options-alist (append options p-options)))
-		    (and menu-entry (list :menu-entry menu-entry))))
-    (org-export-barf-if-invalid-backend parent)
-    ;; Register back-end.
-    (let ((registeredp (assq child org-export-registered-backends)))
-      (if registeredp (setcdr registeredp contents)
-	(push (cons child contents) org-export-registered-backends)))
-    ;; Tell parser to not parse EXPORT-BLOCK blocks.
-    (when export-block
-      (mapc
-       (lambda (name)
-	 (add-to-list 'org-element-block-name-alist
-		      `(,name . org-element-export-block-parser)))
-       export-block))))
-
-(defun org-export-backend-parent (backend)
-  "Return back-end from which BACKEND is derived, or nil."
-  (plist-get (cdr (assq backend org-export-registered-backends)) :parent))
-
-(defun org-export-backend-filters (backend)
-  "Return filters for BACKEND."
-  (plist-get (cdr (assq backend org-export-registered-backends))
-	     :filters-alist))
-
-(defun org-export-backend-menu (backend)
-  "Return menu entry for BACKEND."
-  (plist-get (cdr (assq backend org-export-registered-backends))
-	     :menu-entry))
-
-(defun org-export-backend-options (backend)
-  "Return export options for BACKEND."
-  (plist-get (cdr (assq backend org-export-registered-backends))
-	     :options-alist))
-
-(defun org-export-backend-translate-table (backend)
-  "Return translate table for BACKEND."
-  (plist-get (cdr (assq backend org-export-registered-backends))
-	     :translate-alist))
-
-(defun org-export-barf-if-invalid-backend (backend)
-  "Signal an error if BACKEND isn't defined."
-  (unless (org-export-backend-translate-table backend)
-    (error "Unknown \"%s\" back-end: Aborting export" backend)))
-
-(defun org-export-derived-backend-p (backend &rest backends)
-  "Non-nil if BACKEND is derived from one of BACKENDS."
-  (let ((parent backend))
-    (while (and (not (memq parent backends))
-		(setq parent (org-export-backend-parent parent))))
-    parent))
+    (org-export-register-backend
+     (org-export-create-backend :name child
+				:parent parent
+				:transcoders transcoders
+				:options options
+				:filters filters
+				:blocks blocks
+				:menu menu-entry))))
 
 
 \f
@@ -1448,14 +1486,15 @@ The back-end could then be called with, for example:
 ;;  `org-export--get-subtree-options' and
 ;;  `org-export--get-inbuffer-options'
 ;;
-;; Also, `org-export--install-letbind-maybe' takes care of the part
-;; relative to "#+BIND:" keywords.
+;; Also, `org-export--list-bound-variables' collects bound variables
+;; along with their value in order to set them as buffer local
+;; variables later in the process.
 
 (defun org-export-get-environment (&optional backend subtreep ext-plist)
   "Collect export options from the current buffer.
 
-Optional argument BACKEND is a symbol specifying which back-end
-specific options to read, if any.
+Optional argument BACKEND is an export back-end, as returned by
+`org-export-create-backend'.
 
 When optional argument SUBTREEP is non-nil, assume the export is
 done against the current sub-tree.
@@ -1481,8 +1520,7 @@ inferior to file-local settings."
    (list
     :back-end
     backend
-    :translate-alist
-    (org-export-backend-translate-table backend)
+    :translate-alist (org-export-get-all-transcoders backend)
     :footnote-definition-alist
     ;; Footnotes definitions must be collected in the original
     ;; buffer, as there's no insurance that they will still be in
@@ -1518,11 +1556,12 @@ inferior to file-local settings."
 
 (defun org-export--parse-option-keyword (options &optional backend)
   "Parse an OPTIONS line and return values as a plist.
-Optional argument BACKEND is a symbol specifying which back-end
+Optional argument BACKEND is an export back-end, as returned by,
+e.g., `org-export-create-backend'.  It specifies which back-end
 specific items to read, if any."
   (let* ((all
 	  ;; Priority is given to back-end specific options.
-	  (append (and backend (org-export-backend-options backend))
+	  (append (and backend (org-export-get-all-options backend))
 		  org-export-options-alist))
 	 plist)
     (dolist (option all)
@@ -1542,7 +1581,8 @@ specific items to read, if any."
 
 (defun org-export--get-subtree-options (&optional backend)
   "Get export options in subtree at point.
-Optional argument BACKEND is a symbol specifying back-end used
+Optional argument BACKEND is an export back-end, as returned by,
+e.g., `org-export-create-backend'.  It specifies back-end used
 for export.  Return options as a plist."
   ;; For each buffer keyword, create a headline property setting the
   ;; same property in communication channel. The name for the property
@@ -1594,7 +1634,7 @@ for export.  Return options as a plist."
 			  (t value)))))))))
 	;; Look for both general keywords and back-end specific
 	;; options, with priority given to the latter.
-	(append (and backend (org-export-backend-options backend))
+	(append (and backend (org-export-get-all-options backend))
 		org-export-options-alist)))
      ;; Return value.
      plist)))
@@ -1602,7 +1642,8 @@ for export.  Return options as a plist."
 (defun org-export--get-inbuffer-options (&optional backend)
   "Return current buffer export options, as a plist.
 
-Optional argument BACKEND, when non-nil, is a symbol specifying
+Optional argument BACKEND, when non-nil, is an export back-end,
+as returned by, e.g., `org-export-create-backend'.  It specifies
 which back-end specific options should also be read in the
 process.
 
@@ -1612,7 +1653,7 @@ Assume buffer is in Org mode.  Narrowing, if any, is ignored."
 	 (case-fold-search t)
 	 (options (append
 		   ;; Priority is given to back-end specific options.
-		   (and backend (org-export-backend-options backend))
+		   (and backend (org-export-get-all-options backend))
 		   org-export-options-alist))
 	 (regexp (format "^[ \t]*#\\+%s:"
 			 (regexp-opt (nconc (delq nil (mapcar 'cadr options))
@@ -1725,12 +1766,13 @@ name."
 
 (defun org-export--get-global-options (&optional backend)
   "Return global export options as a plist.
-Optional argument BACKEND, if non-nil, is a symbol specifying
+Optional argument BACKEND, if non-nil, is an export back-end, as
+returned by, e.g., `org-export-create-backend'.  It specifies
 which back-end specific export options should also be read in the
 process."
   (let (plist
 	;; Priority is given to back-end specific options.
-	(all (append (and backend (org-export-backend-options backend))
+	(all (append (and backend (org-export-get-all-options backend))
 		     org-export-options-alist)))
     (dolist (cell all plist)
       (let ((prop (car cell)))
@@ -2058,11 +2100,10 @@ a tree with a select tag."
 ;; back-end output.  It takes care of filtering out elements or
 ;; objects according to export options and organizing the output blank
 ;; lines and white space are preserved.  The function memoizes its
-;; results, so it is cheap to call it within translators.
+;; results, so it is cheap to call it within transcoders.
 ;;
 ;; It is possible to modify locally the back-end used by
 ;; `org-export-data' or even use a temporary back-end by using
-;; `org-export-data-with-translations' and
 ;; `org-export-data-with-backend'.
 ;;
 ;; Internally, three functions handle the filtering of objects and
@@ -2190,24 +2231,6 @@ Return transcoded string."
 	     results)))
 	 (plist-get info :exported-data))))))
 
-(defun org-export-data-with-translations (data translations info)
-  "Convert DATA into another format using a given translation table.
-DATA is an element, an object, a secondary string or a string.
-TRANSLATIONS is an alist between element or object types and
-a functions handling them.  See `org-export-define-backend' for
-more information.  INFO is a plist used as a communication
-channel."
-  (org-export-data
-   data
-   ;; Set-up a new communication channel with TRANSLATIONS as the
-   ;; translate table and a new hash table for memoization.
-   (org-combine-plists
-    info
-    (list :translate-alist translations
-	  ;; Size of the hash table is reduced since this function
-	  ;; will probably be used on short trees.
-	  :exported-data (make-hash-table :test 'eq :size 401)))))
-
 (defun org-export-data-with-backend (data backend info)
   "Convert DATA into BACKEND format.
 
@@ -2217,9 +2240,18 @@ channel.
 
 Unlike to `org-export-with-backend', this function will
 recursively convert DATA using BACKEND translation table."
-  (org-export-barf-if-invalid-backend backend)
-  (org-export-data-with-translations
-   data (org-export-backend-translate-table backend) info))
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (org-export-data
+   data
+   ;; Set-up a new communication channel with translations defined in
+   ;; BACKEND as the translate table and a new hash table for
+   ;; memoization.
+   (org-combine-plists
+    info
+    (list :translate-alist (org-export-get-all-transcoders backend)
+	  ;; Size of the hash table is reduced since this function
+	  ;; will probably be used on short trees.
+	  :exported-data (make-hash-table :test 'eq :size 401)))))
 
 (defun org-export--interpret-p (blob info)
   "Non-nil if element or object BLOB should be interpreted during export.
@@ -2713,18 +2745,19 @@ channel, as a plist.  It must return a string or nil.")
   "Call every function in FILTERS.
 
 Functions are called with arguments VALUE, current export
-back-end and INFO.  A function returning a nil value will be
-skipped.  If it returns the empty string, the process ends and
+back-end's name and INFO.  A function returning a nil value will
+be skipped.  If it returns the empty string, the process ends and
 VALUE is ignored.
 
 Call is done in a LIFO fashion, to be sure that developer
 specified filters, if any, are called first."
   (catch 'exit
-    (dolist (filter filters value)
-      (let ((result (funcall filter value (plist-get info :back-end) info)))
-	(cond ((not result) value)
-	      ((equal value "") (throw 'exit nil))
-	      (t (setq value result)))))))
+    (let ((backend-name (plist-get info :back-end)))
+      (dolist (filter filters value)
+	(let ((result (funcall filter value backend-name info)))
+	  (cond ((not result) value)
+		((equal value "") (throw 'exit nil))
+		(t (setq value result))))))))
 
 (defun org-export-install-filters (info)
   "Install filters properties in communication channel.
@@ -2755,7 +2788,7 @@ Return the updated communication channel."
 		       plist key
 		       (if (atom value) (cons value (plist-get plist key))
 			 (append value (plist-get plist key))))))))
-	  (org-export-backend-filters (plist-get info :back-end)))
+	  (org-export-get-all-filters (plist-get info :back-end)))
     ;; Return new communication channel.
     (org-combine-plists info plist)))
 
@@ -2891,6 +2924,10 @@ The function assumes BUFFER's major mode is `org-mode'."
   (backend &optional subtreep visible-only body-only ext-plist)
   "Transcode current Org buffer into BACKEND code.
 
+BACKEND is either an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.
+
 If narrowing is active in the current buffer, only transcode its
 narrowed part.
 
@@ -2911,6 +2948,7 @@ with external parameters overriding Org default settings, but
 still inferior to file-local settings.
 
 Return code as a string."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
   (org-export-barf-if-invalid-backend backend)
   (save-excursion
     (save-restriction
@@ -2943,8 +2981,9 @@ Return code as a string."
 	;; created, where include keywords, macros are expanded and
 	;; code blocks are evaluated.
 	(org-export-with-buffer-copy
-	 ;; Run first hook with current back-end as argument.
-	 (run-hook-with-args 'org-export-before-processing-hook backend)
+	 ;; Run first hook with current back-end's name as argument.
+	 (run-hook-with-args 'org-export-before-processing-hook
+			     (org-export-backend-name backend))
 	 (org-export-expand-include-keyword)
 	 ;; Update macro templates since #+INCLUDE keywords might have
 	 ;; added some new ones.
@@ -2954,10 +2993,11 @@ Return code as a string."
 	 ;; Update radio targets since keyword inclusion might have
 	 ;; added some more.
 	 (org-update-radio-target-regexp)
-	 ;; Run last hook with current back-end as argument.
+	 ;; Run last hook with current back-end's name as argument.
 	 (goto-char (point-min))
 	 (save-excursion
-	   (run-hook-with-args 'org-export-before-parsing-hook backend))
+	   (run-hook-with-args 'org-export-before-parsing-hook
+			       (org-export-backend-name backend)))
 	 ;; Update communication channel with environment.  Also
 	 ;; install user's and developer's filters.
 	 (setq info
@@ -2980,9 +3020,10 @@ Return code as a string."
 	 ;; Call options filters and update export options.  We do not
 	 ;; use `org-export-filter-apply-functions' here since the
 	 ;; arity of such filters is different.
-	 (dolist (filter (plist-get info :filter-options))
-	   (let ((result (funcall filter info backend)))
-	     (when result (setq info result))))
+	 (let ((backend-name (org-export-backend-name backend)))
+	   (dolist (filter (plist-get info :filter-options))
+	     (let ((result (funcall filter info backend-name)))
+	       (when result (setq info result)))))
 	 ;; Parse buffer and call parse-tree filter on it.
 	 (setq tree
 	       (org-export-filter-apply-functions
@@ -3018,7 +3059,9 @@ Return code as a string."
   (backend buffer &optional subtreep visible-only body-only ext-plist)
   "Call `org-export-as' with output to a specified buffer.
 
-BACKEND is the back-end used for transcoding, as a symbol.
+BACKEND is either an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.
 
 BUFFER is the output buffer.  If it already exists, it will be
 erased first, otherwise, it will be created.
@@ -3046,8 +3089,10 @@ to kill ring.  Return buffer."
   (backend file &optional subtreep visible-only body-only ext-plist)
   "Call `org-export-as' with output to a specified file.
 
-BACKEND is the back-end used for transcoding, as a symbol.  FILE
-is the name of the output file, as a string.
+BACKEND is either an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.  FILE is the name of the output file, as
+a string.
 
 Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
 EXT-PLIST are similar to those used in `org-export-as', which
@@ -3074,6 +3119,10 @@ to kill ring.  Return output file's name."
 (defun org-export-string-as (string backend &optional body-only ext-plist)
   "Transcode STRING into BACKEND code.
 
+BACKEND is either an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.
+
 When optional argument BODY-ONLY is non-nil, only return body
 code, without preamble nor postamble.
 
@@ -3089,7 +3138,10 @@ Return code as a string."
 
 ;;;###autoload
 (defun org-export-replace-region-by (backend)
-  "Replace the active region by its export to BACKEND."
+  "Replace the active region by its export to BACKEND.
+BACKEND is either an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end."
   (if (not (org-region-active-p))
       (user-error "No active region to replace")
     (let* ((beg (region-beginning))
@@ -3103,10 +3155,10 @@ Return code as a string."
 (defun org-export-insert-default-template (&optional backend subtreep)
   "Insert all export keywords with default values at beginning of line.
 
-BACKEND is a symbol representing the export back-end for which
-specific export options should be added to the template, or
-`default' for default template.  When it is nil, the user will be
-prompted for a category.
+BACKEND is a symbol referring to the name of a registered export
+back-end, for which specific export options should be added to
+the template, or `default' for default template.  When it is nil,
+the user will be prompted for a category.
 
 If SUBTREEP is non-nil, export configuration will be set up
 locally for the subtree through node properties."
@@ -3115,17 +3167,22 @@ locally for the subtree through node properties."
   (when (and subtreep (org-before-first-heading-p))
     (user-error "No subtree to set export options for"))
   (let ((node (and subtreep (save-excursion (org-back-to-heading t) (point))))
-	(backend (or backend
-                     (intern
-                      (org-completing-read
-                       "Options category: "
-                       (cons "default"
-                             (mapcar (lambda (b) (symbol-name (car b)))
-                                     org-export-registered-backends))))))
+	(backend
+	 (or backend
+	     (intern
+	      (org-completing-read
+	       "Options category: "
+	       (cons "default"
+		     (mapcar (lambda (b)
+			       (symbol-name (org-export-backend-name b)))
+			     org-export--registered-backends))))))
 	options keywords)
     ;; Populate OPTIONS and KEYWORDS.
-    (dolist (entry (if (eq backend 'default) org-export-options-alist
-		     (org-export-backend-options backend)))
+    (dolist (entry (cond ((eq backend 'default) org-export-options-alist)
+			 ((org-export-backend-p backend)
+			  (org-export-get-all-options backend))
+			 (t (org-export-get-all-options
+			     (org-export-backend-name backend)))))
       (let ((keyword (nth 1 entry))
             (option (nth 2 entry)))
         (cond
@@ -3502,16 +3559,20 @@ Caption lines are separated by a white space."
 ;; back-end, it may be used as a fall-back function once all specific
 ;; cases have been treated.
 
-(defun org-export-with-backend (back-end data &optional contents info)
-  "Call a transcoder from BACK-END on DATA.
-CONTENTS, when non-nil, is the transcoded contents of DATA
-element, as a string.  INFO, when non-nil, is the communication
-channel used for export, as a plist.."
-  (org-export-barf-if-invalid-backend back-end)
+(defun org-export-with-backend (backend data &optional contents info)
+  "Call a transcoder from BACKEND on DATA.
+BACKEND is an export back-end, as returned by, e.g.,
+`org-export-create-backend', or a symbol referring to
+a registered back-end.  DATA is an Org element, object, secondary
+string or string.  CONTENTS, when non-nil, is the transcoded
+contents of DATA element, as a string.  INFO, when non-nil, is
+the communication channel used for export, as a plist."
+  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
+  (org-export-barf-if-invalid-backend backend)
   (let ((type (org-element-type data)))
     (if (memq type '(nil org-data)) (error "No foreign transcoder available")
       (let ((transcoder
-	     (cdr (assq type (org-export-backend-translate-table back-end)))))
+	     (cdr (assq type (org-export-get-all-transcoders backend)))))
 	(if (functionp transcoder) (funcall transcoder data contents info)
 	  (error "No foreign transcoder available"))))))
 
@@ -5849,43 +5910,31 @@ back to standard interface."
 	  (lambda (value)
 	    ;; Fontify VALUE string.
 	    (org-propertize value 'face 'font-lock-variable-name-face)))
-	 ;; Prepare menu entries by extracting them from
-	 ;; `org-export-registered-backends', and sorting them by
-	 ;; access key and by ordinal, if any.
-	 (backends
-	  (sort
-	   (sort
-	    (delq nil
-		  (mapcar
-		   (lambda (b)
-		     (let ((name (car b)))
-		       (catch 'ignored
-			 ;; Ignore any back-end belonging to
-			 ;; `org-export-invisible-backends' or derived
-			 ;; from one of them.
-			 (dolist (ignored org-export-invisible-backends)
-			   (when (org-export-derived-backend-p name ignored)
-			     (throw 'ignored nil)))
-			 (org-export-backend-menu name))))
-		   org-export-registered-backends))
-	    (lambda (a b)
-	      (let ((key-a (nth 1 a))
-		    (key-b (nth 1 b)))
-		(cond ((and (numberp key-a) (numberp key-b))
-		       (< key-a key-b))
-		      ((numberp key-b) t)))))
-	   (lambda (a b) (< (car a) (car b)))))
+	 ;; Prepare menu entries by extracting them from registered
+	 ;; back-ends and sorting them by access key and by ordinal,
+	 ;; if any.
+	 (entries
+	  (sort (sort (delq nil
+			    (mapcar 'org-export-backend-menu
+				    org-export--registered-backends))
+		      (lambda (a b)
+			(let ((key-a (nth 1 a))
+			      (key-b (nth 1 b)))
+			  (cond ((and (numberp key-a) (numberp key-b))
+				 (< key-a key-b))
+				((numberp key-b) t)))))
+		'car-less-than-car))
 	 ;; Compute a list of allowed keys based on the first key
 	 ;; pressed, if any.  Some keys
 	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always
 	 ;; available.
 	 (allowed-keys
 	  (nconc (list 2 22 19 6 1)
-		 (if (not first-key) (org-uniquify (mapcar 'car backends))
+		 (if (not first-key) (org-uniquify (mapcar 'car entries))
 		   (let (sub-menu)
-		     (dolist (backend backends (sort (mapcar 'car sub-menu) '<))
-		       (when (eq (car backend) first-key)
-			 (setq sub-menu (append (nth 2 backend) sub-menu))))))
+		     (dolist (entry entries (sort (mapcar 'car sub-menu) '<))
+		       (when (eq (car entry) first-key)
+			 (setq sub-menu (append (nth 2 entry) sub-menu))))))
 		 (cond ((eq first-key ?P) (list ?f ?p ?x ?a))
 		       ((not first-key) (list ?P)))
 		 (list ?& ?#)
@@ -5944,7 +5993,7 @@ back to standard interface."
 				(nth 1 sub-entry)))
 			     sub-menu "")
 			    (when (zerop (mod index 2)) "\n"))))))))
-		backends ""))
+		entries ""))
 	     ;; Publishing menu is hard-coded.
 	     (format "\n[%s] Publish
     [%s] Current file              [%s] Current project
@@ -5979,7 +6028,7 @@ back to standard interface."
     ;; UI, display an intrusive help buffer.
     (if expertp
 	(org-export--dispatch-action
-	 expert-prompt allowed-keys backends options first-key expertp)
+	 expert-prompt allowed-keys entries options first-key expertp)
       ;; At first call, create frame layout in order to display menu.
       (unless (get-buffer "*Org Export Dispatcher*")
 	(delete-other-windows)
@@ -6002,15 +6051,15 @@ back to standard interface."
 	  (set-window-start nil pos)))
       (org-fit-window-to-buffer)
       (org-export--dispatch-action
-       standard-prompt allowed-keys backends options first-key expertp))))
+       standard-prompt allowed-keys entries options first-key expertp))))
 
 (defun org-export--dispatch-action
-  (prompt allowed-keys backends options first-key expertp)
+  (prompt allowed-keys entries options first-key expertp)
   "Read a character from command input and act accordingly.
 
 PROMPT is the displayed prompt, as a string.  ALLOWED-KEYS is
 a list of characters available at a given step in the process.
-BACKENDS is a list of menu entries.  OPTIONS, FIRST-KEY and
+ENTRIES is a list of menu entries.  OPTIONS, FIRST-KEY and
 EXPERTP are the same as defined in `org-export--dispatch-ui',
 which see.
 
@@ -6067,9 +6116,9 @@ options as CDR."
        first-key expertp))
      ;; Action selected: Send key and options back to
      ;; `org-export-dispatch'.
-     ((or first-key (functionp (nth 2 (assq key backends))))
+     ((or first-key (functionp (nth 2 (assq key entries))))
       (cons (cond
-	     ((not first-key) (nth 2 (assq key backends)))
+	     ((not first-key) (nth 2 (assq key entries)))
 	     ;; Publishing actions are hard-coded.  Send a special
 	     ;; signal to `org-export-dispatch'.
 	     ((eq first-key ?P)
@@ -6082,10 +6131,10 @@ options as CDR."
 	     ;; path. Indeed, derived backends can share the same
 	     ;; FIRST-KEY.
 	     (t (catch 'found
-		  (mapc (lambda (backend)
-			  (let ((match (assq key (nth 2 backend))))
+		  (mapc (lambda (entry)
+			  (let ((match (assq key (nth 2 entry))))
 			    (when match (throw 'found (nth 2 match)))))
-			(member (assq first-key backends) backends)))))
+			(member (assq first-key entries) entries)))))
 	    options))
      ;; Otherwise, enter sub-menu.
      (t (org-export--dispatch-ui options key expertp)))))
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index cbae08a..0ba20f2 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -24,30 +24,22 @@
 (unless (featurep 'ox)
   (signal 'missing-test-dependency "org-export"))
 
-(defmacro org-test-with-backend (backend &rest body)
-  "Execute body with an export back-end defined.
-
-BACKEND is the name of the back-end.  BODY is the body to
-execute.  The defined back-end simply returns parsed data as Org
-syntax."
-  (declare (debug (form body)) (indent 1))
-  `(let ((org-export-registered-backends
-	  ',(list
-	     (list backend
-		   :translate-alist
-		   (let (transcode-table)
-		     (dolist (type (append org-element-all-elements
-					   org-element-all-objects)
-				   transcode-table)
-		       (push
-			(cons type
-			      (lambda (obj contents info)
-				(funcall
-				 (intern (format "org-element-%s-interpreter"
-						 type))
-				 obj contents)))
-			transcode-table)))))))
-     (progn ,@body)))
+(defun org-test-default-backend ()
+  "Return a default export back-end.
+This back-end simply returns parsed data as Org syntax."
+  (org-export-create-backend
+   :transcoders (let (transcode-table)
+		  (dolist (type (append org-element-all-elements
+					org-element-all-objects)
+				transcode-table)
+		    (push
+		     (cons type
+			   (lambda (obj contents info)
+			     (funcall
+			      (intern (format "org-element-%s-interpreter"
+					      type))
+			      obj contents)))
+		     transcode-table)))))
 
 (defmacro org-test-with-parsed-data (data &rest body)
   "Execute body with parsed data available.
@@ -108,12 +100,12 @@ already filled in `info'."
   (should
    (equal "Yes\n"
 	  (org-test-with-temp-text "#+BIND: test-ox-var value"
-	    (let ((org-export-allow-bind-keywords t)
-		  org-export-registered-backends)
-	      (org-export-define-backend 'check
+	    (let ((org-export-allow-bind-keywords t))
+	      (org-export-as
+	       (org-export-create-backend
+		:transcoders
 		'((section . (lambda (s c i)
-			       (if (eq test-ox-var 'value) "Yes" "No")))))
-	      (org-export-as 'check))))))
+			       (if (eq test-ox-var 'value) "Yes" "No")))))))))))
 
 (ert-deftest test-org-export/parse-option-keyword ()
   "Test reading all standard #+OPTIONS: items."
@@ -276,14 +268,14 @@ Paragraph"
     'equal
     (org-test-with-temp-text-in-file "Test"
       (org-mode)
-      (let (org-export-registered-backends)
-	(org-export-define-backend 'test
-	  '((template . (lambda (text info)
-			  (org-element-interpret-data
-			   (plist-get info :title) info)))))
-	(list (org-export-as 'test)
-	      (file-name-nondirectory
-	       (file-name-sans-extension (buffer-file-name))))))))
+      (list (org-export-as
+	     (org-export-create-backend
+	      :transcoders
+	      '((template . (lambda (text info)
+			      (org-element-interpret-data
+			       (plist-get info :title) info))))))
+	    (file-name-nondirectory
+	     (file-name-sans-extension (buffer-file-name)))))))
   ;; If no title is specified, and no file is associated to the
   ;; buffer, use buffer's name.
   (should
@@ -291,36 +283,37 @@ Paragraph"
     'equal
     (org-test-with-temp-text "Test"
       (org-mode)
-      (let (org-export-registered-backends)
-	(org-export-define-backend 'test
-	  '((template . (lambda (text info)
-			  (org-element-interpret-data
-			   (plist-get info :title) info)))))
-	(list (org-export-as 'test) (buffer-name))))))
+      (list (org-export-as
+	     (org-export-create-backend
+	      :transcoders
+	      '((template . (lambda (text info)
+			      (org-element-interpret-data
+			       (plist-get info :title) info))))))
+	    (buffer-name)))))
   ;; If a title is specified, use it.
   (should
    (equal
     "Title"
     (org-test-with-temp-text-in-file "#+TITLE: Title\nTest"
       (org-mode)
-      (let (org-export-registered-backends)
-	(org-export-define-backend 'test
-	  '((template . (lambda (text info)
-			  (org-element-interpret-data
-			   (plist-get info :title) info)))))
-	(org-export-as 'test)))))
+      (org-export-as
+       (org-export-create-backend
+	:transcoders
+	'((template . (lambda (text info)
+			(org-element-interpret-data
+			 (plist-get info :title) info)))))))))
   ;; If an empty title is specified, do not set it.
   (should
    (equal
     ""
     (org-test-with-temp-text-in-file "#+TITLE:\nTest"
       (org-mode)
-      (let (org-export-registered-backends)
-	(org-export-define-backend 'test
-	  '((template . (lambda (text info)
-			  (org-element-interpret-data
-			   (plist-get info :title) info)))))
-	(org-export-as 'test))))))
+      (org-export-as
+       (org-export-create-backend
+	:transcoders
+	'((template . (lambda (text info)
+			(org-element-interpret-data
+			 (plist-get info :title) info))))))))))
 
 (ert-deftest test-org-export/handle-options ()
   "Test if export options have an impact on output."
@@ -328,142 +321,148 @@ Paragraph"
   (should
    (equal ""
 	  (org-test-with-temp-text "* Head1 :noexp:"
-	    (org-test-with-backend test
-	      (org-export-as 'test nil nil nil '(:exclude-tags ("noexp")))))))
+	    (org-export-as (org-test-default-backend)
+			   nil nil nil '(:exclude-tags ("noexp"))))))
   ;; Test include tags for headlines and inlinetasks.
   (should
    (equal "* H2\n** Sub :exp:\n*** Sub Sub\n"
 	  (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub Sub\n* H3"
 	    (let ((org-tags-column 0))
-	      (org-test-with-backend test
-		(org-export-as 'test nil nil nil '(:select-tags ("exp"))))))))
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:select-tags ("exp")))))))
   ;; Test mixing include tags and exclude tags.
-  (org-test-with-temp-text "
+  (should
+   (string-match
+    "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
+    (org-test-with-temp-text "
 * Head1 :export:
 ** Sub-Head1 :noexport:
 ** Sub-Head2
 * Head2 :noexport:
 ** Sub-Head1 :export:"
-    (org-test-with-backend test
-      (should
-       (string-match
-	"\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
-	(org-export-as
-	 'test nil nil nil
-	 '(:select-tags ("export") :exclude-tags ("noexport")))))))
+      (org-export-as (org-test-default-backend) nil nil nil
+		     '(:select-tags ("export") :exclude-tags ("noexport"))))))
   ;; Ignore tasks.
   (should
    (equal ""
 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
 	    (org-test-with-temp-text "* TODO Head1"
-	      (org-test-with-backend test
-		(org-export-as 'test nil nil nil '(:with-tasks nil)))))))
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-tasks nil))))))
   (should
    (equal "* TODO Head1\n"
 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
 	    (org-test-with-temp-text "* TODO Head1"
-	      (org-test-with-backend test
-		(org-export-as 'test nil nil nil '(:with-tasks t)))))))
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-tasks t))))))
   ;; Archived tree.
-  (org-test-with-temp-text "* Head1 :archive:"
-    (let ((org-archive-tag "archive"))
-      (org-test-with-backend test
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-archived-trees nil))
-		"")))))
-  (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
-    (let ((org-archive-tag "archive"))
-      (org-test-with-backend test
-	(should
-	 (string-match
-	  "\\* Head1[ \t]+:archive:"
-	  (org-export-as 'test nil nil nil
-			 '(:with-archived-trees headline)))))))
-  (org-test-with-temp-text "* Head1 :archive:"
-    (let ((org-archive-tag "archive"))
-      (org-test-with-backend test
-	(should
-	 (string-match
-	  "\\`\\* Head1[ \t]+:archive:\n\\'"
-	  (org-export-as 'test nil nil nil '(:with-archived-trees t)))))))
+  (should
+   (equal ""
+	  (org-test-with-temp-text "* Head1 :archive:"
+	    (let ((org-archive-tag "archive"))
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-archived-trees nil))))))
+  (should
+   (string-match
+    "\\* Head1[ \t]+:archive:"
+    (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
+      (let ((org-archive-tag "archive"))
+	(org-export-as (org-test-default-backend) nil nil nil
+		       '(:with-archived-trees headline))))))
+  (should
+   (string-match
+    "\\`\\* Head1[ \t]+:archive:\n\\'"
+    (org-test-with-temp-text "* Head1 :archive:"
+      (let ((org-archive-tag "archive"))
+	(org-export-as (org-test-default-backend)
+		       nil nil nil '(:with-archived-trees t))))))
   ;; Clocks.
-  (let ((org-clock-string "CLOCK:"))
-    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
-      (org-test-with-backend test
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-clocks t))
-		"CLOCK: [2012-04-29 sun. 10:45]\n"))
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-clocks nil)) "")))))
+  (should
+   (equal "CLOCK: [2012-04-29 sun. 10:45]\n"
+	  (let ((org-clock-string "CLOCK:"))
+	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-clocks t))))))
+  (should
+   (equal ""
+	  (let ((org-clock-string "CLOCK:"))
+	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-clocks nil))))))
   ;; Drawers.
-  (let ((org-drawers '("TEST")))
-    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
-      (org-test-with-backend test
-	(should (equal (org-export-as 'test nil nil nil '(:with-drawers nil))
-		       ""))
-	(should (equal (org-export-as 'test nil nil nil '(:with-drawers t))
-		       ":TEST:\ncontents\n:END:\n")))))
-  (let ((org-drawers '("FOO" "BAR")))
-    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
-      (org-test-with-backend test
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-drawers ("FOO")))
-		":FOO:\nkeep\n:END:\n")))))
-  (let ((org-drawers '("FOO" "BAR")))
-    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
-      (org-test-with-backend test
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-drawers (not "BAR")))
-		":FOO:\nkeep\n:END:\n")))))
+  (should
+   (equal ""
+	  (let ((org-drawers '("TEST")))
+	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-drawers nil))))))
+  (should
+   (equal ":TEST:\ncontents\n:END:\n"
+	  (let ((org-drawers '("TEST")))
+	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-drawers t))))))
+  (should
+   (equal ":FOO:\nkeep\n:END:\n"
+	  (let ((org-drawers '("FOO" "BAR")))
+	    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-drawers ("FOO")))))))
+  (should
+   (equal ":FOO:\nkeep\n:END:\n"
+	  (let ((org-drawers '("FOO" "BAR")))
+	    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-drawers (not "BAR")))))))
   ;; Footnotes.
   (should
    (equal "Footnote?"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-test-with-backend test
-		(org-trim
-		 (org-export-as 'test nil nil nil '(:with-footnotes nil))))))))
+	      (org-trim (org-export-as (org-test-default-backend)
+				       nil nil nil '(:with-footnotes nil)))))))
   (should
    (equal "Footnote?[fn:1]\n\n[fn:1] Def"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-test-with-backend test
-		(org-trim
-		 (org-export-as 'test nil nil nil '(:with-footnotes t))))))))
+	      (org-trim (org-export-as (org-test-default-backend)
+				       nil nil nil '(:with-footnotes t)))))))
   ;; Inlinetasks.
   (when (featurep 'org-inlinetask)
     (should
      (equal
+      ""
       (let ((org-inlinetask-min-level 15))
 	(org-test-with-temp-text "*************** Task"
-	  (org-test-with-backend test
-	    (org-export-as 'test nil nil nil '(:with-inlinetasks nil)))))
-      ""))
+	  (org-export-as (org-test-default-backend)
+			 nil nil nil '(:with-inlinetasks nil))))))
     (should
      (equal
+      ""
       (let ((org-inlinetask-min-level 15))
 	(org-test-with-temp-text
 	    "*************** Task\nContents\n*************** END"
-	  (org-test-with-backend test
-	    (org-export-as 'test nil nil nil '(:with-inlinetasks nil)))))
-      "")))
+	  (org-export-as (org-test-default-backend)
+			 nil nil nil '(:with-inlinetasks nil)))))))
   ;; Plannings.
-  (let ((org-closed-string "CLOSED:"))
-    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
-      (org-test-with-backend test
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-planning t))
-		"CLOSED: [2012-04-29 sun. 10:45]\n"))
-	(should
-	 (equal (org-export-as 'test nil nil nil '(:with-planning nil))
-		"")))))
+  (should
+   (equal "CLOSED: [2012-04-29 sun. 10:45]\n"
+	  (let ((org-closed-string "CLOSED:"))
+	    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-planning t))))))
+  (should
+   (equal ""
+	  (let ((org-closed-string "CLOSED:"))
+	    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
+	      (org-export-as (org-test-default-backend)
+			     nil nil nil '(:with-planning nil))))))
   ;; Statistics cookies.
   (should
    (equal ""
 	  (org-test-with-temp-text "[0/0]"
-	    (org-test-with-backend test
-	      (org-export-as
-	       'test nil nil nil '(:with-statistics-cookies nil)))))))
+	    (org-export-as (org-test-default-backend)
+			   nil nil nil '(:with-statistics-cookies nil))))))
 
 (ert-deftest test-org-export/with-timestamps ()
   "Test `org-export-with-timestamps' specifications."
@@ -472,15 +471,15 @@ Paragraph"
    (equal
     "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>\n"
     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
-      (org-test-with-backend test
-	(org-export-as 'test nil nil nil '(:with-timestamps t))))))
+      (org-export-as (org-test-default-backend)
+		     nil nil nil '(:with-timestamps t)))))
   ;; nil value.
   (should
    (equal
     ""
     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
-      (org-test-with-backend test
-	(org-export-as 'test nil nil nil '(:with-timestamps nil))))))
+      (org-export-as (org-test-default-backend)
+		     nil nil nil '(:with-timestamps nil)))))
   ;; `active' value.
   (should
    (equal
@@ -489,9 +488,8 @@ Paragraph"
 	"<2012-03-29 Thu>[2012-03-29 Thu]
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
-      (org-test-with-backend test
-	(org-trim
-	 (org-export-as 'test nil nil nil '(:with-timestamps active)))))))
+      (org-trim (org-export-as (org-test-default-backend)
+			       nil nil nil '(:with-timestamps active))))))
   ;; `inactive' value.
   (should
    (equal
@@ -500,16 +498,16 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	"<2012-03-29 Thu>[2012-03-29 Thu]
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
-      (org-test-with-backend test
-	(org-trim
-	 (org-export-as 'test nil nil nil '(:with-timestamps inactive))))))))
+      (org-trim (org-export-as (org-test-default-backend)
+			       nil nil nil '(:with-timestamps inactive)))))))
 
 (ert-deftest test-org-export/comment-tree ()
   "Test if export process ignores commented trees."
-  (let ((org-comment-string "COMMENT"))
-    (org-test-with-temp-text "* COMMENT Head1"
-      (org-test-with-backend test
-	(should (equal (org-export-as 'test) ""))))))
+  (should
+   (equal ""
+	  (let ((org-comment-string "COMMENT"))
+	    (org-test-with-temp-text "* COMMENT Head1"
+	      (org-export-as (org-test-default-backend)))))))
 
 (ert-deftest test-org-export/export-scope ()
   "Test all export scopes."
@@ -518,22 +516,23 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 ** Head2
 text
 *** Head3"
-    (org-test-with-backend test
-      ;; Subtree.
-      (forward-line 3)
-      (should (equal (org-export-as 'test 'subtree) "text\n*** Head3\n"))
-      ;; Visible.
-      (goto-char (point-min))
-      (forward-line)
-      (org-cycle)
-      (should (equal (org-export-as 'test nil 'visible) "* Head1\n"))
-      ;; Region.
-      (goto-char (point-min))
-      (forward-line 3)
-      (transient-mark-mode 1)
-      (push-mark (point) t t)
-      (goto-char (point-at-eol))
-      (should (equal (org-export-as 'test) "text\n"))))
+    ;; Subtree.
+    (forward-line 3)
+    (should (equal (org-export-as (org-test-default-backend) 'subtree)
+		   "text\n*** Head3\n"))
+    ;; Visible.
+    (goto-char (point-min))
+    (forward-line)
+    (org-cycle)
+    (should (equal (org-export-as (org-test-default-backend) nil 'visible)
+		   "* Head1\n"))
+    ;; Region.
+    (goto-char (point-min))
+    (forward-line 3)
+    (transient-mark-mode 1)
+    (push-mark (point) t t)
+    (goto-char (point-at-eol))
+    (should (equal (org-export-as (org-test-default-backend)) "text\n")))
   ;; Subtree with a code block calling another block outside.
   (should
    (equal ": 3\n"
@@ -547,19 +546,18 @@ text
 #+BEGIN_SRC emacs-lisp
 \(+ 1 2)
 #+END_SRC"
-	    (org-test-with-backend test
-	      (forward-line 1)
-	      (org-export-as 'test 'subtree)))))
+	    (forward-line 1)
+	    (org-export-as (org-test-default-backend) 'subtree))))
   ;; Body only.
-  (org-test-with-temp-text "Text"
-    (org-test-with-backend test
-      (plist-put
-       (cdr (assq 'test org-export-registered-backends))
-       :translate-alist
-       (cons (cons 'template (lambda (body info) (format "BEGIN\n%sEND" body)))
-	     (org-export-backend-translate-table 'test)))
-      (should (equal (org-export-as 'test nil nil 'body-only) "Text\n"))
-      (should (equal (org-export-as 'test) "BEGIN\nText\nEND")))))
+  (let ((backend (org-test-default-backend)))
+    (setf (org-export-backend-transcoders backend)
+	  (cons '(template . (lambda (body i)
+			       (format "BEGIN\n%sEND" body)))
+		(org-export-backend-transcoders backend)))
+    (org-test-with-temp-text "Text"
+      (should (equal (org-export-as backend nil nil 'body-only)
+		     "Text\n"))
+      (should (equal (org-export-as backend) "BEGIN\nText\nEND")))))
 
 (ert-deftest test-org-export/output-file-name ()
   "Test `org-export-output-file-name' specifications."
@@ -667,7 +665,7 @@ body\n")))
   (should
    (equal "#+MACRO: macro1 value\nvalue\n"
 	  (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}"
-	    (org-test-with-backend test (org-export-as 'test)))))
+	    (org-export-as (org-test-default-backend)))))
   ;; Expand specific macros.
   (should
    (equal "me 2012-03-29 me@here Title\n"
@@ -678,7 +676,7 @@ body\n")))
 #+AUTHOR: me
 #+EMAIL: me@here
 {{{author}}} {{{date}}} {{{email}}} {{{title}}}"
-	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	    (let ((output (org-export-as (org-test-default-backend))))
 	      (substring output (string-match ".*\n\\'" output))))))
   ;; Expand specific macros when property contained a regular macro
   ;; already.
@@ -688,7 +686,7 @@ body\n")))
 #+MACRO: macro1 value
 #+TITLE: {{{macro1}}}
 {{{title}}}"
-	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	    (let ((output (org-export-as (org-test-default-backend))))
 	      (substring output (string-match ".*\n\\'" output))))))
   ;; Expand macros with templates in included files.
   (should
@@ -696,57 +694,65 @@ body\n")))
 	  (org-test-with-temp-text
 	      (format "#+INCLUDE: \"%s/examples/macro-templates.org\"
 {{{included-macro}}}" org-test-dir)
-	    (let ((output (org-test-with-backend test (org-export-as 'test))))
+	    (let ((output (org-export-as (org-test-default-backend))))
 	      (substring output (string-match ".*\n\\'" output)))))))
 
 (ert-deftest test-org-export/user-ignore-list ()
   "Test if `:ignore-list' accepts user input."
-  (org-test-with-backend test
-    (flet ((skip-note-head
-	    (data backend info)
-	    ;; Ignore headlines with the word "note" in their title.
-	    (org-element-map data 'headline
-	      (lambda (headline)
-		(when (string-match "\\<note\\>"
-				    (org-element-property :raw-value headline))
-		  (org-export-ignore-element headline info)))
-	      info)
-	    data))
-      ;; Install function in parse tree filters.
-      (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
-	(org-test-with-temp-text "* Head1\n* Head2 (note)\n"
-	  (should (equal (org-export-as 'test) "* Head1\n")))))))
+  (let ((backend (org-test-default-backend)))
+    (setf (org-export-backend-transcoders backend)
+	  (cons '(template . (lambda (body i)
+			       (format "BEGIN\n%sEND" body)))
+		(org-export-backend-transcoders backend)))
+    (org-test-with-temp-text "Text"
+      (should (equal (org-export-as backend nil nil 'body-only)
+		     "Text\n"))
+      (should (equal (org-export-as backend) "BEGIN\nText\nEND"))))
+  (should
+   (equal
+    "* Head1\n"
+    (let ((org-export-filter-parse-tree-functions
+	   '((lambda (data backend info)
+	       ;; Ignore headlines with the word "note" in their title.
+	       (org-element-map data 'headline
+		 (lambda (headline)
+		   (when (string-match "\\<note\\>"
+				       (org-element-property :raw-value
+							     headline))
+		     (org-export-ignore-element headline info)))
+		 info)
+	       data))))
+      (org-test-with-temp-text "* Head1\n* Head2 (note)\n"
+	(org-export-as (org-test-default-backend)))))))
 
 (ert-deftest test-org-export/before-processing-hook ()
   "Test `org-export-before-processing-hook'."
   (should
    (equal
     "#+MACRO: mac val\nTest\n"
-    (org-test-with-backend test
-      (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test"
-	(let ((org-export-before-processing-hook
-	       '((lambda (backend)
-		   (while (re-search-forward "{{{" nil t)
-		     (let ((object (org-element-context)))
-		       (when (eq (org-element-type object) 'macro)
-			 (delete-region
-			  (org-element-property :begin object)
-			  (org-element-property :end object)))))))))
-	  (org-export-as 'test)))))))
+    (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test"
+      (let ((org-export-before-processing-hook
+	     '((lambda (backend)
+		 (while (re-search-forward "{{{" nil t)
+		   (let ((object (org-element-context)))
+		     (when (eq (org-element-type object) 'macro)
+		       (delete-region
+			(org-element-property :begin object)
+			(org-element-property :end object)))))))))
+	(org-export-as (org-test-default-backend)))))))
 
 (ert-deftest test-org-export/before-parsing-hook ()
   "Test `org-export-before-parsing-hook'."
   (should
    (equal "Body 1\nBody 2\n"
-	  (org-test-with-backend test
-	    (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
-	      (let ((org-export-before-parsing-hook
-		     '((lambda (backend)
-			 (goto-char (point-min))
-			 (while (re-search-forward org-outline-regexp-bol nil t)
-			   (delete-region
-			    (point-at-bol) (progn (forward-line) (point))))))))
-		(org-export-as 'test)))))))
+	  (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
+	    (let ((org-export-before-parsing-hook
+		   '((lambda (backend)
+		       (goto-char (point-min))
+		       (while (re-search-forward org-outline-regexp-bol nil t)
+			 (delete-region
+			  (point-at-bol) (progn (forward-line) (point))))))))
+	      (org-export-as (org-test-default-backend)))))))
 
 
 \f
@@ -833,37 +839,37 @@ body\n")))
   ;; Translate table.
   (should
    (equal '((headline . my-headline-test))
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'test '((headline . my-headline-test)))
-	    (org-export-backend-translate-table 'test))))
+	    (org-export-get-all-transcoders 'test))))
   ;; Filters.
   (should
    (equal '((:filter-headline . my-filter))
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'test
 	      '((headline . my-headline-test))
 	      :filters-alist '((:filter-headline . my-filter)))
-	    (org-export-backend-filters 'test))))
+	    (org-export-backend-filters (org-export-get-backend 'test)))))
   ;; Options.
   (should
    (equal '((:prop value))
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'test
 	      '((headline . my-headline-test))
 	      :options-alist '((:prop value)))
-	    (org-export-backend-options 'test))))
+	    (org-export-backend-options (org-export-get-backend 'test)))))
   ;; Menu.
   (should
    (equal '(?k "Test Export" test)
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'test
 	      '((headline . my-headline-test))
 	      :menu-entry '(?k "Test Export" test))
-	    (org-export-backend-menu 'test))))
+	    (org-export-backend-menu (org-export-get-backend 'test)))))
   ;; Export Blocks.
   (should
    (equal '(("TEST" . org-element-export-block-parser))
-	  (let (org-export-registered-backends org-element-block-name-alist)
+	  (let (org-export--registered-backends org-element-block-name-alist)
 	    (org-export-define-backend 'test
 	      '((headline . my-headline-test))
 	      :export-block '("test"))
@@ -873,115 +879,218 @@ body\n")))
   "Test `org-export-define-derived-backend' specifications."
   ;; Error when parent back-end is not defined.
   (should-error
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-derived-backend 'test 'parent)))
   ;; Append translation table to parent's.
   (should
    (equal '((:headline . test) (:headline . parent))
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'parent '((:headline . parent)))
 	    (org-export-define-derived-backend 'test 'parent
 	      :translate-alist '((:headline . test)))
-	    (org-export-backend-translate-table 'test))))
+	    (org-export-get-all-transcoders 'test))))
   ;; Options defined in the new back have priority over those defined
   ;; in parent.
   (should
    (eq 'test
-       (let (org-export-registered-backends)
+       (let (org-export--registered-backends)
 	 (org-export-define-backend 'parent
 	   '((:headline . parent))
 	   :options-alist '((:a nil nil 'parent)))
 	 (org-export-define-derived-backend 'test 'parent
 	   :options-alist '((:a nil nil 'test)))
-	 (plist-get (org-export--get-global-options 'test) :a)))))
+	 (plist-get (org-export--get-global-options
+		     (org-export-get-backend 'test))
+		    :a)))))
 
 (ert-deftest test-org-export/derived-backend-p ()
   "Test `org-export-derived-backend-p' specifications."
   ;; Non-nil with direct match.
   (should
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-derived-backend-p 'test 'test)))
   (should
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-define-derived-backend 'test2 'test)
      (org-export-derived-backend-p 'test2 'test2)))
   ;; Non-nil with a direct parent.
   (should
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-define-derived-backend 'test2 'test)
      (org-export-derived-backend-p 'test2 'test)))
   ;; Non-nil with an indirect parent.
   (should
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-define-derived-backend 'test2 'test)
      (org-export-define-derived-backend 'test3 'test2)
      (org-export-derived-backend-p 'test3 'test)))
   ;; Nil otherwise.
   (should-not
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-define-backend 'test2 '((headline . test2)))
      (org-export-derived-backend-p 'test2 'test)))
   (should-not
-   (let (org-export-registered-backends)
+   (let (org-export--registered-backends)
      (org-export-define-backend 'test '((headline . test)))
      (org-export-define-backend 'test2 '((headline . test2)))
      (org-export-define-derived-backend 'test3 'test2)
      (org-export-derived-backend-p 'test3 'test))))
 
+(ert-deftest test-org-export/get-all-transcoders ()
+  "Test `org-export-get-all-transcoders' specifications."
+  ;; Return nil when back-end cannot be found.
+  (should-not (org-export-get-all-transcoders nil))
+  ;; Same as `org-export-transcoders' if no parent.
+  (should
+   (equal '((headline . ignore))
+	  (org-export-get-all-transcoders
+	   (org-export-create-backend
+	    :transcoders '((headline . ignore))))))
+  ;; But inherit from all ancestors whenever possible.
+  (should
+   (equal '((section . ignore) (headline . ignore))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1 '((headline . ignore)))
+	    (org-export-get-all-transcoders
+	     (org-export-create-backend
+	      :parent 'b1 :transcoders '((section . ignore)))))))
+  (should
+   (equal '((paragraph . ignore) (section . ignore) (headline . ignore))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1 '((headline . ignore)))
+	    (org-export-define-derived-backend 'b2 'b1
+	      :translate-alist '((section . ignore)))
+	    (org-export-get-all-transcoders
+	     (org-export-create-backend
+	      :parent 'b2 :transcoders '((paragraph . ignore)))))))
+  ;; Back-end transcoders overrule inherited ones.
+  (should
+   (eq 'b
+       (let (org-export--registered-backends)
+	 (org-export-define-backend 'b1 '((headline . a)))
+	 (cdr (assq 'headline
+		    (org-export-get-all-transcoders
+		     (org-export-create-backend
+		      :parent 'b1 :transcoders '((headline . b))))))))))
+
+(ert-deftest test-org-export/get-all-options ()
+  "Test `org-export-get-all-options' specifications."
+  ;; Return nil when back-end cannot be found.
+  (should-not (org-export-get-all-options nil))
+  ;; Same as `org-export-options' if no parent.
+  (should
+   (equal '((headline . ignore))
+	  (org-export-get-all-options
+	   (org-export-create-backend
+	    :options '((headline . ignore))))))
+  ;; But inherit from all ancestors whenever possible.
+  (should
+   (equal '((:key2 value2) (:key1 value1))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1 nil :options-alist '((:key1 value1)))
+	    (org-export-get-all-options
+	     (org-export-create-backend
+	      :parent 'b1 :options '((:key2 value2)))))))
+  (should
+   (equal '((:key3 value3) (:key2 value2) (:key1 value1))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1 nil :options-alist '((:key1 value1)))
+	    (org-export-define-derived-backend 'b2 'b1
+	      :options-alist '((:key2 value2)))
+	    (org-export-get-all-options
+	     (org-export-create-backend
+	      :parent 'b2 :options '((:key3 value3)))))))
+  ;; Back-end options overrule inherited ones.
+  (should
+   (eq 'b
+       (let (org-export--registered-backends)
+	 (org-export-define-backend 'b1 nil :options-alist '((:key1 . a)))
+	 (cdr (assq :key1
+		    (org-export-get-all-options
+		     (org-export-create-backend
+		      :parent 'b1 :options '((:key1 . b))))))))))
+
+(ert-deftest test-org-export/get-all-filters ()
+  "Test `org-export-get-all-filters' specifications."
+  ;; Return nil when back-end cannot be found.
+  (should-not (org-export-get-all-filters nil))
+  ;; Same as `org-export-filters' if no parent.
+  (should
+   (equal '((:filter-headline . ignore))
+	  (org-export-get-all-filters
+	   (org-export-create-backend
+	    :filters '((:filter-headline . ignore))))))
+  ;; But inherit from all ancestors whenever possible.
+  (should
+   (equal '((:filter-section . ignore) (:filter-headline . ignore))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1
+	      nil :filters-alist '((:filter-headline . ignore)))
+	    (org-export-get-all-filters
+	     (org-export-create-backend
+	      :parent 'b1 :filters '((:filter-section . ignore)))))))
+  (should
+   (equal '((:filter-paragraph . ignore)
+	    (:filter-section . ignore)
+	    (:filter-headline . ignore))
+	  (let (org-export--registered-backends)
+	    (org-export-define-backend 'b1
+	      nil :filters-alist '((:filter-headline . ignore)))
+	    (org-export-define-derived-backend 'b2 'b1
+	      :filters-alist '((:filter-section . ignore)))
+	    (org-export-get-all-filters
+	     (org-export-create-backend
+	      :parent 'b2 :filters '((:filter-paragraph . ignore)))))))
+  ;; Back-end filters overrule inherited ones.
+  (should
+   (eq 'b
+       (let (org-export--registered-backends)
+	 (org-export-define-backend 'b1 '((:filter-headline . a)))
+	 (cdr (assq :filter-headline
+		    (org-export-get-all-filters
+		     (org-export-create-backend
+		      :parent 'b1 :filters '((:filter-headline . b))))))))))
+
 (ert-deftest test-org-export/with-backend ()
   "Test `org-export-with-backend' definition."
   ;; Error when calling an undefined back-end
-  (should-error
-   (let (org-export-registered-backends)
-     (org-export-with-backend 'test "Test")))
+  (should-error (org-export-with-backend nil "Test"))
   ;; Error when called back-end doesn't have an appropriate
   ;; transcoder.
   (should-error
-   (let (org-export-registered-backends)
-     (org-export-define-backend 'test ((headline . ignore)))
-     (org-export-with-backend 'test "Test")))
+   (org-export-with-backend
+    (org-export-create-backend :transcoders '((headline . ignore)))
+    "Test"))
   ;; Otherwise, export using correct transcoder
   (should
    (equal "Success"
-	  (let (org-export-registered-backends)
+	  (let (org-export--registered-backends)
 	    (org-export-define-backend 'test
 	      '((plain-text . (lambda (text contents info) "Failure"))))
 	    (org-export-define-backend 'test2
 	      '((plain-text . (lambda (text contents info) "Success"))))
 	    (org-export-with-backend 'test2 "Test")))))
 
-(ert-deftest test-org-export/data-with-translations ()
-  "Test `org-export-data-with-translations' specifications."
-  (should
-   (equal
-    "Success!"
-    (org-export-data-with-translations
-     '(bold nil "Test")
-     '((plain-text . (lambda (text info) "Success"))
-       (bold . (lambda (bold contents info) (concat contents "!"))))
-     '(:with-emphasize t)))))
-
 (ert-deftest test-org-export/data-with-backend ()
   "Test `org-export-data-with-backend' specifications."
   ;; Error when calling an undefined back-end.
-  (should-error
-   (let (org-export-registered-backends)
-     (org-export-data-with-backend 'test "Test" nil)))
+  (should-error (org-export-data-with-backend nil "nil" nil))
   ;; Otherwise, export data recursively, using correct back-end.
   (should
    (equal
     "Success!"
-    (let (org-export-registered-backends)
-      (org-export-define-backend 'test
-	'((plain-text . (lambda (text info) "Success"))
-	  (bold . (lambda (bold contents info) (concat contents "!")))))
-      (org-export-data-with-backend
-       '(bold nil "Test") 'test '(:with-emphasize t))))))
+    (org-export-data-with-backend
+     '(bold nil "Test")
+     (org-export-create-backend
+      :transcoders
+      '((plain-text . (lambda (text info) "Success"))
+	(bold . (lambda (bold contents info) (concat contents "!")))))
+     '(:with-emphasize t)))))
 
 
 \f
@@ -989,28 +1098,30 @@ body\n")))
 
 (ert-deftest test-org-export/export-snippet ()
   "Test export snippets transcoding."
+  ;; Standard test.
   (org-test-with-temp-text "@@test:A@@@@t:B@@"
-    (org-test-with-backend test
-      (plist-put
-       (cdr (assq 'test org-export-registered-backends))
-       :translate-alist
-       (cons (cons 'export-snippet
-		   (lambda (snippet contents info)
-		     (when (eq (org-export-snippet-backend snippet) 'test)
-		       (org-element-property :value snippet))))
-	     (org-export-backend-translate-table 'test)))
+    (let ((backend (org-test-default-backend)))
+      (setf (org-export-backend-name backend) 'test)
+      (setf (org-export-backend-transcoders backend)
+	    (cons (cons 'export-snippet
+			(lambda (snippet contents info)
+			  (when (eq (org-export-snippet-backend snippet) 'test)
+			    (org-element-property :value snippet))))
+		  (org-export-backend-transcoders backend)))
       (let ((org-export-snippet-translation-alist nil))
-	(should (equal (org-export-as 'test) "A\n")))
+	(should (equal (org-export-as backend) "A\n")))
       (let ((org-export-snippet-translation-alist '(("t" . "test"))))
-	(should (equal (org-export-as 'test) "AB\n")))))
+	(should (equal (org-export-as backend) "AB\n")))))
   ;; Ignored export snippets do not remove any blank.
   (should
    (equal "begin  end\n"
 	  (org-test-with-parsed-data "begin @@test:A@@ end"
-	    (org-export-data-with-translations
+	    (org-export-data-with-backend
 	     tree
-	     '((paragraph . (lambda (paragraph contents info) contents))
-	       (section . (lambda (section contents info) contents)))
+	     (org-export-create-backend
+	      :transcoders
+	      '((paragraph . (lambda (paragraph contents info) contents))
+		(section . (lambda (section contents info) contents))))
 	     info)))))
 
 
@@ -1036,11 +1147,11 @@ body\n")))
 			    (car (org-element-contents def))))))))
 	  info))))
     ;; 2. Test nested footnotes order.
-    (org-test-with-parsed-data
-	"Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
-      (should
-       (equal
-	'((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
+    (should
+     (equal
+      '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
+      (org-test-with-parsed-data
+	  "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
 	(org-element-map tree 'footnote-reference
 	  (lambda (ref)
 	    (when (org-export-footnote-first-reference-p ref info)
@@ -1060,29 +1171,30 @@ body\n")))
 	(should
 	 (= (length (org-export-collect-footnote-definitions tree info)) 2))))
     ;; 4. Test footnotes definitions collection.
-    (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
+    (should
+     (= 4
+	(org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
 
 \[fn:2] B [fn:3] [fn::D].
 
 \[fn:3] C."
-      (should (= (length (org-export-collect-footnote-definitions tree info))
-		 4)))
+	  (length (org-export-collect-footnote-definitions tree info)))))
     ;; 5. Test export of footnotes defined outside parsing scope.
-    (org-test-with-temp-text "[fn:1] Out of scope
+    (should
+     (equal
+      "ParagraphOut of scope\n"
+      (org-test-with-temp-text "[fn:1] Out of scope
 * Title
 Paragraph[fn:1]"
-      (org-test-with-backend test
-	(plist-put
-	 (cdr (assq 'test org-export-registered-backends))
-	 :translate-alist
-	 (cons (cons 'footnote-reference
-		     (lambda (fn contents info)
-		       (org-element-interpret-data
-			(org-export-get-footnote-definition fn info))))
-	       (org-export-backend-translate-table 'test)))
-	(forward-line)
-	(should (equal "ParagraphOut of scope\n"
-		       (org-export-as 'test 'subtree)))))
+	(let ((backend (org-test-default-backend)))
+	  (setf (org-export-backend-transcoders backend)
+		(cons (cons 'footnote-reference
+			    (lambda (fn contents info)
+			      (org-element-interpret-data
+			       (org-export-get-footnote-definition fn info))))
+		      (org-export-backend-transcoders backend)))
+	  (forward-line)
+	  (org-export-as backend 'subtree)))))
     ;; 6. Footnotes without a definition should be provided a fallback
     ;;    definition.
     (should
@@ -1378,8 +1490,8 @@ Paragraph[fn:1]"
       ""
       (let ((org-inlinetask-min-level 3))
 	(org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** end"
-	  (org-test-with-backend test
-	    (org-export-as 'test nil nil nil '(:exclude-tags ("noexp"))))))))
+	  (org-export-as (org-test-default-backend)
+			 nil nil nil '(:exclude-tags ("noexp")))))))
     ;; Inlinetask with an include tag.
     (should
      (equal
@@ -1387,16 +1499,16 @@ Paragraph[fn:1]"
       (let ((org-inlinetask-min-level 3)
 	    (org-tags-column 0))
 	(org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:"
-	  (org-test-with-backend test
-	    (org-export-as 'test nil nil nil '(:select-tags ("exp"))))))))
+	  (org-export-as (org-test-default-backend)
+			 nil nil nil '(:select-tags ("exp")))))))
     ;; Ignore inlinetask with a TODO keyword and tasks excluded.
     (should
      (equal ""
 	    (let ((org-todo-keywords '((sequence "TODO" "DONE")))
 		  (org-inlinetask-min-level 3))
 	      (org-test-with-temp-text "*** TODO Inline"
-		(org-test-with-backend test
-		  (org-export-as 'test nil nil nil '(:with-tasks nil)))))))))
+		(org-export-as (org-test-default-backend)
+			       nil nil nil '(:with-tasks nil))))))))
 
 
 \f
@@ -2492,41 +2604,40 @@ Another text. (ref:text)
   "Test `inner-template' translator specifications."
   (should
    (equal "Success!"
-	  (let (org-export-registered-backends)
-	    (org-export-define-backend 'test
+	  (org-test-with-temp-text "* Headline"
+	    (org-export-as
+	     (org-export-create-backend
+	      :transcoders
 	      '((inner-template . (lambda (contents info) "Success!"))
-		(headline . (lambda (h c i) "Headline"))))
-	    (org-test-with-temp-text "* Headline"
-	      (org-export-as 'test)))))
+		(headline . (lambda (h c i) "Headline"))))))))
   ;; Inner template is applied even in a "body-only" export.
   (should
    (equal "Success!"
-	  (let (org-export-registered-backends)
-	    (org-export-define-backend 'test
-	      '((inner-template . (lambda (contents info) "Success!"))
-		(headline . (lambda (h c i) "Headline"))))
-	    (org-test-with-temp-text "* Headline"
-	      (org-export-as 'test nil nil 'body-only))))))
+	  (org-test-with-temp-text "* Headline"
+	    (org-export-as
+	     (org-export-create-backend
+	      :transcoders '((inner-template . (lambda (c i) "Success!"))
+			     (headline . (lambda (h c i) "Headline"))))
+	     nil nil 'body-only)))))
 
 (ert-deftest test-org-export/template ()
   "Test `template' translator specifications."
   (should
    (equal "Success!"
-	  (let (org-export-registered-backends)
-	    (org-export-define-backend 'test
-	      '((template . (lambda (contents info) "Success!"))
-	        (headline . (lambda (h c i) "Headline"))))
-	    (org-test-with-temp-text "* Headline"
-	      (org-export-as 'test)))))
+	  (org-test-with-temp-text "* Headline"
+	    (org-export-as
+	     (org-export-create-backend
+	      :transcoders '((template . (lambda (contents info) "Success!"))
+			     (headline . (lambda (h c i) "Headline"))))))))
   ;; Template is not applied in a "body-only" export.
   (should-not
    (equal "Success!"
-	  (let (org-export-registered-backends)
-	    (org-export-define-backend 'test
-	      '((template . (lambda (contents info) "Success!"))
-		(headline . (lambda (h c i) "Headline"))))
-	    (org-test-with-temp-text "* Headline"
-	      (org-export-as 'test nil nil 'body-only))))))
+	  (org-test-with-temp-text "* Headline"
+	    (org-export-as
+	     (org-export-create-backend
+	      :transcoders '((template . (lambda (contents info) "Success!"))
+			     (headline . (lambda (h c i) "Headline"))))
+	     nil nil 'body-only)))))
 
 
 \f
-- 
1.8.3.2


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Export-back-ends-Apply-changes-to-back-end-structure.patch --]
[-- Type: text/x-diff, Size: 9078 bytes --]

From 5c3b1765d219fc55edac393460128b9cd8d0d013 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <n.goaziou@gmail.com>
Date: Mon, 24 Jun 2013 20:55:24 +0200
Subject: [PATCH 2/2] Export back-ends: Apply changes to back-end structure

* lisp/ox-html.el (org-html--format-toc-headline): Make use of
  anonymous back-ends.
* lisp/ox-odt.el (org-odt-footnote-reference): Make use of anonymous
  back-ends.
(org-odt-format-label, org-odt-toc, org-odt-format-headline--wrap):
  Use `org-export-with-backend' instead of
  `org-export-with-translations'.
* contrib/lisp/ox-freemind.el (org-freemind--build-node-contents): Use
  `org-export-with-backend' instead of `org-export-with-translations'.
---
 contrib/lisp/ox-freemind.el | 11 +++---
 lisp/ox-html.el             | 19 ++++++-----
 lisp/ox-odt.el              | 83 +++++++++++++++++++++++----------------------
 3 files changed, 57 insertions(+), 56 deletions(-)

diff --git a/contrib/lisp/ox-freemind.el b/contrib/lisp/ox-freemind.el
index 4e90eff..d31c65f 100644
--- a/contrib/lisp/ox-freemind.el
+++ b/contrib/lisp/ox-freemind.el
@@ -316,12 +316,11 @@ will result in following node:
 	 (element-contents (org-element-contents element))
 	 (section (assoc 'section element-contents))
 	 (section-contents
-	  (let* ((translations
-		  (nconc (list (cons 'section
-				     (lambda (section contents info)
-				       contents)))
-			 (plist-get info :translate-alist))))
-	    (org-export-data-with-translations section translations info)))
+	  (let ((backend (org-export-create-backend
+			  :parent (org-export-backend-name
+				   (plist-get info :back-end))
+			  :translations '(section . (lambda (e c i) c)))))
+	    (org-export-data-with-backend section backend info)))
 	 (itemized-contents-p (let ((first-child-headline
 				     (org-element-map element-contents
 					 'headline 'identity info t)))
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index 9ce73c4..0c997b4 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -1983,16 +1983,17 @@ INFO is a plist used as a communication channel."
 					   headline-number "-"))))
 	    ;; Body.
 	    (concat section-number
-		    (org-export-data-with-translations
+		    (org-export-data-with-backend
 		     (org-export-get-alt-title headline info)
-		     ;; Ignore any footnote-reference, link,
-		     ;; radio-target and target in table of contents.
-		     (append
-		      '((footnote-reference . ignore)
-			(link . (lambda (link desc i) desc))
-			(radio-target . (lambda (radio desc i) desc))
-			(target . ignore))
-		      (org-export-backend-translate-table 'html))
+		     ;; Create an anonymous back-end that will ignore
+		     ;; any footnote-reference, link, radio-target and
+		     ;; target in table of contents.
+		     (org-export-create-backend
+		      :parent 'html
+		      :transcoders '((footnote-reference . ignore)
+				     (link . (lambda (object c i) c))
+				     (radio-target . (lambda (object c i) c))
+				     (target . ignore)))
 		     info)
 		    (and tags "&#xa0;&#xa0;&#xa0;") (org-html--tags tags)))))
 
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index 1cccdc6..abf88cd 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -1152,20 +1152,19 @@ See `org-odt--build-date-styles' for implementation details."
   (let* ((title (org-export-translate "Table of Contents" :utf-8 info))
 	 (headlines (org-export-collect-headlines
 		     info (and (wholenump depth) depth)))
-	 (translations (nconc (mapcar
-			       (lambda (type)
-				 (cons type (lambda (data contents info)
-					      contents)))
-			       (list 'radio-target))
-			      (plist-get info :translate-alist))))
+	 (backend (org-export-create-backend
+		   :parent (org-export-backend-name
+			    (plist-get info :back-end))
+		   :transcoders (mapcar
+				 (lambda (type) (cons type (lambda (d c i) c)))
+				 (list 'radio-target)))))
     (when headlines
       (concat
        (org-odt-begin-toc title depth)
        (mapconcat
 	(lambda (headline)
 	  (let* ((entry (org-odt-format-headline--wrap
-			 headline translations info
-			 'org-odt-format-toc-headline))
+			 headline backend info 'org-odt-format-toc-headline))
 		 (level (org-export-get-relative-level headline info))
 		 (style (format "Contents_20_%d" level)))
 	    (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
@@ -1731,18 +1730,22 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
 	(t
 	 (let* ((raw (org-export-get-footnote-definition
 		      footnote-reference info))
-		(translations
-		 (cons (cons 'paragraph
-			     (lambda (p c i)
-			       (org-odt--format-paragraph
-				p c "Footnote" "OrgFootnoteCenter"
-				"OrgFootnoteQuotations")))
-		       (org-export-backend-translate-table 'odt)))
-		(def (let ((def (org-trim (org-export-data-with-translations
-					   raw translations info))))
-		       (if (eq (org-element-type raw) 'org-data) def
-			 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
-				 "Footnote" def)))))
+		(def
+		 (let ((def (org-trim
+			     (org-export-data-with-backend
+			      raw
+			      (org-export-create-backend
+			       :parent 'odt
+			       :transcoders
+			       '((paragraph . (lambda (p c i)
+						(org-odt--format-paragraph
+						 p c "Footnote"
+						 "OrgFootnoteCenter"
+						 "OrgFootnoteQuotations")))))
+			      info))))
+		   (if (eq (org-element-type raw) 'org-data) def
+		     (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
+			     "Footnote" def)))))
 	   (funcall --format-footnote-definition n def))))))))
 
 
@@ -1775,13 +1778,12 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
 			    "<text:span text:style-name=\"%s\">%s</text:span>"
 			    "OrgTag" tag)) tags " : "))))))
 
-(defun org-odt-format-headline--wrap (headline translations info
-						 &optional format-function
-						 &rest extra-keys)
-  "Transcode a HEADLINE element from Org to ODT.
-CONTENTS holds the contents of the headline.  INFO is a plist
-holding contextual information."
-  (setq translations (or translations (plist-get info :translate-alist)))
+(defun org-odt-format-headline--wrap (headline backend info
+					       &optional format-function
+					       &rest extra-keys)
+  "Transcode a HEADLINE element using BACKEND.
+INFO is a plist holding contextual information."
+  (setq backend (or backend (plist-get info :back-end)))
   (let* ((level (+ (org-export-get-relative-level headline info)))
 	 (headline-number (org-export-get-headline-number headline info))
 	 (section-number (and (org-export-numbered-headline-p headline info)
@@ -1789,13 +1791,13 @@ holding contextual information."
 					 headline-number ".")))
 	 (todo (and (plist-get info :with-todo-keywords)
 		    (let ((todo (org-element-property :todo-keyword headline)))
-		      (and todo (org-export-data-with-translations
-				 todo translations info)))))
+		      (and todo
+			   (org-export-data-with-backend todo backend info)))))
 	 (todo-type (and todo (org-element-property :todo-type headline)))
 	 (priority (and (plist-get info :with-priority)
 			(org-element-property :priority headline)))
-	 (text (org-export-data-with-translations
-		(org-element-property :title headline) translations info))
+	 (text (org-export-data-with-backend
+		(org-element-property :title headline) backend info))
 	 (tags (and (plist-get info :with-tags)
 		    (org-export-get-tags headline info)))
 	 (headline-label (concat "sec-" (mapconcat 'number-to-string
@@ -1805,7 +1807,7 @@ holding contextual information."
 			   ((functionp org-odt-format-headline-function)
 			    (function*
 			     (lambda (todo todo-type priority text tags
-					   &allow-other-keys)
+				      &allow-other-keys)
 			       (funcall org-odt-format-headline-function
 					todo todo-type priority text tags))))
 			   (t 'org-odt-format-headline))))
@@ -1934,7 +1936,7 @@ holding contextual information."
     (let ((format-function
 	   (function*
 	    (lambda (todo todo-type priority text tags
-			  &key contents &allow-other-keys)
+		     &key contents &allow-other-keys)
 	      (funcall org-odt-format-inlinetask-function
 		       todo todo-type priority text tags contents)))))
       (org-odt-format-headline--wrap
@@ -2149,15 +2151,14 @@ SHORT-CAPTION are strings."
 	 ;; will do.
 	 (short-caption
 	  (let ((short-caption (or short-caption caption))
-		(translations (nconc (mapcar
-				      (lambda (type)
-					(cons type (lambda (data contents info)
-						     contents)))
-				      org-element-all-objects)
-				     (plist-get info :translate-alist))))
+		(backend (org-export-create-backend
+			  :parent (org-export-backend-name
+				   (plist-get info :back-end))
+			  :transcoders
+			  (mapcar (lambda (type) (cons type (lambda (o c i) c)))
+				  org-element-all-objects))))
 	    (when short-caption
-	      (org-export-data-with-translations short-caption
-						 translations info)))))
+	      (org-export-data-with-backend short-caption backend info)))))
     (when (or label caption)
       (let* ((default-category
 	       (case (org-element-type element)
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-03 20:57 [RFC] Changing internal representation of back-ends to defstructs Nicolas Goaziou
@ 2013-07-03 21:48 ` Carsten Dominik
  2013-07-04 12:39   ` Nicolas Goaziou
  0 siblings, 1 reply; 15+ messages in thread
From: Carsten Dominik @ 2013-07-03 21:48 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List

Hi Nicolas,

this does look like a good change - and hopefully few enough external backends
have been written for this patch to not pose a problem.

- Carsten

On 3.7.2013, at 22:57, Nicolas Goaziou <n.goaziou@gmail.com> wrote:

> Hello,
> 
> Currently, a back-end is a symbol which may point to an entry in
> `org-export-registered-backends' variable. Therefore a back-end must be
> registered (with a unique name) before one can call it. Or, to put it
> differently, it is impossible to use an anonymous and temporary
> back-end.
> 
> This is not satisfying for developers, as there are situations when you
> need to use a one-shot back-end but don't want to clutter registered
> back-ends list. You can of course let-bind
> `org-export-registered-backends' to something else, but it leads to
> clunky code. The process should be abstracted a bit more.
> 
> The following (long) patches address this by defining back-ends as
> structures (see `defstruct'), possibly anonymous and by separating
> creation from registration process. It allows to quickly create and use
> temporary back-ends. In the example below, we quickly export a string
> using a temporary back-end:
> 
>  (org-export-string-as
>   "* H1\n** H2\nSome string"
>   (org-export-create-backend
>    :transcoders
>    '((headline . (lambda (h contents i)
>                    (let ((m (make-string (org-export-get-relative-level h i) ?=)))
>                      (concat m " " (org-element-property :raw-value h) " " m "\n"
>                              contents))))
>      ;; Contents only.
>      (section . (lambda (e c i) c))
>      (paragraph . (lambda (e c i) c)))))
> 
> It is also possible to create a temporary derived back-end. The
> following export will use registered `latex' back-end, excepted for
> `bold' type objects.
> 
>  (org-export-string-as
>     "Some *bold* /string/"
>     (org-export-create-backend
>      :parent 'latex
>      :transcoders '((italic . (lambda (o c i) (format "\\texit{%s}" c)))))
>     'body-only)
> 
> Besides `org-export-create-backend', tools provided are:
> - `org-export-get-backend'
> - `org-export-register-backend'
> - `org-export-get-all-transcoders' (handles inheritance)
> - `org-export-get-all-options' (handles inheritance)
> - `org-export-get-all-filters' (handles inheritance)
> 
> At a higher level, `org-export-define-backend' and
> `org-export-define-derived-backend' do not change (they are equivalent
> to create and register in a row). So this change only matters for
> back-end developers who used advanced features like
> `org-export-with-translations' (which should now be
> `org-export-with-backend' coupled with an anonymous back-end).
> 
> Also, it leads to a cleaner implementation as it removes the confusion
> between a back-end and its name.
> 
> The next step after applying this patch will be to make
> orgtbl-to-BACKEND functions use anonymous functions in order to
> support :splice property, which is tedious with the new export
> framework.
> 
> Feedback welcome.
> 
> 
> Regards,
> 
> -- 
> Nicolas Goaziou
> From 35bf951a1cd4c455f01863e128a899d36e76a76c Mon Sep 17 00:00:00 2001
> From: Nicolas Goaziou <n.goaziou@gmail.com>
> Date: Mon, 24 Jun 2013 20:52:10 +0200
> Subject: [PATCH 1/2] ox: Change back-ends internal representation to
> structures
> 
> * lisp/ox.el (org-export--registered-backends): Renamed from
>  `org-export-registered-backends'.
> (org-export-invisible-backends): Removed variable.
> (org-export-get-backend, org-export-get-all-transcoders
> org-export-get-all-options, org-export-get-all-filters): New
> functions.  It replaces `org-export-backend-translate-table'.
> (org-export-barf-if-invalid-backend, org-export-derived-backend-p,
> org-export-define-backend, org-export-define-derived-backend):
> Rewrite functions using new representation.
> (org-export-backend-translate-table): Remove function.
> (org-export-get-environment): Use new function.
> (org-export--get-subtree-options, org-export--parse-option-keyword,
> org-export--get-inbuffer-options, org-export--get-global-options,
> org-export-to-buffer org-export-to-file, org-export-string-as
> org-export-replace-region-by): Update docstring.
> (org-export-data-with-translations): Remove function.  Use
> `org-export-data-with-backend' with a temporary back-end instead.
> (org-export-data-with-backend, org-export-as): Reflect new definition
> for back-ends.
> (org-export--dispatch-action, org-export--dispatch-ui): Reflect new
> definition for back-ends and variable removal.  Refactoring.
> (org-export-filter-apply-functions): Call functions with
> current back-end's name, not full back-end.
> * lisp/org.el (org-export-backends, org-create-formula--latex-header):
> Use new structure and variables.
> * testing/lisp/test-ox.el: Update tests.
> 
> This patch separates back-end definition from its registration.  Thus,
> it allows to use anonymous or unregistered back-ends.
> ---
> lisp/org.el             |  58 ++--
> lisp/ox.el              | 527 +++++++++++++++++---------------
> testing/lisp/test-ox.el | 781 +++++++++++++++++++++++++++---------------------
> 3 files changed, 764 insertions(+), 602 deletions(-)
> 
> diff --git a/lisp/org.el b/lisp/org.el
> index 6233972..89cc328 100644
> --- a/lisp/org.el
> +++ b/lisp/org.el
> @@ -436,8 +436,9 @@ For export specific modules, see also `org-export-backends'."
> 	(const :tag "C  wl:                Links to Wanderlust folders/messages" org-wl)
> 	(repeat :tag "External packages" :inline t (symbol :tag "Package"))))
> 
> -(defvar org-export-registered-backends)	; From ox.el
> +(defvar org-export--registered-backends) ; From ox.el.
> (declare-function org-export-derived-backend-p "ox" (backend &rest backends))
> +(declare-function org-export-backend-name "ox" (backend))
> (defcustom org-export-backends '(ascii html icalendar latex)
>   "List of export back-ends that should be always available.
> 
> @@ -451,30 +452,29 @@ needed.
> 
> This variable needs to be set before org.el is loaded.  If you
> need to make a change while Emacs is running, use the customize
> -interface or run the following code, where VALUE stands for the
> -new value of the variable, after updating it:
> +interface or run the following code, where VAL stands for the new
> +value of the variable, after updating it:
> 
>   \(progn
> -    \(setq org-export-registered-backends
> +    \(setq org-export--registered-backends
>           \(org-remove-if-not
>            \(lambda (backend)
> -             \(or (memq backend val)
> -                 \(catch 'parentp
> -                   \(mapc
> -                    \(lambda (b)
> -                      \(and (org-export-derived-backend-p b (car backend))
> -                           \(throw 'parentp t)))
> -                    val)
> -                   nil)))
> -           org-export-registered-backends))
> -    \(let ((new-list (mapcar 'car org-export-registered-backends)))
> +             \(let ((name (org-export-backend-name backend)))
> +               \(or (memq name val)
> +                   \(catch 'parentp
> +                     \(dolist (b val)
> +                       \(and (org-export-derived-backend-p b name)
> +                            \(throw 'parentp t)))))))
> +           org-export--registered-backends))
> +    \(let ((new-list (mapcar 'org-export-backend-name
> +                            org-export--registered-backends)))
>       \(dolist (backend val)
>         \(cond
>          \((not (load (format \"ox-%s\" backend) t t))
>           \(message \"Problems while trying to load export back-end `%s'\"
>                    backend))
>          \((not (memq backend new-list)) (push backend new-list))))
> -      \(set-default var new-list)))
> +      \(set-default 'org-export-backends new-list)))
> 
> Adding a back-end to this list will also pull the back-end it
> depends on, if any."
> @@ -488,21 +488,20 @@ depends on, if any."
> 	   ;; Any back-end not required anymore (not present in VAL and not
> 	   ;; a parent of any back-end in the new value) is removed from the
> 	   ;; list of registered back-ends.
> -	   (setq org-export-registered-backends
> +	   (setq org-export--registered-backends
> 		 (org-remove-if-not
> 		  (lambda (backend)
> -		    (or (memq backend val)
> -			(catch 'parentp
> -			  (mapc
> -			   (lambda (b)
> -			     (and (org-export-derived-backend-p b (car backend))
> -				  (throw 'parentp t)))
> -			   val)
> -			  nil)))
> -		  org-export-registered-backends))
> +		    (let ((name (org-export-backend-name backend)))
> +		      (or (memq name val)
> +			  (catch 'parentp
> +			    (dolist (b val)
> +			      (and (org-export-derived-backend-p b name)
> +				   (throw 'parentp t)))))))
> +		  org-export--registered-backends))
> 	   ;; Now build NEW-LIST of both new back-ends and required
> 	   ;; parents.
> -	   (let ((new-list (mapcar 'car org-export-registered-backends)))
> +	   (let ((new-list (mapcar 'org-export-backend-name
> +				   org-export--registered-backends)))
> 	     (dolist (backend val)
> 	       (cond
> 		((not (load (format "ox-%s" backend) t t))
> @@ -18494,14 +18493,17 @@ share a good deal of logic."
>          "Invalid value of `org-latex-create-formula-image-program'")))
>    string tofile options buffer))
> 
> +(declare-function org-export-get-backend "ox" (name))
> (declare-function org-export--get-global-options "ox" (&optional backend))
> (declare-function org-export--get-inbuffer-options "ox" (&optional backend))
> (declare-function org-latex-guess-inputenc "ox-latex" (header))
> (declare-function org-latex-guess-babel-language "ox-latex" (header info))
> (defun org-create-formula--latex-header ()
>   "Return LaTeX header appropriate for previewing a LaTeX snippet."
> -  (let ((info (org-combine-plists (org-export--get-global-options 'latex)
> -				  (org-export--get-inbuffer-options 'latex))))
> +  (let ((info (org-combine-plists (org-export--get-global-options
> +				   (org-export-get-backend 'latex))
> +				  (org-export--get-inbuffer-options
> +				   (org-export-get-backend 'latex)))))
>     (org-latex-guess-babel-language
>      (org-latex-guess-inputenc
>       (org-splice-latex-header
> diff --git a/lisp/ox.el b/lisp/ox.el
> index 92ad356..71435b7 100644
> --- a/lisp/ox.el
> +++ b/lisp/ox.el
> @@ -47,15 +47,10 @@
> ;; The core function is `org-export-as'.  It returns the transcoded
> ;; buffer as a string.
> ;;
> -;; An export back-end is defined with `org-export-define-backend',
> -;; which defines one mandatory information: his translation table.
> -;; Its value is an alist whose keys are elements and objects types and
> -;; values translator functions.  See function's docstring for more
> -;; information about translators.
> -;;
> -;; Optionally, `org-export-define-backend' can also support specific
> -;; buffer keywords, OPTION keyword's items and filters.  Also refer to
> -;; function documentation for more information.
> +;; An export back-end is defined with `org-export-define-backend'.
> +;; This function can also support specific buffer keywords, OPTION
> +;; keyword's items and filters.  Refer to function's documentation for
> +;; more information.
> ;;
> ;; If the new back-end shares most properties with another one,
> ;; `org-export-define-derived-backend' can be used to simplify the
> @@ -280,14 +275,8 @@ containing the back-end used, as a symbol, and either a process
> or the time at which it finished.  It is used to build the menu
> from `org-export-stack'.")
> 
> -(defvar org-export-registered-backends nil
> +(defvar org-export--registered-backends nil
>   "List of backends currently available in the exporter.
> -
> -A backend is stored as a list where CAR is its name, as a symbol,
> -and CDR is a plist with the following properties:
> -`:filters-alist', `:menu-entry', `:options-alist' and
> -`:translate-alist'.
> -
> This variable is set with `org-export-define-backend' and
> `org-export-define-derived-backend' functions.")
> 
> @@ -830,20 +819,6 @@ process faster and the export more portable."
>   :package-version '(Org . "8.0")
>   :type '(file :must-match t))
> 
> -(defcustom org-export-invisible-backends nil
> -  "List of back-ends that shouldn't appear in the dispatcher.
> -
> -Any back-end belonging to this list or derived from a back-end
> -belonging to it will not appear in the dispatcher menu.
> -
> -Indeed, Org may require some export back-ends without notice.  If
> -these modules are never to be used interactively, adding them
> -here will avoid cluttering the dispatcher menu."
> -  :group 'org-export-general
> -  :version "24.4"
> -  :package-version '(Org . "8.0")
> -  :type '(repeat (symbol :tag "Back-End")))
> -
> (defcustom org-export-dispatch-use-expert-ui nil
>   "Non-nil means using a non-intrusive `org-export-dispatch'.
> In that case, no help buffer is displayed.  Though, an indicator
> @@ -863,25 +838,147 @@ mode."
> 
> ;;; Defining Back-ends
> ;;
> -;; `org-export-define-backend' is the standard way to define an export
> -;; back-end.  It allows to specify translators, filters, buffer
> -;; options and a menu entry.  If the new back-end shares translators
> -;; with another back-end, `org-export-define-derived-backend' may be
> -;; used instead.
> +;; An export back-end is a structure with `org-export-backend' type
> +;; and `name', `parent', `transcoders', `options', `filters', `blocks'
> +;; and `menu' slots.
> +;;
> +;; At the lowest level, a back-end is created with
> +;; `org-export-create-backend' function.
> +;;
> +;; A named back-end can be registered with
> +;; `org-export-register-backend' function.  A registered back-end can
> +;; later be referred to by its name, with `org-export-get-backend'
> +;; function.  Also, such a back-end can become the parent of a derived
> +;; back-end from which slot values will be inherited by default.
> +;; `org-export-derived-backend-p' can check if a given back-end is
> +;; derived from a list of back-end names.
> +;;
> +;; `org-export-get-all-transcoders', `org-export-get-all-options' and
> +;; `org-export-get-all-filters' return the full alist of transcoders,
> +;; options and filters, including those inherited from ancestors.
> ;;
> -;; Internally, a back-end is stored as a list, of which CAR is the
> -;; name of the back-end, as a symbol, and CDR a plist.  Accessors to
> -;; properties of a given back-end are: `org-export-backend-filters',
> -;; `org-export-backend-menu', `org-export-backend-options' and
> -;; `org-export-backend-translate-table'.
> +;; At a higher level, `org-export-define-backend' is the standard way
> +;; to define an export back-end.  If the new back-end is similar to
> +;; a registered back-end, `org-export-define-derived-backend' may be
> +;; used instead.
> ;;
> ;; Eventually `org-export-barf-if-invalid-backend' returns an error
> ;; when a given back-end hasn't been registered yet.
> 
> -(defun org-export-define-backend (backend translators &rest body)
> +(defstruct (org-export-backend (:constructor org-export-create-backend)
> +			       (:copier nil))
> +  name parent transcoders options filters blocks menu)
> +
> +(defun org-export-get-backend (name)
> +  "Return export back-end named after NAME.
> +NAME is a symbol.  Return nil if no such back-end is found."
> +  (catch 'found
> +    (dolist (b org-export--registered-backends)
> +      (when (eq (org-export-backend-name b) name)
> +	(throw 'found b)))))
> +
> +(defun org-export-register-backend (backend)
> +  "Register BACKEND as a known export back-end.
> +BACKEND is a structure with `org-export-backend' type."
> +  ;; Refuse to register an unnamed back-end.
> +  (unless (org-export-backend-name backend)
> +    (error "Cannot register a unnamed export back-end"))
> +  ;; Refuse to register a back-end with an unknown parent.
> +  (let ((parent (org-export-backend-parent backend)))
> +    (when (and parent (not (org-export-get-backend parent)))
> +      (error "Cannot use unknown \"%s\" back-end as a parent" parent)))
> +  ;; Register dedicated export blocks in the parser.
> +  (dolist (name (org-export-backend-blocks backend))
> +    (add-to-list 'org-element-block-name-alist
> +		 (cons name 'org-element-export-block-parser)))
> +  ;; If a back-end with the same name as BACKEND is already
> +  ;; registered, replace it with BACKEND.  Otherwise, simply add
> +  ;; BACKEND to the list of registered back-ends.
> +  (let ((old (org-export-get-backend (org-export-backend-name backend))))
> +    (if old (setcar (memq old org-export--registered-backends) backend)
> +      (push backend org-export--registered-backends))))
> +
> +(defun org-export-barf-if-invalid-backend (backend)
> +  "Signal an error if BACKEND isn't defined."
> +  (unless (org-export-backend-p backend)
> +    (error "Unknown \"%s\" back-end: Aborting export" backend)))
> +
> +(defun org-export-derived-backend-p (backend &rest backends)
> +  "Non-nil if BACKEND is derived from one of BACKENDS.
> +BACKEND is an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.  BACKENDS is constituted of symbols."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (when backend
> +    (catch 'exit
> +      (while (org-export-backend-parent backend)
> +	(when (memq (org-export-backend-name backend) backends)
> +	  (throw 'exit t))
> +	(setq backend
> +	      (org-export-get-backend (org-export-backend-parent backend))))
> +      (memq (org-export-backend-name backend) backends))))
> +
> +(defun org-export-get-all-transcoders (backend)
> +  "Return full translation table for BACKEND.
> +
> +BACKEND is an export back-end, as return by, e.g,,
> +`org-export-create-backend'.  Return value is an alist where
> +keys are element or object types, as symbols, and values are
> +transcoders.
> +
> +Unlike to `org-export-backend-transcoders', this function
> +also returns transcoders inherited from parent back-ends,
> +if any."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (when backend
> +    (let ((transcoders (org-export-backend-transcoders backend))
> +	  parent)
> +      (while (setq parent (org-export-backend-parent backend))
> +	(setq backend (org-export-get-backend parent))
> +	(setq transcoders
> +	      (append transcoders (org-export-backend-transcoders backend))))
> +      transcoders)))
> +
> +(defun org-export-get-all-options (backend)
> +  "Return export options for BACKEND.
> +
> +BACKEND is an export back-end, as return by, e.g,,
> +`org-export-create-backend'.  See `org-export-options-alist'
> +for the shape of the return value.
> +
> +Unlike to `org-export-backend-options', this function also
> +returns options inherited from parent back-ends, if any."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (when backend
> +    (let ((options (org-export-backend-options backend))
> +	  parent)
> +      (while (setq parent (org-export-backend-parent backend))
> +	(setq backend (org-export-get-backend parent))
> +	(setq options (append options (org-export-backend-options backend))))
> +      options)))
> +
> +(defun org-export-get-all-filters (backend)
> +  "Return complete list of filters for BACKEND.
> +
> +BACKEND is an export back-end, as return by, e.g,,
> +`org-export-create-backend'.  Return value is an alist where
> +keys are symbols and values lists of functions.
> +
> +Unlike to `org-export-backend-filters', this function also
> +returns filters inherited from parent back-ends, if any."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (when backend
> +    (let ((filters (org-export-backend-filters backend))
> +	  parent)
> +      (while (setq parent (org-export-backend-parent backend))
> +	(setq backend (org-export-get-backend parent))
> +	(setq filters (append filters (org-export-backend-filters backend))))
> +      filters)))
> +
> +(defun org-export-define-backend (backend transcoders &rest body)
>   "Define a new back-end BACKEND.
> 
> -TRANSLATORS is an alist between object or element types and
> +TRANSCODERS is an alist between object or element types and
> functions handling them.
> 
> These functions should return a string without any trailing
> @@ -997,32 +1094,23 @@ keywords are understood:
>     `org-export-options-alist' for more information about
>     structure of the values."
>   (declare (indent 1))
> -  (let (export-block filters menu-entry options contents)
> +  (let (blocks filters menu-entry options contents)
>     (while (keywordp (car body))
>       (case (pop body)
>         (:export-block (let ((names (pop body)))
> -			 (setq export-block
> -			       (if (consp names) (mapcar 'upcase names)
> -				 (list (upcase names))))))
> +			 (setq blocks (if (consp names) (mapcar 'upcase names)
> +					(list (upcase names))))))
> 	(:filters-alist (setq filters (pop body)))
> 	(:menu-entry (setq menu-entry (pop body)))
>         (:options-alist (setq options (pop body)))
>         (t (pop body))))
> -    (setq contents (append (list :translate-alist translators)
> -			   (and filters (list :filters-alist filters))
> -			   (and options (list :options-alist options))
> -			   (and menu-entry (list :menu-entry menu-entry))))
> -    ;; Register back-end.
> -    (let ((registeredp (assq backend org-export-registered-backends)))
> -      (if registeredp (setcdr registeredp contents)
> -	(push (cons backend contents) org-export-registered-backends)))
> -    ;; Tell parser to not parse EXPORT-BLOCK blocks.
> -    (when export-block
> -      (mapc
> -       (lambda (name)
> -	 (add-to-list 'org-element-block-name-alist
> -		      `(,name . org-element-export-block-parser)))
> -       export-block))))
> +    (org-export-register-backend
> +     (org-export-create-backend :name backend
> +				:transcoders transcoders
> +				:options options
> +				:filters filters
> +				:blocks blocks
> +				:menu menu-entry))))
> 
> (defun org-export-define-derived-backend (child parent &rest body)
>   "Create a new back-end as a variant of an existing one.
> @@ -1077,75 +1165,25 @@ The back-end could then be called with, for example:
> 
>   \(org-export-to-buffer 'my-latex \"*Test my-latex*\")"
>   (declare (indent 2))
> -  (let (export-block filters menu-entry options translators contents)
> +  (let (blocks filters menu-entry options transcoders contents)
>     (while (keywordp (car body))
>       (case (pop body)
> 	(:export-block (let ((names (pop body)))
> -			 (setq export-block
> -			       (if (consp names) (mapcar 'upcase names)
> -				 (list (upcase names))))))
> +			 (setq blocks (if (consp names) (mapcar 'upcase names)
> +					(list (upcase names))))))
>         (:filters-alist (setq filters (pop body)))
> 	(:menu-entry (setq menu-entry (pop body)))
>         (:options-alist (setq options (pop body)))
> -        (:translate-alist (setq translators (pop body)))
> +        (:translate-alist (setq transcoders (pop body)))
>         (t (pop body))))
> -    (setq contents (append
> -		    (list :parent parent)
> -		    (let ((p-table (org-export-backend-translate-table parent)))
> -		      (list :translate-alist (append translators p-table)))
> -		    (let ((p-filters (org-export-backend-filters parent)))
> -		      (list :filters-alist (append filters p-filters)))
> -		    (let ((p-options (org-export-backend-options parent)))
> -		      (list :options-alist (append options p-options)))
> -		    (and menu-entry (list :menu-entry menu-entry))))
> -    (org-export-barf-if-invalid-backend parent)
> -    ;; Register back-end.
> -    (let ((registeredp (assq child org-export-registered-backends)))
> -      (if registeredp (setcdr registeredp contents)
> -	(push (cons child contents) org-export-registered-backends)))
> -    ;; Tell parser to not parse EXPORT-BLOCK blocks.
> -    (when export-block
> -      (mapc
> -       (lambda (name)
> -	 (add-to-list 'org-element-block-name-alist
> -		      `(,name . org-element-export-block-parser)))
> -       export-block))))
> -
> -(defun org-export-backend-parent (backend)
> -  "Return back-end from which BACKEND is derived, or nil."
> -  (plist-get (cdr (assq backend org-export-registered-backends)) :parent))
> -
> -(defun org-export-backend-filters (backend)
> -  "Return filters for BACKEND."
> -  (plist-get (cdr (assq backend org-export-registered-backends))
> -	     :filters-alist))
> -
> -(defun org-export-backend-menu (backend)
> -  "Return menu entry for BACKEND."
> -  (plist-get (cdr (assq backend org-export-registered-backends))
> -	     :menu-entry))
> -
> -(defun org-export-backend-options (backend)
> -  "Return export options for BACKEND."
> -  (plist-get (cdr (assq backend org-export-registered-backends))
> -	     :options-alist))
> -
> -(defun org-export-backend-translate-table (backend)
> -  "Return translate table for BACKEND."
> -  (plist-get (cdr (assq backend org-export-registered-backends))
> -	     :translate-alist))
> -
> -(defun org-export-barf-if-invalid-backend (backend)
> -  "Signal an error if BACKEND isn't defined."
> -  (unless (org-export-backend-translate-table backend)
> -    (error "Unknown \"%s\" back-end: Aborting export" backend)))
> -
> -(defun org-export-derived-backend-p (backend &rest backends)
> -  "Non-nil if BACKEND is derived from one of BACKENDS."
> -  (let ((parent backend))
> -    (while (and (not (memq parent backends))
> -		(setq parent (org-export-backend-parent parent))))
> -    parent))
> +    (org-export-register-backend
> +     (org-export-create-backend :name child
> +				:parent parent
> +				:transcoders transcoders
> +				:options options
> +				:filters filters
> +				:blocks blocks
> +				:menu menu-entry))))
> 
> 
> 
> @@ -1448,14 +1486,15 @@ The back-end could then be called with, for example:
> ;;  `org-export--get-subtree-options' and
> ;;  `org-export--get-inbuffer-options'
> ;;
> -;; Also, `org-export--install-letbind-maybe' takes care of the part
> -;; relative to "#+BIND:" keywords.
> +;; Also, `org-export--list-bound-variables' collects bound variables
> +;; along with their value in order to set them as buffer local
> +;; variables later in the process.
> 
> (defun org-export-get-environment (&optional backend subtreep ext-plist)
>   "Collect export options from the current buffer.
> 
> -Optional argument BACKEND is a symbol specifying which back-end
> -specific options to read, if any.
> +Optional argument BACKEND is an export back-end, as returned by
> +`org-export-create-backend'.
> 
> When optional argument SUBTREEP is non-nil, assume the export is
> done against the current sub-tree.
> @@ -1481,8 +1520,7 @@ inferior to file-local settings."
>    (list
>     :back-end
>     backend
> -    :translate-alist
> -    (org-export-backend-translate-table backend)
> +    :translate-alist (org-export-get-all-transcoders backend)
>     :footnote-definition-alist
>     ;; Footnotes definitions must be collected in the original
>     ;; buffer, as there's no insurance that they will still be in
> @@ -1518,11 +1556,12 @@ inferior to file-local settings."
> 
> (defun org-export--parse-option-keyword (options &optional backend)
>   "Parse an OPTIONS line and return values as a plist.
> -Optional argument BACKEND is a symbol specifying which back-end
> +Optional argument BACKEND is an export back-end, as returned by,
> +e.g., `org-export-create-backend'.  It specifies which back-end
> specific items to read, if any."
>   (let* ((all
> 	  ;; Priority is given to back-end specific options.
> -	  (append (and backend (org-export-backend-options backend))
> +	  (append (and backend (org-export-get-all-options backend))
> 		  org-export-options-alist))
> 	 plist)
>     (dolist (option all)
> @@ -1542,7 +1581,8 @@ specific items to read, if any."
> 
> (defun org-export--get-subtree-options (&optional backend)
>   "Get export options in subtree at point.
> -Optional argument BACKEND is a symbol specifying back-end used
> +Optional argument BACKEND is an export back-end, as returned by,
> +e.g., `org-export-create-backend'.  It specifies back-end used
> for export.  Return options as a plist."
>   ;; For each buffer keyword, create a headline property setting the
>   ;; same property in communication channel. The name for the property
> @@ -1594,7 +1634,7 @@ for export.  Return options as a plist."
> 			  (t value)))))))))
> 	;; Look for both general keywords and back-end specific
> 	;; options, with priority given to the latter.
> -	(append (and backend (org-export-backend-options backend))
> +	(append (and backend (org-export-get-all-options backend))
> 		org-export-options-alist)))
>      ;; Return value.
>      plist)))
> @@ -1602,7 +1642,8 @@ for export.  Return options as a plist."
> (defun org-export--get-inbuffer-options (&optional backend)
>   "Return current buffer export options, as a plist.
> 
> -Optional argument BACKEND, when non-nil, is a symbol specifying
> +Optional argument BACKEND, when non-nil, is an export back-end,
> +as returned by, e.g., `org-export-create-backend'.  It specifies
> which back-end specific options should also be read in the
> process.
> 
> @@ -1612,7 +1653,7 @@ Assume buffer is in Org mode.  Narrowing, if any, is ignored."
> 	 (case-fold-search t)
> 	 (options (append
> 		   ;; Priority is given to back-end specific options.
> -		   (and backend (org-export-backend-options backend))
> +		   (and backend (org-export-get-all-options backend))
> 		   org-export-options-alist))
> 	 (regexp (format "^[ \t]*#\\+%s:"
> 			 (regexp-opt (nconc (delq nil (mapcar 'cadr options))
> @@ -1725,12 +1766,13 @@ name."
> 
> (defun org-export--get-global-options (&optional backend)
>   "Return global export options as a plist.
> -Optional argument BACKEND, if non-nil, is a symbol specifying
> +Optional argument BACKEND, if non-nil, is an export back-end, as
> +returned by, e.g., `org-export-create-backend'.  It specifies
> which back-end specific export options should also be read in the
> process."
>   (let (plist
> 	;; Priority is given to back-end specific options.
> -	(all (append (and backend (org-export-backend-options backend))
> +	(all (append (and backend (org-export-get-all-options backend))
> 		     org-export-options-alist)))
>     (dolist (cell all plist)
>       (let ((prop (car cell)))
> @@ -2058,11 +2100,10 @@ a tree with a select tag."
> ;; back-end output.  It takes care of filtering out elements or
> ;; objects according to export options and organizing the output blank
> ;; lines and white space are preserved.  The function memoizes its
> -;; results, so it is cheap to call it within translators.
> +;; results, so it is cheap to call it within transcoders.
> ;;
> ;; It is possible to modify locally the back-end used by
> ;; `org-export-data' or even use a temporary back-end by using
> -;; `org-export-data-with-translations' and
> ;; `org-export-data-with-backend'.
> ;;
> ;; Internally, three functions handle the filtering of objects and
> @@ -2190,24 +2231,6 @@ Return transcoded string."
> 	     results)))
> 	 (plist-get info :exported-data))))))
> 
> -(defun org-export-data-with-translations (data translations info)
> -  "Convert DATA into another format using a given translation table.
> -DATA is an element, an object, a secondary string or a string.
> -TRANSLATIONS is an alist between element or object types and
> -a functions handling them.  See `org-export-define-backend' for
> -more information.  INFO is a plist used as a communication
> -channel."
> -  (org-export-data
> -   data
> -   ;; Set-up a new communication channel with TRANSLATIONS as the
> -   ;; translate table and a new hash table for memoization.
> -   (org-combine-plists
> -    info
> -    (list :translate-alist translations
> -	  ;; Size of the hash table is reduced since this function
> -	  ;; will probably be used on short trees.
> -	  :exported-data (make-hash-table :test 'eq :size 401)))))
> -
> (defun org-export-data-with-backend (data backend info)
>   "Convert DATA into BACKEND format.
> 
> @@ -2217,9 +2240,18 @@ channel.
> 
> Unlike to `org-export-with-backend', this function will
> recursively convert DATA using BACKEND translation table."
> -  (org-export-barf-if-invalid-backend backend)
> -  (org-export-data-with-translations
> -   data (org-export-backend-translate-table backend) info))
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (org-export-data
> +   data
> +   ;; Set-up a new communication channel with translations defined in
> +   ;; BACKEND as the translate table and a new hash table for
> +   ;; memoization.
> +   (org-combine-plists
> +    info
> +    (list :translate-alist (org-export-get-all-transcoders backend)
> +	  ;; Size of the hash table is reduced since this function
> +	  ;; will probably be used on short trees.
> +	  :exported-data (make-hash-table :test 'eq :size 401)))))
> 
> (defun org-export--interpret-p (blob info)
>   "Non-nil if element or object BLOB should be interpreted during export.
> @@ -2713,18 +2745,19 @@ channel, as a plist.  It must return a string or nil.")
>   "Call every function in FILTERS.
> 
> Functions are called with arguments VALUE, current export
> -back-end and INFO.  A function returning a nil value will be
> -skipped.  If it returns the empty string, the process ends and
> +back-end's name and INFO.  A function returning a nil value will
> +be skipped.  If it returns the empty string, the process ends and
> VALUE is ignored.
> 
> Call is done in a LIFO fashion, to be sure that developer
> specified filters, if any, are called first."
>   (catch 'exit
> -    (dolist (filter filters value)
> -      (let ((result (funcall filter value (plist-get info :back-end) info)))
> -	(cond ((not result) value)
> -	      ((equal value "") (throw 'exit nil))
> -	      (t (setq value result)))))))
> +    (let ((backend-name (plist-get info :back-end)))
> +      (dolist (filter filters value)
> +	(let ((result (funcall filter value backend-name info)))
> +	  (cond ((not result) value)
> +		((equal value "") (throw 'exit nil))
> +		(t (setq value result))))))))
> 
> (defun org-export-install-filters (info)
>   "Install filters properties in communication channel.
> @@ -2755,7 +2788,7 @@ Return the updated communication channel."
> 		       plist key
> 		       (if (atom value) (cons value (plist-get plist key))
> 			 (append value (plist-get plist key))))))))
> -	  (org-export-backend-filters (plist-get info :back-end)))
> +	  (org-export-get-all-filters (plist-get info :back-end)))
>     ;; Return new communication channel.
>     (org-combine-plists info plist)))
> 
> @@ -2891,6 +2924,10 @@ The function assumes BUFFER's major mode is `org-mode'."
>   (backend &optional subtreep visible-only body-only ext-plist)
>   "Transcode current Org buffer into BACKEND code.
> 
> +BACKEND is either an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.
> +
> If narrowing is active in the current buffer, only transcode its
> narrowed part.
> 
> @@ -2911,6 +2948,7 @@ with external parameters overriding Org default settings, but
> still inferior to file-local settings.
> 
> Return code as a string."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
>   (org-export-barf-if-invalid-backend backend)
>   (save-excursion
>     (save-restriction
> @@ -2943,8 +2981,9 @@ Return code as a string."
> 	;; created, where include keywords, macros are expanded and
> 	;; code blocks are evaluated.
> 	(org-export-with-buffer-copy
> -	 ;; Run first hook with current back-end as argument.
> -	 (run-hook-with-args 'org-export-before-processing-hook backend)
> +	 ;; Run first hook with current back-end's name as argument.
> +	 (run-hook-with-args 'org-export-before-processing-hook
> +			     (org-export-backend-name backend))
> 	 (org-export-expand-include-keyword)
> 	 ;; Update macro templates since #+INCLUDE keywords might have
> 	 ;; added some new ones.
> @@ -2954,10 +2993,11 @@ Return code as a string."
> 	 ;; Update radio targets since keyword inclusion might have
> 	 ;; added some more.
> 	 (org-update-radio-target-regexp)
> -	 ;; Run last hook with current back-end as argument.
> +	 ;; Run last hook with current back-end's name as argument.
> 	 (goto-char (point-min))
> 	 (save-excursion
> -	   (run-hook-with-args 'org-export-before-parsing-hook backend))
> +	   (run-hook-with-args 'org-export-before-parsing-hook
> +			       (org-export-backend-name backend)))
> 	 ;; Update communication channel with environment.  Also
> 	 ;; install user's and developer's filters.
> 	 (setq info
> @@ -2980,9 +3020,10 @@ Return code as a string."
> 	 ;; Call options filters and update export options.  We do not
> 	 ;; use `org-export-filter-apply-functions' here since the
> 	 ;; arity of such filters is different.
> -	 (dolist (filter (plist-get info :filter-options))
> -	   (let ((result (funcall filter info backend)))
> -	     (when result (setq info result))))
> +	 (let ((backend-name (org-export-backend-name backend)))
> +	   (dolist (filter (plist-get info :filter-options))
> +	     (let ((result (funcall filter info backend-name)))
> +	       (when result (setq info result)))))
> 	 ;; Parse buffer and call parse-tree filter on it.
> 	 (setq tree
> 	       (org-export-filter-apply-functions
> @@ -3018,7 +3059,9 @@ Return code as a string."
>   (backend buffer &optional subtreep visible-only body-only ext-plist)
>   "Call `org-export-as' with output to a specified buffer.
> 
> -BACKEND is the back-end used for transcoding, as a symbol.
> +BACKEND is either an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.
> 
> BUFFER is the output buffer.  If it already exists, it will be
> erased first, otherwise, it will be created.
> @@ -3046,8 +3089,10 @@ to kill ring.  Return buffer."
>   (backend file &optional subtreep visible-only body-only ext-plist)
>   "Call `org-export-as' with output to a specified file.
> 
> -BACKEND is the back-end used for transcoding, as a symbol.  FILE
> -is the name of the output file, as a string.
> +BACKEND is either an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.  FILE is the name of the output file, as
> +a string.
> 
> Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
> EXT-PLIST are similar to those used in `org-export-as', which
> @@ -3074,6 +3119,10 @@ to kill ring.  Return output file's name."
> (defun org-export-string-as (string backend &optional body-only ext-plist)
>   "Transcode STRING into BACKEND code.
> 
> +BACKEND is either an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.
> +
> When optional argument BODY-ONLY is non-nil, only return body
> code, without preamble nor postamble.
> 
> @@ -3089,7 +3138,10 @@ Return code as a string."
> 
> ;;;###autoload
> (defun org-export-replace-region-by (backend)
> -  "Replace the active region by its export to BACKEND."
> +  "Replace the active region by its export to BACKEND.
> +BACKEND is either an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end."
>   (if (not (org-region-active-p))
>       (user-error "No active region to replace")
>     (let* ((beg (region-beginning))
> @@ -3103,10 +3155,10 @@ Return code as a string."
> (defun org-export-insert-default-template (&optional backend subtreep)
>   "Insert all export keywords with default values at beginning of line.
> 
> -BACKEND is a symbol representing the export back-end for which
> -specific export options should be added to the template, or
> -`default' for default template.  When it is nil, the user will be
> -prompted for a category.
> +BACKEND is a symbol referring to the name of a registered export
> +back-end, for which specific export options should be added to
> +the template, or `default' for default template.  When it is nil,
> +the user will be prompted for a category.
> 
> If SUBTREEP is non-nil, export configuration will be set up
> locally for the subtree through node properties."
> @@ -3115,17 +3167,22 @@ locally for the subtree through node properties."
>   (when (and subtreep (org-before-first-heading-p))
>     (user-error "No subtree to set export options for"))
>   (let ((node (and subtreep (save-excursion (org-back-to-heading t) (point))))
> -	(backend (or backend
> -                     (intern
> -                      (org-completing-read
> -                       "Options category: "
> -                       (cons "default"
> -                             (mapcar (lambda (b) (symbol-name (car b)))
> -                                     org-export-registered-backends))))))
> +	(backend
> +	 (or backend
> +	     (intern
> +	      (org-completing-read
> +	       "Options category: "
> +	       (cons "default"
> +		     (mapcar (lambda (b)
> +			       (symbol-name (org-export-backend-name b)))
> +			     org-export--registered-backends))))))
> 	options keywords)
>     ;; Populate OPTIONS and KEYWORDS.
> -    (dolist (entry (if (eq backend 'default) org-export-options-alist
> -		     (org-export-backend-options backend)))
> +    (dolist (entry (cond ((eq backend 'default) org-export-options-alist)
> +			 ((org-export-backend-p backend)
> +			  (org-export-get-all-options backend))
> +			 (t (org-export-get-all-options
> +			     (org-export-backend-name backend)))))
>       (let ((keyword (nth 1 entry))
>             (option (nth 2 entry)))
>         (cond
> @@ -3502,16 +3559,20 @@ Caption lines are separated by a white space."
> ;; back-end, it may be used as a fall-back function once all specific
> ;; cases have been treated.
> 
> -(defun org-export-with-backend (back-end data &optional contents info)
> -  "Call a transcoder from BACK-END on DATA.
> -CONTENTS, when non-nil, is the transcoded contents of DATA
> -element, as a string.  INFO, when non-nil, is the communication
> -channel used for export, as a plist.."
> -  (org-export-barf-if-invalid-backend back-end)
> +(defun org-export-with-backend (backend data &optional contents info)
> +  "Call a transcoder from BACKEND on DATA.
> +BACKEND is an export back-end, as returned by, e.g.,
> +`org-export-create-backend', or a symbol referring to
> +a registered back-end.  DATA is an Org element, object, secondary
> +string or string.  CONTENTS, when non-nil, is the transcoded
> +contents of DATA element, as a string.  INFO, when non-nil, is
> +the communication channel used for export, as a plist."
> +  (when (symbolp backend) (setq backend (org-export-get-backend backend)))
> +  (org-export-barf-if-invalid-backend backend)
>   (let ((type (org-element-type data)))
>     (if (memq type '(nil org-data)) (error "No foreign transcoder available")
>       (let ((transcoder
> -	     (cdr (assq type (org-export-backend-translate-table back-end)))))
> +	     (cdr (assq type (org-export-get-all-transcoders backend)))))
> 	(if (functionp transcoder) (funcall transcoder data contents info)
> 	  (error "No foreign transcoder available"))))))
> 
> @@ -5849,43 +5910,31 @@ back to standard interface."
> 	  (lambda (value)
> 	    ;; Fontify VALUE string.
> 	    (org-propertize value 'face 'font-lock-variable-name-face)))
> -	 ;; Prepare menu entries by extracting them from
> -	 ;; `org-export-registered-backends', and sorting them by
> -	 ;; access key and by ordinal, if any.
> -	 (backends
> -	  (sort
> -	   (sort
> -	    (delq nil
> -		  (mapcar
> -		   (lambda (b)
> -		     (let ((name (car b)))
> -		       (catch 'ignored
> -			 ;; Ignore any back-end belonging to
> -			 ;; `org-export-invisible-backends' or derived
> -			 ;; from one of them.
> -			 (dolist (ignored org-export-invisible-backends)
> -			   (when (org-export-derived-backend-p name ignored)
> -			     (throw 'ignored nil)))
> -			 (org-export-backend-menu name))))
> -		   org-export-registered-backends))
> -	    (lambda (a b)
> -	      (let ((key-a (nth 1 a))
> -		    (key-b (nth 1 b)))
> -		(cond ((and (numberp key-a) (numberp key-b))
> -		       (< key-a key-b))
> -		      ((numberp key-b) t)))))
> -	   (lambda (a b) (< (car a) (car b)))))
> +	 ;; Prepare menu entries by extracting them from registered
> +	 ;; back-ends and sorting them by access key and by ordinal,
> +	 ;; if any.
> +	 (entries
> +	  (sort (sort (delq nil
> +			    (mapcar 'org-export-backend-menu
> +				    org-export--registered-backends))
> +		      (lambda (a b)
> +			(let ((key-a (nth 1 a))
> +			      (key-b (nth 1 b)))
> +			  (cond ((and (numberp key-a) (numberp key-b))
> +				 (< key-a key-b))
> +				((numberp key-b) t)))))
> +		'car-less-than-car))
> 	 ;; Compute a list of allowed keys based on the first key
> 	 ;; pressed, if any.  Some keys
> 	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always
> 	 ;; available.
> 	 (allowed-keys
> 	  (nconc (list 2 22 19 6 1)
> -		 (if (not first-key) (org-uniquify (mapcar 'car backends))
> +		 (if (not first-key) (org-uniquify (mapcar 'car entries))
> 		   (let (sub-menu)
> -		     (dolist (backend backends (sort (mapcar 'car sub-menu) '<))
> -		       (when (eq (car backend) first-key)
> -			 (setq sub-menu (append (nth 2 backend) sub-menu))))))
> +		     (dolist (entry entries (sort (mapcar 'car sub-menu) '<))
> +		       (when (eq (car entry) first-key)
> +			 (setq sub-menu (append (nth 2 entry) sub-menu))))))
> 		 (cond ((eq first-key ?P) (list ?f ?p ?x ?a))
> 		       ((not first-key) (list ?P)))
> 		 (list ?& ?#)
> @@ -5944,7 +5993,7 @@ back to standard interface."
> 				(nth 1 sub-entry)))
> 			     sub-menu "")
> 			    (when (zerop (mod index 2)) "\n"))))))))
> -		backends ""))
> +		entries ""))
> 	     ;; Publishing menu is hard-coded.
> 	     (format "\n[%s] Publish
>     [%s] Current file              [%s] Current project
> @@ -5979,7 +6028,7 @@ back to standard interface."
>     ;; UI, display an intrusive help buffer.
>     (if expertp
> 	(org-export--dispatch-action
> -	 expert-prompt allowed-keys backends options first-key expertp)
> +	 expert-prompt allowed-keys entries options first-key expertp)
>       ;; At first call, create frame layout in order to display menu.
>       (unless (get-buffer "*Org Export Dispatcher*")
> 	(delete-other-windows)
> @@ -6002,15 +6051,15 @@ back to standard interface."
> 	  (set-window-start nil pos)))
>       (org-fit-window-to-buffer)
>       (org-export--dispatch-action
> -       standard-prompt allowed-keys backends options first-key expertp))))
> +       standard-prompt allowed-keys entries options first-key expertp))))
> 
> (defun org-export--dispatch-action
> -  (prompt allowed-keys backends options first-key expertp)
> +  (prompt allowed-keys entries options first-key expertp)
>   "Read a character from command input and act accordingly.
> 
> PROMPT is the displayed prompt, as a string.  ALLOWED-KEYS is
> a list of characters available at a given step in the process.
> -BACKENDS is a list of menu entries.  OPTIONS, FIRST-KEY and
> +ENTRIES is a list of menu entries.  OPTIONS, FIRST-KEY and
> EXPERTP are the same as defined in `org-export--dispatch-ui',
> which see.
> 
> @@ -6067,9 +6116,9 @@ options as CDR."
>        first-key expertp))
>      ;; Action selected: Send key and options back to
>      ;; `org-export-dispatch'.
> -     ((or first-key (functionp (nth 2 (assq key backends))))
> +     ((or first-key (functionp (nth 2 (assq key entries))))
>       (cons (cond
> -	     ((not first-key) (nth 2 (assq key backends)))
> +	     ((not first-key) (nth 2 (assq key entries)))
> 	     ;; Publishing actions are hard-coded.  Send a special
> 	     ;; signal to `org-export-dispatch'.
> 	     ((eq first-key ?P)
> @@ -6082,10 +6131,10 @@ options as CDR."
> 	     ;; path. Indeed, derived backends can share the same
> 	     ;; FIRST-KEY.
> 	     (t (catch 'found
> -		  (mapc (lambda (backend)
> -			  (let ((match (assq key (nth 2 backend))))
> +		  (mapc (lambda (entry)
> +			  (let ((match (assq key (nth 2 entry))))
> 			    (when match (throw 'found (nth 2 match)))))
> -			(member (assq first-key backends) backends)))))
> +			(member (assq first-key entries) entries)))))
> 	    options))
>      ;; Otherwise, enter sub-menu.
>      (t (org-export--dispatch-ui options key expertp)))))
> diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
> index cbae08a..0ba20f2 100644
> --- a/testing/lisp/test-ox.el
> +++ b/testing/lisp/test-ox.el
> @@ -24,30 +24,22 @@
> (unless (featurep 'ox)
>   (signal 'missing-test-dependency "org-export"))
> 
> -(defmacro org-test-with-backend (backend &rest body)
> -  "Execute body with an export back-end defined.
> -
> -BACKEND is the name of the back-end.  BODY is the body to
> -execute.  The defined back-end simply returns parsed data as Org
> -syntax."
> -  (declare (debug (form body)) (indent 1))
> -  `(let ((org-export-registered-backends
> -	  ',(list
> -	     (list backend
> -		   :translate-alist
> -		   (let (transcode-table)
> -		     (dolist (type (append org-element-all-elements
> -					   org-element-all-objects)
> -				   transcode-table)
> -		       (push
> -			(cons type
> -			      (lambda (obj contents info)
> -				(funcall
> -				 (intern (format "org-element-%s-interpreter"
> -						 type))
> -				 obj contents)))
> -			transcode-table)))))))
> -     (progn ,@body)))
> +(defun org-test-default-backend ()
> +  "Return a default export back-end.
> +This back-end simply returns parsed data as Org syntax."
> +  (org-export-create-backend
> +   :transcoders (let (transcode-table)
> +		  (dolist (type (append org-element-all-elements
> +					org-element-all-objects)
> +				transcode-table)
> +		    (push
> +		     (cons type
> +			   (lambda (obj contents info)
> +			     (funcall
> +			      (intern (format "org-element-%s-interpreter"
> +					      type))
> +			      obj contents)))
> +		     transcode-table)))))
> 
> (defmacro org-test-with-parsed-data (data &rest body)
>   "Execute body with parsed data available.
> @@ -108,12 +100,12 @@ already filled in `info'."
>   (should
>    (equal "Yes\n"
> 	  (org-test-with-temp-text "#+BIND: test-ox-var value"
> -	    (let ((org-export-allow-bind-keywords t)
> -		  org-export-registered-backends)
> -	      (org-export-define-backend 'check
> +	    (let ((org-export-allow-bind-keywords t))
> +	      (org-export-as
> +	       (org-export-create-backend
> +		:transcoders
> 		'((section . (lambda (s c i)
> -			       (if (eq test-ox-var 'value) "Yes" "No")))))
> -	      (org-export-as 'check))))))
> +			       (if (eq test-ox-var 'value) "Yes" "No")))))))))))
> 
> (ert-deftest test-org-export/parse-option-keyword ()
>   "Test reading all standard #+OPTIONS: items."
> @@ -276,14 +268,14 @@ Paragraph"
>     'equal
>     (org-test-with-temp-text-in-file "Test"
>       (org-mode)
> -      (let (org-export-registered-backends)
> -	(org-export-define-backend 'test
> -	  '((template . (lambda (text info)
> -			  (org-element-interpret-data
> -			   (plist-get info :title) info)))))
> -	(list (org-export-as 'test)
> -	      (file-name-nondirectory
> -	       (file-name-sans-extension (buffer-file-name))))))))
> +      (list (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders
> +	      '((template . (lambda (text info)
> +			      (org-element-interpret-data
> +			       (plist-get info :title) info))))))
> +	    (file-name-nondirectory
> +	     (file-name-sans-extension (buffer-file-name)))))))
>   ;; If no title is specified, and no file is associated to the
>   ;; buffer, use buffer's name.
>   (should
> @@ -291,36 +283,37 @@ Paragraph"
>     'equal
>     (org-test-with-temp-text "Test"
>       (org-mode)
> -      (let (org-export-registered-backends)
> -	(org-export-define-backend 'test
> -	  '((template . (lambda (text info)
> -			  (org-element-interpret-data
> -			   (plist-get info :title) info)))))
> -	(list (org-export-as 'test) (buffer-name))))))
> +      (list (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders
> +	      '((template . (lambda (text info)
> +			      (org-element-interpret-data
> +			       (plist-get info :title) info))))))
> +	    (buffer-name)))))
>   ;; If a title is specified, use it.
>   (should
>    (equal
>     "Title"
>     (org-test-with-temp-text-in-file "#+TITLE: Title\nTest"
>       (org-mode)
> -      (let (org-export-registered-backends)
> -	(org-export-define-backend 'test
> -	  '((template . (lambda (text info)
> -			  (org-element-interpret-data
> -			   (plist-get info :title) info)))))
> -	(org-export-as 'test)))))
> +      (org-export-as
> +       (org-export-create-backend
> +	:transcoders
> +	'((template . (lambda (text info)
> +			(org-element-interpret-data
> +			 (plist-get info :title) info)))))))))
>   ;; If an empty title is specified, do not set it.
>   (should
>    (equal
>     ""
>     (org-test-with-temp-text-in-file "#+TITLE:\nTest"
>       (org-mode)
> -      (let (org-export-registered-backends)
> -	(org-export-define-backend 'test
> -	  '((template . (lambda (text info)
> -			  (org-element-interpret-data
> -			   (plist-get info :title) info)))))
> -	(org-export-as 'test))))))
> +      (org-export-as
> +       (org-export-create-backend
> +	:transcoders
> +	'((template . (lambda (text info)
> +			(org-element-interpret-data
> +			 (plist-get info :title) info))))))))))
> 
> (ert-deftest test-org-export/handle-options ()
>   "Test if export options have an impact on output."
> @@ -328,142 +321,148 @@ Paragraph"
>   (should
>    (equal ""
> 	  (org-test-with-temp-text "* Head1 :noexp:"
> -	    (org-test-with-backend test
> -	      (org-export-as 'test nil nil nil '(:exclude-tags ("noexp")))))))
> +	    (org-export-as (org-test-default-backend)
> +			   nil nil nil '(:exclude-tags ("noexp"))))))
>   ;; Test include tags for headlines and inlinetasks.
>   (should
>    (equal "* H2\n** Sub :exp:\n*** Sub Sub\n"
> 	  (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub Sub\n* H3"
> 	    (let ((org-tags-column 0))
> -	      (org-test-with-backend test
> -		(org-export-as 'test nil nil nil '(:select-tags ("exp"))))))))
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:select-tags ("exp")))))))
>   ;; Test mixing include tags and exclude tags.
> -  (org-test-with-temp-text "
> +  (should
> +   (string-match
> +    "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
> +    (org-test-with-temp-text "
> * Head1 :export:
> ** Sub-Head1 :noexport:
> ** Sub-Head2
> * Head2 :noexport:
> ** Sub-Head1 :export:"
> -    (org-test-with-backend test
> -      (should
> -       (string-match
> -	"\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
> -	(org-export-as
> -	 'test nil nil nil
> -	 '(:select-tags ("export") :exclude-tags ("noexport")))))))
> +      (org-export-as (org-test-default-backend) nil nil nil
> +		     '(:select-tags ("export") :exclude-tags ("noexport"))))))
>   ;; Ignore tasks.
>   (should
>    (equal ""
> 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
> 	    (org-test-with-temp-text "* TODO Head1"
> -	      (org-test-with-backend test
> -		(org-export-as 'test nil nil nil '(:with-tasks nil)))))))
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-tasks nil))))))
>   (should
>    (equal "* TODO Head1\n"
> 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
> 	    (org-test-with-temp-text "* TODO Head1"
> -	      (org-test-with-backend test
> -		(org-export-as 'test nil nil nil '(:with-tasks t)))))))
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-tasks t))))))
>   ;; Archived tree.
> -  (org-test-with-temp-text "* Head1 :archive:"
> -    (let ((org-archive-tag "archive"))
> -      (org-test-with-backend test
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-archived-trees nil))
> -		"")))))
> -  (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
> -    (let ((org-archive-tag "archive"))
> -      (org-test-with-backend test
> -	(should
> -	 (string-match
> -	  "\\* Head1[ \t]+:archive:"
> -	  (org-export-as 'test nil nil nil
> -			 '(:with-archived-trees headline)))))))
> -  (org-test-with-temp-text "* Head1 :archive:"
> -    (let ((org-archive-tag "archive"))
> -      (org-test-with-backend test
> -	(should
> -	 (string-match
> -	  "\\`\\* Head1[ \t]+:archive:\n\\'"
> -	  (org-export-as 'test nil nil nil '(:with-archived-trees t)))))))
> +  (should
> +   (equal ""
> +	  (org-test-with-temp-text "* Head1 :archive:"
> +	    (let ((org-archive-tag "archive"))
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-archived-trees nil))))))
> +  (should
> +   (string-match
> +    "\\* Head1[ \t]+:archive:"
> +    (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
> +      (let ((org-archive-tag "archive"))
> +	(org-export-as (org-test-default-backend) nil nil nil
> +		       '(:with-archived-trees headline))))))
> +  (should
> +   (string-match
> +    "\\`\\* Head1[ \t]+:archive:\n\\'"
> +    (org-test-with-temp-text "* Head1 :archive:"
> +      (let ((org-archive-tag "archive"))
> +	(org-export-as (org-test-default-backend)
> +		       nil nil nil '(:with-archived-trees t))))))
>   ;; Clocks.
> -  (let ((org-clock-string "CLOCK:"))
> -    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
> -      (org-test-with-backend test
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-clocks t))
> -		"CLOCK: [2012-04-29 sun. 10:45]\n"))
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-clocks nil)) "")))))
> +  (should
> +   (equal "CLOCK: [2012-04-29 sun. 10:45]\n"
> +	  (let ((org-clock-string "CLOCK:"))
> +	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-clocks t))))))
> +  (should
> +   (equal ""
> +	  (let ((org-clock-string "CLOCK:"))
> +	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-clocks nil))))))
>   ;; Drawers.
> -  (let ((org-drawers '("TEST")))
> -    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
> -      (org-test-with-backend test
> -	(should (equal (org-export-as 'test nil nil nil '(:with-drawers nil))
> -		       ""))
> -	(should (equal (org-export-as 'test nil nil nil '(:with-drawers t))
> -		       ":TEST:\ncontents\n:END:\n")))))
> -  (let ((org-drawers '("FOO" "BAR")))
> -    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
> -      (org-test-with-backend test
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-drawers ("FOO")))
> -		":FOO:\nkeep\n:END:\n")))))
> -  (let ((org-drawers '("FOO" "BAR")))
> -    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
> -      (org-test-with-backend test
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-drawers (not "BAR")))
> -		":FOO:\nkeep\n:END:\n")))))
> +  (should
> +   (equal ""
> +	  (let ((org-drawers '("TEST")))
> +	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-drawers nil))))))
> +  (should
> +   (equal ":TEST:\ncontents\n:END:\n"
> +	  (let ((org-drawers '("TEST")))
> +	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-drawers t))))))
> +  (should
> +   (equal ":FOO:\nkeep\n:END:\n"
> +	  (let ((org-drawers '("FOO" "BAR")))
> +	    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-drawers ("FOO")))))))
> +  (should
> +   (equal ":FOO:\nkeep\n:END:\n"
> +	  (let ((org-drawers '("FOO" "BAR")))
> +	    (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-drawers (not "BAR")))))))
>   ;; Footnotes.
>   (should
>    (equal "Footnote?"
> 	  (let ((org-footnote-section nil))
> 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
> -	      (org-test-with-backend test
> -		(org-trim
> -		 (org-export-as 'test nil nil nil '(:with-footnotes nil))))))))
> +	      (org-trim (org-export-as (org-test-default-backend)
> +				       nil nil nil '(:with-footnotes nil)))))))
>   (should
>    (equal "Footnote?[fn:1]\n\n[fn:1] Def"
> 	  (let ((org-footnote-section nil))
> 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
> -	      (org-test-with-backend test
> -		(org-trim
> -		 (org-export-as 'test nil nil nil '(:with-footnotes t))))))))
> +	      (org-trim (org-export-as (org-test-default-backend)
> +				       nil nil nil '(:with-footnotes t)))))))
>   ;; Inlinetasks.
>   (when (featurep 'org-inlinetask)
>     (should
>      (equal
> +      ""
>       (let ((org-inlinetask-min-level 15))
> 	(org-test-with-temp-text "*************** Task"
> -	  (org-test-with-backend test
> -	    (org-export-as 'test nil nil nil '(:with-inlinetasks nil)))))
> -      ""))
> +	  (org-export-as (org-test-default-backend)
> +			 nil nil nil '(:with-inlinetasks nil))))))
>     (should
>      (equal
> +      ""
>       (let ((org-inlinetask-min-level 15))
> 	(org-test-with-temp-text
> 	    "*************** Task\nContents\n*************** END"
> -	  (org-test-with-backend test
> -	    (org-export-as 'test nil nil nil '(:with-inlinetasks nil)))))
> -      "")))
> +	  (org-export-as (org-test-default-backend)
> +			 nil nil nil '(:with-inlinetasks nil)))))))
>   ;; Plannings.
> -  (let ((org-closed-string "CLOSED:"))
> -    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
> -      (org-test-with-backend test
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-planning t))
> -		"CLOSED: [2012-04-29 sun. 10:45]\n"))
> -	(should
> -	 (equal (org-export-as 'test nil nil nil '(:with-planning nil))
> -		"")))))
> +  (should
> +   (equal "CLOSED: [2012-04-29 sun. 10:45]\n"
> +	  (let ((org-closed-string "CLOSED:"))
> +	    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-planning t))))))
> +  (should
> +   (equal ""
> +	  (let ((org-closed-string "CLOSED:"))
> +	    (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
> +	      (org-export-as (org-test-default-backend)
> +			     nil nil nil '(:with-planning nil))))))
>   ;; Statistics cookies.
>   (should
>    (equal ""
> 	  (org-test-with-temp-text "[0/0]"
> -	    (org-test-with-backend test
> -	      (org-export-as
> -	       'test nil nil nil '(:with-statistics-cookies nil)))))))
> +	    (org-export-as (org-test-default-backend)
> +			   nil nil nil '(:with-statistics-cookies nil))))))
> 
> (ert-deftest test-org-export/with-timestamps ()
>   "Test `org-export-with-timestamps' specifications."
> @@ -472,15 +471,15 @@ Paragraph"
>    (equal
>     "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>\n"
>     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
> -      (org-test-with-backend test
> -	(org-export-as 'test nil nil nil '(:with-timestamps t))))))
> +      (org-export-as (org-test-default-backend)
> +		     nil nil nil '(:with-timestamps t)))))
>   ;; nil value.
>   (should
>    (equal
>     ""
>     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
> -      (org-test-with-backend test
> -	(org-export-as 'test nil nil nil '(:with-timestamps nil))))))
> +      (org-export-as (org-test-default-backend)
> +		     nil nil nil '(:with-timestamps nil)))))
>   ;; `active' value.
>   (should
>    (equal
> @@ -489,9 +488,8 @@ Paragraph"
> 	"<2012-03-29 Thu>[2012-03-29 Thu]
> 
> Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
> -      (org-test-with-backend test
> -	(org-trim
> -	 (org-export-as 'test nil nil nil '(:with-timestamps active)))))))
> +      (org-trim (org-export-as (org-test-default-backend)
> +			       nil nil nil '(:with-timestamps active))))))
>   ;; `inactive' value.
>   (should
>    (equal
> @@ -500,16 +498,16 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
> 	"<2012-03-29 Thu>[2012-03-29 Thu]
> 
> Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
> -      (org-test-with-backend test
> -	(org-trim
> -	 (org-export-as 'test nil nil nil '(:with-timestamps inactive))))))))
> +      (org-trim (org-export-as (org-test-default-backend)
> +			       nil nil nil '(:with-timestamps inactive)))))))
> 
> (ert-deftest test-org-export/comment-tree ()
>   "Test if export process ignores commented trees."
> -  (let ((org-comment-string "COMMENT"))
> -    (org-test-with-temp-text "* COMMENT Head1"
> -      (org-test-with-backend test
> -	(should (equal (org-export-as 'test) ""))))))
> +  (should
> +   (equal ""
> +	  (let ((org-comment-string "COMMENT"))
> +	    (org-test-with-temp-text "* COMMENT Head1"
> +	      (org-export-as (org-test-default-backend)))))))
> 
> (ert-deftest test-org-export/export-scope ()
>   "Test all export scopes."
> @@ -518,22 +516,23 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
> ** Head2
> text
> *** Head3"
> -    (org-test-with-backend test
> -      ;; Subtree.
> -      (forward-line 3)
> -      (should (equal (org-export-as 'test 'subtree) "text\n*** Head3\n"))
> -      ;; Visible.
> -      (goto-char (point-min))
> -      (forward-line)
> -      (org-cycle)
> -      (should (equal (org-export-as 'test nil 'visible) "* Head1\n"))
> -      ;; Region.
> -      (goto-char (point-min))
> -      (forward-line 3)
> -      (transient-mark-mode 1)
> -      (push-mark (point) t t)
> -      (goto-char (point-at-eol))
> -      (should (equal (org-export-as 'test) "text\n"))))
> +    ;; Subtree.
> +    (forward-line 3)
> +    (should (equal (org-export-as (org-test-default-backend) 'subtree)
> +		   "text\n*** Head3\n"))
> +    ;; Visible.
> +    (goto-char (point-min))
> +    (forward-line)
> +    (org-cycle)
> +    (should (equal (org-export-as (org-test-default-backend) nil 'visible)
> +		   "* Head1\n"))
> +    ;; Region.
> +    (goto-char (point-min))
> +    (forward-line 3)
> +    (transient-mark-mode 1)
> +    (push-mark (point) t t)
> +    (goto-char (point-at-eol))
> +    (should (equal (org-export-as (org-test-default-backend)) "text\n")))
>   ;; Subtree with a code block calling another block outside.
>   (should
>    (equal ": 3\n"
> @@ -547,19 +546,18 @@ text
> #+BEGIN_SRC emacs-lisp
> \(+ 1 2)
> #+END_SRC"
> -	    (org-test-with-backend test
> -	      (forward-line 1)
> -	      (org-export-as 'test 'subtree)))))
> +	    (forward-line 1)
> +	    (org-export-as (org-test-default-backend) 'subtree))))
>   ;; Body only.
> -  (org-test-with-temp-text "Text"
> -    (org-test-with-backend test
> -      (plist-put
> -       (cdr (assq 'test org-export-registered-backends))
> -       :translate-alist
> -       (cons (cons 'template (lambda (body info) (format "BEGIN\n%sEND" body)))
> -	     (org-export-backend-translate-table 'test)))
> -      (should (equal (org-export-as 'test nil nil 'body-only) "Text\n"))
> -      (should (equal (org-export-as 'test) "BEGIN\nText\nEND")))))
> +  (let ((backend (org-test-default-backend)))
> +    (setf (org-export-backend-transcoders backend)
> +	  (cons '(template . (lambda (body i)
> +			       (format "BEGIN\n%sEND" body)))
> +		(org-export-backend-transcoders backend)))
> +    (org-test-with-temp-text "Text"
> +      (should (equal (org-export-as backend nil nil 'body-only)
> +		     "Text\n"))
> +      (should (equal (org-export-as backend) "BEGIN\nText\nEND")))))
> 
> (ert-deftest test-org-export/output-file-name ()
>   "Test `org-export-output-file-name' specifications."
> @@ -667,7 +665,7 @@ body\n")))
>   (should
>    (equal "#+MACRO: macro1 value\nvalue\n"
> 	  (org-test-with-temp-text "#+MACRO: macro1 value\n{{{macro1}}}"
> -	    (org-test-with-backend test (org-export-as 'test)))))
> +	    (org-export-as (org-test-default-backend)))))
>   ;; Expand specific macros.
>   (should
>    (equal "me 2012-03-29 me@here Title\n"
> @@ -678,7 +676,7 @@ body\n")))
> #+AUTHOR: me
> #+EMAIL: me@here
> {{{author}}} {{{date}}} {{{email}}} {{{title}}}"
> -	    (let ((output (org-test-with-backend test (org-export-as 'test))))
> +	    (let ((output (org-export-as (org-test-default-backend))))
> 	      (substring output (string-match ".*\n\\'" output))))))
>   ;; Expand specific macros when property contained a regular macro
>   ;; already.
> @@ -688,7 +686,7 @@ body\n")))
> #+MACRO: macro1 value
> #+TITLE: {{{macro1}}}
> {{{title}}}"
> -	    (let ((output (org-test-with-backend test (org-export-as 'test))))
> +	    (let ((output (org-export-as (org-test-default-backend))))
> 	      (substring output (string-match ".*\n\\'" output))))))
>   ;; Expand macros with templates in included files.
>   (should
> @@ -696,57 +694,65 @@ body\n")))
> 	  (org-test-with-temp-text
> 	      (format "#+INCLUDE: \"%s/examples/macro-templates.org\"
> {{{included-macro}}}" org-test-dir)
> -	    (let ((output (org-test-with-backend test (org-export-as 'test))))
> +	    (let ((output (org-export-as (org-test-default-backend))))
> 	      (substring output (string-match ".*\n\\'" output)))))))
> 
> (ert-deftest test-org-export/user-ignore-list ()
>   "Test if `:ignore-list' accepts user input."
> -  (org-test-with-backend test
> -    (flet ((skip-note-head
> -	    (data backend info)
> -	    ;; Ignore headlines with the word "note" in their title.
> -	    (org-element-map data 'headline
> -	      (lambda (headline)
> -		(when (string-match "\\<note\\>"
> -				    (org-element-property :raw-value headline))
> -		  (org-export-ignore-element headline info)))
> -	      info)
> -	    data))
> -      ;; Install function in parse tree filters.
> -      (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
> -	(org-test-with-temp-text "* Head1\n* Head2 (note)\n"
> -	  (should (equal (org-export-as 'test) "* Head1\n")))))))
> +  (let ((backend (org-test-default-backend)))
> +    (setf (org-export-backend-transcoders backend)
> +	  (cons '(template . (lambda (body i)
> +			       (format "BEGIN\n%sEND" body)))
> +		(org-export-backend-transcoders backend)))
> +    (org-test-with-temp-text "Text"
> +      (should (equal (org-export-as backend nil nil 'body-only)
> +		     "Text\n"))
> +      (should (equal (org-export-as backend) "BEGIN\nText\nEND"))))
> +  (should
> +   (equal
> +    "* Head1\n"
> +    (let ((org-export-filter-parse-tree-functions
> +	   '((lambda (data backend info)
> +	       ;; Ignore headlines with the word "note" in their title.
> +	       (org-element-map data 'headline
> +		 (lambda (headline)
> +		   (when (string-match "\\<note\\>"
> +				       (org-element-property :raw-value
> +							     headline))
> +		     (org-export-ignore-element headline info)))
> +		 info)
> +	       data))))
> +      (org-test-with-temp-text "* Head1\n* Head2 (note)\n"
> +	(org-export-as (org-test-default-backend)))))))
> 
> (ert-deftest test-org-export/before-processing-hook ()
>   "Test `org-export-before-processing-hook'."
>   (should
>    (equal
>     "#+MACRO: mac val\nTest\n"
> -    (org-test-with-backend test
> -      (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test"
> -	(let ((org-export-before-processing-hook
> -	       '((lambda (backend)
> -		   (while (re-search-forward "{{{" nil t)
> -		     (let ((object (org-element-context)))
> -		       (when (eq (org-element-type object) 'macro)
> -			 (delete-region
> -			  (org-element-property :begin object)
> -			  (org-element-property :end object)))))))))
> -	  (org-export-as 'test)))))))
> +    (org-test-with-temp-text "#+MACRO: mac val\n{{{mac}}} Test"
> +      (let ((org-export-before-processing-hook
> +	     '((lambda (backend)
> +		 (while (re-search-forward "{{{" nil t)
> +		   (let ((object (org-element-context)))
> +		     (when (eq (org-element-type object) 'macro)
> +		       (delete-region
> +			(org-element-property :begin object)
> +			(org-element-property :end object)))))))))
> +	(org-export-as (org-test-default-backend)))))))
> 
> (ert-deftest test-org-export/before-parsing-hook ()
>   "Test `org-export-before-parsing-hook'."
>   (should
>    (equal "Body 1\nBody 2\n"
> -	  (org-test-with-backend test
> -	    (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
> -	      (let ((org-export-before-parsing-hook
> -		     '((lambda (backend)
> -			 (goto-char (point-min))
> -			 (while (re-search-forward org-outline-regexp-bol nil t)
> -			   (delete-region
> -			    (point-at-bol) (progn (forward-line) (point))))))))
> -		(org-export-as 'test)))))))
> +	  (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
> +	    (let ((org-export-before-parsing-hook
> +		   '((lambda (backend)
> +		       (goto-char (point-min))
> +		       (while (re-search-forward org-outline-regexp-bol nil t)
> +			 (delete-region
> +			  (point-at-bol) (progn (forward-line) (point))))))))
> +	      (org-export-as (org-test-default-backend)))))))
> 
> 
> 
> @@ -833,37 +839,37 @@ body\n")))
>   ;; Translate table.
>   (should
>    (equal '((headline . my-headline-test))
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'test '((headline . my-headline-test)))
> -	    (org-export-backend-translate-table 'test))))
> +	    (org-export-get-all-transcoders 'test))))
>   ;; Filters.
>   (should
>    (equal '((:filter-headline . my-filter))
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'test
> 	      '((headline . my-headline-test))
> 	      :filters-alist '((:filter-headline . my-filter)))
> -	    (org-export-backend-filters 'test))))
> +	    (org-export-backend-filters (org-export-get-backend 'test)))))
>   ;; Options.
>   (should
>    (equal '((:prop value))
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'test
> 	      '((headline . my-headline-test))
> 	      :options-alist '((:prop value)))
> -	    (org-export-backend-options 'test))))
> +	    (org-export-backend-options (org-export-get-backend 'test)))))
>   ;; Menu.
>   (should
>    (equal '(?k "Test Export" test)
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'test
> 	      '((headline . my-headline-test))
> 	      :menu-entry '(?k "Test Export" test))
> -	    (org-export-backend-menu 'test))))
> +	    (org-export-backend-menu (org-export-get-backend 'test)))))
>   ;; Export Blocks.
>   (should
>    (equal '(("TEST" . org-element-export-block-parser))
> -	  (let (org-export-registered-backends org-element-block-name-alist)
> +	  (let (org-export--registered-backends org-element-block-name-alist)
> 	    (org-export-define-backend 'test
> 	      '((headline . my-headline-test))
> 	      :export-block '("test"))
> @@ -873,115 +879,218 @@ body\n")))
>   "Test `org-export-define-derived-backend' specifications."
>   ;; Error when parent back-end is not defined.
>   (should-error
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-derived-backend 'test 'parent)))
>   ;; Append translation table to parent's.
>   (should
>    (equal '((:headline . test) (:headline . parent))
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'parent '((:headline . parent)))
> 	    (org-export-define-derived-backend 'test 'parent
> 	      :translate-alist '((:headline . test)))
> -	    (org-export-backend-translate-table 'test))))
> +	    (org-export-get-all-transcoders 'test))))
>   ;; Options defined in the new back have priority over those defined
>   ;; in parent.
>   (should
>    (eq 'test
> -       (let (org-export-registered-backends)
> +       (let (org-export--registered-backends)
> 	 (org-export-define-backend 'parent
> 	   '((:headline . parent))
> 	   :options-alist '((:a nil nil 'parent)))
> 	 (org-export-define-derived-backend 'test 'parent
> 	   :options-alist '((:a nil nil 'test)))
> -	 (plist-get (org-export--get-global-options 'test) :a)))))
> +	 (plist-get (org-export--get-global-options
> +		     (org-export-get-backend 'test))
> +		    :a)))))
> 
> (ert-deftest test-org-export/derived-backend-p ()
>   "Test `org-export-derived-backend-p' specifications."
>   ;; Non-nil with direct match.
>   (should
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-derived-backend-p 'test 'test)))
>   (should
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-define-derived-backend 'test2 'test)
>      (org-export-derived-backend-p 'test2 'test2)))
>   ;; Non-nil with a direct parent.
>   (should
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-define-derived-backend 'test2 'test)
>      (org-export-derived-backend-p 'test2 'test)))
>   ;; Non-nil with an indirect parent.
>   (should
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-define-derived-backend 'test2 'test)
>      (org-export-define-derived-backend 'test3 'test2)
>      (org-export-derived-backend-p 'test3 'test)))
>   ;; Nil otherwise.
>   (should-not
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-define-backend 'test2 '((headline . test2)))
>      (org-export-derived-backend-p 'test2 'test)))
>   (should-not
> -   (let (org-export-registered-backends)
> +   (let (org-export--registered-backends)
>      (org-export-define-backend 'test '((headline . test)))
>      (org-export-define-backend 'test2 '((headline . test2)))
>      (org-export-define-derived-backend 'test3 'test2)
>      (org-export-derived-backend-p 'test3 'test))))
> 
> +(ert-deftest test-org-export/get-all-transcoders ()
> +  "Test `org-export-get-all-transcoders' specifications."
> +  ;; Return nil when back-end cannot be found.
> +  (should-not (org-export-get-all-transcoders nil))
> +  ;; Same as `org-export-transcoders' if no parent.
> +  (should
> +   (equal '((headline . ignore))
> +	  (org-export-get-all-transcoders
> +	   (org-export-create-backend
> +	    :transcoders '((headline . ignore))))))
> +  ;; But inherit from all ancestors whenever possible.
> +  (should
> +   (equal '((section . ignore) (headline . ignore))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1 '((headline . ignore)))
> +	    (org-export-get-all-transcoders
> +	     (org-export-create-backend
> +	      :parent 'b1 :transcoders '((section . ignore)))))))
> +  (should
> +   (equal '((paragraph . ignore) (section . ignore) (headline . ignore))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1 '((headline . ignore)))
> +	    (org-export-define-derived-backend 'b2 'b1
> +	      :translate-alist '((section . ignore)))
> +	    (org-export-get-all-transcoders
> +	     (org-export-create-backend
> +	      :parent 'b2 :transcoders '((paragraph . ignore)))))))
> +  ;; Back-end transcoders overrule inherited ones.
> +  (should
> +   (eq 'b
> +       (let (org-export--registered-backends)
> +	 (org-export-define-backend 'b1 '((headline . a)))
> +	 (cdr (assq 'headline
> +		    (org-export-get-all-transcoders
> +		     (org-export-create-backend
> +		      :parent 'b1 :transcoders '((headline . b))))))))))
> +
> +(ert-deftest test-org-export/get-all-options ()
> +  "Test `org-export-get-all-options' specifications."
> +  ;; Return nil when back-end cannot be found.
> +  (should-not (org-export-get-all-options nil))
> +  ;; Same as `org-export-options' if no parent.
> +  (should
> +   (equal '((headline . ignore))
> +	  (org-export-get-all-options
> +	   (org-export-create-backend
> +	    :options '((headline . ignore))))))
> +  ;; But inherit from all ancestors whenever possible.
> +  (should
> +   (equal '((:key2 value2) (:key1 value1))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1 nil :options-alist '((:key1 value1)))
> +	    (org-export-get-all-options
> +	     (org-export-create-backend
> +	      :parent 'b1 :options '((:key2 value2)))))))
> +  (should
> +   (equal '((:key3 value3) (:key2 value2) (:key1 value1))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1 nil :options-alist '((:key1 value1)))
> +	    (org-export-define-derived-backend 'b2 'b1
> +	      :options-alist '((:key2 value2)))
> +	    (org-export-get-all-options
> +	     (org-export-create-backend
> +	      :parent 'b2 :options '((:key3 value3)))))))
> +  ;; Back-end options overrule inherited ones.
> +  (should
> +   (eq 'b
> +       (let (org-export--registered-backends)
> +	 (org-export-define-backend 'b1 nil :options-alist '((:key1 . a)))
> +	 (cdr (assq :key1
> +		    (org-export-get-all-options
> +		     (org-export-create-backend
> +		      :parent 'b1 :options '((:key1 . b))))))))))
> +
> +(ert-deftest test-org-export/get-all-filters ()
> +  "Test `org-export-get-all-filters' specifications."
> +  ;; Return nil when back-end cannot be found.
> +  (should-not (org-export-get-all-filters nil))
> +  ;; Same as `org-export-filters' if no parent.
> +  (should
> +   (equal '((:filter-headline . ignore))
> +	  (org-export-get-all-filters
> +	   (org-export-create-backend
> +	    :filters '((:filter-headline . ignore))))))
> +  ;; But inherit from all ancestors whenever possible.
> +  (should
> +   (equal '((:filter-section . ignore) (:filter-headline . ignore))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1
> +	      nil :filters-alist '((:filter-headline . ignore)))
> +	    (org-export-get-all-filters
> +	     (org-export-create-backend
> +	      :parent 'b1 :filters '((:filter-section . ignore)))))))
> +  (should
> +   (equal '((:filter-paragraph . ignore)
> +	    (:filter-section . ignore)
> +	    (:filter-headline . ignore))
> +	  (let (org-export--registered-backends)
> +	    (org-export-define-backend 'b1
> +	      nil :filters-alist '((:filter-headline . ignore)))
> +	    (org-export-define-derived-backend 'b2 'b1
> +	      :filters-alist '((:filter-section . ignore)))
> +	    (org-export-get-all-filters
> +	     (org-export-create-backend
> +	      :parent 'b2 :filters '((:filter-paragraph . ignore)))))))
> +  ;; Back-end filters overrule inherited ones.
> +  (should
> +   (eq 'b
> +       (let (org-export--registered-backends)
> +	 (org-export-define-backend 'b1 '((:filter-headline . a)))
> +	 (cdr (assq :filter-headline
> +		    (org-export-get-all-filters
> +		     (org-export-create-backend
> +		      :parent 'b1 :filters '((:filter-headline . b))))))))))
> +
> (ert-deftest test-org-export/with-backend ()
>   "Test `org-export-with-backend' definition."
>   ;; Error when calling an undefined back-end
> -  (should-error
> -   (let (org-export-registered-backends)
> -     (org-export-with-backend 'test "Test")))
> +  (should-error (org-export-with-backend nil "Test"))
>   ;; Error when called back-end doesn't have an appropriate
>   ;; transcoder.
>   (should-error
> -   (let (org-export-registered-backends)
> -     (org-export-define-backend 'test ((headline . ignore)))
> -     (org-export-with-backend 'test "Test")))
> +   (org-export-with-backend
> +    (org-export-create-backend :transcoders '((headline . ignore)))
> +    "Test"))
>   ;; Otherwise, export using correct transcoder
>   (should
>    (equal "Success"
> -	  (let (org-export-registered-backends)
> +	  (let (org-export--registered-backends)
> 	    (org-export-define-backend 'test
> 	      '((plain-text . (lambda (text contents info) "Failure"))))
> 	    (org-export-define-backend 'test2
> 	      '((plain-text . (lambda (text contents info) "Success"))))
> 	    (org-export-with-backend 'test2 "Test")))))
> 
> -(ert-deftest test-org-export/data-with-translations ()
> -  "Test `org-export-data-with-translations' specifications."
> -  (should
> -   (equal
> -    "Success!"
> -    (org-export-data-with-translations
> -     '(bold nil "Test")
> -     '((plain-text . (lambda (text info) "Success"))
> -       (bold . (lambda (bold contents info) (concat contents "!"))))
> -     '(:with-emphasize t)))))
> -
> (ert-deftest test-org-export/data-with-backend ()
>   "Test `org-export-data-with-backend' specifications."
>   ;; Error when calling an undefined back-end.
> -  (should-error
> -   (let (org-export-registered-backends)
> -     (org-export-data-with-backend 'test "Test" nil)))
> +  (should-error (org-export-data-with-backend nil "nil" nil))
>   ;; Otherwise, export data recursively, using correct back-end.
>   (should
>    (equal
>     "Success!"
> -    (let (org-export-registered-backends)
> -      (org-export-define-backend 'test
> -	'((plain-text . (lambda (text info) "Success"))
> -	  (bold . (lambda (bold contents info) (concat contents "!")))))
> -      (org-export-data-with-backend
> -       '(bold nil "Test") 'test '(:with-emphasize t))))))
> +    (org-export-data-with-backend
> +     '(bold nil "Test")
> +     (org-export-create-backend
> +      :transcoders
> +      '((plain-text . (lambda (text info) "Success"))
> +	(bold . (lambda (bold contents info) (concat contents "!")))))
> +     '(:with-emphasize t)))))
> 
> 
> 
> @@ -989,28 +1098,30 @@ body\n")))
> 
> (ert-deftest test-org-export/export-snippet ()
>   "Test export snippets transcoding."
> +  ;; Standard test.
>   (org-test-with-temp-text "@@test:A@@@@t:B@@"
> -    (org-test-with-backend test
> -      (plist-put
> -       (cdr (assq 'test org-export-registered-backends))
> -       :translate-alist
> -       (cons (cons 'export-snippet
> -		   (lambda (snippet contents info)
> -		     (when (eq (org-export-snippet-backend snippet) 'test)
> -		       (org-element-property :value snippet))))
> -	     (org-export-backend-translate-table 'test)))
> +    (let ((backend (org-test-default-backend)))
> +      (setf (org-export-backend-name backend) 'test)
> +      (setf (org-export-backend-transcoders backend)
> +	    (cons (cons 'export-snippet
> +			(lambda (snippet contents info)
> +			  (when (eq (org-export-snippet-backend snippet) 'test)
> +			    (org-element-property :value snippet))))
> +		  (org-export-backend-transcoders backend)))
>       (let ((org-export-snippet-translation-alist nil))
> -	(should (equal (org-export-as 'test) "A\n")))
> +	(should (equal (org-export-as backend) "A\n")))
>       (let ((org-export-snippet-translation-alist '(("t" . "test"))))
> -	(should (equal (org-export-as 'test) "AB\n")))))
> +	(should (equal (org-export-as backend) "AB\n")))))
>   ;; Ignored export snippets do not remove any blank.
>   (should
>    (equal "begin  end\n"
> 	  (org-test-with-parsed-data "begin @@test:A@@ end"
> -	    (org-export-data-with-translations
> +	    (org-export-data-with-backend
> 	     tree
> -	     '((paragraph . (lambda (paragraph contents info) contents))
> -	       (section . (lambda (section contents info) contents)))
> +	     (org-export-create-backend
> +	      :transcoders
> +	      '((paragraph . (lambda (paragraph contents info) contents))
> +		(section . (lambda (section contents info) contents))))
> 	     info)))))
> 
> 
> @@ -1036,11 +1147,11 @@ body\n")))
> 			    (car (org-element-contents def))))))))
> 	  info))))
>     ;; 2. Test nested footnotes order.
> -    (org-test-with-parsed-data
> -	"Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
> -      (should
> -       (equal
> -	'((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
> +    (should
> +     (equal
> +      '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
> +      (org-test-with-parsed-data
> +	  "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
> 	(org-element-map tree 'footnote-reference
> 	  (lambda (ref)
> 	    (when (org-export-footnote-first-reference-p ref info)
> @@ -1060,29 +1171,30 @@ body\n")))
> 	(should
> 	 (= (length (org-export-collect-footnote-definitions tree info)) 2))))
>     ;; 4. Test footnotes definitions collection.
> -    (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
> +    (should
> +     (= 4
> +	(org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
> 
> \[fn:2] B [fn:3] [fn::D].
> 
> \[fn:3] C."
> -      (should (= (length (org-export-collect-footnote-definitions tree info))
> -		 4)))
> +	  (length (org-export-collect-footnote-definitions tree info)))))
>     ;; 5. Test export of footnotes defined outside parsing scope.
> -    (org-test-with-temp-text "[fn:1] Out of scope
> +    (should
> +     (equal
> +      "ParagraphOut of scope\n"
> +      (org-test-with-temp-text "[fn:1] Out of scope
> * Title
> Paragraph[fn:1]"
> -      (org-test-with-backend test
> -	(plist-put
> -	 (cdr (assq 'test org-export-registered-backends))
> -	 :translate-alist
> -	 (cons (cons 'footnote-reference
> -		     (lambda (fn contents info)
> -		       (org-element-interpret-data
> -			(org-export-get-footnote-definition fn info))))
> -	       (org-export-backend-translate-table 'test)))
> -	(forward-line)
> -	(should (equal "ParagraphOut of scope\n"
> -		       (org-export-as 'test 'subtree)))))
> +	(let ((backend (org-test-default-backend)))
> +	  (setf (org-export-backend-transcoders backend)
> +		(cons (cons 'footnote-reference
> +			    (lambda (fn contents info)
> +			      (org-element-interpret-data
> +			       (org-export-get-footnote-definition fn info))))
> +		      (org-export-backend-transcoders backend)))
> +	  (forward-line)
> +	  (org-export-as backend 'subtree)))))
>     ;; 6. Footnotes without a definition should be provided a fallback
>     ;;    definition.
>     (should
> @@ -1378,8 +1490,8 @@ Paragraph[fn:1]"
>       ""
>       (let ((org-inlinetask-min-level 3))
> 	(org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** end"
> -	  (org-test-with-backend test
> -	    (org-export-as 'test nil nil nil '(:exclude-tags ("noexp"))))))))
> +	  (org-export-as (org-test-default-backend)
> +			 nil nil nil '(:exclude-tags ("noexp")))))))
>     ;; Inlinetask with an include tag.
>     (should
>      (equal
> @@ -1387,16 +1499,16 @@ Paragraph[fn:1]"
>       (let ((org-inlinetask-min-level 3)
> 	    (org-tags-column 0))
> 	(org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:"
> -	  (org-test-with-backend test
> -	    (org-export-as 'test nil nil nil '(:select-tags ("exp"))))))))
> +	  (org-export-as (org-test-default-backend)
> +			 nil nil nil '(:select-tags ("exp")))))))
>     ;; Ignore inlinetask with a TODO keyword and tasks excluded.
>     (should
>      (equal ""
> 	    (let ((org-todo-keywords '((sequence "TODO" "DONE")))
> 		  (org-inlinetask-min-level 3))
> 	      (org-test-with-temp-text "*** TODO Inline"
> -		(org-test-with-backend test
> -		  (org-export-as 'test nil nil nil '(:with-tasks nil)))))))))
> +		(org-export-as (org-test-default-backend)
> +			       nil nil nil '(:with-tasks nil))))))))
> 
> 
> 
> @@ -2492,41 +2604,40 @@ Another text. (ref:text)
>   "Test `inner-template' translator specifications."
>   (should
>    (equal "Success!"
> -	  (let (org-export-registered-backends)
> -	    (org-export-define-backend 'test
> +	  (org-test-with-temp-text "* Headline"
> +	    (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders
> 	      '((inner-template . (lambda (contents info) "Success!"))
> -		(headline . (lambda (h c i) "Headline"))))
> -	    (org-test-with-temp-text "* Headline"
> -	      (org-export-as 'test)))))
> +		(headline . (lambda (h c i) "Headline"))))))))
>   ;; Inner template is applied even in a "body-only" export.
>   (should
>    (equal "Success!"
> -	  (let (org-export-registered-backends)
> -	    (org-export-define-backend 'test
> -	      '((inner-template . (lambda (contents info) "Success!"))
> -		(headline . (lambda (h c i) "Headline"))))
> -	    (org-test-with-temp-text "* Headline"
> -	      (org-export-as 'test nil nil 'body-only))))))
> +	  (org-test-with-temp-text "* Headline"
> +	    (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders '((inner-template . (lambda (c i) "Success!"))
> +			     (headline . (lambda (h c i) "Headline"))))
> +	     nil nil 'body-only)))))
> 
> (ert-deftest test-org-export/template ()
>   "Test `template' translator specifications."
>   (should
>    (equal "Success!"
> -	  (let (org-export-registered-backends)
> -	    (org-export-define-backend 'test
> -	      '((template . (lambda (contents info) "Success!"))
> -	        (headline . (lambda (h c i) "Headline"))))
> -	    (org-test-with-temp-text "* Headline"
> -	      (org-export-as 'test)))))
> +	  (org-test-with-temp-text "* Headline"
> +	    (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders '((template . (lambda (contents info) "Success!"))
> +			     (headline . (lambda (h c i) "Headline"))))))))
>   ;; Template is not applied in a "body-only" export.
>   (should-not
>    (equal "Success!"
> -	  (let (org-export-registered-backends)
> -	    (org-export-define-backend 'test
> -	      '((template . (lambda (contents info) "Success!"))
> -		(headline . (lambda (h c i) "Headline"))))
> -	    (org-test-with-temp-text "* Headline"
> -	      (org-export-as 'test nil nil 'body-only))))))
> +	  (org-test-with-temp-text "* Headline"
> +	    (org-export-as
> +	     (org-export-create-backend
> +	      :transcoders '((template . (lambda (contents info) "Success!"))
> +			     (headline . (lambda (h c i) "Headline"))))
> +	     nil nil 'body-only)))))
> 
> 
> 
> -- 
> 1.8.3.2
> 
> From 5c3b1765d219fc55edac393460128b9cd8d0d013 Mon Sep 17 00:00:00 2001
> From: Nicolas Goaziou <n.goaziou@gmail.com>
> Date: Mon, 24 Jun 2013 20:55:24 +0200
> Subject: [PATCH 2/2] Export back-ends: Apply changes to back-end structure
> 
> * lisp/ox-html.el (org-html--format-toc-headline): Make use of
>  anonymous back-ends.
> * lisp/ox-odt.el (org-odt-footnote-reference): Make use of anonymous
>  back-ends.
> (org-odt-format-label, org-odt-toc, org-odt-format-headline--wrap):
>  Use `org-export-with-backend' instead of
>  `org-export-with-translations'.
> * contrib/lisp/ox-freemind.el (org-freemind--build-node-contents): Use
>  `org-export-with-backend' instead of `org-export-with-translations'.
> ---
> contrib/lisp/ox-freemind.el | 11 +++---
> lisp/ox-html.el             | 19 ++++++-----
> lisp/ox-odt.el              | 83 +++++++++++++++++++++++----------------------
> 3 files changed, 57 insertions(+), 56 deletions(-)
> 
> diff --git a/contrib/lisp/ox-freemind.el b/contrib/lisp/ox-freemind.el
> index 4e90eff..d31c65f 100644
> --- a/contrib/lisp/ox-freemind.el
> +++ b/contrib/lisp/ox-freemind.el
> @@ -316,12 +316,11 @@ will result in following node:
> 	 (element-contents (org-element-contents element))
> 	 (section (assoc 'section element-contents))
> 	 (section-contents
> -	  (let* ((translations
> -		  (nconc (list (cons 'section
> -				     (lambda (section contents info)
> -				       contents)))
> -			 (plist-get info :translate-alist))))
> -	    (org-export-data-with-translations section translations info)))
> +	  (let ((backend (org-export-create-backend
> +			  :parent (org-export-backend-name
> +				   (plist-get info :back-end))
> +			  :translations '(section . (lambda (e c i) c)))))
> +	    (org-export-data-with-backend section backend info)))
> 	 (itemized-contents-p (let ((first-child-headline
> 				     (org-element-map element-contents
> 					 'headline 'identity info t)))
> diff --git a/lisp/ox-html.el b/lisp/ox-html.el
> index 9ce73c4..0c997b4 100644
> --- a/lisp/ox-html.el
> +++ b/lisp/ox-html.el
> @@ -1983,16 +1983,17 @@ INFO is a plist used as a communication channel."
> 					   headline-number "-"))))
> 	    ;; Body.
> 	    (concat section-number
> -		    (org-export-data-with-translations
> +		    (org-export-data-with-backend
> 		     (org-export-get-alt-title headline info)
> -		     ;; Ignore any footnote-reference, link,
> -		     ;; radio-target and target in table of contents.
> -		     (append
> -		      '((footnote-reference . ignore)
> -			(link . (lambda (link desc i) desc))
> -			(radio-target . (lambda (radio desc i) desc))
> -			(target . ignore))
> -		      (org-export-backend-translate-table 'html))
> +		     ;; Create an anonymous back-end that will ignore
> +		     ;; any footnote-reference, link, radio-target and
> +		     ;; target in table of contents.
> +		     (org-export-create-backend
> +		      :parent 'html
> +		      :transcoders '((footnote-reference . ignore)
> +				     (link . (lambda (object c i) c))
> +				     (radio-target . (lambda (object c i) c))
> +				     (target . ignore)))
> 		     info)
> 		    (and tags "&#xa0;&#xa0;&#xa0;") (org-html--tags tags)))))
> 
> diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
> index 1cccdc6..abf88cd 100644
> --- a/lisp/ox-odt.el
> +++ b/lisp/ox-odt.el
> @@ -1152,20 +1152,19 @@ See `org-odt--build-date-styles' for implementation details."
>   (let* ((title (org-export-translate "Table of Contents" :utf-8 info))
> 	 (headlines (org-export-collect-headlines
> 		     info (and (wholenump depth) depth)))
> -	 (translations (nconc (mapcar
> -			       (lambda (type)
> -				 (cons type (lambda (data contents info)
> -					      contents)))
> -			       (list 'radio-target))
> -			      (plist-get info :translate-alist))))
> +	 (backend (org-export-create-backend
> +		   :parent (org-export-backend-name
> +			    (plist-get info :back-end))
> +		   :transcoders (mapcar
> +				 (lambda (type) (cons type (lambda (d c i) c)))
> +				 (list 'radio-target)))))
>     (when headlines
>       (concat
>        (org-odt-begin-toc title depth)
>        (mapconcat
> 	(lambda (headline)
> 	  (let* ((entry (org-odt-format-headline--wrap
> -			 headline translations info
> -			 'org-odt-format-toc-headline))
> +			 headline backend info 'org-odt-format-toc-headline))
> 		 (level (org-export-get-relative-level headline info))
> 		 (style (format "Contents_20_%d" level)))
> 	    (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
> @@ -1731,18 +1730,22 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
> 	(t
> 	 (let* ((raw (org-export-get-footnote-definition
> 		      footnote-reference info))
> -		(translations
> -		 (cons (cons 'paragraph
> -			     (lambda (p c i)
> -			       (org-odt--format-paragraph
> -				p c "Footnote" "OrgFootnoteCenter"
> -				"OrgFootnoteQuotations")))
> -		       (org-export-backend-translate-table 'odt)))
> -		(def (let ((def (org-trim (org-export-data-with-translations
> -					   raw translations info))))
> -		       (if (eq (org-element-type raw) 'org-data) def
> -			 (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
> -				 "Footnote" def)))))
> +		(def
> +		 (let ((def (org-trim
> +			     (org-export-data-with-backend
> +			      raw
> +			      (org-export-create-backend
> +			       :parent 'odt
> +			       :transcoders
> +			       '((paragraph . (lambda (p c i)
> +						(org-odt--format-paragraph
> +						 p c "Footnote"
> +						 "OrgFootnoteCenter"
> +						 "OrgFootnoteQuotations")))))
> +			      info))))
> +		   (if (eq (org-element-type raw) 'org-data) def
> +		     (format "\n<text:p text:style-name=\"%s\">%s</text:p>"
> +			     "Footnote" def)))))
> 	   (funcall --format-footnote-definition n def))))))))
> 
> 
> @@ -1775,13 +1778,12 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
> 			    "<text:span text:style-name=\"%s\">%s</text:span>"
> 			    "OrgTag" tag)) tags " : "))))))
> 
> -(defun org-odt-format-headline--wrap (headline translations info
> -						 &optional format-function
> -						 &rest extra-keys)
> -  "Transcode a HEADLINE element from Org to ODT.
> -CONTENTS holds the contents of the headline.  INFO is a plist
> -holding contextual information."
> -  (setq translations (or translations (plist-get info :translate-alist)))
> +(defun org-odt-format-headline--wrap (headline backend info
> +					       &optional format-function
> +					       &rest extra-keys)
> +  "Transcode a HEADLINE element using BACKEND.
> +INFO is a plist holding contextual information."
> +  (setq backend (or backend (plist-get info :back-end)))
>   (let* ((level (+ (org-export-get-relative-level headline info)))
> 	 (headline-number (org-export-get-headline-number headline info))
> 	 (section-number (and (org-export-numbered-headline-p headline info)
> @@ -1789,13 +1791,13 @@ holding contextual information."
> 					 headline-number ".")))
> 	 (todo (and (plist-get info :with-todo-keywords)
> 		    (let ((todo (org-element-property :todo-keyword headline)))
> -		      (and todo (org-export-data-with-translations
> -				 todo translations info)))))
> +		      (and todo
> +			   (org-export-data-with-backend todo backend info)))))
> 	 (todo-type (and todo (org-element-property :todo-type headline)))
> 	 (priority (and (plist-get info :with-priority)
> 			(org-element-property :priority headline)))
> -	 (text (org-export-data-with-translations
> -		(org-element-property :title headline) translations info))
> +	 (text (org-export-data-with-backend
> +		(org-element-property :title headline) backend info))
> 	 (tags (and (plist-get info :with-tags)
> 		    (org-export-get-tags headline info)))
> 	 (headline-label (concat "sec-" (mapconcat 'number-to-string
> @@ -1805,7 +1807,7 @@ holding contextual information."
> 			   ((functionp org-odt-format-headline-function)
> 			    (function*
> 			     (lambda (todo todo-type priority text tags
> -					   &allow-other-keys)
> +				      &allow-other-keys)
> 			       (funcall org-odt-format-headline-function
> 					todo todo-type priority text tags))))
> 			   (t 'org-odt-format-headline))))
> @@ -1934,7 +1936,7 @@ holding contextual information."
>     (let ((format-function
> 	   (function*
> 	    (lambda (todo todo-type priority text tags
> -			  &key contents &allow-other-keys)
> +		     &key contents &allow-other-keys)
> 	      (funcall org-odt-format-inlinetask-function
> 		       todo todo-type priority text tags contents)))))
>       (org-odt-format-headline--wrap
> @@ -2149,15 +2151,14 @@ SHORT-CAPTION are strings."
> 	 ;; will do.
> 	 (short-caption
> 	  (let ((short-caption (or short-caption caption))
> -		(translations (nconc (mapcar
> -				      (lambda (type)
> -					(cons type (lambda (data contents info)
> -						     contents)))
> -				      org-element-all-objects)
> -				     (plist-get info :translate-alist))))
> +		(backend (org-export-create-backend
> +			  :parent (org-export-backend-name
> +				   (plist-get info :back-end))
> +			  :transcoders
> +			  (mapcar (lambda (type) (cons type (lambda (o c i) c)))
> +				  org-element-all-objects))))
> 	    (when short-caption
> -	      (org-export-data-with-translations short-caption
> -						 translations info)))))
> +	      (org-export-data-with-backend short-caption backend info)))))
>     (when (or label caption)
>       (let* ((default-category
> 	       (case (org-element-type element)
> -- 
> 1.8.3.2
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-03 21:48 ` Carsten Dominik
@ 2013-07-04 12:39   ` Nicolas Goaziou
  2013-07-04 12:48     ` Bastien
  2013-07-04 13:42     ` Nicolas Goaziou
  0 siblings, 2 replies; 15+ messages in thread
From: Nicolas Goaziou @ 2013-07-04 12:39 UTC (permalink / raw)
  To: Carsten Dominik; +Cc: Org Mode List

Hello,

Carsten Dominik <carsten.dominik@gmail.com> writes:

> this does look like a good change - and hopefully few enough external backends
> have been written for this patch to not pose a problem.

Its almost exclusively an internal change. Only the removal of
`org-export-with-translations' might bite back-end developers, but
I would be surprised if this feature was used in the wild.

I applied the patches. Now, onto orgtbl-to-*.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 12:39   ` Nicolas Goaziou
@ 2013-07-04 12:48     ` Bastien
  2013-07-04 12:55       ` Nicolas Goaziou
  2013-07-04 13:42     ` Nicolas Goaziou
  1 sibling, 1 reply; 15+ messages in thread
From: Bastien @ 2013-07-04 12:48 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik

Hi Nicolas,

Nicolas Goaziou <n.goaziou@gmail.com> writes:

> I applied the patches.

Is there anything that backend developers (both in core and contrib/)
should change in their code?

Thanks,

-- 
 Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 12:48     ` Bastien
@ 2013-07-04 12:55       ` Nicolas Goaziou
  2013-07-04 12:59         ` Bastien
  0 siblings, 1 reply; 15+ messages in thread
From: Nicolas Goaziou @ 2013-07-04 12:55 UTC (permalink / raw)
  To: Bastien; +Cc: Org Mode List, Carsten Dominik

Hello,

Bastien <bzg@gnu.org> writes:

> Is there anything that backend developers (both in core and contrib/)
> should change in their code?

I don't think so. AFAICT, the only changes needed were in
ox-html/ox-odt/ox-freemind; I took care of them.

Again, you only need to change something if
`org-export-with-translations' appears in the code. See
758a545fb6da5e3314d609472c0defcee05cf703 for examples.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 12:55       ` Nicolas Goaziou
@ 2013-07-04 12:59         ` Bastien
  2013-07-04 13:32           ` Nicolas Goaziou
  0 siblings, 1 reply; 15+ messages in thread
From: Bastien @ 2013-07-04 12:59 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik

Nicolas Goaziou <n.goaziou@gmail.com> writes:

> Again, you only need to change something if
> `org-export-with-translations' appears in the code. See
> 758a545fb6da5e3314d609472c0defcee05cf703 for examples.

Okay, thanks.

(This is `org-export-data-with-translations', for those who
check this thread.)

I checked and no exporter uses this right now.

-- 
 Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 12:59         ` Bastien
@ 2013-07-04 13:32           ` Nicolas Goaziou
  0 siblings, 0 replies; 15+ messages in thread
From: Nicolas Goaziou @ 2013-07-04 13:32 UTC (permalink / raw)
  To: Bastien; +Cc: Org Mode List, Carsten Dominik

Bastien <bzg@gnu.org> writes:

> Nicolas Goaziou <n.goaziou@gmail.com> writes:
>
>> Again, you only need to change something if
>> `org-export-with-translations' appears in the code. See
>> 758a545fb6da5e3314d609472c0defcee05cf703 for examples.
>
> Okay, thanks.
>
> (This is `org-export-data-with-translations', for those who
> check this thread.)

Indeed. Thanks for correcting function's name.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 12:39   ` Nicolas Goaziou
  2013-07-04 12:48     ` Bastien
@ 2013-07-04 13:42     ` Nicolas Goaziou
  2013-07-04 14:01       ` Bastien
  2013-07-05 17:01       ` Eric S Fraga
  1 sibling, 2 replies; 15+ messages in thread
From: Nicolas Goaziou @ 2013-07-04 13:42 UTC (permalink / raw)
  To: Carsten Dominik; +Cc: Org Mode List

Completing myself,

> I applied the patches. Now, onto orgtbl-to-*.

Here is an example illustrating the new features provided by quick and
anonymous export back-ends:

#+begin_src emacs-lisp
(defun my-orgtbl-to-csv ()
  "Insert a comma separated version of Org table at point.
The table will be inserted at the end of the buffer.  This
function makes no assumption about the major mode in the current
back-end."
  (interactive)
  (unless (org-at-table-p) (user-error "Not at an Org table"))
  (require 'ox-org)
  (let ((table
         (org-export-string-as
          ;; Export the following table...
          (buffer-substring-no-properties (org-table-begin) (org-table-end))
          ;; ... with the following back-end.
          (org-export-create-backend
           :parent 'org
           :transcoders
           '((table . (lambda (table contents info) contents))
             (table-row . (lambda (row contents info)
                            ;; Ignore table separators.
                            (and (eq (org-element-property :type row) 'standard)
                                 (concat contents "\n"))))
             (table-cell . (lambda (cell contents info)
                             (let ((field (org-quote-csv-field contents)))
                               (if (org-export-get-next-element cell info)
                                   (concat field ",")
                                 field))))))
          ;; Focus on the table only.
          'body-only)))
    ;; Insert TABLE at an appropriate location.  For the sake of
    ;; example, it will be point-max.
    (goto-char (point-max))
    (insert table)))
#+end_src

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 13:42     ` Nicolas Goaziou
@ 2013-07-04 14:01       ` Bastien
  2013-07-04 14:45         ` Carsten Dominik
  2013-07-05 17:01       ` Eric S Fraga
  1 sibling, 1 reply; 15+ messages in thread
From: Bastien @ 2013-07-04 14:01 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik

Nicolas Goaziou <n.goaziou@gmail.com> writes:

> Here is an example illustrating the new features provided by quick and
> anonymous export back-ends:

This is very neat indeed!

-- 
 Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 14:01       ` Bastien
@ 2013-07-04 14:45         ` Carsten Dominik
  0 siblings, 0 replies; 15+ messages in thread
From: Carsten Dominik @ 2013-07-04 14:45 UTC (permalink / raw)
  To: Bastien; +Cc: Org Mode List, Nicolas Goaziou


On 4.7.2013, at 16:01, Bastien <bzg@gnu.org> wrote:

> Nicolas Goaziou <n.goaziou@gmail.com> writes:
> 
>> Here is an example illustrating the new features provided by quick and
>> anonymous export back-ends:
> 
> This is very neat indeed!

Wow, indeed. 

- Carsten


> 
> -- 
> Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-04 13:42     ` Nicolas Goaziou
  2013-07-04 14:01       ` Bastien
@ 2013-07-05 17:01       ` Eric S Fraga
  2013-07-05 17:09         ` Bastien
  1 sibling, 1 reply; 15+ messages in thread
From: Eric S Fraga @ 2013-07-05 17:01 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List, Carsten Dominik

Nicolas Goaziou <n.goaziou@gmail.com> writes:

> Completing myself,
>
>> I applied the patches. Now, onto orgtbl-to-*.
>
> Here is an example illustrating the new features provided by quick and
> anonymous export back-ends:

Thanks for this!  

Funnily enough, I had to export a table in CSV format just last week and
this function would have been great.  As it is, a simple regex
replacement did the job but just not as elegant...

-- 
: Eric S Fraga (0xFFFCF67D), Emacs 24.3.50.1, Org release_8.0.3-324-gb61ef4

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-05 17:01       ` Eric S Fraga
@ 2013-07-05 17:09         ` Bastien
  2013-07-08 11:15           ` Eric S Fraga
  0 siblings, 1 reply; 15+ messages in thread
From: Bastien @ 2013-07-05 17:09 UTC (permalink / raw)
  To: Eric S Fraga; +Cc: Org Mode List, Nicolas Goaziou, Carsten Dominik

Hi Eric,

Eric S Fraga <e.fraga@ucl.ac.uk> writes:

> Funnily enough, I had to export a table in CSV format just last week and
> this function would have been great.

Well, you do have

M-x org-table-export RET table.csv

and it exports to csv already...

-- 
 Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-05 17:09         ` Bastien
@ 2013-07-08 11:15           ` Eric S Fraga
  2013-07-08 13:59             ` Bastien
  0 siblings, 1 reply; 15+ messages in thread
From: Eric S Fraga @ 2013-07-08 11:15 UTC (permalink / raw)
  To: Bastien; +Cc: Org Mode List, Nicolas Goaziou, Carsten Dominik

Bastien <bzg@gnu.org> writes:

> Hi Eric,
>
> Eric S Fraga <e.fraga@ucl.ac.uk> writes:
>
>> Funnily enough, I had to export a table in CSV format just last week and
>> this function would have been great.
>
> Well, you do have
>
> M-x org-table-export RET table.csv
> 
> and it exports to csv already...

Arggghhh.  Yes, this is there.  I missed it because I did not give a
.csv file name; I gave a file name without extension for some
reason.  It seems that the export command's options for format depend on
the name of the file so I was only given an export to TSV option.

Playing around now, it seems that if any extension is given (I tried
.csv, .data, .txt, .blah, .foobar), then export-to-csv is the
default.  If no extension is given, export-to-tsv is the only
option.  Not what I expected!

Thanks,
eric

-- 
: Eric S Fraga (0xFFFCF67D), Emacs 24.3.50.1, Org release_8.0.3-225-g8e9c10

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-08 11:15           ` Eric S Fraga
@ 2013-07-08 13:59             ` Bastien
  2013-07-09 16:56               ` Eric S Fraga
  0 siblings, 1 reply; 15+ messages in thread
From: Bastien @ 2013-07-08 13:59 UTC (permalink / raw)
  To: Eric S Fraga; +Cc: Nicolas Goaziou, Org Mode List, Carsten Dominik

Hi Eric,

Eric S Fraga <e.fraga@ucl.ac.uk> writes:

> Playing around now, it seems that if any extension is given (I tried
> .csv, .data, .txt, .blah, .foobar), then export-to-csv is the
> default.  If no extension is given, export-to-tsv is the only
> option.  Not what I expected!

See the docstring the `org-table-export'.

You can change the default table export format with either
`org-table-export-default-format' or TABLE_EXPORT_FORMAT.

HTH,

-- 
 Bastien

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC] Changing internal representation of back-ends to defstructs
  2013-07-08 13:59             ` Bastien
@ 2013-07-09 16:56               ` Eric S Fraga
  0 siblings, 0 replies; 15+ messages in thread
From: Eric S Fraga @ 2013-07-09 16:56 UTC (permalink / raw)
  To: Bastien; +Cc: Nicolas Goaziou, Org Mode List, Carsten Dominik

Bastien <bzg@gnu.org> writes:

> Hi Eric,
>
> Eric S Fraga <e.fraga@ucl.ac.uk> writes:
>
>> Playing around now, it seems that if any extension is given (I tried
>> .csv, .data, .txt, .blah, .foobar), then export-to-csv is the
>> default.  If no extension is given, export-to-tsv is the only
>> option.  Not what I expected!
>
> See the docstring the `org-table-export'.

Sure: this explains why I end up with -tsv when no extension is
given.  However, it doesn't explain why I end up with -csv for
extensions such as .blah and .foobar!  Just sayin' ;-)

> You can change the default table export format with either
> `org-table-export-default-format' or TABLE_EXPORT_FORMAT.

Thanks.

-- 
: Eric S Fraga (0xFFFCF67D), Emacs 24.3.50.1, Org release_8.0.3-324-gb61ef4

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2013-07-09 16:56 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-03 20:57 [RFC] Changing internal representation of back-ends to defstructs Nicolas Goaziou
2013-07-03 21:48 ` Carsten Dominik
2013-07-04 12:39   ` Nicolas Goaziou
2013-07-04 12:48     ` Bastien
2013-07-04 12:55       ` Nicolas Goaziou
2013-07-04 12:59         ` Bastien
2013-07-04 13:32           ` Nicolas Goaziou
2013-07-04 13:42     ` Nicolas Goaziou
2013-07-04 14:01       ` Bastien
2013-07-04 14:45         ` Carsten Dominik
2013-07-05 17:01       ` Eric S Fraga
2013-07-05 17:09         ` Bastien
2013-07-08 11:15           ` Eric S Fraga
2013-07-08 13:59             ` Bastien
2013-07-09 16:56               ` Eric S Fraga

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).