ffigen/process.sch

374 lines
12 KiB
Scheme

; -*- scheme -*-
;
; Generic program to process output from ffigen.
; Lars Thomas Hansen [lth@cs.uoregon.edu] / January 29, 1996
;
; Copyright (C) 1996 The University of Oregon. All rights reserved.
;
; This file may be freely redistributed in its entirety with or without
; modification provided that this copyright notice is not removed. It
; may not be sold for profit or incorporated in commercial software
; products without the prior written permission of the copyright holder.
;
; USAGE
; (process <filename>)
; where the file is the output of ffigen.
;
; DESCRIPTION
; There are some general instructions in the section marked CUSTOMIZABLE
; below.
;
; INPUT FORMAT
; Input consists of s-expressions of the following forms:
; <record> -> (function <filename> <name> <type> <attrs>)
; | (var <filename> <name> <type> <attrs>)
; | (type <filename> <name> <type>)
; | (struct <filename> <name> ((<name> <type>) ...))
; | (union <filename> <name> ((<name> <type>) ...))
; | (enum <filename> <name> ((<name> <value>) ...))
; | (enum-ident <filename> <name> <value>)
; | (macro <filename> <name+args> <body>)
;
; <type> -> (<primitive> <attrs>)
; | (struct-ref <tag>)
; | (union-ref <tag>)
; | (enum-ref <tag>)
; | (function (<type> ...) <type>)
; | (pointer <type>)
; | (array <value> <type>)
; <attrs> -> (<attr> ...)
; <attr> -> static | extern | const | volatile
;
; <primitive> -> char | signed-char | unsigned-char | short | unsigned-short
; | int | unsigned | long | unsigned-long | void
;
; <value> -> <integer>
; <filename> -> <string>
; <name> -> <string>
; <body> -> <string>
; <name+args> -> <string>
;
; Functions which are known to take no parameters (i.e. t f(void)) have
; one parameter, of type "void".
;
; Functions which have a variable number of arguments have at least one
; defined parameter and a last parameter of type "void".
;
; The ordering of records in the input have little or no relation to the
; relative ordering of declarations in the original source.
;
; Multidimensional arrays are represented as nested array types with the
; leftmost dimension outermost in the expected way; i.e., it looks like
; an array of arrays.
;
; Not all attributes are possible in all places, of course.
;
; Unresolved issues:
; - Handling of bitfields. Might want primitive types (bitfield n)
; and (unsigned-bitfield n), but alignment is a real issue. Another
; option is a field which contains all the bitfield in it:
; (bitfield (i 0 3) (j 3 4) (k 7 10)) says that i starts at bit 0 and
; is 3 bits long, etc. Ditto unsigned.
; - Transmission of compiler-computed alignment and size data in general.
; - Evaluation of macros as far as possible; use of integer values where
; reasonable.
(define functions '()) ; list of function records
(define vars '()) ; list of var records
(define types '()) ; list of type records
(define structs '()) ; list of struct records
(define unions '()) ; list of union records
(define macros '()) ; list of macro records
(define enums '()) ; list of enum records
(define enum-idents '()) ; list of enum-ident records
(define source-file #f) ; name of the input file itself
(define filenames '()) ; names of all files in the input
(define caddddr (lambda (x) (car (cddddr x))))
(define warnings 0)
(define (process filename)
(set! source-file filename)
(set! functions '())
(set! vars '())
(set! types '())
(set! structs '())
(set! unions '())
(set! macros '())
(set! enums '())
(set! enum-idents '())
(set! filenames '())
(set! warnings 0)
(call-with-input-file filename
(lambda (p)
(do ((item (read p) (read p)))
((eof-object? item)
(process-records)
(newline)
(display warnings) (display " warnings.") (newline)
#t)
(let ((fn (name item)))
(if (not (member fn filenames))
(set! filenames (cons fn filenames))))
(case (car item)
((function) (set! functions (cons item functions)))
((var) (set! vars (cons item vars)))
((type) (set! types (cons item types)))
((struct) (set! structs (cons item structs)))
((union) (set! unions (cons item unions)))
((macro) (set! macros (cons item macros)))
((enum) (set! enums (cons item enums)))
((enum-ident) (set! enum-idents (cons item enum-idents)))
(else (error 'process "~a" item)))))))
; Processing after reading.
(define (process-records)
(select-functions)
(compute-referenced-types)
(generate-translation))
; File name utilities.
(define (strip-extension fn)
(do ((i (- (string-length fn) 1) (- i 1)))
((or (< i 0)
(char=? (string-ref fn i) #\.)
(char=? (string-ref fn i) #\/))
(if (and (>= i 0) (char=? (string-ref fn i) #\.))
(substring fn 0 i)
(string-copy fn)))))
(define (strip-path fn)
(do ((i (- (string-length fn) 1) (- i 1)))
((or (< i 0)
(char=? (string-ref fn i) #\/))
(if (and (>= i 0) (char=? (string-ref fn i) #\/))
(substring fn (+ i 1) (string-length fn))
(string-copy fn)))))
(define (get-path fn)
(let ((x (strip-path fn)))
(if (= (string-length fn) (string-length x))
x
(substring fn 0 (- (string-length fn) (string-length x))))))
; Accessors.
;
; Representation: Each <record> and <type> is represented exactly like it
; was in the input _except_ for the first element. The element is either
; the symbol like it was in the input, or a pair. If it is a pair, then
; the car of the pair is the symbol from the input and the cdr is
; system-internal information. It's a list which currently holds:
; (referenced-bit cached-info)
;
; All other data are represented exactly as in the input.
;
; Some of the generic functions operate on data structures which do not
; have a record-tag (fields, for example). They examine the datum to figure
; out what to do. For example, if the car is a string then it's a field,
; otherwise it's not.
(define file cadr) ; file name in records which have one
(define (name x) ; name in records which have one
(if (string? (car x))
(car x) ; fields
(caddr x))) ; others
(define (type x) ; type in records which have one
(if (string? (car x))
(cadr x) ; fields
(cadddr x))) ; others
(define attrs caddddr) ; attrs in records which have one
(define fields cadddr) ; fields in struct/union type
(define value cadddr) ; value of enum-tag record
(define (tag x) ; tag in struct-ref/union-ref or struct/union record
(let ((rt (record-tag x)))
(if (or (eq? rt 'struct-ref)
(eq? rt 'union-ref)
(eq? rt 'enum-ref))
(cadr x) ; refs
(caddr x)))) ; others
(define arglist cadr) ; function argument list (list of types)
(define (rett x) (caddr x)) ; function return type
(define (record-tag r) ; always use this.
(if (symbol? (car r))
(car r)
(caar r)))
(define (sysinfo r)
(if (symbol? (car r))
(let ((info (list #f '())))
(set-car! r (cons (car r) info))
info)
(cdar r)))
(define (referenced? x) (car (sysinfo x)))
(define (referenced! x) (set-car! (sysinfo x) #t))
(define (unreferenced! x) (set-car! (sysinfo x) #f))
(define (cache-name r n)
(let ((i (sysinfo r)))
(set-car! (cdr i) (cons n (cadr i)))))
(define (cached-names r)
(cadr (sysinfo r)))
; Compute the referenced bit for all referenced structure and union types.
; This may be useful for some systems, and it can be used for getting rid of
; structures included from "incidental" headers, esp. if only some functions
; have been selected.
(define ref-queue '())
(define (compute-referenced-types)
(define (t-ref t)
(case (record-tag t)
((function)
(for-each t-ref (arglist t))
(t-ref (rett t)))
((struct-ref)
(let ((struct (lookup (tag t) structs)))
(if (not (referenced? struct))
(begin (referenced! struct)
(set! ref-queue (cons struct ref-queue))))))
((union-ref)
(let ((union (lookup (tag t) unions)))
(if (not (referenced? union))
(begin (referenced! union)
(set! ref-queue (cons union ref-queue))))))
((pointer)
(t-ref (cadr t)))
((array)
(t-ref (caddr t)))))
(define (struct/union-loop)
(if (not (null? ref-queue))
(let ((queue ref-queue))
(set! ref-queue '())
(for-each (lambda (t)
(for-each (lambda (f)
(t-ref (cadr f)))
(fields t)))
queue)
(struct/union-loop))))
(set! ref-queue '())
(for-each (lambda (f)
(if (referenced? f)
(t-ref (type f))))
functions)
(for-each (lambda (v)
(t-ref (type v)))
vars)
(struct/union-loop)
#t)
; Lookup by the 'name' field of whatever it is.
(define (lookup key items)
(do ((items items (cdr items)))
((or (null? items)
(string=? key (name (car items))))
(if (null? items)
#f
(car items)))))
; Simple macro expander. Given a template (a string) and some arguments
; (a vector of strings) expand the arguments in the template, returning
; a fresh string. If an @ is seen in the template, it must be followed by
; a simple digit which is the index into the argument vector.
(define (instantiate template args)
(define (get-arg n)
(reverse (string->list (vector-ref args n))))
(let ((limit (string-length template)))
(let loop ((i 0) (r '()))
(cond ((= i limit)
(list->string (reverse r)))
((char=? (string-ref template i) #\@)
(let ((k (- (char->integer (string-ref template (+ i 1)))
(char->integer #\0))))
(loop (+ i 2) (append (get-arg k) r))))
(else
(loop (+ i 1) (cons (string-ref template i) r)))))))
; Given a struct, find the names for the structure. The name is the
; structure tag itself (we'll prefix it by "struct_") and the names of
; any typedef names which refer directly to the structure.
(define (struct-names struct)
(struct-union-names struct "struct " 'struct-ref))
(define (union-names union)
(struct-union-names union "union " 'union-ref))
(define (struct-union-names struct/union srctag reffer)
(let ((names '()))
(do ((t types (cdr t)))
((null? t) names)
(let ((x (type (car t))))
(if (and (eq? (record-tag x) reffer)
(string=? (tag struct/union) (tag x)))
(set! names (cons (name (car t)) names)))))))
(define (user-defined-tag? x)
(and (> (string-length x) 0)
(not (char-numeric? (string-ref x 0)))))
(define warn
(let ((out (current-output-port)))
(lambda (msg . rest)
(set! warnings (+ warnings 1))
(display "WARNING: " out)
(display msg out)
(for-each (lambda (x)
(display " " out)
(display x out))
rest)
(newline out))))
(define (basic-type? x)
(or (primitive-type? x)
(pointer-type? x)))
(define (pointer-type? x)
(eq? (record-tag x) 'pointer))
(define (primitive-type? x)
(memq (record-tag x)
'(int unsigned short unsigned-short long unsigned-long
double float char signed-char unsigned-char void)))
(define (array-type? x)
(eq? (record-tag x) 'array))
(define (structured-type? x)
(or (eq? (record-tag x) 'struct-ref)
(eq? (record-tag x) 'union-ref)))
(define canonical-name
(let ((char-canon-case (if (char=? (string-ref (symbol->string 'a) 0) #\a)
char-downcase
char-upcase)))
(lambda (s)
(list->string (map char-canon-case (string->list s))))))
(define (struct/union-ref record)
(case (record-tag record)
((struct) `(struct-ref ,(tag record)))
((union) `(union-ref ,(tag record)))
(else (error 'struct/union-ref "What's a " record))))
; eof