Commit | Line | Data |
---|---|---|
86fbb8ca CD |
1 | ;;; ob-ref.el --- org-babel functions for referencing external data |
2 | ||
73b0cd50 | 3 | ;; Copyright (C) 2009-2011 Free Software Foundation, Inc. |
86fbb8ca | 4 | |
acedf35c | 5 | ;; Author: Eric Schulte, Dan Davison |
86fbb8ca CD |
6 | ;; Keywords: literate programming, reproducible research |
7 | ;; Homepage: http://orgmode.org | |
acedf35c | 8 | ;; Version: 7.4 |
86fbb8ca CD |
9 | |
10 | ;; This file is part of GNU Emacs. | |
11 | ||
12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation, either version 3 of the License, or | |
15 | ;; (at your option) any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
24 | ||
25 | ;;; Commentary: | |
26 | ||
27 | ;; Functions for referencing data from the header arguments of a | |
28 | ;; org-babel block. The syntax of such a reference should be | |
29 | ||
30 | ;; #+VAR: variable-name=file:resource-id | |
31 | ||
32 | ;; - variable-name :: the name of the variable to which the value | |
33 | ;; will be assigned | |
34 | ||
35 | ;; - file :: path to the file containing the resource, or omitted if | |
36 | ;; resource is in the current file | |
37 | ||
38 | ;; - resource-id :: the id or name of the resource | |
39 | ||
40 | ;; So an example of a simple src block referencing table data in the | |
41 | ;; same file would be | |
42 | ||
43 | ;; #+TBLNAME: sandbox | |
44 | ;; | 1 | 2 | 3 | | |
45 | ;; | 4 | org-babel | 6 | | |
46 | ;; | |
47 | ;; #+begin_src emacs-lisp :var table=sandbox | |
48 | ;; (message table) | |
49 | ;; #+end_src | |
50 | ||
51 | ;;; Code: | |
52 | (require 'ob) | |
53 | (eval-when-compile | |
acedf35c | 54 | (require 'org-list) |
86fbb8ca CD |
55 | (require 'cl)) |
56 | ||
57 | (declare-function org-remove-if-not "org" (predicate seq)) | |
58 | (declare-function org-at-table-p "org" (&optional table-type)) | |
59 | (declare-function org-count "org" (CL-ITEM CL-SEQ)) | |
acedf35c | 60 | (declare-function org-in-item-p "org-list" ()) |
86fbb8ca | 61 | |
86fbb8ca CD |
62 | (defvar org-babel-ref-split-regexp |
63 | "[ \f\t\n\r\v]*\\(.+?\\)[ \f\t\n\r\v]*=[ \f\t\n\r\v]*\\(.+\\)[ \f\t\n\r\v]*") | |
64 | ||
afe98dfa | 65 | (defun org-babel-ref-parse (assignment) |
86fbb8ca CD |
66 | "Parse a variable ASSIGNMENT in a header argument. |
67 | If the right hand side of the assignment has a literal value | |
68 | return that value, otherwise interpret as a reference to an | |
69 | external resource and find it's value using | |
afe98dfa CD |
70 | `org-babel-ref-resolve'. Return a list with two elements. The |
71 | first element of the list will be the name of the variable, and | |
72 | the second will be an emacs-lisp representation of the value of | |
73 | the variable." | |
74 | (when (string-match org-babel-ref-split-regexp assignment) | |
75 | (let ((var (match-string 1 assignment)) | |
76 | (ref (match-string 2 assignment))) | |
77 | (cons (intern var) | |
acedf35c CD |
78 | (let ((out (org-babel-read ref))) |
79 | (if (equal out ref) | |
80 | (if (string-match "^\".+\"$" ref) | |
81 | (read ref) | |
82 | (org-babel-ref-resolve ref)) | |
83 | out)))))) | |
86fbb8ca CD |
84 | |
85 | (defvar org-babel-library-of-babel) | |
afe98dfa | 86 | (defun org-babel-ref-resolve (ref) |
86fbb8ca CD |
87 | "Resolve the reference REF and return its value." |
88 | (save-excursion | |
89 | (let ((case-fold-search t) | |
acedf35c CD |
90 | type args new-refere new-header-args new-referent result |
91 | lob-info split-file split-ref index index-row index-col) | |
86fbb8ca | 92 | ;; if ref is indexed grab the indices -- beware nested indices |
acedf35c | 93 | (when (and (string-match "\\[\\([^\\[]+\\)\\]$" ref) |
86fbb8ca CD |
94 | (let ((str (substring ref 0 (match-beginning 0)))) |
95 | (= (org-count ?( str) (org-count ?) str)))) | |
96 | (setq index (match-string 1 ref)) | |
97 | (setq ref (substring ref 0 (match-beginning 0)))) | |
98 | ;; assign any arguments to pass to source block | |
acedf35c CD |
99 | (when (string-match |
100 | "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)\(\\(.*\\)\)$" ref) | |
101 | (setq new-refere (match-string 1 ref)) | |
102 | (setq new-header-args (match-string 3 ref)) | |
103 | (setq new-referent (match-string 5 ref)) | |
86fbb8ca | 104 | (when (> (length new-refere) 0) |
acedf35c CD |
105 | (when (> (length new-referent) 0) |
106 | (setq args (mapcar (lambda (ref) (cons :var ref)) | |
107 | (org-babel-ref-split-args new-referent)))) | |
108 | (when (> (length new-header-args) 0) | |
109 | (setq args (append (org-babel-parse-header-arguments new-header-args) | |
110 | args))) | |
86fbb8ca CD |
111 | (setq ref new-refere))) |
112 | (when (string-match "^\\(.+\\):\\(.+\\)$" ref) | |
113 | (setq split-file (match-string 1 ref)) | |
114 | (setq split-ref (match-string 2 ref)) | |
115 | (find-file split-file) (setq ref split-ref)) | |
116 | (save-restriction | |
117 | (widen) | |
118 | (goto-char (point-min)) | |
afe98dfa CD |
119 | (if (let ((result_regexp (concat "^[ \t]*#\\+\\(TBLNAME\\|RESNAME" |
120 | "\\|RESULTS\\):[ \t]*" | |
86fbb8ca CD |
121 | (regexp-quote ref) "[ \t]*$")) |
122 | (regexp (concat org-babel-src-name-regexp | |
123 | (regexp-quote ref) "\\(\(.*\)\\)?" "[ \t]*$"))) | |
124 | ;; goto ref in the current buffer | |
125 | (or (and (not args) | |
126 | (or (re-search-forward result_regexp nil t) | |
127 | (re-search-backward result_regexp nil t))) | |
128 | (re-search-forward regexp nil t) | |
129 | (re-search-backward regexp nil t) | |
130 | ;; check the Library of Babel | |
afe98dfa CD |
131 | (setq lob-info (cdr (assoc (intern ref) |
132 | org-babel-library-of-babel))))) | |
86fbb8ca CD |
133 | (unless lob-info (goto-char (match-beginning 0))) |
134 | ;; ;; TODO: allow searching for names in other buffers | |
135 | ;; (setq id-loc (org-id-find ref 'marker) | |
136 | ;; buffer (marker-buffer id-loc) | |
137 | ;; loc (marker-position id-loc)) | |
138 | ;; (move-marker id-loc nil) | |
139 | (error "reference '%s' not found in this buffer" ref)) | |
140 | (if lob-info | |
141 | (setq type 'lob) | |
142 | (while (not (setq type (org-babel-ref-at-ref-p))) | |
143 | (forward-line 1) | |
144 | (beginning-of-line) | |
145 | (if (or (= (point) (point-min)) (= (point) (point-max))) | |
146 | (error "reference not found")))) | |
afe98dfa CD |
147 | (let ((params (append args '((:results . "silent"))))) |
148 | (setq result | |
149 | (case type | |
150 | ('results-line (org-babel-read-result)) | |
151 | ('table (org-babel-read-table)) | |
acedf35c | 152 | ('list (org-babel-read-list)) |
afe98dfa CD |
153 | ('file (org-babel-read-link)) |
154 | ('source-block (org-babel-execute-src-block nil nil params)) | |
155 | ('lob (org-babel-execute-src-block nil lob-info params))))) | |
86fbb8ca CD |
156 | (if (symbolp result) |
157 | (format "%S" result) | |
158 | (if (and index (listp result)) | |
159 | (org-babel-ref-index-list index result) | |
160 | result)))))) | |
161 | ||
162 | (defun org-babel-ref-index-list (index lis) | |
163 | "Return the subset of LIS indexed by INDEX. | |
164 | ||
165 | Indices are 0 based and negative indices count from the end of | |
166 | LIS, so 0 references the first element of LIS and -1 references | |
167 | the last. If INDEX is separated by \",\"s then each \"portion\" | |
168 | is assumed to index into the next deepest nesting or dimension. | |
169 | ||
170 | A valid \"portion\" can consist of either an integer index, two | |
171 | integers separated by a \":\" in which case the entire range is | |
172 | returned, or an empty string or \"*\" both of which are | |
173 | interpreted to mean the entire range and as such are equivalent | |
174 | to \"0:-1\"." | |
175 | (if (and (> (length index) 0) (string-match "^\\([^,]*\\),?" index)) | |
176 | (let ((ind-re "\\(\\([-[:digit:]]+\\):\\([-[:digit:]]+\\)\\|\*\\)") | |
177 | (length (length lis)) | |
178 | (portion (match-string 1 index)) | |
179 | (remainder (substring index (match-end 0)))) | |
180 | (flet ((wrap (num) (if (< num 0) (+ length num) num)) | |
181 | (open (ls) (if (and (listp ls) (= (length ls) 1)) (car ls) ls))) | |
182 | (open | |
183 | (mapcar | |
184 | (lambda (sub-lis) (org-babel-ref-index-list remainder sub-lis)) | |
185 | (if (or (= 0 (length portion)) (string-match ind-re portion)) | |
186 | (mapcar | |
187 | (lambda (n) (nth n lis)) | |
afe98dfa | 188 | (apply 'org-number-sequence |
86fbb8ca CD |
189 | (if (and (> (length portion) 0) (match-string 2 portion)) |
190 | (list | |
191 | (wrap (string-to-number (match-string 2 portion))) | |
192 | (wrap (string-to-number (match-string 3 portion)))) | |
193 | (list (wrap 0) (wrap -1))))) | |
194 | (list (nth (wrap (string-to-number portion)) lis))))))) | |
195 | lis)) | |
196 | ||
197 | (defun org-babel-ref-split-args (arg-string) | |
198 | "Split ARG-STRING into top-level arguments of balanced parenthesis." | |
199 | (let ((index 0) (depth 0) (buffer "") holder return) | |
200 | ;; crawl along string, splitting at any ","s which are on the top level | |
201 | (while (< index (length arg-string)) | |
202 | (setq holder (substring arg-string index (+ 1 index))) | |
203 | (setq buffer (concat buffer holder)) | |
204 | (setq index (+ 1 index)) | |
205 | (cond | |
206 | ((string= holder ",") | |
207 | (when (= depth 0) | |
208 | (setq return (reverse (cons (substring buffer 0 -1) return))) | |
209 | (setq buffer ""))) | |
210 | ((or (string= holder "(") (string= holder "[")) (setq depth (+ depth 1))) | |
211 | ((or (string= holder ")") (string= holder "]")) (setq depth (- depth 1))))) | |
212 | (mapcar #'org-babel-trim (reverse (cons buffer return))))) | |
213 | ||
214 | (defvar org-bracket-link-regexp) | |
215 | (defun org-babel-ref-at-ref-p () | |
216 | "Return the type of reference located at point. | |
217 | Return nil if none of the supported reference types are found. | |
218 | Supported reference types are tables and source blocks." | |
219 | (cond ((org-at-table-p) 'table) | |
acedf35c | 220 | ((org-in-item-p) 'list) |
86fbb8ca CD |
221 | ((looking-at "^[ \t]*#\\+BEGIN_SRC") 'source-block) |
222 | ((looking-at org-bracket-link-regexp) 'file) | |
223 | ((looking-at org-babel-result-regexp) 'results-line))) | |
224 | ||
225 | (provide 'ob-ref) | |
226 | ||
86fbb8ca CD |
227 | |
228 | ;;; ob-ref.el ends here |