live-bootstrap/sysa/mes-0.22/module/nyacc/lang/c99/util.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

368 lines
12 KiB
Scheme

;;; nyacc/lang/c99/util.scm - C parser utilities
;; 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/>.
;;; Code:
(define-module (nyacc lang c99 util)
#:export (c99-def-help
c99-std-help
get-gcc-cpp-defs
get-gcc-inc-dirs
remove-inc-trees
merge-inc-trees!
move-attributes attrl->attrs attrs->attrl extract-attr
elifify)
#:use-module (nyacc lang util)
#:use-module (nyacc lang sx-util)
#:use-module ((srfi srfi-1) #:select (append-reverse fold-right))
#:use-module (srfi srfi-2) ; and-let*
#:use-module (sxml fold)
#:use-module (ice-9 popen) ; gen-gcc-cpp-defs
#:use-module (ice-9 rdelim) ; gen-gcc-cpp-defs
)
(define c99-def-help
'(("__builtin"
"__builtin_va_list=void*"
"__inline__=inline" "__inline=__inline__"
"__restrict__=restrict" "__restrict=__restrict__"
"__signed__=signed" "__signed=__signed__"
"asm(X)=__asm__(X)" "__asm(X)=__asm__(X)"
"__attribute(X)=__attribute__(X)"
"__volatile__=volatile" "__volatile=__volatile__"
"__extension__=" "__extension=__extension__"
"asm=__asm__" "__asm=__asm__"
"__attribute(X)=__attribute__(X)"
)))
;; include-helper for C99 std
(define c99-std-help
(append
c99-def-help
'(("alloca.h")
("complex.h" "complex" "imaginary" "_Imaginary_I=C99_ANY" "I=C99_ANY")
("ctype.h")
("fenv.h" "fenv_t" "fexcept_t")
("float.h" "float_t" "FLT_MAX=C99_ANY" "DBL_MAX=C99_ANY")
("inttypes.h"
"int8_t" "uint8_t" "int16_t" "uint16_t" "int32_t" "uint32_t"
"int64_t" "uint64_t" "uintptr_t" "intptr_t" "intmax_t" "uintmax_t"
"int_least8_t" "uint_least8_t" "int_least16_t" "uint_least16_t"
"int_least32_t" "uint_least32_t" "int_least64_t" "uint_least64_t"
"imaxdiv_t")
("limits.h"
"INT_MIN=C99_ANY" "INT_MAX=C99_ANY" "LONG_MIN=C99_ANY" "LONG_MAX=C99_ANY")
("math.h" "float_t" "double_t")
("regex.h" "regex_t" "regmatch_t")
("setjmp.h" "jmp_buf")
("signal.h" "sig_atomic_t")
("stdarg.h" "va_list")
("stddef.h" "ptrdiff_t" "size_t" "wchar_t")
("stdint.h"
"int8_t" "uint8_t" "int16_t" "uint16_t" "int32_t" "uint32_t"
"int64_t" "uint64_t" "uintptr_t" "intptr_t" "intmax_t" "uintmax_t"
"int_least8_t" "uint_least8_t" "int_least16_t" "uint_least16_t"
"int_least32_t" "uint_least32_t" "int_least64_t" "uint_least64_t")
("stdio.h" "FILE" "size_t")
("stdlib.h" "div_t" "ldiv_t" "lldiv_t" "wchar_t")
("string.h" "size_t")
("strings.h" "size_t")
("time.h" "time_t" "clock_t" "size_t")
("unistd.h" "size_t" "ssize_t" "div_t" "ldiv_t")
("wchar.h" "wchar_t" "wint_t" "mbstate_t" "size_t")
("wctype.h" "wctrans_t" "wctype_t" "wint_t"))))
(define (resolve-CC CC)
(cond
(CC CC)
((getenv "CC") => identity)
(else "gcc")))
;; @deffn {Procedure} convert-def line
;; Convert string in gcc cpp defs to pair of strings for term and replacement.
;; @end deffn
(define (convert-line line)
(with-input-from-string line
(lambda ()
(let loop ((term '()) (acc '()) (st 0) (ch (read-char)))
(case st
((0) ;; skip #define
(if (char=? ch #\space)
(loop term acc 1 (read-char))
(loop term acc 0 (read-char))))
((1) ;; read term
(if (char=? ch #\space)
(loop (reverse-list->string acc) '() 2 (read-char))
(loop term (cons ch acc) st (read-char))))
((2) ;; read rest
(if (or (eof-object? ch) (char=? ch #\newline))
(string-append term "=" (reverse-list->string acc))
(loop term (cons ch acc) st (read-char)))))))))
;; @deffn {Procedure} get-gcc-cpp-defs [args] [#:CC "gcc"] => '("ABC=123" ...)
;; Generate a list of default defines produced by gcc (or other comiler).
;; If keyword arg @arg{CC} is not provided this procedure looks for environment
;; variable @code{"CC"}, else it defaults to @code{"gcc"}.
;; @end deffn
(define* (get-gcc-cpp-defs #:optional (args '()) #:key CC)
;; @code{"gcc -dM -E"} will generate lines like @code{"#define ABC 123"}.
;; We generate and return a list like @code{'(("ABC" . "123") ...)}.
(let* ((cmd (string-append (resolve-CC CC) " -dM -E - </dev/null"))
(ip (open-input-pipe cmd)))
(let loop ((line (read-line ip 'trim)))
(if (eof-object? line) '()
(cons (convert-line line) (loop (read-line ip 'trim)))))))
;; @deffn {Procedure} get-gcc-inc-dirs [args] [#:CC "gcc"] =>
;; Generate a list of compiler-internal include directories (for gcc). If
;; keyword arg @arg{CC} is not provided this procedure looks for environment
;; variable @code{"CC"}, else it defaults to @code{"gcc"}.
;; @end deffn
(define* (get-gcc-inc-dirs #:optional (args '()) #:key CC)
(let ((ip (open-input-pipe (string-append
(resolve-CC CC) " -E -Wp,-v - </dev/null 2>&1"))))
(let loop ((dirs '()) (grab #f) (line (read-line ip 'trim)))
(cond
((eof-object? line) dirs)
((string=? line "#include <...> search starts here:")
(loop dirs #t (read-line ip 'trim)))
((string=? line "End of search list.") dirs)
(grab
(loop (cons (string-trim-both line) dirs)
grab (read-line ip 'trim)))
(else
(loop dirs grab (read-line ip 'trim)))))))
;; @deffn {Procedure} remove-inc-trees tree
;; Remove the trees included with cpp-include statements.
;; @example
;; '(... (cpp-stmt (include "<foo.h>" (trans-unit ...))) ...)
;; => '(... (cpp-stmt (include "<foo.h>")) ...)
;; @end example
;; @end deffn
(define (remove-inc-trees tree)
(if (not (eqv? 'trans-unit (car tree)))
(throw 'nyacc-error "expecting c-tree"))
(let loop ((rslt (make-tl 'trans-unit))
;;(head '(trans-unit)) (tail (cdr tree))
(tree (cdr tree)))
(cond
((null? tree) (tl->list rslt))
((and (eqv? 'cpp-stmt (car (car tree)))
(eqv? 'include (caadr (car tree))))
(loop (tl-append rslt `(cpp-stmt (include ,(cadadr (car tree)))))
(cdr tree)))
(else (loop (tl-append rslt (car tree)) (cdr tree))))))
;; @deffn {Procedure} merge-inc-trees! tree => tree
;; This will (recursively) merge code from cpp-includes into the tree.
;; @example
;; (trans-unit
;; (decl (a))
;; (cpp-stmt (include "<hello.h>" (trans-unit (decl (b)))))
;; (decl (c)))
;; =>
;; (trans-unit (decl (a)) (decl (b)) (decl (c)))
;; @end example
;; @end deffn
(define (merge-inc-trees! tree)
;; @item find-span (trans-unit a b c) => ((a . +->) . (c . '())
(define (find-span tree)
(cond
((not (pair? tree)) '()) ; maybe parse failed
((not (eqv? 'trans-unit (car tree))) (throw 'c99-error "expecting c-tree"))
((null? (cdr tree)) (throw 'c99-error "null c99-tree"))
(else
(let ((fp tree)) ; first pair
(let loop ((lp tree) ; last pair
(np (cdr tree))) ; next pair
(cond
((null? np) (cons (cdr fp) lp))
;; The following is an ugly hack to find cpp-include
;; with trans-unit attached.
((and-let* ((expr (car np))
((eqv? 'cpp-stmt (car expr)))
((eqv? 'include (caadr expr)))
(rest (cddadr expr))
((pair? rest))
(span (find-span (car rest))))
(set-cdr! lp (car span))
(loop (cdr span) (cdr np))))
(else
(set-cdr! lp np)
(loop np (cdr np)))))))))
;; Use cons to generate a new reference:
;; (cons (car tree) (car (find-span tree)))
;; or not:
(find-span tree)
tree)
;; --- attributes ----------------------
(define (join-string-literal str-lit)
(sx-list 'string (sx-attr str-lit) (string-join (sx-tail str-lit) "")))
;; used in c99-spec actions for attribute-specifiers
(define (attr-expr-list->string attr-expr-list)
(string-append "(" (string-join (cdr attr-expr-list) ",") ")"))
;; ((attribute-list ...) (type-spec ...) (attribute-list ...)) =>
;; (values (attribute-list ...) ((type-spec ...) ...))
;; @deffn extract-attr tail => (values attr-tree tail)
;; Extract attributes from a sexp tail.
;; @end deffn
(define (extract-attr tail) ;; => (values attr-tree tail)
(let loop ((atl '()) (tail1 '()) (tail0 tail))
(cond
((null? tail0)
(if (null? atl)
(values '() tail)
(values `(attribute-list . ,atl) (reverse tail1))))
((eq? 'attribute-list (sx-tag (car tail0)))
(loop (append (sx-tail (car tail0)) atl) tail1 (cdr tail0)))
(else
(loop atl (cons (car tail0) tail1) (cdr tail0))))))
;; (attribute-list (attribute (ident "__packed__")) ...)
;; =>
;; (attributes "__packed__;...")
;; OR
;; () => ()
(define (attrl->attrs attr-list)
(define (spec->str spec)
(sx-match spec
((ident ,name) name)
((attribute ,name) (spec->str name))
((attribute ,name ,args)
(string-append (spec->str name) "(" (spec->str args) ")"))
((attr-expr-list . ,expr-list)
(string-join (map spec->str expr-list) ","))
((fixed ,val) val)
((float ,val) val)
((char ,val) val)
((string . ,val) (string-append "\"" (string-join val "") "\""))
((type-name (decl-spec-list (type-spec ,spec))) (spec->str spec))
((fixed-type ,name) name)
((float-type ,name) name)
(,_ (sferr "c99/util: missed ~S\n" spec) "MISSED")))
(if (null? attr-list) '()
`(attributes ,(string-join (map spec->str (sx-tail attr-list)) ";"))))
;; (attributes "__packed__;__aligned__;__alignof__(8)")
;; =>
;; (attribute-list (attribute "__packed
;; OR
;; #f => #f
(use-modules (nyacc lex))
(define (astng->atree form)
(define a-mtab
'(("(" . lparen) ((")" . rparen))
("," . comma) ($ident . ident)))
(define attlexgen (make-lexer-generator a-mtab))
(define attlex (attlexgen))
(with-input-from-string form
(lambda ()
(define (p-expr-list lx) ;; see 'lparen
(and
(eq? 'lparen (car lx))
(let loop ((args '()) (lx (attlex)))
(case (car lx)
((rparen) `(attr-expr-list . ,args))
((comma) (loop args (attlex)))
(else (p-expr lx))))))
(define (p-expr lx)
#f)
(let ((lx (attlex)))
(sferr "lx=~S\n" lx)
(case (car lx)
((ident)
(let* ((id (cdr lx)) (lx (attlex)))
(case (car lx)
(($end) `(attribute ,id))
((lparen) `(attribute ,id ,(p-expr-list lx)))
(else (throw 'nyacc-error "error ~S" lx)))))
(else (throw 'nyacc-error "missed ~S" lx)))))))
(define (attrs->attrl attr-sexp)
(and
attr-sexp
(let* ((attrs (cadr attr-sexp))
(attl (string-split attrs #\;)))
`(attribute-list ,@(map astng->atree attl)))))
;; @deffn {Procedure} move-attributes sexp
;; Given a sexpr, combine attribute-list kids and move to attribute ??
;; @example
;; (decl (decl-spec-list
;; (attributes "__packed__" "__aligned__")
;; (attributes "__alignof__(8)"))
;; (type-spec (fixed-type "int")))
;; (declr-init-list ...))
;; =>
;; (decl (decl-spec-list
;; (@ (attributes "__packed__;__aligned__;__alignof__(8)"))
;; (type-spec (fixed-type "int")))
;; (declr-init-list ...))
;; @end example
;; @end deffn
(define (move-attributes sexp)
(let ((tag (sx-tag sexp)) (attr (sx-attr sexp)) (tail (sx-tail sexp)))
(call-with-values (lambda () (extract-attr tail))
(lambda (attrl stail)
(sx-cons*
tag
(cond
((null? attrl) attr)
((null? attr)`(@ ,(attrl->attrs attrl)))
(else (append attr (list (attrl->attrs attrl)))))
stail)))))
;; --- random stuff
;; @deffn {Procedure} elifify tree => tree
;; This procedure will find patterns of
;; @example
;; (if cond-1 then-part-1
;; (if cond-2 then-part-2
;; else-part-2
;; @end example
;; @noindent
;; and convert to
;; @example
;; (if cond-1 then-part-1
;; (elif cond-2 then-part-2)
;; else-part-2
;; @end example
;; @end deffn
(define (elifify tree)
(define (fU tree)
(sx-match tree
((if ,x1 ,t1 (if ,x2 ,t2 (else-if ,x3 ,t3) . ,rest))
`(if ,x1 ,t1 (else-if ,x2 ,t2) (else-if ,x3 ,t3) . ,rest))
((if ,x1 ,t1 (if ,x2 ,t2 . ,rest))
`(if ,x1 ,t1 (else-if ,x2 ,t2) . ,rest))
(else
tree)))
(foldt fU identity tree))
;; --- last line ---