util.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. ;;; nyacc/lang/c99/util.scm - C parser utilities
  2. ;; Copyright (C) 2015-2019 Matthew R. Wette
  3. ;;
  4. ;; This library is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public
  6. ;; License as published by the Free Software Foundation; either
  7. ;; version 3 of the License, or (at your option) any later version.
  8. ;;
  9. ;; This library is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public License
  15. ;; along with this library; if not, see <http://www.gnu.org/licenses/>.
  16. ;;; Code:
  17. (define-module (nyacc lang c99 util)
  18. #:export (c99-def-help
  19. c99-std-help
  20. get-gcc-cpp-defs
  21. get-gcc-inc-dirs
  22. remove-inc-trees
  23. merge-inc-trees!
  24. move-attributes attrl->attrs attrs->attrl extract-attr
  25. elifify)
  26. #:use-module (nyacc lang util)
  27. #:use-module (nyacc lang sx-util)
  28. #:use-module ((srfi srfi-1) #:select (append-reverse fold-right))
  29. #:use-module (srfi srfi-2) ; and-let*
  30. #:use-module (sxml fold)
  31. #:use-module (ice-9 popen) ; gen-gcc-cpp-defs
  32. #:use-module (ice-9 rdelim) ; gen-gcc-cpp-defs
  33. )
  34. (define c99-def-help
  35. '(("__builtin"
  36. "__builtin_va_list=void*"
  37. "__inline__=inline" "__inline=__inline__"
  38. "__restrict__=restrict" "__restrict=__restrict__"
  39. "__signed__=signed" "__signed=__signed__"
  40. "asm(X)=__asm__(X)" "__asm(X)=__asm__(X)"
  41. "__attribute(X)=__attribute__(X)"
  42. "__volatile__=volatile" "__volatile=__volatile__"
  43. "__extension__=" "__extension=__extension__"
  44. "asm=__asm__" "__asm=__asm__"
  45. "__attribute(X)=__attribute__(X)"
  46. )))
  47. ;; include-helper for C99 std
  48. (define c99-std-help
  49. (append
  50. c99-def-help
  51. '(("alloca.h")
  52. ("complex.h" "complex" "imaginary" "_Imaginary_I=C99_ANY" "I=C99_ANY")
  53. ("ctype.h")
  54. ("fenv.h" "fenv_t" "fexcept_t")
  55. ("float.h" "float_t" "FLT_MAX=C99_ANY" "DBL_MAX=C99_ANY")
  56. ("inttypes.h"
  57. "int8_t" "uint8_t" "int16_t" "uint16_t" "int32_t" "uint32_t"
  58. "int64_t" "uint64_t" "uintptr_t" "intptr_t" "intmax_t" "uintmax_t"
  59. "int_least8_t" "uint_least8_t" "int_least16_t" "uint_least16_t"
  60. "int_least32_t" "uint_least32_t" "int_least64_t" "uint_least64_t"
  61. "imaxdiv_t")
  62. ("limits.h"
  63. "INT_MIN=C99_ANY" "INT_MAX=C99_ANY" "LONG_MIN=C99_ANY" "LONG_MAX=C99_ANY")
  64. ("math.h" "float_t" "double_t")
  65. ("regex.h" "regex_t" "regmatch_t")
  66. ("setjmp.h" "jmp_buf")
  67. ("signal.h" "sig_atomic_t")
  68. ("stdarg.h" "va_list")
  69. ("stddef.h" "ptrdiff_t" "size_t" "wchar_t")
  70. ("stdint.h"
  71. "int8_t" "uint8_t" "int16_t" "uint16_t" "int32_t" "uint32_t"
  72. "int64_t" "uint64_t" "uintptr_t" "intptr_t" "intmax_t" "uintmax_t"
  73. "int_least8_t" "uint_least8_t" "int_least16_t" "uint_least16_t"
  74. "int_least32_t" "uint_least32_t" "int_least64_t" "uint_least64_t")
  75. ("stdio.h" "FILE" "size_t")
  76. ("stdlib.h" "div_t" "ldiv_t" "lldiv_t" "wchar_t")
  77. ("string.h" "size_t")
  78. ("strings.h" "size_t")
  79. ("time.h" "time_t" "clock_t" "size_t")
  80. ("unistd.h" "size_t" "ssize_t" "div_t" "ldiv_t")
  81. ("wchar.h" "wchar_t" "wint_t" "mbstate_t" "size_t")
  82. ("wctype.h" "wctrans_t" "wctype_t" "wint_t"))))
  83. (define (resolve-CC CC)
  84. (cond
  85. (CC CC)
  86. ((getenv "CC") => identity)
  87. (else "gcc")))
  88. ;; @deffn {Procedure} convert-def line
  89. ;; Convert string in gcc cpp defs to pair of strings for term and replacement.
  90. ;; @end deffn
  91. (define (convert-line line)
  92. (with-input-from-string line
  93. (lambda ()
  94. (let loop ((term '()) (acc '()) (st 0) (ch (read-char)))
  95. (case st
  96. ((0) ;; skip #define
  97. (if (char=? ch #\space)
  98. (loop term acc 1 (read-char))
  99. (loop term acc 0 (read-char))))
  100. ((1) ;; read term
  101. (if (char=? ch #\space)
  102. (loop (reverse-list->string acc) '() 2 (read-char))
  103. (loop term (cons ch acc) st (read-char))))
  104. ((2) ;; read rest
  105. (if (or (eof-object? ch) (char=? ch #\newline))
  106. (string-append term "=" (reverse-list->string acc))
  107. (loop term (cons ch acc) st (read-char)))))))))
  108. ;; @deffn {Procedure} get-gcc-cpp-defs [args] [#:CC "gcc"] => '("ABC=123" ...)
  109. ;; Generate a list of default defines produced by gcc (or other comiler).
  110. ;; If keyword arg @arg{CC} is not provided this procedure looks for environment
  111. ;; variable @code{"CC"}, else it defaults to @code{"gcc"}.
  112. ;; @end deffn
  113. (define* (get-gcc-cpp-defs #:optional (args '()) #:key CC)
  114. ;; @code{"gcc -dM -E"} will generate lines like @code{"#define ABC 123"}.
  115. ;; We generate and return a list like @code{'(("ABC" . "123") ...)}.
  116. (let* ((cmd (string-append (resolve-CC CC) " -dM -E - </dev/null"))
  117. (ip (open-input-pipe cmd)))
  118. (let loop ((line (read-line ip 'trim)))
  119. (if (eof-object? line) '()
  120. (cons (convert-line line) (loop (read-line ip 'trim)))))))
  121. ;; @deffn {Procedure} get-gcc-inc-dirs [args] [#:CC "gcc"] =>
  122. ;; Generate a list of compiler-internal include directories (for gcc). If
  123. ;; keyword arg @arg{CC} is not provided this procedure looks for environment
  124. ;; variable @code{"CC"}, else it defaults to @code{"gcc"}.
  125. ;; @end deffn
  126. (define* (get-gcc-inc-dirs #:optional (args '()) #:key CC)
  127. (let ((ip (open-input-pipe (string-append
  128. (resolve-CC CC) " -E -Wp,-v - </dev/null 2>&1"))))
  129. (let loop ((dirs '()) (grab #f) (line (read-line ip 'trim)))
  130. (cond
  131. ((eof-object? line) dirs)
  132. ((string=? line "#include <...> search starts here:")
  133. (loop dirs #t (read-line ip 'trim)))
  134. ((string=? line "End of search list.") dirs)
  135. (grab
  136. (loop (cons (string-trim-both line) dirs)
  137. grab (read-line ip 'trim)))
  138. (else
  139. (loop dirs grab (read-line ip 'trim)))))))
  140. ;; @deffn {Procedure} remove-inc-trees tree
  141. ;; Remove the trees included with cpp-include statements.
  142. ;; @example
  143. ;; '(... (cpp-stmt (include "<foo.h>" (trans-unit ...))) ...)
  144. ;; => '(... (cpp-stmt (include "<foo.h>")) ...)
  145. ;; @end example
  146. ;; @end deffn
  147. (define (remove-inc-trees tree)
  148. (if (not (eqv? 'trans-unit (car tree)))
  149. (throw 'nyacc-error "expecting c-tree"))
  150. (let loop ((rslt (make-tl 'trans-unit))
  151. ;;(head '(trans-unit)) (tail (cdr tree))
  152. (tree (cdr tree)))
  153. (cond
  154. ((null? tree) (tl->list rslt))
  155. ((and (eqv? 'cpp-stmt (car (car tree)))
  156. (eqv? 'include (caadr (car tree))))
  157. (loop (tl-append rslt `(cpp-stmt (include ,(cadadr (car tree)))))
  158. (cdr tree)))
  159. (else (loop (tl-append rslt (car tree)) (cdr tree))))))
  160. ;; @deffn {Procedure} merge-inc-trees! tree => tree
  161. ;; This will (recursively) merge code from cpp-includes into the tree.
  162. ;; @example
  163. ;; (trans-unit
  164. ;; (decl (a))
  165. ;; (cpp-stmt (include "<hello.h>" (trans-unit (decl (b)))))
  166. ;; (decl (c)))
  167. ;; =>
  168. ;; (trans-unit (decl (a)) (decl (b)) (decl (c)))
  169. ;; @end example
  170. ;; @end deffn
  171. (define (merge-inc-trees! tree)
  172. ;; @item find-span (trans-unit a b c) => ((a . +->) . (c . '())
  173. (define (find-span tree)
  174. (cond
  175. ((not (pair? tree)) '()) ; maybe parse failed
  176. ((not (eqv? 'trans-unit (car tree))) (throw 'c99-error "expecting c-tree"))
  177. ((null? (cdr tree)) (throw 'c99-error "null c99-tree"))
  178. (else
  179. (let ((fp tree)) ; first pair
  180. (let loop ((lp tree) ; last pair
  181. (np (cdr tree))) ; next pair
  182. (cond
  183. ((null? np) (cons (cdr fp) lp))
  184. ;; The following is an ugly hack to find cpp-include
  185. ;; with trans-unit attached.
  186. ((and-let* ((expr (car np))
  187. ((eqv? 'cpp-stmt (car expr)))
  188. ((eqv? 'include (caadr expr)))
  189. (rest (cddadr expr))
  190. ((pair? rest))
  191. (span (find-span (car rest))))
  192. (set-cdr! lp (car span))
  193. (loop (cdr span) (cdr np))))
  194. (else
  195. (set-cdr! lp np)
  196. (loop np (cdr np)))))))))
  197. ;; Use cons to generate a new reference:
  198. ;; (cons (car tree) (car (find-span tree)))
  199. ;; or not:
  200. (find-span tree)
  201. tree)
  202. ;; --- attributes ----------------------
  203. (define (join-string-literal str-lit)
  204. (sx-list 'string (sx-attr str-lit) (string-join (sx-tail str-lit) "")))
  205. ;; used in c99-spec actions for attribute-specifiers
  206. (define (attr-expr-list->string attr-expr-list)
  207. (string-append "(" (string-join (cdr attr-expr-list) ",") ")"))
  208. ;; ((attribute-list ...) (type-spec ...) (attribute-list ...)) =>
  209. ;; (values (attribute-list ...) ((type-spec ...) ...))
  210. ;; @deffn extract-attr tail => (values attr-tree tail)
  211. ;; Extract attributes from a sexp tail.
  212. ;; @end deffn
  213. (define (extract-attr tail) ;; => (values attr-tree tail)
  214. (let loop ((atl '()) (tail1 '()) (tail0 tail))
  215. (cond
  216. ((null? tail0)
  217. (if (null? atl)
  218. (values '() tail)
  219. (values `(attribute-list . ,atl) (reverse tail1))))
  220. ((eq? 'attribute-list (sx-tag (car tail0)))
  221. (loop (append (sx-tail (car tail0)) atl) tail1 (cdr tail0)))
  222. (else
  223. (loop atl (cons (car tail0) tail1) (cdr tail0))))))
  224. ;; (attribute-list (attribute (ident "__packed__")) ...)
  225. ;; =>
  226. ;; (attributes "__packed__;...")
  227. ;; OR
  228. ;; () => ()
  229. (define (attrl->attrs attr-list)
  230. (define (spec->str spec)
  231. (sx-match spec
  232. ((ident ,name) name)
  233. ((attribute ,name) (spec->str name))
  234. ((attribute ,name ,args)
  235. (string-append (spec->str name) "(" (spec->str args) ")"))
  236. ((attr-expr-list . ,expr-list)
  237. (string-join (map spec->str expr-list) ","))
  238. ((fixed ,val) val)
  239. ((float ,val) val)
  240. ((char ,val) val)
  241. ((string . ,val) (string-append "\"" (string-join val "") "\""))
  242. ((type-name (decl-spec-list (type-spec ,spec))) (spec->str spec))
  243. ((fixed-type ,name) name)
  244. ((float-type ,name) name)
  245. (,_ (sferr "c99/util: missed ~S\n" spec) "MISSED")))
  246. (if (null? attr-list) '()
  247. `(attributes ,(string-join (map spec->str (sx-tail attr-list)) ";"))))
  248. ;; (attributes "__packed__;__aligned__;__alignof__(8)")
  249. ;; =>
  250. ;; (attribute-list (attribute "__packed
  251. ;; OR
  252. ;; #f => #f
  253. (use-modules (nyacc lex))
  254. (define (astng->atree form)
  255. (define a-mtab
  256. '(("(" . lparen) ((")" . rparen))
  257. ("," . comma) ($ident . ident)))
  258. (define attlexgen (make-lexer-generator a-mtab))
  259. (define attlex (attlexgen))
  260. (with-input-from-string form
  261. (lambda ()
  262. (define (p-expr-list lx) ;; see 'lparen
  263. (and
  264. (eq? 'lparen (car lx))
  265. (let loop ((args '()) (lx (attlex)))
  266. (case (car lx)
  267. ((rparen) `(attr-expr-list . ,args))
  268. ((comma) (loop args (attlex)))
  269. (else (p-expr lx))))))
  270. (define (p-expr lx)
  271. #f)
  272. (let ((lx (attlex)))
  273. (sferr "lx=~S\n" lx)
  274. (case (car lx)
  275. ((ident)
  276. (let* ((id (cdr lx)) (lx (attlex)))
  277. (case (car lx)
  278. (($end) `(attribute ,id))
  279. ((lparen) `(attribute ,id ,(p-expr-list lx)))
  280. (else (throw 'nyacc-error "error ~S" lx)))))
  281. (else (throw 'nyacc-error "missed ~S" lx)))))))
  282. (define (attrs->attrl attr-sexp)
  283. (and
  284. attr-sexp
  285. (let* ((attrs (cadr attr-sexp))
  286. (attl (string-split attrs #\;)))
  287. `(attribute-list ,@(map astng->atree attl)))))
  288. ;; @deffn {Procedure} move-attributes sexp
  289. ;; Given a sexpr, combine attribute-list kids and move to attribute ??
  290. ;; @example
  291. ;; (decl (decl-spec-list
  292. ;; (attributes "__packed__" "__aligned__")
  293. ;; (attributes "__alignof__(8)"))
  294. ;; (type-spec (fixed-type "int")))
  295. ;; (declr-init-list ...))
  296. ;; =>
  297. ;; (decl (decl-spec-list
  298. ;; (@ (attributes "__packed__;__aligned__;__alignof__(8)"))
  299. ;; (type-spec (fixed-type "int")))
  300. ;; (declr-init-list ...))
  301. ;; @end example
  302. ;; @end deffn
  303. (define (move-attributes sexp)
  304. (let ((tag (sx-tag sexp)) (attr (sx-attr sexp)) (tail (sx-tail sexp)))
  305. (call-with-values (lambda () (extract-attr tail))
  306. (lambda (attrl stail)
  307. (sx-cons*
  308. tag
  309. (cond
  310. ((null? attrl) attr)
  311. ((null? attr)`(@ ,(attrl->attrs attrl)))
  312. (else (append attr (list (attrl->attrs attrl)))))
  313. stail)))))
  314. ;; --- random stuff
  315. ;; @deffn {Procedure} elifify tree => tree
  316. ;; This procedure will find patterns of
  317. ;; @example
  318. ;; (if cond-1 then-part-1
  319. ;; (if cond-2 then-part-2
  320. ;; else-part-2
  321. ;; @end example
  322. ;; @noindent
  323. ;; and convert to
  324. ;; @example
  325. ;; (if cond-1 then-part-1
  326. ;; (elif cond-2 then-part-2)
  327. ;; else-part-2
  328. ;; @end example
  329. ;; @end deffn
  330. (define (elifify tree)
  331. (define (fU tree)
  332. (sx-match tree
  333. ((if ,x1 ,t1 (if ,x2 ,t2 (else-if ,x3 ,t3) . ,rest))
  334. `(if ,x1 ,t1 (else-if ,x2 ,t2) (else-if ,x3 ,t3) . ,rest))
  335. ((if ,x1 ,t1 (if ,x2 ,t2 . ,rest))
  336. `(if ,x1 ,t1 (else-if ,x2 ,t2) . ,rest))
  337. (else
  338. tree)))
  339. (foldt fU identity tree))
  340. ;; --- last line ---