mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 76bbd07de7add0f9d13c6914f158d19630fe2f62
commit | author | age
76bbd0 1 ;;; ob-C.el --- Babel Functions for C and Similar Languages -*- lexical-binding: t; -*-
C 2
3 ;; Copyright (C) 2010-2018 Free Software Foundation, Inc.
4
5 ;; Author: Eric Schulte
6 ;;      Thierry Banel
7 ;; Keywords: literate programming, reproducible research
8 ;; Homepage: https://orgmode.org
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 <https://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; Org-Babel support for evaluating C, C++, D code.
28 ;;
29 ;; very limited implementation:
30 ;; - currently only support :results output
31 ;; - not much in the way of error feedback
32
33 ;;; Code:
34
35 (require 'cc-mode)
36 (require 'ob)
37
38
39 (declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
40 (declare-function org-remove-indentation "org" (code &optional n))
41 (declare-function org-trim "org" (s &optional keep-lead))
42
43 (defvar org-babel-tangle-lang-exts)
44 (add-to-list 'org-babel-tangle-lang-exts '("C++" . "cpp"))
45 (add-to-list 'org-babel-tangle-lang-exts '("D" . "d"))
46
47 (defvar org-babel-default-header-args:C '())
48
49 (defconst org-babel-header-args:C '((includes . :any)
50                     (defines . :any)
51                     (main    . :any)
52                     (flags   . :any)
53                     (cmdline . :any)
54                     (libs    . :any))
55   "C/C++-specific header arguments.")
56
57 (defconst org-babel-header-args:C++
58   (append '((namespaces . :any))
59       org-babel-header-args:C)
60   "C++-specific header arguments.")
61
62 (defcustom org-babel-C-compiler "gcc"
63   "Command used to compile a C source code file into an executable.
64 May be either a command in the path, like gcc
65 or an absolute path name, like /usr/local/bin/gcc
66 parameter may be used, like gcc -v"
67   :group 'org-babel
68   :version "24.3"
69   :type 'string)
70
71 (defcustom org-babel-C++-compiler "g++"
72   "Command used to compile a C++ source code file into an executable.
73 May be either a command in the path, like g++
74 or an absolute path name, like /usr/local/bin/g++
75 parameter may be used, like g++ -v"
76   :group 'org-babel
77   :version "24.3"
78   :type 'string)
79
80 (defcustom org-babel-D-compiler "rdmd"
81   "Command used to compile and execute a D source code file.
82 May be either a command in the path, like rdmd
83 or an absolute path name, like /usr/local/bin/rdmd
84 parameter may be used, like rdmd --chatty"
85   :group 'org-babel
86   :version "24.3"
87   :type 'string)
88
89 (defvar org-babel-c-variant nil
90   "Internal variable used to hold which type of C (e.g. C or C++ or D)
91 is currently being evaluated.")
92
93 (defun org-babel-execute:cpp (body params)
94   "Execute BODY according to PARAMS.
95 This function calls `org-babel-execute:C++'."
96   (org-babel-execute:C++ body params))
97
98 (defun org-babel-expand-body:cpp (body params)
99   "Expand a block of C++ code with org-babel according to its
100 header arguments."
101   (org-babel-expand-body:C++ body params))
102
103 (defun org-babel-execute:C++ (body params)
104   "Execute a block of C++ code with org-babel.
105 This function is called by `org-babel-execute-src-block'."
106   (let ((org-babel-c-variant 'cpp)) (org-babel-C-execute body params)))
107
108 (defun org-babel-expand-body:C++ (body params)
109   "Expand a block of C++ code with org-babel according to its
110 header arguments."
111   (let ((org-babel-c-variant 'cpp)) (org-babel-C-expand-C++ body params)))
112
113 (defun org-babel-execute:D (body params)
114   "Execute a block of D code with org-babel.
115 This function is called by `org-babel-execute-src-block'."
116   (let ((org-babel-c-variant 'd)) (org-babel-C-execute body params)))
117
118 (defun org-babel-expand-body:D (body params)
119   "Expand a block of D code with org-babel according to its
120 header arguments."
121   (let ((org-babel-c-variant 'd)) (org-babel-C-expand-D body params)))
122
123 (defun org-babel-execute:C (body params)
124   "Execute a block of C code with org-babel.
125 This function is called by `org-babel-execute-src-block'."
126   (let ((org-babel-c-variant 'c)) (org-babel-C-execute body params)))
127
128 (defun org-babel-expand-body:C (body params)
129   "Expand a block of C code with org-babel according to its
130 header arguments."
131   (let ((org-babel-c-variant 'c)) (org-babel-C-expand-C body params)))
132
133 (defun org-babel-C-execute (body params)
134   "This function should only be called by `org-babel-execute:C'
135 or `org-babel-execute:C++' or `org-babel-execute:D'."
136   (let* ((tmp-src-file (org-babel-temp-file
137             "C-src-"
138             (pcase org-babel-c-variant
139               (`c ".c") (`cpp ".cpp") (`d ".d"))))
140      (tmp-bin-file            ;not used for D
141       (org-babel-process-file-name
142        (org-babel-temp-file "C-bin-" org-babel-exeext)))
143      (cmdline (cdr (assq :cmdline params)))
144      (cmdline (if cmdline (concat " " cmdline) ""))
145      (flags (cdr (assq :flags params)))
146      (flags (mapconcat 'identity
147                (if (listp flags) flags (list flags)) " "))
148      (libs (org-babel-read
149         (or (cdr (assq :libs params))
150             (org-entry-get nil "libs" t))
151         nil))
152      (libs (mapconcat #'identity
153               (if (listp libs) libs (list libs))
154               " "))
155      (full-body
156       (pcase org-babel-c-variant
157         (`c (org-babel-C-expand-C body params))
158         (`cpp (org-babel-C-expand-C++ body params))
159         (`d (org-babel-C-expand-D body params)))))
160     (with-temp-file tmp-src-file (insert full-body))
161     (pcase org-babel-c-variant
162       ((or `c `cpp)
163        (org-babel-eval
164     (format "%s -o %s %s %s %s"
165         (pcase org-babel-c-variant
166           (`c org-babel-C-compiler)
167           (`cpp org-babel-C++-compiler))
168         tmp-bin-file
169         flags
170         (org-babel-process-file-name tmp-src-file)
171         libs)
172     ""))
173       (`d nil)) ;; no separate compilation for D
174     (let ((results
175        (org-babel-eval
176         (pcase org-babel-c-variant
177           ((or `c `cpp)
178            (concat tmp-bin-file cmdline))
179           (`d
180            (format "%s %s %s %s"
181                org-babel-D-compiler
182                flags
183                (org-babel-process-file-name tmp-src-file)
184                cmdline)))
185         "")))
186       (when results
187     (setq results (org-trim (org-remove-indentation results)))
188     (org-babel-reassemble-table
189      (org-babel-result-cond (cdr (assq :result-params params))
190        (org-babel-read results t)
191        (let ((tmp-file (org-babel-temp-file "c-")))
192          (with-temp-file tmp-file (insert results))
193          (org-babel-import-elisp-from-file tmp-file)))
194      (org-babel-pick-name
195       (cdr (assq :colname-names params)) (cdr (assq :colnames params)))
196      (org-babel-pick-name
197       (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))
198       )))
199
200 (defun org-babel-C-expand-C++ (body params)
201   "Expand a block of C or C++ code with org-babel according to
202 its header arguments."
203   (org-babel-C-expand-C body params))
204
205 (defun org-babel-C-expand-C (body params)
206   "Expand a block of C or C++ code with org-babel according to
207 its header arguments."
208   (let ((vars (org-babel--get-vars params))
209     (colnames (cdr (assq :colname-names params)))
210     (main-p (not (string= (cdr (assq :main params)) "no")))
211     (includes (org-babel-read
212            (cdr (assq :includes params))
213            nil))
214     (defines (org-babel-read
215           (cdr (assq :defines params))
216           nil))
217     (namespaces (org-babel-read
218              (cdr (assq :namespaces params))
219              nil)))
220     (when (stringp includes)
221       (setq includes (split-string includes)))
222     (when (stringp namespaces)
223       (setq namespaces (split-string namespaces)))
224     (when (stringp defines)
225       (let ((y nil)
226         (result (list t)))
227     (dolist (x (split-string defines))
228       (if (null y)
229           (setq y x)
230         (nconc result (list (concat y " " x)))
231         (setq y nil)))
232     (setq defines (cdr result))))
233     (mapconcat 'identity
234            (list
235         ;; includes
236         (mapconcat
237          (lambda (inc) (format "#include %s" inc))
238          includes "\n")
239         ;; defines
240         (mapconcat
241          (lambda (inc) (format "#define %s" inc))
242          (if (listp defines) defines (list defines)) "\n")
243         ;; namespaces
244         (mapconcat
245          (lambda (inc) (format "using namespace %s;" inc))
246          namespaces
247          "\n")
248         ;; variables
249         (mapconcat 'org-babel-C-var-to-C vars "\n")
250         ;; table sizes
251         (mapconcat 'org-babel-C-table-sizes-to-C vars "\n")
252         ;; tables headers utility
253         (when colnames
254           (org-babel-C-utility-header-to-C))
255         ;; tables headers
256         (mapconcat 'org-babel-C-header-to-C colnames "\n")
257         ;; body
258         (if main-p
259             (org-babel-C-ensure-main-wrap body)
260           body) "\n") "\n")))
261
262 (defun org-babel-C-expand-D (body params)
263   "Expand a block of D code with org-babel according to
264 its header arguments."
265   (let ((vars (org-babel--get-vars params))
266     (colnames (cdr (assq :colname-names params)))
267     (main-p (not (string= (cdr (assq :main params)) "no")))
268     (imports (or (cdr (assq :imports params))
269              (org-babel-read (org-entry-get nil "imports" t)))))
270     (when (stringp imports)
271       (setq imports (split-string imports)))
272     (setq imports (append imports '("std.stdio" "std.conv")))
273     (mapconcat 'identity
274            (list
275         "module mmm;"
276         ;; imports
277         (mapconcat
278          (lambda (inc) (format "import %s;" inc))
279          imports "\n")
280         ;; variables
281         (mapconcat 'org-babel-C-var-to-C vars "\n")
282         ;; table sizes
283         (mapconcat 'org-babel-C-table-sizes-to-C vars "\n")
284         ;; tables headers utility
285         (when colnames
286           (org-babel-C-utility-header-to-C))
287         ;; tables headers
288         (mapconcat 'org-babel-C-header-to-C colnames "\n")
289         ;; body
290         (if main-p
291             (org-babel-C-ensure-main-wrap body)
292           body) "\n") "\n")))
293
294 (defun org-babel-C-ensure-main-wrap (body)
295   "Wrap BODY in a \"main\" function call if none exists."
296   (if (string-match "^[ \t]*[intvod]+[ \t\n\r]*main[ \t]*(.*)" body)
297       body
298     (format "int main() {\n%s\nreturn 0;\n}\n" body)))
299
300 (defun org-babel-prep-session:C (_session _params)
301   "This function does nothing as C is a compiled language with no
302 support for sessions"
303   (error "C is a compiled language -- no support for sessions"))
304
305 (defun org-babel-load-session:C (_session _body _params)
306   "This function does nothing as C is a compiled language with no
307 support for sessions"
308   (error "C is a compiled language -- no support for sessions"))
309
310 ;; helper functions
311
312 (defun org-babel-C-format-val (type val)
313   "Handle the FORMAT part of TYPE with the data from VAL."
314   (let ((format-data (cadr type)))
315     (if (stringp format-data)
316     (cons "" (format format-data val))
317       (funcall format-data val))))
318
319 (defun org-babel-C-val-to-C-type (val)
320   "Determine the type of VAL.
321 Return a list (TYPE-NAME FORMAT).  TYPE-NAME should be the name of the type.
322 FORMAT can be either a format string or a function which is called with VAL."
323   (let* ((basetype (org-babel-C-val-to-base-type val))
324      (type
325       (pcase basetype
326         (`integerp '("int" "%d"))
327         (`floatp '("double" "%f"))
328         (`stringp
329          (list
330           (if (eq org-babel-c-variant 'd) "string" "const char*")
331           "\"%s\""))
332         (_ (error "unknown type %S" basetype)))))
333     (cond
334      ((integerp val) type) ;; an integer declared in the #+begin_src line
335      ((floatp val) type) ;; a numeric declared in the #+begin_src line
336      ((and (listp val) (listp (car val))) ;; a table
337       `(,(car type)
338     (lambda (val)
339       (cons
340        (format "[%d][%d]" (length val) (length (car val)))
341        (concat
342         (if (eq org-babel-c-variant 'd) "[\n" "{\n")
343         (mapconcat
344          (lambda (v)
345            (concat
346         (if (eq org-babel-c-variant 'd) " [" " {")
347         (mapconcat (lambda (w) (format ,(cadr type) w)) v ",")
348         (if (eq org-babel-c-variant 'd) "]" "}")))
349          val
350          ",\n")
351         (if (eq org-babel-c-variant 'd) "\n]" "\n}"))))))
352      ((or (listp val) (vectorp val)) ;; a list declared in the #+begin_src line
353       `(,(car type)
354     (lambda (val)
355       (cons
356        (format "[%d]" (length val))
357        (concat
358         (if (eq org-babel-c-variant 'd) "[" "{")
359         (mapconcat (lambda (v) (format ,(cadr type) v)) val ",")
360         (if (eq org-babel-c-variant 'd) "]" "}"))))))
361      (t ;; treat unknown types as string
362       type))))
363
364 (defun org-babel-C-val-to-base-type (val)
365   "Determine the base type of VAL which may be
366 `integerp' if all base values are integers
367 `floatp' if all base values are either floating points or integers
368 `stringp' otherwise."
369   (cond
370    ((integerp val) 'integerp)
371    ((floatp val) 'floatp)
372    ((or (listp val) (vectorp val))
373     (let ((type nil))
374       (mapc (lambda (v)
375           (pcase (org-babel-C-val-to-base-type v)
376         (`stringp (setq type 'stringp))
377         (`floatp
378          (if (or (not type) (eq type 'integerp))
379              (setq type 'floatp)))
380         (`integerp
381          (unless type (setq type 'integerp)))))
382         val)
383       type))
384    (t 'stringp)))
385
386 (defun org-babel-C-var-to-C (pair)
387   "Convert an elisp val into a string of C code specifying a var
388 of the same value."
389   ;; TODO list support
390   (let ((var (car pair))
391     (val (cdr pair)))
392     (when (symbolp val)
393       (setq val (symbol-name val))
394       (when (= (length val) 1)
395     (setq val (string-to-char val))))
396     (let* ((type-data (org-babel-C-val-to-C-type val))
397        (type (car type-data))
398        (formated (org-babel-C-format-val type-data val))
399        (suffix (car formated))
400        (data (cdr formated)))
401       (format "%s %s%s = %s;"
402           type
403           var
404           suffix
405           data))))
406
407 (defun org-babel-C-table-sizes-to-C (pair)
408   "Create constants of table dimensions, if PAIR is a table."
409   (when (listp (cdr pair))
410     (cond
411      ((listp (cadr pair)) ;; a table
412       (concat
413        (format "const int %s_rows = %d;" (car pair) (length (cdr pair)))
414        "\n"
415        (format "const int %s_cols = %d;" (car pair) (length (cadr pair)))))
416      (t ;; a list declared in the #+begin_src line
417       (format "const int %s_cols = %d;" (car pair) (length (cdr pair)))))))
418
419 (defun org-babel-C-utility-header-to-C ()
420   "Generate a utility function to convert a column name
421 into a column number."
422   (pcase org-babel-c-variant
423     ((or `c `cpp)
424      "int get_column_num (int nbcols, const char** header, const char* column)
425 {
426   int c;
427   for (c=0; c<nbcols; c++)
428     if (strcmp(header[c],column)==0)
429       return c;
430   return -1;
431 }
432 ")
433     (`d
434      "int get_column_num (string[] header, string column)
435 {
436   foreach (c, h; header)
437     if (h==column)
438       return to!int(c);
439   return -1;
440 }
441 ")))
442
443 (defun org-babel-C-header-to-C (head)
444   "Convert an elisp list of header table into a C or D vector
445 specifying a variable with the name of the table."
446   (let ((table (car head))
447         (headers (cdr head)))
448     (concat
449      (format
450       (pcase org-babel-c-variant
451     ((or `c `cpp) "const char* %s_header[%d] = {%s};")
452     (`d "string %s_header[%d] = [%s];"))
453       table
454       (length headers)
455       (mapconcat (lambda (h) (format "%S" h)) headers ","))
456      "\n"
457      (pcase org-babel-c-variant
458        ((or `c `cpp)
459     (format
460      "const char* %s_h (int row, const char* col) { return %s[row][get_column_num(%d,%s_header,col)]; }"
461      table table (length headers) table))
462        (`d
463     (format
464      "string %s_h (size_t row, string col) { return %s[row][get_column_num(%s_header,col)]; }"
465      table table table))))))
466
467 (provide 'ob-C)
468
469 ;;; ob-C.el ends here