view lisp/org/ob-haskell.el @ 110410:f2e111723c3a

Merge changes made in Gnus trunk. Reimplement nnimap, and do tweaks to the rest of the code to support that. * gnus-int.el (gnus-finish-retrieve-group-infos) (gnus-retrieve-group-data-early): New functions. * gnus-range.el (gnus-range-nconcat): New function. * gnus-start.el (gnus-get-unread-articles): Support early retrieval of data. (gnus-read-active-for-groups): Support finishing the early retrieval of data. * gnus-sum.el (gnus-summary-move-article): Pass the move-to group name if the move is internal, so that nnimap can do fast internal moves. * gnus.el (gnus-article-special-mark-lists): Add uid/active tuples, for nnimap usage. * nnimap.el: Rewritten. * nnmail.el (nnmail-inhibit-default-split-group): New internal variable to allow the mail splitting to not return a default group. This is useful for nnimap, which will leave unmatched mail in the inbox. * utf7.el (utf7-encode): Autoload. Implement shell connection. * nnimap.el (nnimap-open-shell-stream): New function. (nnimap-open-connection): Use it. Get the number of lines by using BODYSTRUCTURE. (nnimap-transform-headers): Get the number of lines in each message. (nnimap-retrieve-headers): Query for BODYSTRUCTURE so that we get the number of lines. Not all servers return UIDNEXT. Work past this problem. Remove junk from end of file. Fix typo in "bogus" section. Make capabilties be case-insensitive. Require cl when compiling. Don't bug out if the LIST command doesn't have any parameters. 2010-09-17 Knut Anders Hatlen <kahatlen@gmail.com> (tiny change) * nnimap.el (nnimap-get-groups): Don't bug out if the LIST command doesn't have any parameters. (mm-text-html-renderer): Document gnus-article-html. 2010-09-17 Julien Danjou <julien@danjou.info> (tiny fix) * mm-decode.el (mm-text-html-renderer): Document gnus-article-html. * dgnushack.el: Define netrc-credentials. If the user doesn't have a /etc/services, supply some sensible port defaults. Have `unseen-or-unread' select an unread unseen article first. (nntp-open-server): Return whether the open was successful or not. Throughout all files, replace (save-excursion (set-buffer ...)) with (with-current-buffer ... ). Save result so that it doesn't say "failed" all the time. Add ~/.authinfo to the default, since that's probably most useful for users. Don't use the "finish" method when we're reading from the agent. Add some more nnimap-relevant agent stuff to nnagent.el. * nnimap.el (nnimap-with-process-buffer): Removed. Revert one line that was changed by mistake in the last checkin. (nnimap-open-connection): Don't error out when we can't make a connection nnimap-related changes to avoid bugging out if we can't contact a server. * gnus-start.el (gnus-get-unread-articles): Don't try to scan groups from methods that are denied. * nnimap.el (nnimap-possibly-change-group): Return nil if we can't log in. (nnimap-finish-retrieve-group-infos): Make sure we're not waiting for nothing. * gnus-sum.el (gnus-select-newsgroup): Indent.
author Katsumi Yamaoka <yamaoka@jpl.org>
date Sat, 18 Sep 2010 10:02:19 +0000
parents a150e8a14679
children 5cb272c831e8
line wrap: on
line source

;;; ob-haskell.el --- org-babel functions for haskell evaluation

;; Copyright (C) 2009, 2010  Free Software Foundation, Inc.

;; Author: Eric Schulte
;; Keywords: literate programming, reproducible research
;; Homepage: http://orgmode.org
;; Version: 7.01

;; 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 <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Org-Babel support for evaluating haskell source code.  This one will
;; be sort of tricky because haskell programs must be compiled before
;; they can be run, but haskell code can also be run through an
;; interactive interpreter.
;;
;; For now lets only allow evaluation using the haskell interpreter.

;;; Requirements:

;; - haskell-mode :: http://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode
;;
;; - inf-haskell :: http://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode
;;
;; - (optionally) lhs2tex :: http://people.cs.uu.nl/andres/lhs2tex/

;;; Code:
(require 'ob)
(require 'ob-comint)
(require 'comint)
(eval-when-compile (require 'cl))

(declare-function org-remove-indentation "org" (code &optional n))
(declare-function haskell-mode "ext:haskell-mode" ())
(declare-function run-haskell "ext:inf-haskell" (&optional arg))
(declare-function inferior-haskell-load-file
		  "ext:inf-haskell" (&optional reload))

(add-to-list 'org-babel-tangle-lang-exts '("haskell" . "hs"))

(defvar org-babel-default-header-args:haskell '())

(defvar org-babel-haskell-lhs2tex-command "lhs2tex")

(defvar org-babel-haskell-eoe "\"org-babel-haskell-eoe\"")

(defun org-babel-expand-body:haskell (body params &optional processed-params)
  "Expand BODY according to PARAMS, return the expanded body."
  (let ((vars (nth 1 (or processed-params (org-babel-process-params params)))))
    (concat
     (mapconcat
      (lambda (pair) (format "let %s = %s"
			(car pair)
			(org-babel-haskell-var-to-haskell (cdr pair))))
      vars "\n") "\n" body "\n")))

(defun org-babel-execute:haskell (body params)
  "Execute a block of Haskell code."
  (let* ((processed-params (org-babel-process-params params))
         (session (nth 0 processed-params))
         (vars (nth 1 processed-params))
         (result-type (nth 3 processed-params))
         (full-body (org-babel-expand-body:haskell body params processed-params))
         (session (org-babel-haskell-initiate-session session params))
         (raw (org-babel-comint-with-output
		  (session org-babel-haskell-eoe t full-body)
                (insert (org-babel-trim full-body))
                (comint-send-input nil t)
                (insert org-babel-haskell-eoe)
                (comint-send-input nil t)))
         (results (mapcar
                   #'org-babel-haskell-read-string
                   (cdr (member org-babel-haskell-eoe
                                (reverse (mapcar #'org-babel-trim raw)))))))
    (org-babel-reassemble-table
     (cond 
      ((equal result-type 'output)
       (mapconcat #'identity (reverse (cdr results)) "\n"))
      ((equal result-type 'value)
       (org-babel-haskell-table-or-string (car results))))
     (org-babel-pick-name (nth 4 processed-params) (cdr (assoc :colnames params)))
     (org-babel-pick-name (nth 5 processed-params) (cdr (assoc :rownames params))))))

(defun org-babel-haskell-read-string (string)
  "Strip \\\"s from around a haskell string."
  (if (string-match "^\"\\([^\000]+\\)\"$" string)
      (match-string 1 string)
    string))

(defun org-babel-haskell-initiate-session (&optional session params)
  "Initiate a haskell session.
If there is not a current inferior-process-buffer in SESSION
then create one.  Return the initialized session."
  (require 'inf-haskell)
  (or (get-buffer "*haskell*")
      (save-window-excursion (run-haskell) (sleep-for 0.25) (current-buffer))))

(defun org-babel-load-session:haskell
  (session body params &optional processed-params)
  "Load BODY into SESSION."
  (save-window-excursion
    (let* ((buffer (org-babel-prep-session:haskell
		    session params processed-params))
           (load-file (concat (make-temp-file "org-babel-haskell-load") ".hs")))
      (with-temp-buffer
        (insert body) (write-file load-file)
        (haskell-mode) (inferior-haskell-load-file))
      buffer)))

(defun org-babel-prep-session:haskell
  (session params &optional processed-params)
  "Prepare SESSION according to the header arguments in PARAMS."
  (save-window-excursion
    (let ((pp (or processed-params (org-babel-process-params params)))
	  (buffer (org-babel-haskell-initiate-session session)))
      (org-babel-comint-in-buffer buffer
      	(mapc
      	 (lambda (pair)
      	   (insert (format "let %s = %s"
      			   (car pair)
      			   (org-babel-haskell-var-to-haskell (cdr pair))))
      	   (comint-send-input nil t))
      	 (nth 1 pp)))
      (current-buffer))))

(defun org-babel-haskell-table-or-string (results)
  "Convert RESULTS to an Emacs-lisp table or string.
If RESULTS look like a table, then convert them into an
Emacs-lisp table, otherwise return the results as a string."
  (org-babel-read
   (if (and (stringp results) (string-match "^\\[.+\\]$" results))
       (org-babel-read
        (concat "'"
                (replace-regexp-in-string
                 "\\[" "(" (replace-regexp-in-string
                            "\\]" ")" (replace-regexp-in-string
                                       "," " " (replace-regexp-in-string
                                                "'" "\"" results))))))
     results)))

(defun org-babel-haskell-var-to-haskell (var)
  "Convert an elisp value VAR into a haskell variable.
The elisp VAR is converted to a string of haskell source code
specifying a variable of the same value."
  (if (listp var)
      (concat "[" (mapconcat #'org-babel-haskell-var-to-haskell var ", ") "]")
    (format "%S" var)))

(defvar org-src-preserve-indentation)
(defun org-babel-haskell-export-to-lhs (&optional arg)
  "Export to a .lhs file with all haskell code blocks escaped.
When called with a prefix argument the resulting
.lhs file will be exported to a .tex file.  This function will
create two new files, base-name.lhs and base-name.tex where
base-name is the name of the current org-mode file.

Note that all standard Babel literate programming
constructs (header arguments, no-web syntax etc...) are ignored."
  (interactive "P")
  (let* ((contents (buffer-string))
         (haskell-regexp
          (concat "^\\([ \t]*\\)#\\+begin_src[ \t]haskell*\\(.*\\)?[\r\n]"
                  "\\([^\000]*?\\)[\r\n][ \t]*#\\+end_src.*"))
         (base-name (file-name-sans-extension (buffer-file-name)))
         (tmp-file (make-temp-file "ob-haskell"))
         (tmp-org-file (concat tmp-file ".org"))
         (tmp-tex-file (concat tmp-file ".tex"))
         (lhs-file (concat base-name ".lhs"))
         (tex-file (concat base-name ".tex"))
         (command (concat org-babel-haskell-lhs2tex-command " " lhs-file " > " tex-file))
         (preserve-indentp org-src-preserve-indentation)
         indentation)
    ;; escape haskell source-code blocks
    (with-temp-file tmp-org-file
      (insert contents)
      (goto-char (point-min))
      (while (re-search-forward haskell-regexp nil t)
        (save-match-data (setq indentation (length (match-string 1))))
        (replace-match (save-match-data
                         (concat
                          "#+begin_latex\n\\begin{code}\n"
                          (if (or preserve-indentp
                                  (string-match "-i" (match-string 2)))
                              (match-string 3)
                            (org-remove-indentation (match-string 3)))
                          "\n\\end{code}\n#+end_latex\n"))
                       t t)
        (indent-code-rigidly (match-beginning 0) (match-end 0) indentation)))
    (save-excursion
      ;; export to latex w/org and save as .lhs
      (find-file tmp-org-file) (funcall 'org-export-as-latex nil)
      (kill-buffer)
      (delete-file tmp-org-file)
      (find-file tmp-tex-file)
      (goto-char (point-min)) (forward-line 2)
      (insert "%include polycode.fmt\n")
      ;; ensure all \begin/end{code} statements start at the first column
      (while (re-search-forward "^[ \t]+\\\\begin{code}[^\000]+\\\\end{code}" nil t)
        (replace-match (save-match-data (org-remove-indentation (match-string 0)))
                       t t))
      (setq contents (buffer-string))
      (save-buffer) (kill-buffer))
    (delete-file tmp-tex-file)
    ;; save org exported latex to a .lhs file
    (with-temp-file lhs-file (insert contents))
    (if (not arg)
        (find-file lhs-file)
      ;; process .lhs file with lhs2tex
      (message "running %s" command) (shell-command command) (find-file tex-file))))

(provide 'ob-haskell)

;; arch-tag: b53f75f3-ba1a-4b05-82d9-a2a0d4e70804

;;; ob-haskell.el ends here