live-bootstrap/sysa/mes-0.22/module/nyacc/lang/c99/munge.scm
fosslinux 649d7b68dc Add mes and mescc-tools-extra
mescc-tools-extra contains two important tools:
- cp
- chmod

mes first builds itself from a mes 0.21 seed as used by guix, and then
builds a mes 0.22 and then mes 0.22 using that created mes 0.22.

It does /not/ use bootstrap.sh as we don't have a proper shell at this
point, it has been manually adapted for kaem.
2020-12-25 18:40:14 +11:00

1516 lines
54 KiB
Scheme

;;; nyacc/lang/c99/munge.scm - util's for processing output of the C99 parser
;; Copyright (C) 2015-2018 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:
;; Todo:
;; 1) mdecl == munged (unwrapped) declaration
;; 2) I want a way to keep named enums in expand-typerefs.
;; Currently, they are expanded to int.
;; 3) Usual sequence is: expand-typerefs, stripdown-udecl, udecl->mdecl.
;; 4) Unitize-decl is shallow. It does not dive into structs and unitize.
;; 5) In expand-typerefs if need to expand `foo_t *x' then change to
;; a) if struct use `struct foo *x;'
;; b) if fixed/float use `int *x;' etc
;; c> if function use `void *x;'
;; 6) Check use of comments as attributes.
;;; Code:
(define-module (nyacc lang c99 munge)
#:export (c99-trans-unit->udict
c99-trans-unit->udict/deep
udict-ref udict-struct-ref udict-union-ref udict-enum-ref
;; generating def's dict
c99-trans-unit->ddict udict-enums->ddict
;; munging
expand-typerefs
stripdown-udecl
udecl-rem-type-qual specl-rem-type-qual
udecl->mdecl udecl->mdecl/comm mdecl->udecl
unwrap-decl
canize-enum-def-list
fixed-width-int-names
typedef-decl?
unitize-decl unitize-comp-decl unitize-param-decl
declr-ident declr-id decl-id
iter-declrs
split-decl split-udecl
clean-field-list clean-fields
inc-keeper?
;; deprecated
udecl->mspec udecl->mspec/comm mspec->udecl
tree->udict tree->udict/deep
declr->ident
match-decl match-comp-decl match-param-decl
expand-decl-typerefs
fix-fields
;; debugging
stripdown-1
tdef-splice-specl
tdef-splice-declr)
#:use-module ((nyacc lang c99 cpp) #:select (eval-cpp-expr))
#:use-module (nyacc lang c99 cxeval) ;; eval-c99-cx
#:use-module (nyacc lang c99 pprint)
#:use-module (nyacc lang c99 util)
#:use-module (nyacc lang util)
#:use-module (nyacc lang sx-util)
#:use-module ((sxml fold) #:select (foldts foldts*))
#:use-module (sxml match)
#:use-module (srfi srfi-11) ; let-values
#:use-module (srfi srfi-2)
#:use-module (srfi srfi-1)
#:use-module (system base pmatch)
;; debugging:
#:use-module (system vm trace)
#:use-module (ice-9 pretty-print))
;; undocumented Guile builtins: or-map
(define (sferr fmt . args) (apply simple-format (current-error-port) fmt args))
(define (pperr exp)
(pretty-print exp (current-error-port) #:per-line-prefix " "))
(define OA object-address)
(define tmap-fmt
'(("char" "%hhd")
("unsigned char" "%hhu")
("short int" "%hd")
("unsigned short int" "%hu")
("int" "%d")
("unsigned int" "%u")
("long int" "%ld")
("unsigned long int" "%lu")
("long long int" "%lld")
("unsigned long long int" "%llu")))
;; @deffn {Procedure} attr-append . attr-lists
;; This is hack for now: it is @code{append}. It needs to be set up to combine
;; @code{attributes} and @code{comment}.
;; @end deffn
;; TODO FIXME
(define attr-append append)
;; @deffn {Procedure} pointer-declr? declr
;; This predictate indicates if @var{declr} is a pointer.
;; Does not handle @code{(*ftn)()}.
;; The argument can also be @code{init-declr-list} or @code{comp-declr-list}
;; in which case all elements need to be pointers.
;; @end deffn
(define (pointer-declr? declr)
;;(sferr "pointer-declr? ~S\n" declr)
(and
declr
(sx-match declr
((init-declr ,declr) (pointer-declr? declr))
((comp-declr ,declr) (pointer-declr? declr))
((param-declr ,declr) (pointer-declr? declr))
;;
((ptr-declr ,pointer ,dir-declr) #t)
((array-of . ,rest) #t)
((ftn-declr . ,rest) #t)
((abs-declr (pointer . ,r1) . ,r2) #t)
;;
((init-declr-list . ,declrs)
(fold (lambda (dcl seed) (and (pointer-declr? dcl) seed)) #t declrs))
((comp-declr-list . ,declrs)
(fold (lambda (dcl seed) (and (pointer-declr? dcl) seed)) #t declrs))
;;
(else #f))))
;; @deffn {Procedure} pointer-pass-declr? declr => #t|#f
;; This predicate determines if the declarator is implemented as a pointer.
;; That is, it is an explicit pointer, an array (ERROR), or a function.
;; @end deffn
(define (pointer-pass-declr? declr)
(and
declr
(sx-match declr
((init-declr ,declr) (pointer-declr? declr))
((comp-declr ,declr) (pointer-declr? declr))
((param-declr ,declr) (pointer-declr? declr))
;;
((ptr-declr ,pointer ,dir-declr) #t)
((array-of . ,rest) #t)
((ftn-declr . ,rest) #t)
((abs-declr (pointer . ,r1) . ,r2) #t)
;;
((init-declr-list . ,declrs)
(fold (lambda (dcl seed) (and (pointer-declr? dcl) seed)) #t declrs))
((comp-declr-list . ,declrs)
(fold (lambda (dcl seed) (and (pointer-declr? dcl) seed)) #t declrs))
;;
(else #f))))
;; Use the term @dfn{udecl}, or unit-declaration, for a declaration which has
;; only one decl-item. That is where,
;; @example
;; @end example
;; (decl (decl-spec-list ...) (init-declr-list (init-declr ...) ...))
;; @noindent
;; has been replaced by
;; (decl (decl-spec-list ...) (init-declr ...))
;; ...
;; @example
;; @end example
;; mdecl is
;; ("foo" (pointer-to) (array-of 3) (fixed-type "unsigned int"))
;; which can be converted to
;; ("(*foo) (array-of 3) (fixed-type "unsigned int"))
;; which can be converted to
;; (("((*foo)[0])" (fixed-type "unsigned int"))
;; ("((*foo)[1])" (fixed-type "unsigned int"))
;; ("((*foo)[2])" (fixed-type "unsigned int"))
;; may need to replace (typename "int32_t") with (fixed-type "int32_t")
;; @deffn {Procedure} inc-keeper? tree inc-filter => #f| tree
;; This is a helper. @var{inc-filter} is @code{#t}, @code{#f} or a
;; precicate procedure, which given the name of the file, determines if
;; it should be processed.
;; @end deffn
(define (inc-keeper? tree filter)
(if (and (eqv? (sx-tag tree) 'cpp-stmt)
(eqv? (sx-tag (sx-ref tree 1)) 'include)
(pair? (sx-ref* tree 1 2))
(if (procedure? filter)
(let ((incl (sx-ref* tree 1 1))
(path (sx-attr-ref (sx-ref tree 1) 'path)))
(filter incl path))
filter))
(sx-ref* tree 1 2)
#f))
;; @deffn {Procedure} c99-trans-unit->udict tree [seed] [#:inc-filter f]
;; @deffnx {Procedure} c99-trans-unit->udict/deep tree [seed]
;; Convert a C parse tree into a assoc-list of global names and definitions.
;; This will unwrap @code{init-declr-list} into list of decls w/
;; @code{init-declr}.
;; The declarations come reversed from order in file!
;; @example
;; BUG: need to add struct and union defn's: struct foo { int x; };
;; how to deal with this
;; lookup '(struct . "foo"), "struct foo", ???
;; wanted "struct" -> dict but that is not great
;; solution: unitize-decl => '(struct . "foo") then filter to generate
;; ("struct" ("foo" . decl) ("bar" . decl) ...)
;; ("union" ("bar" . decl) ("bar" . decl) ...)
;; ("enum" ("" . decl) ("foo" . decl) ("bar" . decl) ...)
;; @end example
;; So globals could be in udict, udefs or anon-enum.
;; @example
;; What about anonymous enums? And enums in general?
;; Anonmous enum should be expaneded into
;; @end example
;; @noindent
;; Notes:
;; @itemize
;; @item
;; If @var{tree} is not a pair then @var{seed} -- or @code{'()} -- is returned.
;; The inc-filter @var{f} is either @code{#t}, @code{#f} or predicate procedure
;; of one argument, the include path, to indicate whether it should be included
;; in the dictionary.
;; @item
;; If this routine is called multiple times on the same tree the u-decl's will
;; not be @code{eq?} since the top-level lists are generated on the fly.
;; (See @code{unitize-decl}.)
;; @end itemize
;; @end deffn
(define* (c99-trans-unit->udict tree #:optional (seed '()) #:key inc-filter)
(if (pair? tree)
(fold-right
(lambda (tree seed)
(cond
((eqv? (sx-tag tree) 'decl)
(unitize-decl tree seed))
((inc-keeper? tree inc-filter) =>
(lambda (inc-tree)
(c99-trans-unit->udict inc-tree seed #:inc-filter inc-filter)))
(else seed)))
seed
(cdr tree))
seed))
(define (c99-trans-unit->udict/deep tree)
(c99-trans-unit->udict tree #:inc-filter #t))
;; @deffn {Procedure} iter-declrs tag specl declrs seed
;; @deffnx {Procedure} iter-declrs-right tag specl declrs tail seed
;; This is a support procedure for the munge routines. If no decl'rs
;; then @var{declrs} should be #f.
;; Since @code{trans-unit->udict} is fold-right, this must be fold-right.
;; @end deffn
(define (iter-declrs tag attr specl declrs seed)
(if (pair? declrs)
(fold-right
(lambda (declr seed)
(acons (declr-id declr) (sx-list tag attr specl declr) seed))
seed declrs)
seed))
;; @deffn {Procedure} split-decl decl => values
;; This routine splits a declaration (or comp-decl or param-decl) into
;; its constituent parts. Attributes are currently not passed.
;; Get @code{(values tag spec-l declrs)}. If the declrator is
;; already unitized you get that, else the list (w/o tag) of declarators.
;; @example
;; (split-decl
;; '(decl (decl-spec-list (typedef) (fixed-type "int"))
;; (init-declr-list (declr (ident "a")) (declr (ident "b")))
;; (comment " good stuff ")))
;; =>
;; (values decl
;; #f
;; (decl-spec-list (typedef) (fixed-type "int")
;; ((declr (ident "a")) (declr (ident "b")))
;; ((comment " good stuff ")))
;; @end example
;; @end deffn
(define (split-decl decl)
(let* ((tag (sx-tag decl))
(attr (sx-attr decl))
(spec-l (sx-ref decl 1)) ; (decl-spec-list ...)
(dclr-l (sx-ref decl 2)) ; (init-declr-list (...))
(declrs (and=> dclr-l sx-tail))) ; ((...))
(values tag attr spec-l declrs)))
;; @deffn {Procedure} split-udecl decl => tag attr decl declr
;; This routine splits a unitized declaration into its constituent parts.
;; Get @code{(values tag attr spec-l declrs tail)}.
;; @example
;; (split-udecl
;; '(udecl (decl-spec-list (typedef) (fixed-type "int"))
;; (declr (ident "a"))
;; =>
;; (values decl
;; #f
;; (decl-spec-list (typedef) (fixed-type "int")
;; (declr (ident "a")))
;; @end example
;; @end deffn
(define (split-udecl udecl)
(let ((tag (sx-tag udecl))
(attr (sx-attr udecl))
(specl (sx-ref udecl 1)) ; (decl-spec-list ...)
(declr (sx-ref udecl 2))) ; (declr ...)|#f
(values tag attr specl declr)))
;; @deffn {Procedure} unitize-decl decl [seed] [#:expand-enums #f] => seed
;; This is a fold iterator intended to be used by @code{c99-trans-unit->udict}.
;; It converts the multiple @code{init-declr} items in an @code{init-declr-list}
;; of a @code{decl} into an a-list of multiple pairs of name and @code{udecl}
;; trees with a single @code{init-declr} and no @code{init-declr-list}.
;; That is, a @code{decl} of the form
;; @example
;; (decl (decl-spec-list ...)
;; (init-declr-list (init-declr (... "a")) (init-declr (... "b")) ...))
;; @end example
;; @noindent
;; is munged into list with elements
;; @example
;; ("a" . (udecl (decl-spec-list ...) (init-declr (... "a"))))
;; ("b" . (udecl (decl-spec-list ...) (init-declr (... "b"))))
;; @end example
;; Here we generate a dictionary of all declared items in a file:
;; @example
;; (let* ((sx0 (with-input-from-file src-file parse-c))
;; (sx1 (merge-inc-trees! sx0))
;; (name-dict (fold unitize-decl-1 '() (cdr sx1))))
;; @end example
;; Enum, struct and union def's have keys @code{(enum . "name")},
;; @code{(struct . "name")} and @code{(union . "name)}, respectively.
;; See @code{udict-struct-ref}, @code{udict-union-ref}, @code{udict-enum-ref}
;; and @code{udict-ref}. This procecure is robust to already munged decls.
;; To capture enum values as globals use @code{->ddict}.
;; @*
;; Notes: Now saving attributes at top level. Adding attributes for
;; struct and union (e.g., @code{__packed__}). The latter is needed
;; because they appear in files under @file{/usr/include}.
;; @end deffn
(define* (unitize-decl decl #:optional (seed '()))
(define* (make-udecl type-tag attr guts #:optional typename)
(if (and attr (pair? attr))
`(udecl (decl-spec-list (type-spec ,(cons* type-tag `(@ ,@attr) guts))))
`(udecl (decl-spec-list (type-spec ,(cons type-tag guts))))))
;; update depends on whether unitize- procedures use fold or fold-right
(define (update-left name value tag attr specl declrs seed)
(acons name value (iter-declrs tag attr specl declrs seed)))
(define (update-right name value tag attr specl declrs seed)
(iter-declrs tag attr specl declrs (acons name value seed)))
(define update update-right)
;;(simple-format #t "unitize-decl ~S\n" decl)
(cond
((not (pair? decl))
(throw 'nyacc-error "unitize-decl: bad arg: ~S" decl))
((eqv? (sx-tag decl) 'udecl)
(acons (udecl-id decl) decl seed))
;; todo: think more about attributes
;; * specl attributes are merged into structs and unions right now.
;; any others?
((eqv? (sx-tag decl) 'decl)
(let*-values (((tag decl-attr specl declrs) (split-decl decl))
((tag) (values 'udecl)))
;; TODO: for typedefs add attr (typedef "name") to associated udecls
;;(sferr "\nsx-match specl: attr=~S\n" attr) (pperr specl) (pperr declrs)
(sx-match specl
;; struct typedefs
((decl-spec-list
(@ . ,specl-attr)
(stor-spec (typedef))
(type-spec (struct-def (@ . ,attr) (ident ,name) . ,rest2) . ,rest1))
(update `(struct . ,name)
(make-udecl 'struct-def (attr-append attr specl-attr)
`((ident ,name) . ,rest2))
tag '() specl declrs seed))
((decl-spec-list
(stor-spec (typedef))
(type-spec (struct-def . ,rest2) . ,rest1))
(iter-declrs tag decl-attr specl declrs seed))
;; union typedefs
((decl-spec-list
(@ . ,specl-attr)
(stor-spec (typedef))
(type-spec (union-def (@ . ,attr) (ident ,name) . ,rest2) . ,rest1))
(update `(union . ,name)
(make-udecl 'union-def (attr-append attr specl-attr)
`((ident ,name) . ,rest2))
tag decl-attr specl declrs seed))
((decl-spec-list
(stor-spec (typedef))
(type-spec (union-def . ,rest2) . ,rest1))
(iter-declrs tag decl-attr specl declrs seed))
;; enum typedefs -- todo: handle attributes
((decl-spec-list
(stor-spec (typedef))
(type-spec (enum-def (ident ,name) . ,rest2) . ,rest1))
(update `(enum . ,name)
(make-udecl 'enum-def #f `((ident ,name) . ,rest2))
tag decl-attr specl declrs seed))
((decl-spec-list
(stor-spec (typedef))
(type-spec (enum-def . ,rest2) . ,rest1))
(iter-declrs tag #f specl declrs
(acons `(enum . "*anon*") (make-udecl 'enum-def #f rest2)
seed))
(update `(enum . "*anon*") (make-udecl 'enum-def #f rest2)
tag decl-attr specl declrs seed))
;; structs
((decl-spec-list
(@ . ,specl-attr)
(type-spec (struct-def (@ . ,attr) (ident ,name) . ,rest2) . ,rest1))
(update `(struct . ,name)
(make-udecl 'struct-def (attr-append attr specl-attr)
`((ident ,name) . ,rest2))
tag decl-attr specl declrs seed))
((decl-spec-list
(type-spec (struct-def . ,rest2) . ,rest1))
(iter-declrs tag decl-attr specl declrs seed))
;; unions
((decl-spec-list
(type-spec (union-def (@ . ,aattr) (ident ,name) . ,rest2) . ,rest1))
(update `(union . ,name)
(make-udecl 'union-def aattr `((ident ,name) . ,rest2))
tag decl-attr specl declrs seed))
((decl-spec-list
(type-spec (union-def . ,rest2) . ,rest1))
(iter-declrs tag decl-attr specl declrs seed))
;; enums
((decl-spec-list
(type-spec (enum-def (ident ,name) . ,rest2) . ,rest1))
(update `(enum . ,name)
(make-udecl 'enum-def #f `((ident ,name) . ,rest2))
tag decl-attr specl declrs seed))
((decl-spec-list
(type-spec (enum-def . ,rest2) . ,rest1))
(update `(enum . "*anon*") (make-udecl 'enum-def #f rest2)
tag decl-attr specl declrs seed))
(else (iter-declrs tag decl-attr specl declrs seed)))))
((eqv? (sx-tag decl) 'comp-udecl) (acons (udecl-id decl) decl seed))
((eqv? (sx-tag decl) 'comp-decl) (unitize-comp-decl decl seed))
((eqv? (sx-tag decl) 'param-decl) (unitize-param-decl decl seed))
(else seed)))
;; @deffn {Procedure} unitize-comp-decl decl [seed]
;; This will turn
;; @example
;; (comp-decl (decl-spec-list (type-spec "int"))
;; (comp-decl-list
;; (comp-declr (ident "a")) (comp-declr (ident "b"))))
;; @end example
;; @noindent
;; into
;; @example
;; ("a" . (comp-udecl (decl-spec-list ...) (comp-declr (ident "a"))))
;; ("b" . (comp-udecl (decl-spec-list ...) (comp-declr (ident "b"))))
;; @end example
;; @noindent
;; This is coded to be used with fold to be consistent with other unitize
;; functions @code{struct} and @code{union} field lists. The result needs
;; to be reversed.
;; @end deffn
(define* (unitize-comp-decl decl #:optional (seed '()))
(cond
((not (pair? decl))
(throw 'nyacc-error "unitize-decl: bad arg: ~S" decl))
((eqv? (sx-tag decl) 'comp-udecl)
(acons (udecl-id decl) decl seed))
((eqv? (sx-tag (sx-ref decl 2)) 'comp-declr-list)
(let-values (((tag attr spec-l declrs) (split-decl decl)))
(iter-declrs 'comp-udecl attr spec-l declrs seed)))
(else
seed)))
;; @deffn {Procedure} unitize-param-decl param-decl [seed] [#:expand-enums #f]
;; This will turn
;; @example
;; (param-decl (decl-spec-list (type-spec "int"))
;; (param-declr (ident "a")))
;; @end example
;; @noindent
;; into
;; @example
;; ("a" . (param-decl (decl-spec-list ...) (param-declr (ident "a"))))
;; @end example
;; @noindent
;; This is coded to be used with fold-right in order to preserve order
;; in @code{struct} and @code{union} field lists.
;; @*
;; TODO: What about abstract declarators? Should use "*anon*".
;; @end deffn
(define* (unitize-param-decl decl #:optional (seed '()) #:key (expand-enums #f))
(if (not (eqv? 'param-decl (car decl))) seed
(let* ((tag (sx-ref decl 0))
(attr (sx-attr decl))
(spec (sx-ref decl 1)) ; (type-spec ...)
(declr (sx-ref decl 2)) ; (param-declr ...)
(ident (declr-ident declr))
(name (cadr ident)))
(acons name decl seed))))
;; @deffn {Procedure} declr-ident declr => (ident "name")
;; Given a declarator, aka @code{init-declr}, return the identifier.
;; This is used by @code{trans-unit->udict}.
;; @end deffn
(define (declr-ident declr)
(sx-match declr
((ident ,name) declr)
((init-declr ,declr . ,rest) (declr-ident declr))
((comp-declr ,declr) (declr-ident declr))
((param-declr ,declr) (declr-ident declr))
((array-of ,dir-declr ,array-spec) (declr-ident dir-declr))
((array-of ,dir-declr) (declr-ident dir-declr))
((ptr-declr ,pointer ,dir-declr) (declr-ident dir-declr))
((ftn-declr ,dir-declr . ,rest) (declr-ident dir-declr))
((scope ,declr) (declr-ident declr))
((bit-field ,ident . ,rest) ident)
(else (throw 'c99-error "c99/munge: unknown declarator: ~S" declr))))
;; @deffn {Procedure} declr-id decl => "name"
;; This extracts the name from the return value of @code{declr-ident}.
;; @end deffn
(define (declr-id declr)
(and=> (declr-ident declr) cadr))
;; @deffn {Procedure} udecl-id udecl => string
;; generate the name
;; @end deffn
(define (udecl-id udecl)
;; must be udecl w/ name
(declr-id (sx-ref udecl 2)))
;; like member but returns first non-declr of type in dict
(define (non-declr type udict)
(let loop ((dict udict))
(cond ((null? dict) '())
((and (pair? (caar dict)) (eqv? type (caaar dict))) dict)
(else (loop (cdr dict))))))
(define (enum-decl-val enum-udecl name)
;; (decl (decl-spec-list (type-sped (enum-def (enum-def-list ...) => "123"
(enum-ref (cadadr (cadadr enum-udecl)) name))
;; @deffn {Procedure} udict-ref name
;; @deffnx {Procedure} udict-struct-ref name
;; @deffnx {Procedure} udict-union-ref name
;; @deffnx {Procedure} udict-enum-ref name
;; @deffnx {Procedure} udict-enum-val name
;; Look up refernce in a u-dict. If the reference is found return the
;; u-decl. In the case of @code{udict-enum-val} the string value is returned.
;; @end deffn
(define (udict-ref udict name)
(or (assoc-ref udict name)
(let loop ((dict (non-declr 'enum udict)))
(cond
((null? dict) #f)
((enum-decl-val (cdar dict) name) =>
(lambda (val) (gen-enum-udecl name val)))
(else (loop (non-declr 'enum (cdr dict))))))))
(define (udict-struct-ref udict name)
(assoc-ref udict `(struct . ,name)))
(define (udict-union-ref udict name)
(assoc-ref udict `(union . ,name)))
(define* (udict-enum-ref udict name)
(assoc-ref udict `(enum . ,name)))
(define* (udict-enum-val udict name)
(let loop ((dict (non-declr 'enum udict)))
(cond ((null? dict) #f)
((enum-decl-val (cdar dict) name))
(else (loop (non-declr 'enum (cdr dict)))))))
;; @deffn {Variable} fixed-width-int-names
;; This is a list of standard integer names (e.g., @code{"uint8_t"}).
;; @end deffn
(define fixed-width-int-names
'("int8_t" "uint8_t" "int16_t" "uint16_t"
"int32_t" "uint32_t" "int64_t" "uint64_t"))
;; @deffn {Procedure} typedef-decl? decl)
;; @end deffn
(define (typedef-decl? decl)
(sx-match decl
((decl (decl-spec-list (stor-spec (typedef)) . ,r1) . ,r2) #t)
(else #f)))
;; @deffn {Procedure} repl-typespec decl-spec-list repl-type-spec
;; In the decl-spec-list replace the type-specifier.
;; @end deffn
(define (repl-typespec decl-spec-list replacement)
(fold-right
(lambda (item seed)
(cond ((symbol? item) (cons item seed))
((eq? 'type-spec (car item)) (cons replacement seed))
(else (cons item seed))))
'() decl-spec-list))
;; @deffn {Procedure} declr-list? declr
;; Determine if declr it is a list or not.
;; Often declr can be xxxx-declr-list or xxxx-declr.
;; @end deffn
(define (declr-list? declr)
(member (sx-tag declr) '(init-declr-list comp-declr-list)))
;; === typedef expansion ===============
;; allows only one storage specifier besides typedef
;; call this (injest-in-specl orig-specl repl-specl)
(define (tdef-splice-specl orig-specl repl-specl)
(let loop ((specl '()) (repll '()) (origl (cdr orig-specl)))
(cond
((pair? repll)
(cond
((equal? (car repll) '(stor-spec (typedef)))
(loop specl (cdr repll) origl))
((equal? (car repll) '(stor-spec (const)))
(loop (cons (car repll) specl) (cdr repll) origl))
((member (car repll) specl) ; don't duplicate other stor-spec's
(loop specl (cdr repll) origl))
(else
(loop (cons (car repll) specl) (cdr repll) origl))))
((pair? origl)
(cond
((pmatch (car origl) ((type-spec (typename ,name)) #t) (,othersize #f))
(loop specl (cdr repl-specl) (cdr origl))) ; now insert replacement
((equal? (car origl) '(stor-spec (const)))
(loop (cons (car repll) specl) repll (cdr origl)))
((member (car origl) specl) ; don't duplicate "auto", "extern"
(loop specl repll (cdr origl)))
(else
(loop (cons (car origl) specl) repll (cdr origl)))))
(else
(cons 'decl-spec-list (reverse specl))))))
;; Consider
;; @example
;; typedef int *foo_t;
;; foo_t bla[3];
;; @end example
;; @noindent
;; maps
;; @example
;; bla[3] => *(bla[3])
;; @end example
(define (tdef-splice-declr orig-declr tdef-declr)
(define (probe-declr declr)
(sx-match declr
((ident ,name)
(sx-ref orig-declr 1))
((init-declr ,declr . ,rest)
`(init-declr ,(probe-declr declr) . ,rest))
((comp-declr ,declr)
`(comp-declr ,(probe-declr declr)))
((param-declr ,declr)
`(param-declr ,(probe-declr declr)))
((array-of ,dir-declr ,array-spec)
`(array-of ,(probe-declr dir-declr) ,array-spec))
((array-of ,dir-declr)
`(array-of ,(probe-declr dir-declr)))
((ptr-declr ,pointer ,dir-declr)
`(ptr-declr ,pointer ,(probe-declr dir-declr)))
((ftn-declr ,dir-declr . ,rest)
`(ftn-declr ,(probe-declr dir-declr) . ,rest))
((scope ,declr)
`(scope ,(probe-declr declr)))
(else (throw 'c99-error "c99/munge: unknown declarator: ~S" declr))))
(probe-declr tdef-declr))
;; @deffn {Procedure} tdef-splice-declr-list orig-declr-list tdef-declr
;; iterate tdef-splice-declr over a declr-init-list (or equiv)
;; @end deffn
(define (tdef-splice-declr-list orig-declr-list tdef-declr)
(sx-cons*
(sx-tag orig-declr-list)
(sx-attr orig-declr-list)
(fold-right
(lambda (declr seed)
(cons (tdef-splice-declr declr tdef-declr) seed))
'() (sx-tail orig-declr-list 1))))
;; @deffn {Procedure} compound-key type-spec-tag name
;; type-spec-tag is struct/union-ref/def
;; @example
;; (compound-key 'struct-ref) => struct
;; (compount-key 'union-ref "_foo") => (union . "_foo")
;; @end example
;; @end deffn
(define* (compound-key type-spec-tag #:optional name)
(let ((key (case type-spec-tag
((struct-ref struct-def) 'struct)
((union-ref union-def) 'union))))
(if name (cons key name) key)))
;; Replace the type-spec in @var{decl-spec-list} with @var{type-spec}.
(define (replace-type-spec decl-spec-list type-spec)
(sx-cons*
(sx-tag decl-spec-list)
(sx-attr decl-spec-list)
(map
(lambda (elt) (if (eq? (sx-tag elt) 'type-spec) type-spec elt))
(sx-tail decl-spec-list 1))))
;; declr can be xxxx-declr-list or xxxx-declr
;; This needs to be able to accept @code{#f} @var{declr}. <= done, methinks
(define (expand-specl-typerefs specl declr udict keep)
;; In the process of expanding typerefs it is crutial that routines which
;; expand parts return the original if no change made. That is, if there
;; are no changes then @code{(eq? (expand-typerefs expr) expr)} is true.
;; If not, then infinite loop will result.
(define (re-expand specl declr) ;; applied after typename
(expand-specl-typerefs specl declr udict keep))
(define (splice-typename specl declr name udict)
(let* ((decl (or (assoc-ref udict name) ; decl for typename
(throw 'c99-error "typedef not found for: ~S" name)))
(tdef-specl (sx-ref decl 1)) ; specs for typename
(tdef-declr (sx-ref decl 2))) ; declr for typename
(values ;; fixdd-specl fixed-declr
(tdef-splice-specl specl tdef-specl)
(cond ;; #f, init-declr-list, init-declr|comp-declr
((not declr) declr)
((declr-list? declr) (tdef-splice-declr-list declr tdef-declr))
(else (tdef-splice-declr declr tdef-declr))))))
(let* ((tspec (and=> (sx-find 'type-spec specl) cadr))
(class (sx-tag tspec)) ; e.g., typename, fixed-type
(name (sx-ref tspec 1))) ; e.g., "foo_t"
(case class
((typename)
(cond
((member name keep) ; keeper; don't expand
(values specl declr))
((and #t ;; (pointer-declr? declr)
(member (cons 'pointer name) keep))
(values specl declr))
(#f ;;(pointer-declr? name) ; replace with void*
(let ((specl (replace-type-spec specl '(type-spec (void)))))
(call-with-values
(lambda () (splice-typename specl declr name udict))
(lambda (specl declr) (re-expand specl declr)))))
(else ; expand
(call-with-values
(lambda () (splice-typename specl declr name udict))
(lambda (specl declr) (re-expand specl declr))))))
((struct-def union-def)
(let* ((tag (sx-tag tspec))
(attr (sx-attr tspec))
(fld1 (sx-ref tspec 1))
(ident (if (eq? 'ident (sx-tag fld1)) fld1 #f))
(field-list (if ident (sx-ref tspec 2) fld1))
(field-list (clean-field-list field-list)) ; why remove comments?
(orig-flds (sx-tail field-list 1))
(fixd-flds (map
(lambda (fld) (expand-typerefs fld udict keep))
orig-flds))
(fixd-field-list `(field-list ,@fixd-flds))
(fixd-struct (if ident
(sx-list tag attr ident fixd-field-list)
(sx-list tag attr fixd-field-list)))
(fixd-tspec `(type-spec ,fixd-struct))
(fixd-specl (replace-type-spec specl fixd-tspec)))
(values fixd-specl declr)))
((struct-ref union-ref) ;; compound reference; replace unless pointer
(let* ((c-name (and=> (sx-find 'ident tspec)
(lambda (id) (sx-ref id 1))))
(c-key (compound-key class c-name)) ;; e.g., (struct . "foo")
(c-decl (and c-key (assoc-ref udict c-key)))
(t-spec (and c-decl (sx-find 'type-spec (sx-ref c-decl 1)))))
(if (or (and c-key
(or (member c-key keep)
(member (cons 'pointer c-key) keep)))
(not c-decl)
(pointer-declr? declr))
(values specl declr)
(let ((r-specl (replace-type-spec specl t-spec)))
(re-expand r-specl declr)))))
((enum-ref enum-def)
;; If not keeper, then replace enum with int.
(let* ((type (sx-ref (sx-find 'type-spec specl) 1))
(name (and=> (sx-find 'ident type) cadr)))
(cond
((and name (member `(enum . ,name) keep))
(values specl declr))
(else
(values
(repl-typespec specl `(type-spec (fixed-type "int")))
declr)))))
(else (values specl declr)))))
;; @deffn {Procedure} expand-typerefs adecl udict [keep]
;; Given a declaration or component-declaration, return a udecl with all
;; typenames (not in the list @var{keep}), structs and unions, expanded,
;; and enums turned into int.
;; @example
;; typedef const int (*foo_t)(int a, double b);
;; extern foo_t fctns[2];
;; =>
;; extern const int (*fctns[2])(int a, double b);
;; @end example
;; @noindent
;; Note: @var{keep} was formally keyword argument.@*
;; Note: works with @code{(struct . "foo")}@*
;; Note: works with @code{(pointer . "foo_t")}
;; @end deffn
;; idea: if we have a pointer to an undefined type, then use void*
;; @*BUG HERE? if we run into a struct then the struct members have not
;; been munged into udecls. The behavior is actually NOT DEFINED.
;; @end deffn
(define* (expand-typerefs adecl udict #:optional (keep '()))
;; In the process of expanding typerefs it is crutial that routines which
;; expand parts return the original if no change made. That is, if there
;; are no changes then @code{(eq? (expand-typerefs expr) expr)} is true.
;; If not, then infinite loop will result.
(define (fix-param-list param-list)
(let* ((tail (sx-tail param-list 1))
(xtail
(let loop ((xparams '()) (chg? #f) (params tail))
(if (null? params)
(if chg? (reverse xparams) tail)
(case (sx-tag (car params))
((param-decl)
(let* ((param (car params))
(xparam (expand-typerefs param udict keep)))
(if (eq? param xparam)
(loop (cons param xparams) chg? (cdr params))
(loop (cons xparam xparams) #t (cdr params)))))
((ellipsis)
(loop (cons (car params) xparams) chg? (cdr params))))))))
(if (eq? xtail tail)
param-list
(sx-cons* (sx-tag param-list) (sx-attr param-list) xtail))))
;; This will check for function declrs and fix parameters.
(define (fix-declr declr)
;;(sferr "fix-declr:\n") (pperr declr)
(and
declr
(sx-match declr
((ident ,name) declr)
((bit-field . ,rest) declr)
((init-declr ,declr1 . ,rest)
(let ((xdeclr (fix-declr declr1)))
(if (eq? xdeclr declr1) declr `(init-declr ,xdeclr . ,rest))))
((comp-declr ,declr1)
(let ((xdeclr (fix-declr declr1)))
(if (eq? xdeclr declr1) declr `(comp-declr ,xdeclr))))
((param-declr ,declr1)
(let ((xdeclr (fix-declr declr1)))
(if (eq? xdeclr declr1) declr `(param-declr ,xdeclr))))
((array-of ,declr1 ,array-spec)
(let ((xdeclr (fix-declr declr1)))
(if (eq? xdeclr declr1) declr `(array-of ,xdeclr ,array-spec))))
((array-of ,dir-declr)
(let ((xdeclr (fix-declr dir-declr)))
(if (eq? xdeclr dir-declr) declr `(array-of ,xdeclr))))
((ptr-declr ,pointer ,dir-declr)
(let ((xdeclr (fix-declr dir-declr)))
(if (eq? xdeclr dir-declr) declr `(ptr-declr ,pointer ,xdeclr))))
((scope ,declr1)
(let ((xdeclr (fix-declr declr1)))
(if (eq? xdeclr declr1) declr `(scope ,xdeclr))))
;; abstract declarator and direct abstract declarator
((abs-declr ,pointer ,dir-abs-declr)
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr `(abs-declr ,pointer ,xdeclr))))
((abs-declr (pointer))
declr)
((abs-declr (pointer ,pointer-val))
declr)
((abs-declr ,dir-abs-declr)
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr `(abs-declr ,xdeclr))))
;; declr-scope
;; declr-array dir-abs-declr
;; declr-array dir-abs-declr assn-expr
;; declr-array dir-abs-declr type-qual-list
;; declr-array dir-abs-declr type-qual-list assn-expr
((declr-scope ,abs-declr) ; ( abs-declr )
(let ((xdeclr (fix-declr abs-declr)))
(if (eq? xdeclr abs-declr) declr `(declr-scope ,xdeclr))))
((declr-array ,dir-abs-declr) ; []
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr `(declr-array ,xdeclr))))
((declr-array ,dir-abs-declr (type-qual-list . ,type-quals))
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr
`(declr-array ,xdeclr (type-qual-list . ,type-quals)))))
((declr-array ,dir-abs-declr ,assn-expr)
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr
`(declr-array ,xdeclr ,assn-expr))))
((declr-array ,dir-abs-declr ,type-qual-list ,assn-expr)
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr
`(declr-array ,xdeclr ,type-qual-list ,assn-expr))))
;; declr-anon-array type-qual-list assn-expr
((declr-anon-array ,type-qual-list ,assn-expr) declr)
;; declr-anon-array type-qual-list
((declr-anon-array (type-qual-list ,type-qual-tail)) declr)
;; ???? declr-anon-array assn-expr
((declr-anon-array ,assn-expr) declr)
;; declr-anon-array
((declr-anon-array) declr)
;; declr-star dir-abs-decl
((declr-star ,dir-abs-declr)
(let ((xdeclr (fix-declr dir-abs-declr)))
(if (eq? xdeclr dir-abs-declr) declr
`(declr-star ,xdeclr))))
;; declr-star
((declr-star) declr)
;; ftn-declr
((ftn-declr ,dir-declr ,param-list)
(let ((xdeclr (fix-declr dir-declr))
(xparam-list (fix-param-list param-list)))
(if (and (eq? xdeclr dir-declr) (eq? xparam-list param-list)) declr
`(ftn-declr ,xdeclr ,xparam-list))))
((abs-ftn-declr ,dir-declr ,param-list)
(let ((xdeclr (fix-declr dir-declr))
(xparam-list (if (eq? 'param-list (sx-tag param-list))
(fix-param-list param-list)
param-list)))
(if (and (eq? xdeclr dir-declr) (eq? xparam-list param-list)) declr
`(abs-ftn-declr ,xdeclr ,xparam-list))))
((anon-ftn-declr ,param-list)
(let ((xparam-list (fix-param-list param-list)))
(if (eq? xparam-list param-list) declr
`(anon-ftn-declr ,xparam-list))))
;; declr-lists too
((init-declr-list . ,declrs)
(let ((xdeclrs (map fix-declr declrs)))
(if (fold (lambda (l r seed) (and (eq? l r) seed)) #t xdeclrs declrs)
declr
`(init-declr-list . ,xdeclrs))))
((comp-declr-list . ,declrs)
(let ((xdeclrs (map fix-declr declrs)))
;;(sferr " declrs, xdeclrs:\n") (pperr declrs) (pperr xdeclrs)
(if (fold (lambda (l r seed) (and (eq? l r) seed)) #t xdeclrs declrs)
declr
`(comp-declr-list . ,xdeclrs))))
(else (throw 'c99-error "c99/munge: unknown declarator: " declr)))))
(let*-values (((tag attr orig-specl orig-declr)
(split-udecl adecl))
((repl-specl repl-declr)
(expand-specl-typerefs orig-specl orig-declr udict keep)))
;;(sferr "orig-specl, orig-declr, repl-specl, repl-declr\n")
;;(pperr orig-specl) (pperr orig-declr)
;;(pperr repl-specl) (pperr repl-declr)
(let ((repl-declr (fix-declr repl-declr)))
(if (and (eq? orig-specl repl-specl)
(eq? orig-declr repl-declr))
adecl ;; <= unchanged; return original
(sx-list tag attr repl-specl repl-declr)))))
;; === enums and defines ===============
;; @deffn {Procedure} c99-trans-unit->ddict tree [seed] [#:inc-filter proc]
;; Extract the #defines from a tree as
;; @example
;; (define (name "ABC") (repl "repl"))
;; (define (name "MAX") (args "X" "Y") (repl "(X)..."))
;; =>
;; (("ABC" . "repl") ("MAX" ("X" "Y") . "(X)...") ...)
;; @end example
;; @noindent
;; The entries appear in reverse order wrt how in file.
;; @*
;; New option: #:skip-fdefs to skip function defs
;; @end deffn
(define* (c99-trans-unit->ddict tree
#:optional (ddict '())
#:key inc-filter skip-fdefs)
(define (can-def-stmt defn)
(let* ((tail (sx-tail defn 1))
(name (car (assq-ref tail 'name)))
(args (assq-ref tail 'args))
(repl (car (assq-ref tail 'repl))))
(cons name (if args (cons args repl) repl))))
(define (cpp-def? tree)
(if (and (eq? 'cpp-stmt (sx-tag tree))
(eq? 'define (sx-tag (sx-ref tree 1)))
(not (and skip-fdefs (assq 'args (sx-tail (sx-ref tree 1) 1)))))
(can-def-stmt (sx-ref tree 1))
#f))
(if (pair? tree)
(fold
(lambda (tree ddict)
(cond
((cpp-def? tree) =>
(lambda (def-stmt)
(cons def-stmt ddict)))
((inc-keeper? tree inc-filter) =>
(lambda (tree)
(c99-trans-unit->ddict tree ddict #:inc-filter inc-filter)))
(else ddict)))
ddict
(cdr tree))
ddict))
;; @deffn {Procedure} udict-enums->ddict udict [ddict] => defs
;; Given a udict this generates a list that looke like the internal
;; CPP define structure. That is,
;; @example
;; (enum-def-list (enum-def (ident "ABC")) ...)
;; @end example
;; @noindent
;; to
;; @example
;; (("ABC" . "0") ...)
;; @end example
;; @end deffn
(define* (udict-enums->ddict udict #:optional (ddict '()))
(define (gen-nvl enum-def-list ddict)
(let ((def-list (and=> (canize-enum-def-list enum-def-list ddict udict)
cdr)))
(fold (lambda (edef ddict) ;; (enum-def (ident ,name) (fixed ,val) ...
(acons (sx-ref* edef 1 1) (sx-ref* edef 2 1) ddict))
ddict def-list)))
(fold
(lambda (pair ddict)
(if (and (pair? (car pair)) (eq? 'enum (caar pair)))
;; (enum . ,name) ...
(let* ((specl (sx-ref (cdr pair) 1))
(tspec (car (assq-ref specl 'type-spec))))
(if (not (eq? 'enum-def (car tspec)))
(throw 'nyacc-error "udict-enums->ddict: expecting enum-def"))
(gen-nvl (assq 'enum-def-list (cdr tspec)) ddict))
;; else ...
ddict))
ddict udict))
;; === enum handling ===================
;; @deffn {Procedure} canize-enum-def-list enum-def-list [ddict] [udict] \
;; => enum-def-list
;; Fill in constants for all entries of an enum list.
;; Expects @code{(enum-def-list (...))} (i.e., not the tail).
;; All enum-defs will have the form like @code{(fixed "1")}.
;; This will perform the transformation
;; @example
;; (enum-def-list (enum-def (ident "FOO") ...))
;; =>
;; (enum-def-list (enum-def (ident "FOO") (fixed "0") ...))
;; @end example
;; @noindent
;; @end deffn
(define* (canize-enum-def-list enum-def-list #:optional (ddict '()) (udict '()))
(define (fail ident)
(sferr "*** failed to convert enum ~S to constant" (sx-ref ident 1)) #f)
(let loop ((rez '()) (nxt 0) (ddict ddict) (edl (sx-tail enum-def-list 1)))
(cond
((null? edl)
(sx-cons* (sx-tag enum-def-list) (sx-attr enum-def-list) (reverse rez)))
(else
(sx-match (car edl)
((enum-defn (@ . ,attr) ,ident)
(let ((sval (number->string nxt)))
(loop (cons (sx-list 'enum-defn attr ident `(fixed ,sval)) rez)
(1+ nxt) (acons (sx-ref ident 1) sval ddict) (cdr edl))))
((enum-defn ,ident)
(let ((sval (number->string nxt)))
(loop (cons (sx-list 'enum-defn #f ident `(fixed ,sval)) rez)
(1+ nxt) (acons (sx-ref ident 1) sval ddict) (cdr edl))))
((enum-defn (@ . attr) ,ident ,expr)
(let* ((ival (or (eval-c99-cx expr udict ddict) (fail ident) nxt))
(sval (number->string iv)))
(loop (cons (sx-list 'enum-defn attr ident `(fixed ,sval)) rez)
(1+ ival) (acons (sx-ref ident 1) sval ddict) (cdr edl))))
((enum-defn ,ident ,expr)
(let* ((ival (or (eval-c99-cx expr udict ddict) (fail ident) nxt))
(sval (number->string ival)))
(loop (cons (sx-list 'enum-defn #f ident `(fixed ,sval)) rez)
(1+ ival) (acons (sx-ref ident 1) sval ddict) (cdr edl)))))))))
;; @deffn {Procecure} enum-ref enum-def-list name => string
;; Gets value of enum where @var{enum-def-list} looks like
;; @example
;; (enum-def-list (enum-defn (ident "ABC") (p-expr (fixed "123")) ...))
;; @end example
;; so that
;; @example
;; (enum-def-list edl "ABC") => "123"
;; @end example
(define (enum-ref enum-def-list name)
(let loop ((el (cdr (canize-enum-def-list enum-def-list))))
(cond
((null? el) #f)
((not (eqv? 'enum-defn (sx-tag (car el)))) (loop (cdr el)))
((string=? name (sx-ref* (car el) 1 1)) (sx-ref* (car el) 1 2 1))
(else (loop (cdr el))))))
;; @deffn {Procedure} gen-enum-udecl nstr vstr => (udecl ...)
;; @example
;; (gen-enum-udecl "ABC" "123")
;; =>
;; (udecl (decl-spec-list
;; (type-spec
;; (enum-def
;; (enum-def-list
;; (enum-defn (ident "ABC") (p-expr (fixed "123")))))))))
;; @end example
;; @end deffn
(define (gen-enum-udecl nstr vstr)
`(udecl (decl-spec-list
(type-spec
(enum-def
(enum-def-list
(enum-defn (ident ,nstr) (p-expr (fixed ,vstr)))))))))
;; === stripdown =======================
;; Remove remove @emph{stor-spec} elements and attributes from a u-decl.
;; what all?
;; * attributes (incl comments)
;; * type-specifiers: register, auto
;; * declrs: pointer-to type => pointer-to (void)
;; (stor-spec "auto") => empty
;; needs to work as stream processor, like sxpath, or foldts
;; (strip-attr)
;; (stor-spec ,name) w/ name in ("auto" "extern" "register" "static" "typedef")
;; (type-spec ,type) (void) (fixed-type ,name) (float-type ,name) aggr array
(define (stripdown-specl specl)
(cons (sx-tag specl)
(fold-right
(lambda (form seed)
(case (sx-tag form)
((type-spec) (cons form seed))
((stor-spec)
(if (eq? 'typedef (caadr form)) (cons form seed) seed))
(else seed)))
'() (sx-tail specl))))
(define (stripdown-declr declr)
(define (fD seed tree)
(sx-match tree
((pointer ,_1 ,_2) (values '() `(pointer ,_2)))
((pointer (type-qual-list . ,_)) (values '() '(pointer)))
(,_ (values '() tree))))
(define (fU seed kseed tree)
(cond
((null? seed) (reverse kseed))
((eqv? (sx-tag tree) 'stor-spec) seed)
((eqv? (sx-tag tree) 'type-qual) seed)
(else (cons (reverse kseed) seed))))
(define (fH seed tree)
(cons tree seed))
(foldts* fD fU fH '() declr))
;; @deffn {Procedure} stripdown-udecl udecl => udecl
;; This routine removes forms from a declaration that are presumably not
;; required for FFI generation.
;; See also @code{cleanup-udecl} in @file{ffi-help.scm}.
;; @example
;; =>
;; @end example
;; @noindent
;; @end deffn
(define (stripdown-udecl udecl)
(call-with-values
(lambda () (split-udecl udecl))
(lambda (tag attr specl declr)
(sx-list tag attr (stripdown-specl specl) (stripdown-declr declr)))))
;; remove type qualifiers: "const" "volatile" and "restrict"
(define (specl-tail-rem-type-qual specl-tail)
(remove (lambda (elt) (eq? 'type-qual (car elt))) specl-tail))
(define (specl-rem-type-qual specl)
(if (not (eq? (sx-tag specl) 'decl-spec-list))
(throw 'nyacc-error "expecting specl"))
(sx-cons* (sx-tag specl) (sx-attr specl)
(specl-tail-rem-type-qual (sx-tail specl 1))))
(define (udecl-rem-type-qual udecl)
(let ((tag (sx-tag udecl))
(attr (sx-attr udecl))
(specl (sx-ref udecl 1))
(tail (sx-tail udecl 2)))
(sx-cons* tag attr (specl-rem-type-qual specl) tail)))
;; @deffn {Procedure} strip-decl-spec-tail dsl-tail [#:keep-const? #f]
;; Remove cruft from declaration-specifiers (tail). ??
;; @end deffn
(define* (strip-decl-spec-tail dsl-tail #:key keep-const?)
;;(simple-format #t "spec=tail: ~S\n" dsl-tail)
(let loop ((dsl1 '()) (const-seen? #f) (tail dsl-tail))
(if (null? tail)
(reverse (if (and const-seen? keep-const?)
(cons '(type-qual "const") dsl1)
dsl1))
(case (caar tail)
((type-qual)
(if (string=? (cadar tail) "const")
(loop dsl1 #t (cdr tail))
(loop dsl1 const-seen? (cdr tail))))
((stor-spec)
(loop dsl1 const-seen? (cdr tail)))
(else
(loop (cons (car tail) dsl1) const-seen? (cdr tail)))))))
;; @deffn {Procedure} clean-field-list field-list => field-list
;; @deffnx {Procedure} clean-fields fields => fields
;; Process the tagged field-list element of a struct and remove lone comments.
;; If a field following a lone comment has no code-comment, the lone comment
;; will be used. For example,
;; @example
;; /* foo */
;; int x;
;; @end example
;; @noindent
;; will be treated as if it was denereed
;; @example
;; int x; /* foo */
;; @end example
;; @noindent
;; @end deffn
(define (clean-fields fields)
(define (str-app-rev strl) (apply string-append (reverse strl)))
(let loop ((rz '()) (cl '()) (fl fields))
(if (null? fl)
(reverse rz)
(sx-match (car fl)
((comment ,text)
(loop rz (cons text cl) (cdr fl)))
(((comp-udecl comp-decl) (@ . ,attr) . ,rest)
(let* ((comm (assq-ref attr 'comment))
(decl (car fl))
(decl
(cond
(comm decl)
((null? cl) decl)
(else (sx-attr-add decl 'comment (str-app-rev cl))))))
(loop (cons decl rz) '() (cdr fl))))
(,_ (throw 'nyacc-error "clean-field-list: ~S" (car fl)))))))
(define (clean-field-list field-list)
(cons (car field-list) (clean-fields (cdr field-list))))
;; === munged specification ============
;; @deffn {Procedure} udecl->mdecl udecl [#:add-name #f]
;; @deffnx {Procedure} udecl->mdecl/comm udecl [#:def-comm ""]
;; Turn a stripped-down unit-declaration into an m-spec. The second version
;; includes the comment. This assumes decls have been run through
;; @code{stripdown}.
;; @example
;; (decl (decl-spec-list (type-spec "double"))
;; (init-declr-list (
;; (comment "state vector")
;; =>
;; ("x" "state vector" (array-of 10) (float "double")
;; @end example
;; @noindent
;; The optional keyword argument @var{add-name} provides a dummy indentifier
;; to add for abstract declarators. If an identifier is not provided, a
;; random identifier starting with @code{@} will be provided.
;; @end deffn
(define* (udecl->mdecl decl #:key add-name)
;; Hmm. We convert array size back to C code (string). Now that I am working
;; on constant expression eval (eval-c99-cx) maybe we should change that.
(define (cnvt-size-expr size-spec)
;;(with-output-to-string (lambda () (pretty-print-c99 size-spec)))
size-spec)
(define (unwrap-specl specl)
(and=> (assq-ref (sx-tail specl) 'type-spec) car))
(define (unwrap-pointer pointer) ;; =>list IGNORES TYPE QUALIFIERS
;;(sferr "unwrap-pointer ~S\n" pointer)
(sx-match pointer
((pointer (type-qual-list . ,type-qual) ,pointer)
(cons '(pointer-to) (unwrap-pointer pointer)))
((pointer (type-qual-list . ,type-qual)) '((pointer-to)))
((pointer ,pointer) (cons '(pointer-to) (unwrap-pointer pointer)))
((pointer) '((pointer-to)))
(else
(sferr "unwrap-pointer failed on:\n") (pperr pointer)
(throw 'nyacc-error "unwrap-pointer"))))
(define (make-abs-dummy) ;; for abstract declarator make a dummy
(or add-name (symbol->string (gensym "@"))))
(define (make-abs-dummy-tail)
(list (make-abs-dummy)))
(define* (unwrap-declr declr #:key (const #f))
;;(sferr "unwrap-declr:\n") (pperr declr #:per-line-prefix " ")
(sx-match declr
((ident ,name)
(list name))
((init-declr ,item)
(unwrap-declr item #:const const))
((array-of ,dir-declr ,size)
(cons `(array-of ,(cnvt-size-expr size)) (unwrap-declr dir-declr)))
((array-of ,dir-declr)
(cons `(array-of) (unwrap-declr dir-declr)))
((ftn-declr ,dir-declr ,param-list)
(cons `(function-returning ,param-list) (unwrap-declr dir-declr)))
((abs-ftn-declr ,dir-abs-declr)
(cons `(function-returning) (unwrap-declr dir-abs-declr)))
((abs-ftn-declr ,dir-abs-declr ,param-list)
(cons `(function-returning ,param-list) (unwrap-declr dir-abs-declr)))
;;((anon-ftn-declr ,param-list) ???
((scope ,expr) (unwrap-declr expr))
((ptr-declr ,pointer ,dir-declr)
(let ((res (append (unwrap-pointer pointer) (unwrap-declr dir-declr))))
(if const (cons '(const) res) res)))
;; abstract declarator and direct abstract declarator
((abs-declr ,pointer ,dir-abs-declr)
(append (unwrap-pointer pointer) (unwrap-declr dir-abs-declr)))
((abs-declr (pointer))
(append (unwrap-pointer (sx-ref declr 1)) (make-abs-dummy-tail)))
((abs-declr (pointer ,pointer-val))
(cons* '(pointer-to) (make-abs-dummy-tail)))
((abs-declr ,dir-abs-declr)
(unwrap-declr dir-abs-declr))
;; declr-scope
;; declr-array dir-abs-declr
;; declr-array dir-abs-declr assn-expr
;; declr-array dir-abs-declr type-qual-list
;; declr-array dir-abs-declr type-qual-list assn-expr
((declr-scope ,abs-declr) ; ( abs-declr )
(unwrap-declr abs-declr))
((declr-array ,dir-abs-declr) ; []
(cons '(array-of "") (make-abs-dummy-tail))) ;; ???
((declr-array ,dir-abs-declr (type-qual-list . ,type-quals))
;; TODO: deal with "const" type-qualifier
(cons '(array-of "") (make-abs-dummy-tail)))
((declr-array ,dir-abs-declr ,assn-expr)
(cons `(array-of ,assn-expr) (unwrap-declr dir-abs-declr)))
((declr-array ,dir-abs-declr ,type-qual-list ,assn-expr)
;; TODO: deal with "const" type-qualifier
(cons `(array-of ,assn-expr) (unwrap-declr dir-abs-declr)))
((bit-field (ident ,name) ,size)
(list `(bit-field ,(cnvt-size-expr size)) name))
((comp-declr ,item) (unwrap-declr item))
((param-declr ,item) (unwrap-declr item))
(else
(sferr "munge/unwrap-declr missed:\n")
(pperr declr)
(throw 'nyacc-error "c99/munge: udecl->mdecl failed")
#f)))
;;(sferr "decl:\n") (pperr decl)
(let-values (((tag attr specl declr) (split-udecl decl)))
(let* ((tspec (sx-ref specl 1)) ; type-spec
(const (and=> (sx-ref specl 2) ; const pointer ???
(lambda (sx) (equal? (sx-ref sx 1) "const"))))
(declr (or (sx-ref decl 2) ; param-decl -- e.g., f(int)
`(ident ,(make-abs-dummy))))
(m-specl (unwrap-specl specl))
(m-declr (unwrap-declr declr #:const const))
(m-decl (reverse (cons m-specl m-declr))))
;;(sferr "decl:\n") (pperr decl)
;;(sferr "declr:\n") (pperr declr)
;;(sferr "r-mdecl: ~S\n" (cons m-specl m-declr))
m-decl)))
(define* (udecl->mdecl/comm decl #:key (def-comm ""))
(let* ((comm (or (and=> (assq 'comment (sx-attr decl)) cadr) def-comm))
(spec (udecl->mdecl decl)))
(cons* (car spec) comm (cdr spec))))
;; @deffn {Procedure} mdecl->udecl mdecl xxx
;; needed for xxx
;; @end deffn
(define* (mdecl->udecl mdecl)
(define (make-udecl types declr)
;; TODO: w/ attr needed?
`(udecl (decl-spec-list (type-spec ,types)) (init-declr ,declr)))
(define (doit declr mdecl-tail)
(pmatch mdecl-tail
(((fixed-type ,name)) (make-udecl (car mdecl-tail) declr))
(((float-type ,name)) (make-udecl (car mdecl-tail) declr))
(((typename ,name)) (make-udecl (car mdecl-tail) declr))
(((void)) (make-udecl (car mdecl-tail) declr))
(((pointer-to) . ,rest)
(doit `(ptr-declr (pointer) ,declr) rest))
(((array-of ,size) . ,rest)
(doit `(array-of ,declr ,size) rest))
(,_
(sferr "munge/mdecl->udecl missed:\n")
(pperr mdecl-tail)
(throw 'nyacc-error "munge/mdecl->udecl failed")
#f)))
(let ((name (car mdecl))
(rest (cdr mdecl)))
(doit `(ident ,name) rest)))
;; === deprecated ====================
(define tree->udict c99-trans-unit->udict)
(define tree->udict/deep c99-trans-unit->udict/deep)
(define unwrap-decl unitize-decl)
(define match-comp-decl unitize-comp-decl)
(define match-param-decl unitize-param-decl)
(define expand-decl-typerefs expand-typerefs)
(define declr->ident declr-ident)
(define (fix-fields flds) (cdr (clean-field-list `(field-list . ,flds))))
(define declr-is-ptr? pointer-declr?)
(define udecl->mspec udecl->mdecl)
(define udecl->mspec/comm udecl->mdecl/comm)
(define mspec->udecl mdecl->udecl)
;;@deffn {Procedure} stripdown-1 udecl decl-dict [options]=> decl
;; This is deprecated.
;; 1) remove stor-spec
;; 2) expand typenames
;; @example
;; typedef int *x_t;
;; x_t a[10];
;; (spec (typename x_t)) (init-declr (array-of 10 (ident a)))
;; (spec (typedef) (fixed-type "int")) (init-declr (pointer) (ident "x_t"))
;; =>
;; (udecl (decl-spec-list (type-spec ...) ... (type-qual "const"))
;; (init-declr (ptr-declr (pointer ...)
;; @end example
;; @end deffn
(define* (stripdown-1 udecl decl-dict #:key (keep '()))
;;(define strip-list '(stor-spec type-qual comment))
(define strip-list '(stor-spec type-qual))
(define (fsD seed tree)
'())
(define (fsU seed kseed tree)
(cond
((eqv? (sx-tag tree) 'stor-spec) seed)
((eqv? (sx-tag tree) 'type-qual) seed)
((null? seed) (reverse kseed))
(else (cons (reverse kseed) seed))))
(define (fsH seed tree)
(cons tree seed))
(let* ((xdecl (expand-decl-typerefs udecl decl-dict keep))
(tag (sx-tag xdecl))
(attr (sx-attr xdecl))
(specl (sx-ref xdecl 1))
(declr (sx-ref xdecl 2))
(specl1 (foldts fsD fsU fsH '() specl)))
(list tag specl1 declr)))
(define* (stripdown udecl #:key keep-const-ptr)
(let* (;;(speclt (sx-tail udecl)) ; decl-spec-list tail
(xdecl udecl)
(tag (sx-tag xdecl))
(attr (sx-attr xdecl))
(specl (sx-ref xdecl 1))
(declr (sx-ref xdecl 2))
(s-declr (stripdown-declr declr))
(is-ptr? (declr-is-ptr? declr))
;;
(s-tag (sx-tag specl))
(s-attr (sx-attr specl))
(s-tail (strip-decl-spec-tail
(sx-tail specl)
#:keep-const? (and keep-const-ptr is-ptr?)))
(specl (sx-cons* s-tag s-attr s-tail)))
;;(pretty-print declr)
;;(pretty-print s-declr)
(sx-list tag attr specl s-declr)))
;; --- last line ---