
(module sigutil mzscheme
  ;; Used by unitsig.rkt
  ;; (needs an overhaul, too)

  (require syntax/stx
	   syntax/struct
	   syntax/context)

  (require "sigmatch.rkt")
  (require "../unit200.rkt")
  (require "../list.rkt")

  (define-struct signature (name      ; sym
			    src       ; sym
			    elems     ; list of syms and signatures
			    ctxs      ; list of stx
			    structs)) ; list of struct-infos
  (define-struct parsed-unit (imports renames vars import-vars body stx-checks))

  (define-struct struct-def (name super-name names))

  (define-struct sigdef (content interned))
  (define (make-sig x) (make-sigdef x #f))
  (provide make-sig)

  (define inline-sig-name '<unnamed>)

  (define-syntax literal?
    (lambda (expr)
      (syntax-case expr ()
	[(_ a)
	 (syntax (eq? (syntax-e (syntax a)) 'a))])))

  (define (stx->sym s)
    (if (syntax? s) (syntax-e s) s))

  (define syntax-error
    (case-lambda 
     [(who expr msg sub)
      (raise-syntax-error who msg expr sub)]
     [(who expr msg)
      (raise-syntax-error who msg expr)]))

  (define undef-sig-error
    (lambda (who expr what)
      (syntax-error #f expr 
		    "signature not defined"
		    what)))

  (define not-a-sig-error
    (lambda (who expr what)
      (syntax-error #f expr 
		    "not a signature"
		    what)))

  (define rename-signature
    (lambda (sig name main-ctx)
      (make-signature name 
		      (signature-src sig) 
		      (signature-elems sig)
		      (if main-ctx
			  (map (lambda (ctx) (or ctx main-ctx)) (signature-ctxs sig))
			  (signature-ctxs sig))
		      (signature-structs sig))))

  (define intern-signature
    (lambda (name desc)
      (let ([elems (vector->list (car desc))])
	(make-signature
	 name
	 name
	 (map
	  (lambda (elem)
	    (cond
	     [(symbol? elem) elem]
	     [(and (pair? elem) (symbol? (car elem)))
	      (intern-signature (car elem) (cdr elem))]
	     [else (error "intern failed")]))
	  elems)
	 (map (lambda (elem) #f) elems)
	 (map
	  (lambda (elem)
	    (make-struct-def (vector-ref elem 0)
			     (vector-ref elem 1)
			     (cddr (vector->list elem))))
	  (vector->list (cdr desc)))))))

  (define get-sig
    (lambda (who expr name sigid main-ctx)
      (if (not (identifier? sigid))
	  (parse-signature who expr 
			   (if name
			       name
			       inline-sig-name)
			   sigid
			   main-ctx)
	  (let ([v (syntax-local-value sigid (lambda () #f))])
	    (unless v
	      (undef-sig-error who expr sigid))
	    (unless (sigdef? v)
	      (not-a-sig-error who expr sigid))
	    (unless (sigdef-interned v)
	      (set-sigdef-interned! v (intern-signature (syntax-e sigid) (sigdef-content v))))
	    (let ([s (sigdef-interned v)])
	      (if name
		  (rename-signature s (stx->sym name) (and main-ctx sigid))
		  s))))))

  (define check-unique
    (lambda (names error-k)
      (let ([dup (check-duplicate-identifier 
		  (map (lambda (n)
			 (if (syntax? n)
			     n
			     (datum->syntax-object #f n #f)))
		       names))])
	(when dup
	  (error-k dup)))))

  (define parse-signature
    (lambda (who expr name body main-ctx)
      (let-values ([(elems ctxs struct-defs)
		    (let loop ([body body][accum null][ctx-accum null][struct-accum null])
		      (syntax-case body ()
			[() (values (reverse accum) (reverse ctx-accum) (reverse struct-accum))]
			[(something . rest)
			 (syntax-case (syntax something) ()
			   [:
			    (literal? :)
			    (syntax-error #f expr
					  "misplaced `:'"
					  (syntax something))]
			   [id
			    (identifier? (syntax id))
			    (loop
			     (syntax rest)
			     (cons (syntax id) accum)
			     (cons (syntax id) ctx-accum)
			     struct-accum)]
			   [(struct name (field ...) omission ...)
			    (literal? struct)
			    (let ([name (syntax name)]
				  [fields (syntax->list (syntax (field ...)))]
				  [omissions (syntax->list (syntax (omission ...)))])
			      (unless (or (identifier? name)
					  ;; >>> disabled the `(name super)' case for now <<<
					  (and #f (syntax-case name ()
						    [(name super)
						     (and (identifier? (syntax name))
							  (identifier? (syntax super)))]
						    [_else #f])))
				(syntax-error #f expr
					      "struct name is not an identifier" ;; " or a parenthesized name--super sequence of identifiers"
					      name))
			      (for-each
			       (lambda (fld)
				 (unless (identifier? fld)
				   (syntax-error #f expr
						 "field name is not an identifier"
						 fld)))
			       fields)
			      (let-values ([(omit-names
					     omit-setters?
					     omit-selectors?)
					    (let loop ([omissions omissions]
						       [names null]
						       [no-set? #f]
						       [no-sel? #f])
					      (if (null? omissions)
						  (values names no-set? no-sel?)
						  (let ([rest (cdr omissions)])
						    (syntax-case (car omissions) ()
						      [-selectors
						       (literal? -selectors)
						       (loop rest names no-sel? #t)]
						      [-setters
						       (literal? -setters)
						       (loop rest names #t no-set?)]
						      [(- name)
						       (and (literal? -) (identifier? (syntax name)))
						       (loop rest (cons (syntax name) names)
							     no-set? no-sel?)]
						      [else
						       (syntax-error #f expr
								     "bad struct omission"
								     (car omissions))]))))]
					   [(name super-name) (if (identifier? name)
								  (values name #f)
								  (values (stx-car name)
									  (stx-car (stx-cdr name))))])
				(letrec ([names (build-struct-names 
						 name fields 
						 omit-selectors? omit-setters?)]
					 [filter
					  (lambda (names)
					    (cond
					     [(null? names) null]
					     [(ormap (lambda (x) (eq? (syntax-e (car names))
								      (syntax-e x)))
						     omit-names)
					      (filter (cdr names))]
					     [else (cons (car names) (filter (cdr names)))]))])
				  (let ([elems (if (null? omit-names)
						   names 
						   (filter names))])
				    (loop (syntax rest)
					  (append
					   elems
					   accum)
					  (append
					   (map (lambda (elem) name) elems)
					   ctx-accum)
					  (cons (make-struct-def (syntax-e name) 
								 (and super-name (syntax-e super-name))
								 names)
						struct-accum))))))]
			   [(struct . _)
			    (literal? struct)
			    (syntax-error #f expr
					  "bad `struct' clause form"
					  (syntax something))]
			   [(unit name : sig) 
			    (and (literal? unit)
				 (identifier? (syntax name)))
			    (let ([s (get-sig who expr (syntax name) (syntax sig) (and main-ctx (syntax sig)))])
			      (loop (syntax rest)
				    (cons s accum)
				    (cons (syntax name) ctx-accum)
				    struct-accum))]
			   [(unit . _)
			    (literal? unit)
			    (syntax-error #f expr 
					  "bad `unit' clause form"
					  (syntax something))]
			   [(open sig)
			    (literal? open)
			    (let ([s (get-sig who expr #f (syntax sig) (and main-ctx (syntax sig)))])
			      (loop (syntax rest)
				    (append (signature-elems s) accum)
				    (append
				     (map (lambda (e ctx)
					    (or ctx (syntax sig)))
					  (signature-elems s)
					  (signature-ctxs s))
				     ctx-accum)
				    (append (signature-structs s) struct-accum)))]
			   [(open . _)
			    (literal? open)
			    (syntax-error #f expr 
					  "bad `open' clause form"
					  (syntax something))]
			   [else
			    (syntax-error #f expr "improper signature clause type"
					  (syntax something))])]
			[_else (syntax-error #f expr "ill-formed signature"
					     body)]))])
	(check-unique (map
		       (lambda (elem)
			 (cond
			  [(symbol? elem) elem]
			  [(identifier? elem) (syntax-e elem)]
			  [else (signature-name elem)]))
		       elems)
		      (lambda (name)
			(syntax-error #f expr
				      "duplicate name in signature"
				      name)))
	(let ([sorted (sort-signature-elems (map cons 
						 (map (lambda (id)
							(if (identifier? id)
							    (syntax-e id)
							    id))
						      elems)
						 (if main-ctx
						     (map (lambda (ctx) (or ctx main-ctx)) ctxs)
						     (map (lambda (id) #f) ctxs))))])
	  (make-signature (stx->sym name)
			  (stx->sym name)
			  (map car sorted)
			  (map cdr sorted)
			  struct-defs)))))

  (define (intern-vector intern-box v)
    (if (and intern-box
	     (equal? #() (cdr v))
	     (andmap symbol? (vector->list (car v))))
	(or (ormap (lambda (i)
		     (and (equal? (car v) (caadr i))
			  (list 'unquote (car i))))
		   (unbox intern-box))
	    (let ([name (car (generate-temporaries '(idvec)))])
	      (set-box! intern-box
			(cons (list name v)
			      (unbox intern-box)))
	      (list 'unquote name)))
	v))

  (define explode-sig
    (lambda (sig intern-box)
      (intern-vector
       intern-box
       (cons
	(list->vector
	 (map
	  (lambda (v)
	    (if (symbol? v)
		v
		(cons
		 (signature-name v)
		 (explode-sig v intern-box))))
	  (signature-elems sig)))
	(list->vector
	 (map
	  (lambda (v)
	    (list->vector (list* (struct-def-name v)
				 (struct-def-super-name v)
				 (struct-def-names v))))
	  (signature-structs sig)))))))

  (define explode-named-sig
    (lambda (s intern-box)
      (cons
       (cond
	[(signature-name s)]
	[(signature-src s)]
	[else inline-sig-name])
       (explode-sig s intern-box))))

  (define explode-named-sigs
    (lambda (sigs intern-box)
      (map (lambda (sig) (explode-named-sig sig intern-box)) sigs)))

  (define sort-signature-elems
    (lambda (elems)
      (map car
	   (sort (map (lambda (ip)
                        (let ([i (car ip)])
                          (cons ip (symbol->string
                                    (if (symbol? i) i (signature-name i))))))
                      elems)
                 ;; Less-than; put subs at front
                 (lambda (a b)
                   (if (symbol? (caar a))
                     (if (symbol? (caar b))
                       (string<? (cdr a) (cdr b))
                       #f)
                     (if (symbol? (caar b))
                       #t
                       (string<? (cdr a) (cdr b)))))))))
  
  (define flatten-signature
    (lambda (id sig main-ctx)
      (apply
       append
       (map
	(lambda (elem ctx)
	  (if (symbol? elem)
	      (let ([sym
		     (if id
			 (string->symbol (string-append id ":" (symbol->string elem)))
			 elem)])
		(list
		 (if main-ctx
		     (datum->syntax-object (or ctx main-ctx) sym)
		     sym)))
	      (flatten-signature (let* ([n (signature-name elem)]
					[s (if n
					       (symbol->string n)
					       #f)])
				   (if (and id s)
				       (string-append id ":" s)
				       (or id s)))
				 elem
				 (or ctx main-ctx))))
	(signature-elems sig)
	(signature-ctxs sig)))))

  (define flatten-signatures
    (lambda (sigs main-ctx)
      (apply append (map (lambda (s) 
			   (let* ([name (signature-name s)]
				  [id (if name
					  (symbol->string name)
					  #f)])
			     (flatten-signature id s main-ctx)))
			 sigs))))

  (define signature-parts
    (lambda (q?)
      (lambda (sig)
	(let loop ([elems (signature-elems sig)])
	  (cond
	   [(null? elems) null]
	   [(q? (car elems)) (cons (car elems) (loop (cdr elems)))]
	   [else (loop (cdr elems))])))))
  (define signature-vars (signature-parts symbol?))
  (define signature-subsigs (signature-parts signature?))

  (define do-rename
    (lambda (export-name renames)
      (let loop ([renames renames])
	(cond
	 [(null? renames) export-name]
	 [(eq? (cadar renames) export-name)
	  (caar renames)]
	 [else (loop (cdr renames))]))))

  (define (make-struct-stx-decls sig prefix init-prefix? src-stx check?)
    ;; If check? is #f, generates a syntax definition for <name> for
    ;; each <name> struct form in `sig'. Used for imports.
    ;; If check? is #t, generates an empty syntax "definition" that has
    ;; the side-effect of checking <name> against its expected shape.
    ;; CURRENTLY, check? is always #f.
    (let ([signame (and init-prefix?
			(signature-name sig))])
      (append
       (apply
	append
	(map (lambda (s)
	       (make-struct-stx-decls s
				      (if signame
					  (format "~a~a:"
						  (or prefix "")
						  signame)
					  prefix)
				      #t
				      src-stx
				      check?))
	     (filter signature? (signature-elems sig))))
       (map (lambda (si)
	      (let ([names (struct-def-names si)]
		    [pfx (lambda (s)
			   `(quote-syntax
			     ,(let ([id (string->symbol 
					 (format "~a~a~a" 
						 (or prefix "")
						 (if signame
						     (format "~a:" signame)
						     "")
						 (if (syntax? s)
						     (syntax-e s)
						     s)))])
				(datum->syntax-object src-stx id))))])
		(let* ([name (pfx (struct-def-name si))]
		       [check (if check?
				  (lambda (l)
				    `(verify-struct-shape ,name ,l))
				  values)])
		  `(define-syntaxes (,@(if check? null (list (cadr name))))
		     ,(check
		       `(list ,(pfx (car names))
                              ,(pfx (cadr names))
                              ,(pfx (caddr names))
                              ;; trailing #fs below mean that we don't know whether we have all the fields:
                              (list
                               ,@(map pfx (every-other (cdddr names)))
                               #f)
                              (list
                               ,@(map pfx (every-other (if (null? (cdddr names)) null (cddddr names))))
                               #f)
                              #f))))))
	    (signature-structs sig)))))

  ;; Could be called at expansion time from the result of a `unit/sig' expansion.
  ;; NOT CURRENTLY USED.
  (define (verify-struct-shape name shape)
    (let ([v (syntax-local-value name (lambda () #f))]
	  [n (length (list-ref shape 3))])
      (unless (and (list? v)
		   (= (length v) 5)
		   (identifier? (car v))
		   (module-identifier=? (car v) (car shape))
		   (identifier? (cadr v))
		   (module-identifier=? (cadr v) (cadr shape))
		   (identifier? (caddr v))
		   (module-identifier=? (caddr v) (caddr shape))
		   (list? (list-ref v 3))
		   (= (length (list-ref v 3)) n)
		   (andmap identifier? (list-ref v 3))
		   (andmap module-identifier=? (list-ref v 3) (list-ref shape 3))
		   (list? (list-ref v 4))
		   (= (length (list-ref v 4)) n)
		   (andmap identifier? (list-ref v 4))
		   (andmap module-identifier=? (list-ref v 4) (list-ref shape 4)))
	(raise-syntax-error
	 'unit/sig
	 (format "struct definition for `~a' within the unit does not match the export signature (~a)"
		 (let ([s (symbol->string (syntax-e name))])
		   (substring s 0 (- (string-length s) 2)))
		 ;; Say why:
		 (cond
		  [(not v) "definition is missing or does not use `define-struct'"]
		  [(not (and (list? v)
			     (= (length v) 5)
			     (identifier? (car v))
			     (identifier? (cadr v))
			     (identifier? (caddr v))
			     (list? (list-ref v 3))
			     (andmap identifier? (list-ref v 3))
			     (list? (list-ref v 4))
			     (andmap identifier? (list-ref v 4))
			     (= (length (list-ref v 4)) (length (list-ref v 3)))))
		   "compile-time struct information is stragely malformed"]
		  [(not (< (length (list-ref v 3)) n))
		   "extra fields in definition"]
		  [(not (> (length (list-ref v 3)) n))
		   "missing fields in definition"]
		  [else
		   "different field names or order"]))))
      ;; Return 0 values to context
      (values)))
	 

  (define (every-other l)
    (let loop ([l l][r null])
      (cond
       [(null? l) r]
       [(null? (cdr l)) (cons (car l) r)]
       [else (loop (cddr l) (cons (car l) r))])))
  
  ;; ------------------------------------------------------------
      
  (define check-signature-unit-body
    (lambda (sig a-unit renames who expr)
      (let ([vars (map syntax-e (parsed-unit-vars a-unit))])
	(for-each
	 (lambda (var)
	   (let ([renamed (let ([s (do-rename var renames)])
			    (if (syntax? s)
				(syntax-e s)
				s))])
	     (unless (memq renamed vars)
		     (syntax-error #f expr
				   (format 
				    "signature \"~s\" requires variable \"~s\"~a"
				    (signature-src sig)
				    var
				    (if (eq? var renamed)
					""
					(format " renamed \"~s\"" renamed)))))))
	 (signature-vars sig))
	(unless (null? (signature-subsigs sig))
		(syntax-error #f expr
			      (format 
			       "signature \"~s\" requires sub-units"
			       (signature-src sig)))))))

  (define parse-imports
    (lambda (who untagged-legal? really-import? expr clause keep-ctx?)
      (let ([bad
	     (lambda (why . rest)
	       (apply
		syntax-error #f expr 
		(format (if really-import?
			    "bad `import' clause~a" 
			    "bad linkage specification~a")
			why)
		rest))])
	(let ([clause (stx->list clause)])
	  (unless (stx-list? clause)
	    (bad ""))
	  (map
	   (lambda (item)
	     (syntax-case item ()
	       [id 
		(and (identifier? (syntax id))
		     untagged-legal?)
		(rename-signature (get-sig who expr #f item (and keep-ctx? (syntax id))) #f (syntax id))]
	       [(id : sig)
		(and (identifier? (syntax id))
		     (literal? :))
		(get-sig who expr (syntax id) (syntax sig) (and keep-ctx? (syntax sig)))]
	       [any
		untagged-legal?
		(rename-signature (get-sig who expr #f item (and keep-ctx? (syntax any))) #f (syntax any))]
	       [_else
		(bad "" item)]))
	   clause)))))

  (define parse-unit
    (lambda (expr body sig user-stx-forms dv-stx ds-stx begin-stx)
      (let ([body (stx->list body)])
	(unless body
	  (syntax-error #f expr "illegal use of `.'"))
	(unless (and (pair? body)
		     (stx-pair? (car body))
		     (eq? 'import (syntax-e (stx-car (car body)))))
	  (syntax-error #f expr 
			"expected `import' clause"))
	(let* ([imports (parse-imports 'unit/sig #t #t expr (stx-cdr (car body)) #t)]
	       [imported-names (flatten-signatures imports #f)]
	       [exported-names (flatten-signature #f sig #f)]
	       [def-ctx (syntax-local-make-definition-context)]
	       [body (cdr body)])
	  (let-values ([(renames body)
			(if (and (stx-pair? body)
				 (stx-pair? (car body))
				 (eq? 'rename (syntax-e (stx-car (car body)))))
			    (values (map (lambda (p)
					   (list (stx-car p)
						 (syntax-e (stx-car (stx-cdr p)))))
					 (cdr (stx->list 
					       (let ([rn (car body)])
						 (local-expand rn
							       'expression
							       (list (stx-car rn))
							       def-ctx)))))
				    (cdr body))
			    (values null body))])
	    (unless renames
	      (syntax-error #f expr "illegal use of `.'" (car body)))
	    ;; Check renames:
	    (let ([bad
		   (lambda (why sub)
		     (syntax-error #f expr 
				   (format "bad `rename' clause~a" why)
				   sub))])
	      (for-each
	       (lambda (id)
		 (syntax-case id ()
		   [(iid eid)
		    (begin
		      (unless (identifier? (syntax iid))
			(bad ": original name is not an identifier" (syntax iid)))
		      (unless (identifier? (syntax eid))
			(bad ": new name is not an identifier" (syntax eid))))]
		   [else
		    (bad "" id)]))
	       renames))
	    (check-unique (map car renames)
			  (lambda (name)
			    (syntax-error #f expr
					  "id renamed twice"
					  name)))
	    (let* ([renamed-internals (map car renames)]
		   [swapped-renames (map (lambda (s) (cons (cadr s) (car s))) renames)]
		   [filtered-exported-names 
		    (if (null? renames) ;; an optimization
			exported-names
			(let loop ([e exported-names])
			  (if (null? e)
			      e
			      (if (ormap (lambda (rn) (eq? (car rn) (car e))) 
					 swapped-renames)
				  (loop (cdr e))
				  (cons (car e) (loop (cdr e)))))))]
		   [local-vars (map (lambda (s)
                                      (datum->syntax-object expr s))
                                    (append renamed-internals filtered-exported-names imported-names))]
		   [expand-context (generate-expand-context)]
		   [import-stxes (apply append (map (lambda (i) 
						      (map
						       (lambda (d)
							 (datum->syntax-object expr d))
						       (make-struct-stx-decls i #f #t expr #f)))
						    imports))]
		   [import-vars
		    (let ([vars (map (lambda (sym) (datum->syntax-object expr sym expr))
				     (flatten-signatures imports 'must-have-ctx))])
		      ;; Treat imported names like internal definitions:
		      (syntax-local-bind-syntaxes vars #f def-ctx)
		      (cdr (syntax->list (local-expand #`(stop #,@vars)
						       'expression
						       (list #'stop)
						       def-ctx))))])
	      (let loop ([pre-lines null][lines (append import-stxes body)][port #f][port-name #f][body null][vars null])
		(cond
		 [(and (null? pre-lines) (not port) (null? lines))
                  (internal-definition-context-seal def-ctx)
		  (make-parsed-unit imports 
				    renames 
				    vars 
				    import-vars
				    body
				    (lambda (src-stx) 
				      ;; Disabled until we have a mechanism for declaring precise information in signatures:
				      ;; (make-struct-stx-decls sig #f #f src-stx #t)
				      null))]
		 [(and (null? pre-lines) (not port) (not (pair? lines)))
		  (syntax-error #f expr "improper body list form")]
		 [else
		  (let-values ([(line) (let ([s (cond
						 [(pair? pre-lines) (car pre-lines)]
						 [port (read-syntax port-name port)]
						 [else (car lines)])])
					 (if (eof-object? s)
					     s
					     (local-expand s    
							   expand-context
							   (append
							    user-stx-forms
							    local-vars)
							   def-ctx)))]
			       [(rest-pre-lines) 
				(if (null? pre-lines)
				    null
				    (cdr pre-lines))]
			       [(rest-lines)
				(if (and (null? pre-lines) (not port))
				    (cdr lines)
				    lines)])
		    (cond
		     [(and (null? pre-lines) 
			   port
			   (eof-object? line))
		      (values lines body vars)]
		     [(and (stx-pair? line)
			   (identifier? (stx-car line))
			   (module-identifier=? (stx-car line) dv-stx))
		      (syntax-case line ()
			[(_ (id ...) rhs)
			 (let ([ids (syntax->list #'(id ...))])
			   (for-each (lambda (id)
				       (unless (identifier? #'id)
					 (syntax-error #f id "not an identifier" line)))
				     ids)
			   (syntax-local-bind-syntaxes ids #f def-ctx)
			   (loop rest-pre-lines
				 rest-lines
				 port
				 port-name
				 (cons line body)
				 (append ids vars)))]
			[else
			 (syntax-error #f expr 
				       "improper `define-values' clause form"
				       line)])]
		     [(and (stx-pair? line)
			   (identifier? (stx-car line))
			   (module-identifier=? (stx-car line) ds-stx))
		      (syntax-case line ()
			[(_ (id ...) rhs)
			 (let ([ids (syntax->list #'(id ...))])
			   (for-each (lambda (id)
				       (unless (identifier? #'id)
					 (syntax-error #f id "not an identifier" line)))
				     ids)
			   (with-syntax ([rhs (local-transformer-expand
					       #'rhs
					       'expression
					       null)])
			     (syntax-local-bind-syntaxes ids #'rhs def-ctx)
			     (loop rest-pre-lines
				   rest-lines
				   port
				   port-name
				   (cons line body)
				   vars)))]
			[else
			 (syntax-error #f expr 
				       "improper `define-syntaxes' clause form"
				       line)])]
		     [(and (stx-pair? line)
			   (identifier? (stx-car line))
			   (module-identifier=? (stx-car line) begin-stx))
		      (let ([line-list (stx->list line)])
			(unless line-list
			  (syntax-error #f expr 
					"improper `begin' clause form"
					line))
			(loop (append (cdr line-list) rest-pre-lines)
			      rest-lines
			      port
			      port-name
			      body
			      vars))]
		     [else
		      (loop rest-pre-lines
			    rest-lines
			    port
			    port-name
			    (cons line body)
			    vars)]))]))))))))
  
  (define-struct link (name sig expr links))
  (define-struct sig-explode-pair (sigpart exploded))

  (define parse-compound-unit
    (lambda (expr body)
      (syntax-case body ()
	[((import . imports)
	  (link . links)
	  (export . exports))
	 (and (literal? import) (literal? link) (literal? export))
	 (let* ([imports (parse-imports 'compound-unit/sig #f #t expr (syntax imports) #f)])
	   (let ([link-list (syntax->list (syntax links))])
	     (unless link-list
	       (syntax-error #f expr 
			     "improper `link' clause form"
			     (syntax links)))
	     (let* ([bad
		     (lambda (why sub)
		       (syntax-error #f expr 
				     (format "bad `link' element~a" why)
				     sub))]
		    [links
		     (map
		      (lambda (line)
			(syntax-case line ()
			  [(tag : sig (expr linkage ...))
			   (literal? :)
			   (begin
			     (unless (identifier? (syntax tag))
			       (bad ": link tag is not an identifier" line))
			     (make-link (syntax-e (syntax tag))
					(get-sig 'compound-unit/sig (syntax expr) #f (syntax sig) #f)
					(syntax expr)
					(syntax->list (syntax (linkage ...)))))]
			  [(tag . x)
			   (not (identifier? (syntax tag)))
			   (bad ": tag is not an identifier" (syntax tag))]
			  [(tag : sig (expr linkage ...) . rest)
			   (literal? :)
			   (bad ": extra expressions in sub-clause" line)]
			  [(tag : sig (expr . rest))
			   (literal? :)
			   (bad ": illegal use of `.' in linkages" line)]
			  [(tag : sig)
			   (literal? :)
			   (bad ": expected a unit expression and its linkages" line)]
			  [(tag : sig . e)
			   (literal? :)
			   (bad ": unit expression and its linkages not parenthesized" line)]
			  [(tag :)
			   (literal? :)
			   (bad ": expected a signature" line)]
			  [(tag)
			   (bad ": expected `:'" line)]
			  [_else
			   (bad "" line)]))
		      link-list)]
		    [in-sigs imports]
		    [find-link
		     (lambda (name links)
		       (let loop ([links links])
			 (cond
			  [(null? links) #f]
			  [(eq? name (link-name (car links)))
			   (car links)]
			  [else (loop (cdr links))])))]
		    [find-sig
		     (lambda (name sigs)
		       (let loop ([sigs sigs])
			 (cond
			  [(null? sigs) #f]
			  [(and (signature? (car sigs))
				(eq? name (signature-name (car sigs))))
			   (car sigs)]
			  [else (loop (cdr sigs))])))]
		    [flatten-path
		     (lambda (clause path var-k unit-k)
		       (letrec ([check-sig
				 (lambda (sig use-sig)
				   (when use-sig
				     (with-handlers ([exn:fail:unit?
						      (lambda (exn)
							(syntax-error 
							 #f expr
							 (exn-message exn)))])
				       (alt-verify-signature-match
					'compound-unit/sig #f
					(format "signature ~s" (signature-src use-sig))
					(explode-sig use-sig #f)
					(format "signature ~s" (signature-src sig))
					(explode-sig sig #f)))))]
				[flatten-subpath
				 (lambda (base last use-sig name sig p)
				   (cond
				    [(stx-null? p) 
				     (check-sig sig use-sig)
				     (unit-k base last name (if use-sig
								use-sig
								sig))]
				    [(or (not (stx-pair? p))
					 (not (identifier? (stx-car p))))
				     (syntax-error #f expr
						   path)]
				    [(memq (syntax-e (stx-car p)) (signature-vars sig))
				     (if (and (stx-null? (stx-cdr p)) (not use-sig))
					 (let* ([id-nopath (syntax-e (stx-car p))]
						[id (if name
							(string->symbol 
							 (string-append name
									":"
									(symbol->string id-nopath)))
							id-nopath)])
					   (var-k base id id-nopath))
					 (syntax-error #f expr
						       (format 
							"bad `~a' path: \"~a\" is a variable" 
							clause
							(syntax-e (stx-car p)))
						       path))]
				    [(find-sig (syntax-e (stx-car p)) (signature-elems sig))
				     =>
				     (lambda (s)
				       (flatten-subpath base
							(syntax-e (stx-car p))
							use-sig
							(let ([n (symbol->string 
								  (signature-name s))])
							  (if name
							      (string-append name ":" n)
							      n))
							s
							(stx-cdr p)))]
				    [else
				     (syntax-error #f expr
						   (format 
						    "bad `~a' path: \"~a\" not found"
						    clause
						    (syntax-e (stx-car p)))
						   path)]))])
			 (let-values ([(p use-sig)
				       (syntax-case path ()
					[_
					 (identifier? path)
					 (values (list path) #f)]
					[(name : sig)
					 (and (identifier? (syntax name))
					      (literal? :))
					 (values (list (syntax name))
						 (get-sig 'compound-unit/sig expr
							  #f
							  (syntax sig)
							  #f))]
					[((elem ...) : sig)
					 (and (andmap (lambda (s)
							(and (identifier? s)
							     (not (eq? (syntax-e s) ':))))
						      (syntax->list (syntax (elem ...))))
					      (literal? :))
					 (values (syntax (elem ...))
						 (get-sig 'compound-unit/sig expr
							  #f
							  (syntax sig)
							  #f))]
					[(elem1 elem ...)
					 (andmap (lambda (s)
						   (and (identifier? s)
							(not (eq? (syntax-e s) ':))))
						 (syntax->list (syntax (elem1 elem ...))))
					 (values path #f)]
					[else
					 (syntax-error #f expr
						       (format 
							"bad `~a' path"
							clause)
						       path)])])
			   (cond
			    [(find-link (syntax-e (stx-car p)) links)
			     => (lambda (link) 
				  (flatten-subpath (link-name link)
						   (syntax-e (stx-car p))
						   use-sig
						   #f
						   (link-sig link)
						   (stx-cdr p)))]
			    [(find-sig (syntax-e (stx-car p)) in-sigs)
			     => (lambda (sig) 
				  (let ([s (symbol->string (signature-name sig))])
				    (flatten-subpath #f
						     (syntax-e (stx-car p))
						     use-sig
						     s
						     sig
						     (stx-cdr p))))]
			    [else
			     (syntax-error #f expr
					   (format 
					    "bad `~a' path: \"~a\" not found"
					    clause
					    (syntax-e (stx-car p)))
					   path)]))))])
	       (check-unique (map link-name links)
			     (lambda (name)
			       (syntax-error #f expr
					     (format "duplicate sub-unit tag \"~s\"" name))))
	       (check-unique (map signature-name imports)
			     (lambda (name)
			       (syntax-error #f expr
					     (format "duplicate import identifier \"~s\"" name))))
	       (check-unique (append (map signature-name imports)
				     (map link-name links))
			     (lambda (name)
			       (syntax-error #f expr
					     (format 
					      "name \"~s\" is both import and sub-unit identifier" 
					      name))))
	       ;; Expand `link' clause using signatures
	       (for-each
		(lambda (link)
		  (set-link-links! 
		   link
		   (map
		    (lambda (link)
		      (flatten-path 'link link
				    (lambda (base var var-nopath)
				      (make-sig-explode-pair
				       var
				       (list
					(if base
					    (list base var)
					    var))))
				    (lambda (base last id sig)
				      (make-sig-explode-pair
				       (rename-signature sig last #f)
				       (if base
					   (list (cons base (flatten-signature id sig #f)))
					   (flatten-signature id sig #f))))))
		    (link-links link))))
		links)
	       (let ([export-list (syntax->list (syntax exports))])
		 (unless export-list
		   (syntax-error #f expr 
				 "improper `export' clause form"
				 (syntax exports)))
		 (let* ([upath? (lambda (p)
				  (or (identifier? p)
				      (and (stx-list? p)
					   (andmap identifier? (stx->list p)))))]
			[spath? (lambda (p)
				  (syntax-case p ()
				    [(name : sig)
				     (and (literal? :)
					  (upath? (syntax name))
					  (or (identifier? (syntax sig))
					      (parse-signature 'compound-unit/sig expr #f (syntax sig) #f)))
				     #t]
				    [_else
				     (upath? p)]))]
			[exports 
			 (map
			  (lambda (export)
			    (syntax-case export ()
			      [(open spath)
			       (literal? open)
			       (begin
				 (unless (spath? (syntax spath))
				   (syntax-error #f expr 
						 "bad `open' sub-clause of `export'"
						 export))
				 (flatten-path 'export
					       (syntax spath)
					       (lambda (base var var-nopath)
						 (syntax-error 
						  #f expr 
						  "`open' sub-clause path is a variable"
						  (car export)))
					       (lambda (base last name sig)
						 (if base
						     (make-sig-explode-pair
						      (signature-elems sig)
						      (cons base 
							    (map
							     list
							     (flatten-signature name sig #f)
							     (flatten-signature #f sig #f))))
						     (syntax-error 
						      #f expr 
						      "cannot export imported variables"
						      export)))))]
			      [(var (upath vname) . exname)
			       (literal? var)
			       (let ([upath (syntax upath)]
				     [vname (syntax vname)]
				     [exname (syntax exname)])
				 (unless (and (upath? upath)
					      (identifier? vname)
					      (or (stx-null? exname)
						  (and (stx-pair? exname)
						       (identifier? (stx-car exname))
						       (stx-null? (stx-cdr exname)))))
				   (syntax-error #f expr 
						 "bad `var' sub-clause of `export'"
						 export))
				 (flatten-path 'export
					       (if (identifier? upath)
						   (list upath vname)
						   (append (stx->list upath) (list vname)))
					       (lambda (base var var-nopath)
						 (if base
						     (make-sig-explode-pair
						      (list (if (stx-null? exname)
								var-nopath
								(syntax-e (stx-car exname))))
						      (list base
							    (if (stx-null? exname)
								(list var var-nopath)
								(list var (syntax-e (stx-car exname))))))
						     (syntax-error 
						      #f expr 
						      "cannot export imported variables"
						      export)))
					       (lambda (base last name var)
						 (syntax-error 
						  #f expr 
						  "`var' sub-clause path specifies a unit"
						  export))))]
			      [(unit spath . exname)
			       (literal? unit)
			       (let ([spath (syntax spath)]
				     [exname (syntax exname)])
				 (unless (and (spath? spath)
					      (or (stx-null? exname)
						  (and (stx-pair? exname)
						       (identifier? (stx-car exname))
						       (stx-null? (stx-cdr exname)))))
				   (syntax-error #f expr 
						 "bad `unit' sub-clause of `export'"
						 export))
				 (flatten-path 'export
					       spath
					       (lambda (base var var-nopath)
						 (syntax-error 
						  #f expr 
						  "`unit' sub-clause path is a variable"
						  export))
					       (lambda (base last name sig)
						 (if base
						     (make-sig-explode-pair
						      (list (rename-signature
							     sig
							     (if (stx-null? exname)
								 last
								 (syntax-e (stx-car exname)))
							     #f))
						      (let ([flat (flatten-signature name sig #f)])
							(cons base 
							      (map
							       list
							       flat
							       (flatten-signature 
								(symbol->string
								 (if (stx-null? exname)
								     last
								     (syntax-e (stx-car exname))))
								sig
								#f)))))
						     (syntax-error 
						      #f expr 
						      "cannot export imported variables"
						      export)))))]
			      [_else
			       (syntax-error #f expr 
					     (format 
					      "bad `export' sub-clause")
					     export)]))
			  export-list)]
			[interned-vectors (box null)])
		   (check-unique (map
				  (lambda (s)
				    (if (signature? s)
					(signature-name s)
					s))
				  (apply
				   append
				   (map sig-explode-pair-sigpart exports)))
				 (lambda (name)
				   (syntax-error #f expr
						 "name is exported twice"
						 name)))
		   (values (map link-name links)
			   (map link-expr links)
			   (map (lambda (link) (explode-sig (link-sig link) interned-vectors)) links)
			   (map
			    (lambda (link)
			      (map (lambda (sep)
				     (explode-named-sig (sig-explode-pair-sigpart sep) interned-vectors))
				   (link-links link)))
			    links)
			   (flatten-signatures imports #f)
			   (map (lambda (link)
				  (apply
				   append
				   (map 
				    sig-explode-pair-exploded 
				    (link-links link))))
				links)
			   (map sig-explode-pair-exploded exports)
			   (explode-named-sigs imports interned-vectors)
			   (explode-sig
			    (let ([elems (apply
					  append
					  (map sig-explode-pair-sigpart exports))])
			      (make-signature
			       'dummy
			       'dummy
			       elems
			       (map (lambda (x) #f) elems)
			       null))
			    interned-vectors)
			   interned-vectors))))))]
	[_else (raise-syntax-error 
		#f
		"bad syntax"
		expr)])))
  
  (define parse-invoke-vars
    (lambda (who rest expr)
      (parse-imports who #t #f expr rest #f)))

  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (provide parse-unit
	   parse-compound-unit
	   parse-invoke-vars

	   parsed-unit-renames
	   parsed-unit-imports
	   parsed-unit-import-vars
	   parsed-unit-body
	   parsed-unit-stx-checks
	   parsed-unit-vars

	   make-struct-stx-decls
	   verify-struct-shape

	   signature-vars
	   signature-structs
	   do-rename
	   get-sig
	   explode-sig
	   explode-named-sigs
	   check-signature-unit-body
	   flatten-signature
	   flatten-signatures
	   struct-def-name))
