emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* language support for m4
       [not found] <1363290911.1944832.1522910146358.ref@mail.yahoo.com>
@ 2018-04-05  6:35 ` Brad Knotwell
  2018-04-05 14:58   ` Nicolas Goaziou
  0 siblings, 1 reply; 2+ messages in thread
From: Brad Knotwell @ 2018-04-05  6:35 UTC (permalink / raw)
  To: emacs-orgmode


[-- Attachment #1.1: Type: text/plain, Size: 1104 bytes --]

Good evening all--
The attached file adds m4 support.  It was tested against org-9.1.7 and used ob-sed.el and ob-shell.el for inspiration.  Both code execution and tangling have been tested with simple inputs as well as tables (easiest way to verify correctness is to inspect the tangled output).  It was good fun for an evening project (converting the tables to m4 list syntax was the trickiest part and required a foray into ob-shell.el; pcase is a nifty function).
When putting it together, I debated between putting the definitions representing variables in as -DNAME=value pairs in the command-line or putting them in the body of the generate file.  Even though it's a bit uglier, I decided to put them in the body of the file as it makes the generated file capable of being run by hand.
Finally, thanks for the work on org-mode and babel.  Vanilla org-mode has made my meeting notes are significantly easier and PDF exports look terrific.  Likewise, while I've written numerous documents using R/knitr, I appreciate having a literate environment that supports additional languages.
--Brad

[-- Attachment #1.2: Type: text/html, Size: 1366 bytes --]

[-- Attachment #2: ob-m4.el --]
[-- Type: application/octet-stream, Size: 6610 bytes --]

;;; ob-m4.el --- Babel Functions for m4 scripts    -*- lexical-binding: t; -*-

;; Copyright (C) 2015-2018 Free Software Foundation, Inc.

;; Author: Brad Knotwell
;; Keywords: literate programming, reproducible research
;; Version: 0.1.0

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Provides a way to evaluate m4 scripts in Org mode.

;;; Usage:

;; Add to your Emacs config:

;; (org-babel-do-load-languages
;;  'org-babel-load-languages
;;  '((m4 . t)))

;; In addition to the normal header arguments, ob-m4 also provides
;; two specific options:
;;  :cmd-line -- allows a user to append arguments to the call
;;  :prefix-buildins -- calls with the -P option to automatically
;;       prefix all built-in macros with 'm4_'
;; Note:  it is a bad idea to explicitly pass the "-P" option in
;; :cmd-line to enable prefixing as any arguments specified via
;; :var NAME=value will be prefixed incorrectly.
;;
;; Explaining this with a bit more detail, all :var NAME=value arguments are
;; prepended to the body as definitions:
;;     define(NAME,value)
;;            or
;;     m4_define(NAME,value) iff :prefix-builtins is set
;;
;; Note:  the :prefix-builtin option is incompatible with Posix
;;        compliant m4 implementations.  This is fine for two reasons:
;;     * anyone using a Posix compliant utility is fine by default as
;;       the -P option is omitted
;;     * GNU's m4 implementation is (essentially) standard at this point
(require 'ob)

(defvar org-babel-m4-command "m4"
  "Name of the m4 executable command.")

(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("m4" . "m4"))

(defconst org-babel-header-args:m4
  '((:cmd-line . :any)
    (:prefix-builtins))
  "M4 specific header arguments.")

(defvar org-babel-default-header-args:m4 '()
  "Default arguments for evaluating a m4 source block.")

;; build the passed-in macro definitions as a single string to prepend to the body
;; the hocus-pocus with :prefix-builtins is necessary for the -P option to work

(defun __org-babel-m4-prefix (params) (if (assq :prefix-builtins params) "m4_"))
(defun org-babel--variable-assignment:m4_generic (params varname values)
  (concat (__org-babel-m4-prefix params)
	  "define(" (format "%s" varname) "," (format "%s" values) ")"
	  (__org-babel-m4-prefix params)
	  "dnl\n"))

(defun org-babel--variable-assignment:m4_list (params varname values)
  (concat (__org-babel-m4-prefix params)
	  "define(" (format "%s,[" varname)
	  (mapconcat (lambda (value)
		       (if (= (length value) 1) (format "%s" (car value))
		       (concat "["
			       (mapconcat (lambda (x) (format "%s" x)) value ",")
			       "]"))) values ",")
	  "])" (__org-babel-m4-prefix params) "dnl\n"))

(defun org-babel--variable-assignments:m4 (params varnames values)
  "Represent the parameters as m4 definitions"
  (pcase values
    (`(,_ . ,_) (org-babel--variable-assignment:m4_list params varnames values))
    (_ (org-babel--variable-assignment:m4_generic params varnames values))))

(defun org-babel-variable-assignments:m4 (params)
  (apply 'concat (mapcar (lambda (pair) (org-babel--variable-assignments:m4
					 params (car pair) (cdr pair)))
			 (org-babel--get-vars params))))

;; required to make tangling work
;; the final "\n" is required to make m4 work as the body doesn't end in a newline
(defun org-babel-expand-body:m4 (body params)
  "Expand BODY according to PARAMS, return the expanded body."
  (concat (org-babel-variable-assignments:m4 params) body "\n"))

(defun org-babel-execute:m4 (body params)
  "Execute a block of m4 code with Org Babel.
BODY is the source inside a m4 source block and PARAMS is an
association list over the source block configurations.  This
function is called by `org-babel-execute-src-block'."
  (message "executing m4 source code block")
  (let* ((result-params (cdr (assq :result-params params)))
         (cmd-line (cdr (assq :cmd-line params)))
	 (prefix-builtins (assq :prefix-builtins params))
	 (code-file (let ((file (org-babel-temp-file "m4-")))
                      (with-temp-file file
			(insert (org-babel-expand-body:m4 body params) file)) file))
	 (stdin (let ((stdin (cdr (assq :stdin params))))
		   (when stdin
		     (let ((tmp (org-babel-temp-file "m4-stdin-"))
			   (res (org-babel-ref-resolve stdin)))
		       (with-temp-file tmp
			 (insert res))
		       tmp))))
         (cmd (mapconcat #'identity
			 (remq nil
			       (list org-babel-m4-command
				     cmd-line (if prefix-builtins "-P") "<" code-file))
			 " ")))
    (org-babel-reassemble-table
     (let ((results
            (cond
             (stdin (with-temp-buffer
                      (call-process-shell-command cmd stdin (current-buffer))
                      (buffer-string)))
             (t (org-babel-eval cmd "")))))
       (when results
         (org-babel-result-cond result-params
	   results
	   (let ((tmp (org-babel-temp-file "m4-results-")))
	     (with-temp-file tmp (insert results))
	     (org-babel-import-elisp-from-file tmp)))))
     (org-babel-pick-name
      (cdr (assq :colname-names params)) (cdr (assq :colnames params)))
     (org-babel-pick-name
      (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))

(provide 'ob-m4)
;;; ob-m4.el ends here

;; Test examples below:
;;
;;
;; #+begin_src m4 :results output :var XXXX='hello' :var YYYY='world'
;; define(this1, one will expand)dnl
;; m4_define(this2,one will not expand)m4_dnl
;; this1
;; this2
;; XXXX
;; YYYY
;; ZZZZ
;; #+end_src

;; #+RESULTS:
;; : m4_define(this2,one will not expand)m4_dnl
;; : one will expand
;; : this2
;; : hello
;; : world
;; : ZZZZ

;; #+begin_src m4 :prefix-builtins :results output :var XXXX='hello' :var YYYY='world'
;; define(this1, one will not expand)dnl
;; m4_define(this2,one will expand)m4_dnl
;; this1
;; this2
;; XXXX
;; YYYY
;; ZZZZ
;; #+end_src

;; #+RESULTS:
;; : define(this1, one will not expand)dnl
;; : this1
;; : one will expand
;; : hello
;; : world
;; : ZZZZ

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

* Re: language support for m4
  2018-04-05  6:35 ` language support for m4 Brad Knotwell
@ 2018-04-05 14:58   ` Nicolas Goaziou
  0 siblings, 0 replies; 2+ messages in thread
From: Nicolas Goaziou @ 2018-04-05 14:58 UTC (permalink / raw)
  To: Brad Knotwell; +Cc: emacs-orgmode

Hello,

Brad Knotwell <bknotwell@yahoo.com> writes:

> The attached file adds m4 support.  It was tested against org-9.1.7
> and used ob-sed.el and ob-shell.el for inspiration.  Both code
> execution and tangling have been tested with simple inputs as well as
> tables (easiest way to verify correctness is to inspect the tangled
> output).  It was good fun for an evening project (converting the
> tables to m4 list syntax was the trickiest part and required a foray
> into ob-shell.el; pcase is a nifty function).
> When putting it together, I debated between putting the definitions
> representing variables in as -DNAME=value pairs in the command-line or
> putting them in the body of the generate file.  Even though it's a bit
> uglier, I decided to put them in the body of the file as it makes the
> generated file capable of being run by hand.

Thank you. Some comments follow.

First, what is your status wrt FSF papers. If you haven't signed them,
this can go in contrib/ until it is sorted out.

> ;; Copyright (C) 2015-2018 Free Software Foundation, Inc.

The copyright years are probably not accurate.

> ;; build the passed-in macro definitions as a single string to prepend to the body
> ;; the hocus-pocus with :prefix-builtins is necessary for the -P option to work

Could you make two separate sentences? The above is a bit annoying to
grok.

> (defun __org-babel-m4-prefix (params) (if (assq :prefix-builtins params) "m4_"))

The name of the function is not right. It should probably be
`org-babel--m4-prefix'.  Also, it needs a docstring.

Nitpick:

  (and (assq :prefix-builtins params) "m4_")

> (defun org-babel--variable-assignment:m4_generic (params varname values)

Ditto about the missing docstring.

>   (concat (__org-babel-m4-prefix params)
> 	  "define(" (format "%s" varname) "," (format "%s" values) ")"
> 	  (__org-babel-m4-prefix params)
> 	  "dnl\n"))

  (format "define(%s,%s)" varname values)

>
> (defun org-babel--variable-assignment:m4_list (params varname values)

Ditto.

>   (concat (__org-babel-m4-prefix params)
> 	  "define(" (format "%s,[" varname)

Per above:

  (format "define(%s,[%s])"
          varname
          (mapconcat ...))

> 	  (mapconcat (lambda (value)
> 		       (if (= (length value) 1) (format "%s" (car value))

  (and (= (length value) 1) (format "%s" (car values)))

Aren't `values' strings already?

> 		       (concat "["
> 			       (mapconcat (lambda (x) (format "%s" x)) value ",")
> 			       "]"))) values ",")
> 	  "])" (__org-babel-m4-prefix params) "dnl\n"))
>
> (defun org-babel--variable-assignments:m4 (params varnames values)
>   "Represent the parameters as m4 definitions"
>   (pcase values
>     (`(,_ . ,_) (org-babel--variable-assignment:m4_list params varnames values))
>     (_ (org-babel--variable-assignment:m4_generic params varnames values))))
>
> (defun org-babel-variable-assignments:m4 (params)
>   (apply 'concat (mapcar (lambda (pair) (org-babel--variable-assignments:m4
> 					 params (car pair) (cdr pair)))
> 			 (org-babel--get-vars params))))

  (apply #'concat ...)
  
>
> ;; required to make tangling work
> ;; the final "\n" is required to make m4 work as the body doesn't end in a newline

Missing capitals and full stops.

> ;; Test examples below:

These could go into a "test-ob-m4.el" file.

Regards,

-- 
Nicolas Goaziou

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

end of thread, other threads:[~2018-04-06 21:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1363290911.1944832.1522910146358.ref@mail.yahoo.com>
2018-04-05  6:35 ` language support for m4 Brad Knotwell
2018-04-05 14:58   ` Nicolas Goaziou

Code repositories for project(s) associated with this 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).