body.scm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. ;;; lang/c99/body.scm - parser body, inserted in parser.scm
  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. ;;; Notes:
  17. ;; Notes on the code design may be found in doc/nyacc/lang/c99-hg.info
  18. ;; @section The C99 Parser Body
  19. ;; This code provides the front end to the C99 parser, including the lexical
  20. ;; analyzer and optional CPP processing. In @code{'file} mode the lex'er
  21. ;; passes CPP statements to the parser; in @code{'code} mode the lex'er
  22. ;; parses and evaluates the CPP statements. In the case of included files
  23. ;; (e.g., via @code{#include <file.h>}) the include files are parsed if
  24. ;; not in @code{inc-help}. The a-list @code{inc-help} maps
  25. ;; include file names to typenames (e.g., @code{stdio.h} to @code{FILE}) and
  26. ;; CPP defines (e.g., "INT_MAX=12344").
  27. ;; issue w/ brlev: not intended to beused with `extern "C" {'
  28. ;;; Code:
  29. (use-modules (nyacc lang sx-util))
  30. (use-modules (nyacc lang util))
  31. (use-modules ((srfi srfi-1) #:select (fold-right append-reverse)))
  32. (use-modules ((srfi srfi-9) #:select (define-record-type)))
  33. (use-modules (ice-9 pretty-print)) ; for debugging
  34. (define (sf fmt . args) (apply simple-format #t fmt args))
  35. (define pp pretty-print)
  36. ;; C parser info (?)
  37. (define-record-type cpi
  38. (make-cpi-1)
  39. cpi?
  40. (debug cpi-debug set-cpi-debug!) ; debug #t #f
  41. (shinc cpi-shinc set-cpi-shinc!) ; show includes
  42. (defines cpi-defs set-cpi-defs!) ; #defines
  43. (incdirs cpi-incs set-cpi-incs!) ; #includes
  44. (inc-tynd cpi-itynd set-cpi-itynd!) ; a-l of incfile => typenames
  45. (inc-defd cpi-idefd set-cpi-idefd!) ; a-l of incfile => defines
  46. (ptl cpi-ptl set-cpi-ptl!) ; parent typename list
  47. (ctl cpi-ctl set-cpi-ctl!) ; current typename list
  48. (blev cpi-blev set-cpi-blev!) ; curr brace/block level
  49. )
  50. ;;.@deffn Procedure split-cppdef defstr => (<name> . <repl>)| \
  51. ;; (<name> <args> . <repl>)|#f
  52. ;; Convert define string to a dict item. Examples:
  53. ;; @example
  54. ;; "ABC=123" => '("ABC" . "123")
  55. ;; "MAX(X,Y)=((X)>(Y)?(X):(Y))" => ("MAX" ("X" "Y") . "((X)>(Y)?(X):(Y))")
  56. ;; @end example
  57. ;; @end deffn
  58. (define (split-cppdef defstr)
  59. (let ((x2st (string-index defstr #\()) ; start of args
  60. (x2nd (string-index defstr #\))) ; end of args
  61. (x3 (string-index defstr #\=))) ; start of replacement
  62. (cond
  63. ((not x3) #f)
  64. ((and x2st x3)
  65. ;;(if (not (eq? (1+ x2nd) x3)) (c99-err "bad CPP def: ~S" defstr))
  66. (cons* (substring defstr 0 x2st)
  67. (string-split
  68. (string-delete #\space (substring defstr (1+ x2st) x2nd))
  69. #\,)
  70. (substring defstr (1+ x3))))
  71. (else
  72. (cons (substring defstr 0 x3) (substring defstr (1+ x3)))))))
  73. ;; @deffn Procedure make-cpi debug defines incdirs inchelp
  74. ;; I think there is a potential bug here in that the alist of cpp-defs/helpers
  75. ;; should be last-in-first-seen ordered. Probably helpers low prio.
  76. ;; The (CPP) defines can appear as pairs: then they have already been split.
  77. ;; (This is used by @code{parse-c99x}.)
  78. ;; @end deffn
  79. (define (make-cpi debug shinc defines incdirs inchelp)
  80. ;; convert inchelp into inc-file->typenames and inc-file->defines
  81. ;; Any entry for an include file which contains `=' is considered
  82. ;; a define; otherwise, the entry is a typename.
  83. (define (split-helper helper)
  84. (let ((file (car helper)))
  85. (let loop ((tyns '()) (defs '()) (ents (cdr helper)))
  86. (cond
  87. ((null? ents) (values (cons file tyns) (cons file defs)))
  88. ((split-cppdef (car ents)) =>
  89. (lambda (def) (loop tyns (cons def defs) (cdr ents))))
  90. (else (loop (cons (car ents) tyns) defs (cdr ents)))))))
  91. (define (split-if-needed def)
  92. (if (pair? def) def (split-cppdef def)))
  93. (let* ((cpi (make-cpi-1)))
  94. (set-cpi-debug! cpi debug) ; print states debug
  95. (set-cpi-shinc! cpi shinc) ; print includes
  96. (set-cpi-defs! cpi (map split-if-needed defines)) ; def's as pairs
  97. (set-cpi-incs! cpi incdirs) ; list of include dir's
  98. (set-cpi-ptl! cpi '()) ; list of lists of typenames
  99. (set-cpi-ctl! cpi '()) ; list of current typenames
  100. (set-cpi-blev! cpi 0) ; brace/block level
  101. ;; Break up the helpers into typenames and defines.
  102. (let loop ((itynd '()) (idefd '()) (helpers inchelp))
  103. (cond ((null? helpers)
  104. (set-cpi-itynd! cpi itynd)
  105. (set-cpi-idefd! cpi idefd))
  106. (else
  107. (call-with-values
  108. (lambda () (split-helper (car helpers)))
  109. (lambda (ityns idefs)
  110. (loop (cons ityns itynd) (cons idefs idefd) (cdr helpers)))))))
  111. ;; Assign builtins.
  112. (and=> (assoc-ref (cpi-itynd cpi) "__builtin")
  113. (lambda (tl) (set-cpi-ctl! cpi (append tl (cpi-ctl cpi)))))
  114. (and=> (assoc-ref (cpi-idefd cpi) "__builtin")
  115. (lambda (tl) (set-cpi-defs! cpi (append tl (cpi-defs cpi)))))
  116. cpi))
  117. (define *info* (make-fluid))
  118. (define cpi-inc-blev!
  119. (case-lambda
  120. ((info) (set-cpi-blev! info (1+ (cpi-blev info))))
  121. (() (cpi-inc-blev! (fluid-ref *info*)))))
  122. (define cpi-dec-blev!
  123. (case-lambda
  124. ((info) (set-cpi-blev! info (1- (cpi-blev info))))
  125. (() (cpi-dec-blev! (fluid-ref *info*)))))
  126. (define cpi-top-blev?
  127. (case-lambda
  128. ((info) (zero? (cpi-blev info)))
  129. (() (cpi-top-blev? (fluid-ref *info*)))))
  130. (define cpi-push
  131. (case-lambda
  132. ((info)
  133. (set-cpi-ptl! info (cons (cpi-ctl info) (cpi-ptl info)))
  134. (set-cpi-ctl! info '())
  135. #t)
  136. (() (cpi-push (fluid-ref *info*)))))
  137. (define cpi-pop
  138. (case-lambda
  139. ((info)
  140. (set-cpi-ctl! info (car (cpi-ptl info)))
  141. (set-cpi-ptl! info (cdr (cpi-ptl info)))
  142. #t)
  143. (() (cpi-pop (fluid-ref *info*)))))
  144. (define (cpi-push-x) ;; on #if
  145. ;;(sf "\ncpi-push-x:\n") (pp (fluid-ref *info*))
  146. (let ((cpi (fluid-ref *info*)))
  147. (set-cpi-ptl! cpi (cons (cpi-ctl cpi) (cpi-ptl cpi)))
  148. (set-cpi-ctl! cpi '())))
  149. (define (cpi-shift-x) ;; on #elif #else
  150. ;;(sf "\ncpi-shift-x:\n") (pp (fluid-ref *info*))
  151. (set-cpi-ctl! (fluid-ref *info*) '()))
  152. (define (cpi-pop-x) ;; on #endif
  153. ;;(sf "\ncpi-pop-x:\n") (pp (fluid-ref *info*))
  154. (let ((cpi (fluid-ref *info*)))
  155. (set-cpi-ctl! cpi (append (cpi-ctl cpi) (car (cpi-ptl cpi))))
  156. (set-cpi-ptl! cpi (cdr (cpi-ptl cpi)))))
  157. ;; @deffn {Procedure} typename? name
  158. ;; Called by lexer to determine if symbol is a typename.
  159. ;; Check current sibling for each generation.
  160. ;; @end deffn
  161. (define (typename? name)
  162. (let ((cpi (fluid-ref *info*)))
  163. (if (member name (cpi-ctl cpi)) #t
  164. (let loop ((ptl (cpi-ptl cpi)))
  165. (if (null? ptl) #f
  166. (if (member name (car ptl)) #t
  167. (loop (cdr ptl))))))))
  168. ;; @deffn {Procedure} add-typename name
  169. ;; Helper for @code{save-typenames}.
  170. ;; @end deffn
  171. (define (add-typename name)
  172. (let ((cpi (fluid-ref *info*)))
  173. (set-cpi-ctl! cpi (cons name (cpi-ctl cpi)))))
  174. ;; @deffn {Procedure} find-new-typenames decl
  175. ;; Helper for @code{save-typenames}.
  176. ;; Given declaration return a list of new typenames (via @code{typedef}).
  177. ;; @end deffn
  178. (define (find-new-typenames decl)
  179. ;; like declr-id in util2.scm
  180. (define (declr->id-name declr)
  181. (case (car declr)
  182. ((ident) (sx-ref declr 1))
  183. ((init-declr) (declr->id-name (sx-ref declr 1)))
  184. ((comp-declr) (declr->id-name (sx-ref declr 1)))
  185. ((array-of) (declr->id-name (sx-ref declr 1)))
  186. ((ptr-declr) (declr->id-name (sx-ref declr 2)))
  187. ((ftn-declr) (declr->id-name (sx-ref declr 1)))
  188. ((scope) (declr->id-name (sx-ref declr 1)))
  189. (else (error "coding bug: " declr))))
  190. ;;(sf "\ndecl:\n") (pp decl)
  191. (let* ((spec (sx-ref decl 1))
  192. (stor (sx-find 'stor-spec spec))
  193. (id-l (sx-ref decl 2)))
  194. (if (and stor (eqv? 'typedef (caadr stor)))
  195. (let loop ((res '()) (idl (cdr id-l)))
  196. (if (null? idl) res
  197. (loop (cons (declr->id-name (sx-ref (car idl) 1)) res)
  198. (cdr idl))))
  199. '())))
  200. ;; @deffn {Procedure} save-typenames decl
  201. ;; Save the typenames for the lexical analyzer and return the decl.
  202. ;; @end deffn
  203. (define (save-typenames decl)
  204. ;; This finds typenames using @code{find-new-typenames} and adds via
  205. ;; @code{add-typename}. Then return the decl.
  206. (for-each add-typename (find-new-typenames decl))
  207. decl)
  208. ;; (string "abc" "def") -> (string "abcdef")
  209. ;; In the case that declaration-specifiers only returns a list of
  210. ;; attribute-specifiers then this has to be an empty-statemnet with
  211. ;; attributes. See:
  212. ;; https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Statement-Attributes.html
  213. (define (XXX-only-attr-specs? specs)
  214. (let loop ((specs specs))
  215. (cond
  216. ((null? specs) #t)
  217. ((not (eqv? 'attributes (sx-tag (car specs)))) #f)
  218. (else (loop (cdr specs))))))
  219. ;; ------------------------------------------------------------------------
  220. (define (c99-err . args)
  221. (apply throw 'c99-error args))
  222. ;; @deffn {Procedure} read-cpp-line ch => #f | (cpp-xxxx)??
  223. ;; Given if ch is #\# read a cpp-statement.
  224. ;; The standard implies that comments are tossed here but we keep them
  225. ;; so that they can end up in the pretty-print output.
  226. ;; @end deffn
  227. (define (read-cpp-line ch)
  228. (if (not (eq? ch #\#)) #f
  229. (let loop ((cl '()) (ch (read-char)))
  230. (cond
  231. ;;((eof-object? ch) (throw 'cpp-error "CPP lines must end in newline"))
  232. ((eof-object? ch) (reverse-list->string cl))
  233. ((eq? ch #\newline) (unread-char ch) (reverse-list->string cl))
  234. ((eq? ch #\\)
  235. (let ((c2 (read-char)))
  236. (if (eq? c2 #\newline)
  237. (loop cl (read-char))
  238. (loop (cons* c2 ch cl) (read-char)))))
  239. ((eq? ch #\/) ;; swallow comments, even w/ newlines
  240. (let ((c2 (read-char)))
  241. (cond
  242. ((eqv? c2 #\*)
  243. (let loop2 ((cl2 (cons* #\* #\/ cl)) (ch (read-char)))
  244. (cond
  245. ((eq? ch #\*)
  246. (let ((c2 (read-char)))
  247. (if (eqv? c2 #\/)
  248. (loop (cons* #\/ #\* cl2) (read-char)) ;; keep comment
  249. (loop2 (cons #\* cl2) c2))))
  250. (else
  251. (loop2 (cons ch cl2) (read-char))))))
  252. (else
  253. (loop (cons #\/ cl) c2)))))
  254. (else (loop (cons ch cl) (read-char)))))))
  255. (define (def-xdef? name mode)
  256. (not (eqv? mode 'file)))
  257. ;; @deffn {Procedure} make-c99-lexer-generator match-table raw-parser => proc
  258. ;; This generates a procedure which has the signature
  259. ;; @example
  260. ;; proc [#:mode mode] [#:xdef? proc] => procedure
  261. ;; @end example
  262. ;; to be passed to the c99 parsers.
  263. ;; The proc will generate a context-sensitive lexer for the C99 language.
  264. ;; The arg @var{match-table} is an element of a specification returned
  265. ;; by @code{make-lalr-spec} or machine generated by @code{make-lalr-machine}.
  266. ;; The argument @var{raw-parse} must be ...
  267. ;; The generated
  268. ;; lexical analyzer reads and passes comments and optionally CPP statements
  269. ;; to the parser. The keyword argument @var{mode} will determine if CPP
  270. ;; statements are passed (@code{'file} mode) or parsed and executed
  271. ;; (@code{'file} mode) as described above. Comments will be passed as
  272. ;; ``line'' comments or ``lone'' comments: lone comments appear on a line
  273. ;; without code. The @code{xdef?} keyword argument allows user to pass
  274. ;; a predicate which determines whether CPP symbols in code are expanded.
  275. ;; The default predicate is
  276. ;; @example
  277. ;; (define (def-xdef? mode name) (eqv? mode 'code))
  278. ;; @end example
  279. ;; @end deffn
  280. (define (make-c99-lexer-generator match-table raw-parser)
  281. ;; This gets ugly in order to handle cpp. The CPP will tokenize, expand,
  282. ;; then convert back to a string.
  283. ;;
  284. ;; todo: check if @code{1.3f} gets parsed as a number.
  285. ;; todo: I think there is a bug wrt the comment reader because // ... \n
  286. ;; will end up in same mode... so after
  287. ;; int x; // comment
  288. ;; the lexer will think we are not at BOL.
  289. ;;
  290. ;; The state variable `suppress' is used to suppress re-expansion of input
  291. ;; text generated by the CPP macro expander. The CPP replacement text
  292. ;; inserted via a string-port on the port stack. When that port is fully
  293. ;; read (i.e., the reader sees eof-object) then @var{suppress} is changed
  294. ;; to @code{#t}.
  295. (define (getdefs stmts) ; extract defines
  296. (fold-right
  297. (lambda (stmt seed)
  298. ;;(sx-match stmt
  299. ;; ((cpp-stmt (define . ,rest)) (cons (sx-ref stmt 1) seed))
  300. ;; (else seed)))
  301. (if (and (eqv? 'cpp-stmt (sx-tag stmt))
  302. (eqv? 'define (sx-tag (sx-ref stmt 1))))
  303. (cons (sx-ref stmt 1) seed)
  304. seed))
  305. '() stmts))
  306. (let* ((ident-like? (make-ident-like-p read-c-ident))
  307. ;;
  308. (strtab (filter-mt string? match-table)) ; strings in grammar
  309. (kwstab (filter-mt ident-like? strtab)) ; keyword strings =>
  310. (keytab (map-mt string->symbol kwstab)) ; keywords in grammar
  311. (chrseq (remove-mt ident-like? strtab)) ; character sequences
  312. (symtab (filter-mt symbol? match-table)) ; symbols in grammar
  313. (chrtab (filter-mt char? match-table)) ; characters in grammar
  314. ;;
  315. (read-chseq (make-chseq-reader chrseq))
  316. (assc-$ (lambda (pair)
  317. (cons (assq-ref symtab (car pair)) (cdr pair))))
  318. ;;
  319. (t-ident (assq-ref symtab '$ident))
  320. (t-typename (assq-ref symtab 'typename)))
  321. ;; mode: 'code|'file|'decl
  322. ;; xdef?: (proc name mode) => #t|#f : do we expand #define?
  323. ;;(lambda* (#:key (mode 'code) xdef? show-incs)
  324. (define* (lexer #:key (mode 'code) xdef? show-incs)
  325. (define (run-parse)
  326. (let ((info (fluid-ref *info*)))
  327. (raw-parser (lexer #:mode 'decl #:show-incs (cpi-shinc info))
  328. #:debug (cpi-debug info))))
  329. (let ((bol #t) ; begin-of-line condition
  330. (suppress #f) ; parsing cpp expanded text (kludge?)
  331. (ppxs (list 'keep)) ; CPP execution state stack
  332. (info (fluid-ref *info*)) ; info shared w/ parser
  333. ;;(brlev 0) ; brace level
  334. (x-def? (cond ((procedure? xdef?) xdef?)
  335. ((eq? xdef? #t) (lambda (n m) #t))
  336. (else def-xdef?))))
  337. ;; Return the first (tval . lval) pair not excluded by the CPP.
  338. (lambda ()
  339. (define (add-define tree)
  340. (let* ((tail (cdr tree))
  341. (name (car (assq-ref tail 'name)))
  342. (args (assq-ref tail 'args))
  343. (repl (car (assq-ref tail 'repl)))
  344. (cell (cons name (if args (cons args repl) repl))))
  345. (set-cpi-defs! info (cons cell (cpi-defs info)))))
  346. (define (rem-define name)
  347. (set-cpi-defs! info (acons name #f (cpi-defs info))))
  348. (define (apply-helper file)
  349. ;; file will include <> or "", need to strip
  350. (let* ((tyns (assoc-ref (cpi-itynd info) file))
  351. (defs (assoc-ref (cpi-idefd info) file)))
  352. (when tyns
  353. (for-each add-typename tyns)
  354. (set-cpi-defs! info (append defs (cpi-defs info))))
  355. tyns))
  356. (define (inc-stmt->file-spec stmt) ;; retain <> or ""
  357. (let* ((arg (cadr stmt)))
  358. (if (ident-like? arg) ;; #include MYFILE
  359. (expand-cpp-macro-ref arg (cpi-defs info))
  360. arg)))
  361. (define (file-spec->file spec)
  362. (substring/shared spec 1 (1- (string-length spec))))
  363. (define (inc-file-spec->path spec next)
  364. (find-incl-in-dirl spec (cpi-incs info) next))
  365. (define (code-if stmt)
  366. (case (car ppxs)
  367. ((skip-look skip-done skip) ;; don't eval if excluded
  368. (set! ppxs (cons 'skip ppxs)))
  369. (else
  370. (let* ((defs (cpi-defs info))
  371. (val (eval-cpp-cond-text (cadr stmt) defs
  372. #:inc-dirs (cpi-incs info))))
  373. (if (not val) (c99-err "unresolved: ~S" (cadr stmt)))
  374. (if (eq? 'keep (car ppxs))
  375. (if (zero? val)
  376. (set! ppxs (cons 'skip-look ppxs))
  377. (set! ppxs (cons 'keep ppxs)))
  378. (set! ppxs (cons 'skip-done ppxs))))))
  379. stmt)
  380. (define (code-elif stmt)
  381. (case (car ppxs)
  382. ((skip) #t) ;; don't eval if excluded
  383. (else
  384. (let* ((defs (cpi-defs info))
  385. (val (eval-cpp-cond-text (cadr stmt) defs
  386. #:inc-dirs (cpi-incs info))))
  387. (if (not val) (c99-err "unresolved: ~S" (cadr stmt)))
  388. (case (car ppxs)
  389. ((skip-look) (if (not (zero? val)) (set-car! ppxs 'keep)))
  390. ((keep) (set-car! ppxs 'skip-done))))))
  391. stmt)
  392. (define (code-else stmt)
  393. (case (car ppxs)
  394. ((skip-look) (set-car! ppxs 'keep))
  395. ((keep) (set-car! ppxs 'skip-done)))
  396. stmt)
  397. (define (code-endif stmt)
  398. (set! ppxs (cdr ppxs))
  399. stmt)
  400. (define* (eval-cpp-incl/here stmt #:optional next) ;; => stmt
  401. (let* ((spec (inc-stmt->file-spec stmt))
  402. (file (file-spec->file spec))
  403. (path (inc-file-spec->path spec next)))
  404. (if show-incs (sferr "include ~A => ~S\n" spec path))
  405. (cond
  406. ((apply-helper file) stmt)
  407. ((not path) (c99-err "not found: ~S" file))
  408. (else (set! bol #t)
  409. (push-input (open-input-file path))
  410. (if path (sx-attr-add stmt 'path path) stmt)))))
  411. (define* (eval-cpp-incl/tree stmt #:optional next) ;; => stmt
  412. ;; include file as a new tree
  413. (let* ((spec (inc-stmt->file-spec stmt))
  414. (file (file-spec->file spec))
  415. (path (inc-file-spec->path spec next)))
  416. (if show-incs (sferr "include ~A => ~S\n" spec path))
  417. (cond
  418. ((apply-helper file) stmt)
  419. ((not path) (c99-err "not found: ~S" file))
  420. ((with-input-from-file path run-parse) =>
  421. (lambda (tree) ;; add tree
  422. (for-each add-define (getdefs tree))
  423. (append (if path (sx-attr-add stmt 'path path) stmt)
  424. (list tree)))))))
  425. (define (eval-cpp-stmt/code stmt) ;; => stmt
  426. (case (car stmt)
  427. ((if) (code-if stmt))
  428. ((elif) (code-elif stmt))
  429. ((else) (code-else stmt))
  430. ((endif) (code-endif stmt))
  431. (else
  432. (if (eqv? 'keep (car ppxs))
  433. (case (car stmt)
  434. ((include) (eval-cpp-incl/here stmt))
  435. ((include-next) (eval-cpp-incl/here stmt 'next))
  436. ((define) (add-define stmt) stmt)
  437. ((undef) (rem-define (cadr stmt)) stmt)
  438. ((error) (c99-err "error: #error ~A" (cadr stmt)))
  439. ((warning) (report-error "warning: ~A" (cdr stmt)))
  440. ((pragma) stmt)
  441. ((line) stmt)
  442. (else
  443. (sferr "stmt: ~S\n" stmt)
  444. (error "nyacc eval-cpp-stmt/code: bad cpp flow stmt")))
  445. stmt))))
  446. (define (eval-cpp-stmt/decl stmt) ;; => stmt
  447. (case (car stmt)
  448. ((if) (code-if stmt))
  449. ((elif) (code-elif stmt))
  450. ((else) (code-else stmt))
  451. ((endif) (code-endif stmt))
  452. (else
  453. (if (eqv? 'keep (car ppxs))
  454. (case (car stmt)
  455. ((include) ; use tree unless inside braces
  456. (if (cpi-top-blev? info)
  457. (eval-cpp-incl/tree stmt)
  458. (eval-cpp-incl/here stmt)))
  459. ((include-next) ; gcc extension
  460. (if (cpi-top-blev? info)
  461. (eval-cpp-incl/tree stmt 'next)
  462. (eval-cpp-incl/here stmt 'next)))
  463. ((define) (add-define stmt) stmt)
  464. ((undef) (rem-define (cadr stmt)) stmt)
  465. ((error) (c99-err "error: #error ~A" (cadr stmt)))
  466. ((warning) (report-error "warning: ~A" (cdr stmt)) stmt)
  467. ((pragma) stmt) ;; ignore for now
  468. ((line) stmt)
  469. (else
  470. (sferr "stmt: ~S\n" stmt)
  471. (error "eval-cpp-stmt/decl: bad cpp flow stmt")))
  472. stmt))))
  473. (define (eval-cpp-stmt/file stmt) ;; => stmt
  474. (case (car stmt)
  475. ((if) (cpi-push-x) stmt)
  476. ((elif else) (cpi-shift-x) stmt)
  477. ((endif) (cpi-pop-x) stmt)
  478. ((include) (eval-cpp-incl/tree stmt))
  479. ((define) (add-define stmt) stmt)
  480. ((undef) (rem-define (cadr stmt)) stmt)
  481. ((error) stmt)
  482. ((warning) stmt)
  483. ((pragma) stmt)
  484. ((line) stmt)
  485. (else
  486. (sferr "stmt: ~S\n" stmt)
  487. (error "eval-cpp-stmt/file: bad cpp flow stmt"))))
  488. ;; Maybe evaluate the CPP statement.
  489. (define (eval-cpp-stmt stmt)
  490. (with-throw-handler
  491. 'cpp-error
  492. (lambda ()
  493. (case mode
  494. ((code) (eval-cpp-stmt/code stmt))
  495. ((decl) (eval-cpp-stmt/decl stmt))
  496. ((file) (eval-cpp-stmt/file stmt))
  497. (else (error "nyacc eval-cpp-stmt: coding error"))))
  498. (lambda (key fmt . rest)
  499. (report-error fmt rest)
  500. (throw 'c99-error "CPP error"))))
  501. ;; Predicate to determine if we pass the cpp-stmt to the parser.
  502. ;; @itemize
  503. ;; If code mode, never
  504. ;; If file mode, all except includes between { }
  505. ;; If decl mode, only defines and includes outside {}
  506. ;; @end itemize
  507. (define (pass-cpp-stmt stmt)
  508. (if (eq? 'pragma (car stmt))
  509. (if (eq? mode 'file)
  510. `(cpp-stmt ,stmt)
  511. `($pragma . ,(cadr stmt)))
  512. (case mode
  513. ((code) #f)
  514. ((decl) (and (cpi-top-blev? info)
  515. (memq (car stmt) '(include define include-next))
  516. `(cpp-stmt . ,stmt)))
  517. ((file) (and
  518. (or (cpi-top-blev? info)
  519. (not (memq (car stmt) '(include include-next))))
  520. `(cpp-stmt . ,stmt)))
  521. (else (error "nyacc pass-cpp-stmt: coding error")))))
  522. ;; Composition of @code{read-cpp-line} and @code{eval-cpp-line}.
  523. (define (read-cpp-stmt ch)
  524. (and=> (read-cpp-line ch) cpp-line->stmt))
  525. (define (read-token)
  526. (let loop ((ch (read-char)))
  527. (cond
  528. ((eof-object? ch)
  529. (set! suppress #f)
  530. (if (pop-input)
  531. (loop (read-char))
  532. (assc-$ '($end . "#<eof>"))))
  533. ((eq? ch #\newline) (set! bol #t) (loop (read-char)))
  534. ((char-set-contains? c:ws ch) (loop (read-char)))
  535. (bol
  536. (set! bol #f)
  537. (cond ;; things that require bol
  538. ((read-c-comm ch #t #:skip-prefix #t) => assc-$)
  539. ((read-cpp-stmt ch) =>
  540. (lambda (stmt)
  541. (cond ((pass-cpp-stmt (eval-cpp-stmt stmt)) => assc-$)
  542. (else (loop (read-char))))))
  543. (else (loop ch))))
  544. ((read-c-chlit ch) => assc-$) ; before ident for [ULl]'c'
  545. ((read-c-ident ch) =>
  546. (lambda (name)
  547. (let ((symb (string->symbol name))
  548. (defs (cpi-defs info)))
  549. (cond
  550. ((and (not suppress)
  551. (x-def? name mode)
  552. (expand-cpp-macro-ref name defs))
  553. => (lambda (repl)
  554. (set! suppress #t) ; don't rescan
  555. (push-input (open-input-string repl))
  556. (loop (read-char))))
  557. ((assq-ref keytab symb)
  558. ;;^minor bug: won't work on #define keyword xxx
  559. ;; try (and (not (assoc-ref name defs))
  560. ;; (assq-ref keytab symb))
  561. => (lambda (t) (cons t name)))
  562. ((typename? name)
  563. (cons t-typename name))
  564. (else
  565. (cons t-ident name))))))
  566. ((read-c-num ch) => assc-$)
  567. ((read-c-string ch) => assc-$)
  568. ((read-c-comm ch #f #:skip-prefix #t) => assc-$)
  569. ;; Keep track of brace level and scope for typedefs.
  570. ((and (char=? ch #\{)
  571. (eqv? 'keep (car ppxs)) (cpi-inc-blev! info)
  572. #f) #f)
  573. ((and (char=? ch #\})
  574. (eqv? 'keep (car ppxs)) (cpi-dec-blev! info)
  575. #f) #f)
  576. ((read-chseq ch) => identity)
  577. ((assq-ref chrtab ch) => (lambda (t) (cons t (string ch))))
  578. ((eqv? ch #\\) ;; C allows \ at end of line to continue
  579. (let ((ch (read-char)))
  580. (cond ((eqv? #\newline ch) (loop (read-char))) ;; extend line
  581. (else (unread-char ch) (cons #\\ "\\"))))) ;; parse err
  582. (else (cons ch (string ch))))))
  583. ;; Loop between reading tokens and skipping tokens via CPP logic.
  584. (let loop ((pair (read-token)))
  585. ;;(report-error "lx loop=>~S" (list pair))
  586. (case (car ppxs)
  587. ((keep)
  588. pair)
  589. ((skip-done skip-look skip)
  590. (loop (read-token)))
  591. (else (error "make-c99-lexer-generator: coding error")))))))
  592. lexer))
  593. ;; --- last line ---