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