123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- ;;; lang/c99/body.scm - parser body, inserted in parser.scm
- ;; Copyright (C) 2015-2019 Matthew R. Wette
- ;;
- ;; This library is free software; you can redistribute it and/or
- ;; modify it under the terms of the GNU Lesser General Public
- ;; License as published by the Free Software Foundation; either
- ;; version 3 of the License, or (at your option) any later version.
- ;;
- ;; This library is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;; Lesser General Public License for more details.
- ;;
- ;; You should have received a copy of the GNU Lesser General Public License
- ;; along with this library; if not, see <http://www.gnu.org/licenses/>.
- ;;; Notes:
- ;; Notes on the code design may be found in doc/nyacc/lang/c99-hg.info
- ;; @section The C99 Parser Body
- ;; This code provides the front end to the C99 parser, including the lexical
- ;; analyzer and optional CPP processing. In @code{'file} mode the lex'er
- ;; passes CPP statements to the parser; in @code{'code} mode the lex'er
- ;; parses and evaluates the CPP statements. In the case of included files
- ;; (e.g., via @code{#include <file.h>}) the include files are parsed if
- ;; not in @code{inc-help}. The a-list @code{inc-help} maps
- ;; include file names to typenames (e.g., @code{stdio.h} to @code{FILE}) and
- ;; CPP defines (e.g., "INT_MAX=12344").
- ;; issue w/ brlev: not intended to beused with `extern "C" {'
- ;;; Code:
- (use-modules (nyacc lang sx-util))
- (use-modules (nyacc lang util))
- (use-modules ((srfi srfi-1) #:select (fold-right append-reverse)))
- (use-modules ((srfi srfi-9) #:select (define-record-type)))
- (use-modules (ice-9 pretty-print)) ; for debugging
- (define (sf fmt . args) (apply simple-format #t fmt args))
- (define pp pretty-print)
- ;; C parser info (?)
- (define-record-type cpi
- (make-cpi-1)
- cpi?
- (debug cpi-debug set-cpi-debug!) ; debug #t #f
- (shinc cpi-shinc set-cpi-shinc!) ; show includes
- (defines cpi-defs set-cpi-defs!) ; #defines
- (incdirs cpi-incs set-cpi-incs!) ; #includes
- (inc-tynd cpi-itynd set-cpi-itynd!) ; a-l of incfile => typenames
- (inc-defd cpi-idefd set-cpi-idefd!) ; a-l of incfile => defines
- (ptl cpi-ptl set-cpi-ptl!) ; parent typename list
- (ctl cpi-ctl set-cpi-ctl!) ; current typename list
- (blev cpi-blev set-cpi-blev!) ; curr brace/block level
- )
- ;;.@deffn Procedure split-cppdef defstr => (<name> . <repl>)| \
- ;; (<name> <args> . <repl>)|#f
- ;; Convert define string to a dict item. Examples:
- ;; @example
- ;; "ABC=123" => '("ABC" . "123")
- ;; "MAX(X,Y)=((X)>(Y)?(X):(Y))" => ("MAX" ("X" "Y") . "((X)>(Y)?(X):(Y))")
- ;; @end example
- ;; @end deffn
- (define (split-cppdef defstr)
- (let ((x2st (string-index defstr #\()) ; start of args
- (x2nd (string-index defstr #\))) ; end of args
- (x3 (string-index defstr #\=))) ; start of replacement
- (cond
- ((not x3) #f)
- ((and x2st x3)
- ;;(if (not (eq? (1+ x2nd) x3)) (c99-err "bad CPP def: ~S" defstr))
- (cons* (substring defstr 0 x2st)
- (string-split
- (string-delete #\space (substring defstr (1+ x2st) x2nd))
- #\,)
- (substring defstr (1+ x3))))
- (else
- (cons (substring defstr 0 x3) (substring defstr (1+ x3)))))))
- ;; @deffn Procedure make-cpi debug defines incdirs inchelp
- ;; I think there is a potential bug here in that the alist of cpp-defs/helpers
- ;; should be last-in-first-seen ordered. Probably helpers low prio.
- ;; The (CPP) defines can appear as pairs: then they have already been split.
- ;; (This is used by @code{parse-c99x}.)
- ;; @end deffn
- (define (make-cpi debug shinc defines incdirs inchelp)
- ;; convert inchelp into inc-file->typenames and inc-file->defines
- ;; Any entry for an include file which contains `=' is considered
- ;; a define; otherwise, the entry is a typename.
- (define (split-helper helper)
- (let ((file (car helper)))
- (let loop ((tyns '()) (defs '()) (ents (cdr helper)))
- (cond
- ((null? ents) (values (cons file tyns) (cons file defs)))
- ((split-cppdef (car ents)) =>
- (lambda (def) (loop tyns (cons def defs) (cdr ents))))
- (else (loop (cons (car ents) tyns) defs (cdr ents)))))))
- (define (split-if-needed def)
- (if (pair? def) def (split-cppdef def)))
- (let* ((cpi (make-cpi-1)))
- (set-cpi-debug! cpi debug) ; print states debug
- (set-cpi-shinc! cpi shinc) ; print includes
- (set-cpi-defs! cpi (map split-if-needed defines)) ; def's as pairs
- (set-cpi-incs! cpi incdirs) ; list of include dir's
- (set-cpi-ptl! cpi '()) ; list of lists of typenames
- (set-cpi-ctl! cpi '()) ; list of current typenames
- (set-cpi-blev! cpi 0) ; brace/block level
- ;; Break up the helpers into typenames and defines.
- (let loop ((itynd '()) (idefd '()) (helpers inchelp))
- (cond ((null? helpers)
- (set-cpi-itynd! cpi itynd)
- (set-cpi-idefd! cpi idefd))
- (else
- (call-with-values
- (lambda () (split-helper (car helpers)))
- (lambda (ityns idefs)
- (loop (cons ityns itynd) (cons idefs idefd) (cdr helpers)))))))
- ;; Assign builtins.
- (and=> (assoc-ref (cpi-itynd cpi) "__builtin")
- (lambda (tl) (set-cpi-ctl! cpi (append tl (cpi-ctl cpi)))))
- (and=> (assoc-ref (cpi-idefd cpi) "__builtin")
- (lambda (tl) (set-cpi-defs! cpi (append tl (cpi-defs cpi)))))
- cpi))
- (define *info* (make-fluid))
-
- (define cpi-inc-blev!
- (case-lambda
- ((info) (set-cpi-blev! info (1+ (cpi-blev info))))
- (() (cpi-inc-blev! (fluid-ref *info*)))))
- (define cpi-dec-blev!
- (case-lambda
- ((info) (set-cpi-blev! info (1- (cpi-blev info))))
- (() (cpi-dec-blev! (fluid-ref *info*)))))
- (define cpi-top-blev?
- (case-lambda
- ((info) (zero? (cpi-blev info)))
- (() (cpi-top-blev? (fluid-ref *info*)))))
- (define cpi-push
- (case-lambda
- ((info)
- (set-cpi-ptl! info (cons (cpi-ctl info) (cpi-ptl info)))
- (set-cpi-ctl! info '())
- #t)
- (() (cpi-push (fluid-ref *info*)))))
- (define cpi-pop
- (case-lambda
- ((info)
- (set-cpi-ctl! info (car (cpi-ptl info)))
- (set-cpi-ptl! info (cdr (cpi-ptl info)))
- #t)
- (() (cpi-pop (fluid-ref *info*)))))
- (define (cpi-push-x) ;; on #if
- ;;(sf "\ncpi-push-x:\n") (pp (fluid-ref *info*))
- (let ((cpi (fluid-ref *info*)))
- (set-cpi-ptl! cpi (cons (cpi-ctl cpi) (cpi-ptl cpi)))
- (set-cpi-ctl! cpi '())))
- (define (cpi-shift-x) ;; on #elif #else
- ;;(sf "\ncpi-shift-x:\n") (pp (fluid-ref *info*))
- (set-cpi-ctl! (fluid-ref *info*) '()))
- (define (cpi-pop-x) ;; on #endif
- ;;(sf "\ncpi-pop-x:\n") (pp (fluid-ref *info*))
- (let ((cpi (fluid-ref *info*)))
- (set-cpi-ctl! cpi (append (cpi-ctl cpi) (car (cpi-ptl cpi))))
- (set-cpi-ptl! cpi (cdr (cpi-ptl cpi)))))
- ;; @deffn {Procedure} typename? name
- ;; Called by lexer to determine if symbol is a typename.
- ;; Check current sibling for each generation.
- ;; @end deffn
- (define (typename? name)
- (let ((cpi (fluid-ref *info*)))
- (if (member name (cpi-ctl cpi)) #t
- (let loop ((ptl (cpi-ptl cpi)))
- (if (null? ptl) #f
- (if (member name (car ptl)) #t
- (loop (cdr ptl))))))))
- ;; @deffn {Procedure} add-typename name
- ;; Helper for @code{save-typenames}.
- ;; @end deffn
- (define (add-typename name)
- (let ((cpi (fluid-ref *info*)))
- (set-cpi-ctl! cpi (cons name (cpi-ctl cpi)))))
- ;; @deffn {Procedure} find-new-typenames decl
- ;; Helper for @code{save-typenames}.
- ;; Given declaration return a list of new typenames (via @code{typedef}).
- ;; @end deffn
- (define (find-new-typenames decl)
- ;; like declr-id in util2.scm
- (define (declr->id-name declr)
- (case (car declr)
- ((ident) (sx-ref declr 1))
- ((init-declr) (declr->id-name (sx-ref declr 1)))
- ((comp-declr) (declr->id-name (sx-ref declr 1)))
- ((array-of) (declr->id-name (sx-ref declr 1)))
- ((ptr-declr) (declr->id-name (sx-ref declr 2)))
- ((ftn-declr) (declr->id-name (sx-ref declr 1)))
- ((scope) (declr->id-name (sx-ref declr 1)))
- (else (error "coding bug: " declr))))
-
- ;;(sf "\ndecl:\n") (pp decl)
- (let* ((spec (sx-ref decl 1))
- (stor (sx-find 'stor-spec spec))
- (id-l (sx-ref decl 2)))
- (if (and stor (eqv? 'typedef (caadr stor)))
- (let loop ((res '()) (idl (cdr id-l)))
- (if (null? idl) res
- (loop (cons (declr->id-name (sx-ref (car idl) 1)) res)
- (cdr idl))))
- '())))
- ;; @deffn {Procedure} save-typenames decl
- ;; Save the typenames for the lexical analyzer and return the decl.
- ;; @end deffn
- (define (save-typenames decl)
- ;; This finds typenames using @code{find-new-typenames} and adds via
- ;; @code{add-typename}. Then return the decl.
- (for-each add-typename (find-new-typenames decl))
- decl)
- ;; (string "abc" "def") -> (string "abcdef")
- ;; In the case that declaration-specifiers only returns a list of
- ;; attribute-specifiers then this has to be an empty-statemnet with
- ;; attributes. See:
- ;; https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Statement-Attributes.html
- (define (XXX-only-attr-specs? specs)
- (let loop ((specs specs))
- (cond
- ((null? specs) #t)
- ((not (eqv? 'attributes (sx-tag (car specs)))) #f)
- (else (loop (cdr specs))))))
- ;; ------------------------------------------------------------------------
- (define (c99-err . args)
- (apply throw 'c99-error args))
- ;; @deffn {Procedure} read-cpp-line ch => #f | (cpp-xxxx)??
- ;; Given if ch is #\# read a cpp-statement.
- ;; The standard implies that comments are tossed here but we keep them
- ;; so that they can end up in the pretty-print output.
- ;; @end deffn
- (define (read-cpp-line ch)
- (if (not (eq? ch #\#)) #f
- (let loop ((cl '()) (ch (read-char)))
- (cond
- ;;((eof-object? ch) (throw 'cpp-error "CPP lines must end in newline"))
- ((eof-object? ch) (reverse-list->string cl))
- ((eq? ch #\newline) (unread-char ch) (reverse-list->string cl))
- ((eq? ch #\\)
- (let ((c2 (read-char)))
- (if (eq? c2 #\newline)
- (loop cl (read-char))
- (loop (cons* c2 ch cl) (read-char)))))
- ((eq? ch #\/) ;; swallow comments, even w/ newlines
- (let ((c2 (read-char)))
- (cond
- ((eqv? c2 #\*)
- (let loop2 ((cl2 (cons* #\* #\/ cl)) (ch (read-char)))
- (cond
- ((eq? ch #\*)
- (let ((c2 (read-char)))
- (if (eqv? c2 #\/)
- (loop (cons* #\/ #\* cl2) (read-char)) ;; keep comment
- (loop2 (cons #\* cl2) c2))))
- (else
- (loop2 (cons ch cl2) (read-char))))))
- (else
- (loop (cons #\/ cl) c2)))))
- (else (loop (cons ch cl) (read-char)))))))
- (define (def-xdef? name mode)
- (not (eqv? mode 'file)))
-
- ;; @deffn {Procedure} make-c99-lexer-generator match-table raw-parser => proc
- ;; This generates a procedure which has the signature
- ;; @example
- ;; proc [#:mode mode] [#:xdef? proc] => procedure
- ;; @end example
- ;; to be passed to the c99 parsers.
- ;; The proc will generate a context-sensitive lexer for the C99 language.
- ;; The arg @var{match-table} is an element of a specification returned
- ;; by @code{make-lalr-spec} or machine generated by @code{make-lalr-machine}.
- ;; The argument @var{raw-parse} must be ...
- ;; The generated
- ;; lexical analyzer reads and passes comments and optionally CPP statements
- ;; to the parser. The keyword argument @var{mode} will determine if CPP
- ;; statements are passed (@code{'file} mode) or parsed and executed
- ;; (@code{'file} mode) as described above. Comments will be passed as
- ;; ``line'' comments or ``lone'' comments: lone comments appear on a line
- ;; without code. The @code{xdef?} keyword argument allows user to pass
- ;; a predicate which determines whether CPP symbols in code are expanded.
- ;; The default predicate is
- ;; @example
- ;; (define (def-xdef? mode name) (eqv? mode 'code))
- ;; @end example
- ;; @end deffn
- (define (make-c99-lexer-generator match-table raw-parser)
- ;; This gets ugly in order to handle cpp. The CPP will tokenize, expand,
- ;; then convert back to a string.
- ;;
- ;; todo: check if @code{1.3f} gets parsed as a number.
- ;; todo: I think there is a bug wrt the comment reader because // ... \n
- ;; will end up in same mode... so after
- ;; int x; // comment
- ;; the lexer will think we are not at BOL.
- ;;
- ;; The state variable `suppress' is used to suppress re-expansion of input
- ;; text generated by the CPP macro expander. The CPP replacement text
- ;; inserted via a string-port on the port stack. When that port is fully
- ;; read (i.e., the reader sees eof-object) then @var{suppress} is changed
- ;; to @code{#t}.
- (define (getdefs stmts) ; extract defines
- (fold-right
- (lambda (stmt seed)
- ;;(sx-match stmt
- ;; ((cpp-stmt (define . ,rest)) (cons (sx-ref stmt 1) seed))
- ;; (else seed)))
- (if (and (eqv? 'cpp-stmt (sx-tag stmt))
- (eqv? 'define (sx-tag (sx-ref stmt 1))))
- (cons (sx-ref stmt 1) seed)
- seed))
- '() stmts))
-
- (let* ((ident-like? (make-ident-like-p read-c-ident))
- ;;
- (strtab (filter-mt string? match-table)) ; strings in grammar
- (kwstab (filter-mt ident-like? strtab)) ; keyword strings =>
- (keytab (map-mt string->symbol kwstab)) ; keywords in grammar
- (chrseq (remove-mt ident-like? strtab)) ; character sequences
- (symtab (filter-mt symbol? match-table)) ; symbols in grammar
- (chrtab (filter-mt char? match-table)) ; characters in grammar
- ;;
- (read-chseq (make-chseq-reader chrseq))
- (assc-$ (lambda (pair)
- (cons (assq-ref symtab (car pair)) (cdr pair))))
- ;;
- (t-ident (assq-ref symtab '$ident))
- (t-typename (assq-ref symtab 'typename)))
- ;; mode: 'code|'file|'decl
- ;; xdef?: (proc name mode) => #t|#f : do we expand #define?
- ;;(lambda* (#:key (mode 'code) xdef? show-incs)
- (define* (lexer #:key (mode 'code) xdef? show-incs)
- (define (run-parse)
- (let ((info (fluid-ref *info*)))
- (raw-parser (lexer #:mode 'decl #:show-incs (cpi-shinc info))
- #:debug (cpi-debug info))))
-
- (let ((bol #t) ; begin-of-line condition
- (suppress #f) ; parsing cpp expanded text (kludge?)
- (ppxs (list 'keep)) ; CPP execution state stack
- (info (fluid-ref *info*)) ; info shared w/ parser
- ;;(brlev 0) ; brace level
- (x-def? (cond ((procedure? xdef?) xdef?)
- ((eq? xdef? #t) (lambda (n m) #t))
- (else def-xdef?))))
- ;; Return the first (tval . lval) pair not excluded by the CPP.
- (lambda ()
- (define (add-define tree)
- (let* ((tail (cdr tree))
- (name (car (assq-ref tail 'name)))
- (args (assq-ref tail 'args))
- (repl (car (assq-ref tail 'repl)))
- (cell (cons name (if args (cons args repl) repl))))
- (set-cpi-defs! info (cons cell (cpi-defs info)))))
-
- (define (rem-define name)
- (set-cpi-defs! info (acons name #f (cpi-defs info))))
-
- (define (apply-helper file)
- ;; file will include <> or "", need to strip
- (let* ((tyns (assoc-ref (cpi-itynd info) file))
- (defs (assoc-ref (cpi-idefd info) file)))
- (when tyns
- (for-each add-typename tyns)
- (set-cpi-defs! info (append defs (cpi-defs info))))
- tyns))
- (define (inc-stmt->file-spec stmt) ;; retain <> or ""
- (let* ((arg (cadr stmt)))
- (if (ident-like? arg) ;; #include MYFILE
- (expand-cpp-macro-ref arg (cpi-defs info))
- arg)))
- (define (file-spec->file spec)
- (substring/shared spec 1 (1- (string-length spec))))
- (define (inc-file-spec->path spec next)
- (find-incl-in-dirl spec (cpi-incs info) next))
- (define (code-if stmt)
- (case (car ppxs)
- ((skip-look skip-done skip) ;; don't eval if excluded
- (set! ppxs (cons 'skip ppxs)))
- (else
- (let* ((defs (cpi-defs info))
- (val (eval-cpp-cond-text (cadr stmt) defs
- #:inc-dirs (cpi-incs info))))
- (if (not val) (c99-err "unresolved: ~S" (cadr stmt)))
- (if (eq? 'keep (car ppxs))
- (if (zero? val)
- (set! ppxs (cons 'skip-look ppxs))
- (set! ppxs (cons 'keep ppxs)))
- (set! ppxs (cons 'skip-done ppxs))))))
- stmt)
- (define (code-elif stmt)
- (case (car ppxs)
- ((skip) #t) ;; don't eval if excluded
- (else
- (let* ((defs (cpi-defs info))
- (val (eval-cpp-cond-text (cadr stmt) defs
- #:inc-dirs (cpi-incs info))))
- (if (not val) (c99-err "unresolved: ~S" (cadr stmt)))
- (case (car ppxs)
- ((skip-look) (if (not (zero? val)) (set-car! ppxs 'keep)))
- ((keep) (set-car! ppxs 'skip-done))))))
- stmt)
- (define (code-else stmt)
- (case (car ppxs)
- ((skip-look) (set-car! ppxs 'keep))
- ((keep) (set-car! ppxs 'skip-done)))
- stmt)
- (define (code-endif stmt)
- (set! ppxs (cdr ppxs))
- stmt)
-
- (define* (eval-cpp-incl/here stmt #:optional next) ;; => stmt
- (let* ((spec (inc-stmt->file-spec stmt))
- (file (file-spec->file spec))
- (path (inc-file-spec->path spec next)))
- (if show-incs (sferr "include ~A => ~S\n" spec path))
- (cond
- ((apply-helper file) stmt)
- ((not path) (c99-err "not found: ~S" file))
- (else (set! bol #t)
- (push-input (open-input-file path))
- (if path (sx-attr-add stmt 'path path) stmt)))))
- (define* (eval-cpp-incl/tree stmt #:optional next) ;; => stmt
- ;; include file as a new tree
- (let* ((spec (inc-stmt->file-spec stmt))
- (file (file-spec->file spec))
- (path (inc-file-spec->path spec next)))
- (if show-incs (sferr "include ~A => ~S\n" spec path))
- (cond
- ((apply-helper file) stmt)
- ((not path) (c99-err "not found: ~S" file))
- ((with-input-from-file path run-parse) =>
- (lambda (tree) ;; add tree
- (for-each add-define (getdefs tree))
- (append (if path (sx-attr-add stmt 'path path) stmt)
- (list tree)))))))
- (define (eval-cpp-stmt/code stmt) ;; => stmt
- (case (car stmt)
- ((if) (code-if stmt))
- ((elif) (code-elif stmt))
- ((else) (code-else stmt))
- ((endif) (code-endif stmt))
- (else
- (if (eqv? 'keep (car ppxs))
- (case (car stmt)
- ((include) (eval-cpp-incl/here stmt))
- ((include-next) (eval-cpp-incl/here stmt 'next))
- ((define) (add-define stmt) stmt)
- ((undef) (rem-define (cadr stmt)) stmt)
- ((error) (c99-err "error: #error ~A" (cadr stmt)))
- ((warning) (report-error "warning: ~A" (cdr stmt)))
- ((pragma) stmt)
- ((line) stmt)
- (else
- (sferr "stmt: ~S\n" stmt)
- (error "nyacc eval-cpp-stmt/code: bad cpp flow stmt")))
- stmt))))
-
- (define (eval-cpp-stmt/decl stmt) ;; => stmt
- (case (car stmt)
- ((if) (code-if stmt))
- ((elif) (code-elif stmt))
- ((else) (code-else stmt))
- ((endif) (code-endif stmt))
- (else
- (if (eqv? 'keep (car ppxs))
- (case (car stmt)
- ((include) ; use tree unless inside braces
- (if (cpi-top-blev? info)
- (eval-cpp-incl/tree stmt)
- (eval-cpp-incl/here stmt)))
- ((include-next) ; gcc extension
- (if (cpi-top-blev? info)
- (eval-cpp-incl/tree stmt 'next)
- (eval-cpp-incl/here stmt 'next)))
- ((define) (add-define stmt) stmt)
- ((undef) (rem-define (cadr stmt)) stmt)
- ((error) (c99-err "error: #error ~A" (cadr stmt)))
- ((warning) (report-error "warning: ~A" (cdr stmt)) stmt)
- ((pragma) stmt) ;; ignore for now
- ((line) stmt)
- (else
- (sferr "stmt: ~S\n" stmt)
- (error "eval-cpp-stmt/decl: bad cpp flow stmt")))
- stmt))))
-
- (define (eval-cpp-stmt/file stmt) ;; => stmt
- (case (car stmt)
- ((if) (cpi-push-x) stmt)
- ((elif else) (cpi-shift-x) stmt)
- ((endif) (cpi-pop-x) stmt)
- ((include) (eval-cpp-incl/tree stmt))
- ((define) (add-define stmt) stmt)
- ((undef) (rem-define (cadr stmt)) stmt)
- ((error) stmt)
- ((warning) stmt)
- ((pragma) stmt)
- ((line) stmt)
- (else
- (sferr "stmt: ~S\n" stmt)
- (error "eval-cpp-stmt/file: bad cpp flow stmt"))))
- ;; Maybe evaluate the CPP statement.
- (define (eval-cpp-stmt stmt)
- (with-throw-handler
- 'cpp-error
- (lambda ()
- (case mode
- ((code) (eval-cpp-stmt/code stmt))
- ((decl) (eval-cpp-stmt/decl stmt))
- ((file) (eval-cpp-stmt/file stmt))
- (else (error "nyacc eval-cpp-stmt: coding error"))))
- (lambda (key fmt . rest)
- (report-error fmt rest)
- (throw 'c99-error "CPP error"))))
- ;; Predicate to determine if we pass the cpp-stmt to the parser.
- ;; @itemize
- ;; If code mode, never
- ;; If file mode, all except includes between { }
- ;; If decl mode, only defines and includes outside {}
- ;; @end itemize
- (define (pass-cpp-stmt stmt)
- (if (eq? 'pragma (car stmt))
- (if (eq? mode 'file)
- `(cpp-stmt ,stmt)
- `($pragma . ,(cadr stmt)))
- (case mode
- ((code) #f)
- ((decl) (and (cpi-top-blev? info)
- (memq (car stmt) '(include define include-next))
- `(cpp-stmt . ,stmt)))
- ((file) (and
- (or (cpi-top-blev? info)
- (not (memq (car stmt) '(include include-next))))
- `(cpp-stmt . ,stmt)))
- (else (error "nyacc pass-cpp-stmt: coding error")))))
- ;; Composition of @code{read-cpp-line} and @code{eval-cpp-line}.
- (define (read-cpp-stmt ch)
- (and=> (read-cpp-line ch) cpp-line->stmt))
- (define (read-token)
- (let loop ((ch (read-char)))
- (cond
- ((eof-object? ch)
- (set! suppress #f)
- (if (pop-input)
- (loop (read-char))
- (assc-$ '($end . "#<eof>"))))
- ((eq? ch #\newline) (set! bol #t) (loop (read-char)))
- ((char-set-contains? c:ws ch) (loop (read-char)))
- (bol
- (set! bol #f)
- (cond ;; things that require bol
- ((read-c-comm ch #t #:skip-prefix #t) => assc-$)
- ((read-cpp-stmt ch) =>
- (lambda (stmt)
- (cond ((pass-cpp-stmt (eval-cpp-stmt stmt)) => assc-$)
- (else (loop (read-char))))))
- (else (loop ch))))
- ((read-c-chlit ch) => assc-$) ; before ident for [ULl]'c'
- ((read-c-ident ch) =>
- (lambda (name)
- (let ((symb (string->symbol name))
- (defs (cpi-defs info)))
- (cond
- ((and (not suppress)
- (x-def? name mode)
- (expand-cpp-macro-ref name defs))
- => (lambda (repl)
- (set! suppress #t) ; don't rescan
- (push-input (open-input-string repl))
- (loop (read-char))))
- ((assq-ref keytab symb)
- ;;^minor bug: won't work on #define keyword xxx
- ;; try (and (not (assoc-ref name defs))
- ;; (assq-ref keytab symb))
- => (lambda (t) (cons t name)))
- ((typename? name)
- (cons t-typename name))
- (else
- (cons t-ident name))))))
- ((read-c-num ch) => assc-$)
- ((read-c-string ch) => assc-$)
- ((read-c-comm ch #f #:skip-prefix #t) => assc-$)
- ;; Keep track of brace level and scope for typedefs.
- ((and (char=? ch #\{)
- (eqv? 'keep (car ppxs)) (cpi-inc-blev! info)
- #f) #f)
- ((and (char=? ch #\})
- (eqv? 'keep (car ppxs)) (cpi-dec-blev! info)
- #f) #f)
- ((read-chseq ch) => identity)
- ((assq-ref chrtab ch) => (lambda (t) (cons t (string ch))))
- ((eqv? ch #\\) ;; C allows \ at end of line to continue
- (let ((ch (read-char)))
- (cond ((eqv? #\newline ch) (loop (read-char))) ;; extend line
- (else (unread-char ch) (cons #\\ "\\"))))) ;; parse err
- (else (cons ch (string ch))))))
- ;; Loop between reading tokens and skipping tokens via CPP logic.
- (let loop ((pair (read-token)))
- ;;(report-error "lx loop=>~S" (list pair))
- (case (car ppxs)
- ((keep)
- pair)
- ((skip-done skip-look skip)
- (loop (read-token)))
- (else (error "make-c99-lexer-generator: coding error")))))))
- lexer))
- ;; --- last line ---
|