You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
17 KiB
Scheme
454 lines
17 KiB
Scheme
;;; Copyright (c) 2000-2018 Dipanwita Sarkar, Andrew W. Keep, R. Kent Dybvig, Oscar Waddell
|
|
;;; See the accompanying file Copyright for details
|
|
|
|
(library (nanopass helpers)
|
|
(export
|
|
;; auxiliary keywords for language/pass definitions
|
|
extends definitions entry terminals nongenerative-id maybe
|
|
|
|
;; predicates for looking for identifiers independent of context
|
|
ellipsis? unquote? colon? arrow? plus? minus? double-arrow?
|
|
|
|
;; things for dealing with syntax and idetnfieris
|
|
all-unique-identifiers? construct-id construct-unique-id gentemp
|
|
bound-id-member? bound-id-union partition-syn datum
|
|
|
|
;; things for dealing with language meta-variables
|
|
meta-var->raw-meta-var combine unique-name
|
|
|
|
;; convenience syntactic forms
|
|
rec with-values define-who trace-define-who
|
|
|
|
;; source information funtions
|
|
syntax->source-info
|
|
|
|
;;; stuff imported from implementation-helpers
|
|
|
|
;; formatting
|
|
format printf pretty-print
|
|
|
|
;; listy stuff
|
|
iota make-list list-head
|
|
|
|
;; gensym stuff (related to nongenerative languages)
|
|
gensym regensym
|
|
|
|
;; library export stuff (needed for when used inside module to
|
|
;; auto-indirect export things)
|
|
indirect-export
|
|
|
|
;; compile-time environment helpers
|
|
define-property make-compile-time-value
|
|
|
|
;; code organization helpers
|
|
module
|
|
|
|
;; useful for warning items
|
|
warningf errorf
|
|
|
|
;; used to get the best performance from hashtables
|
|
eq-hashtable-set! eq-hashtable-ref
|
|
|
|
;; debugging support
|
|
trace-lambda trace-define-syntax trace-let trace-define
|
|
|
|
;; needed to know what code to generate
|
|
optimize-level
|
|
|
|
;; the base record, so that we can use gensym syntax
|
|
define-nanopass-record
|
|
|
|
;; handy syntactic stuff
|
|
with-implicit with-r6rs-quasiquote with-extended-quasiquote
|
|
extended-quasiquote with-auto-unquote
|
|
|
|
;; abstraction of the grabbing the syntactic environment that will work in
|
|
;; Chez, Ikarus, & Vicare
|
|
with-compile-time-environment
|
|
|
|
;; expose the source information stuff
|
|
syntax->source-information
|
|
source-information-source-file
|
|
source-information-byte-offset-start
|
|
source-information-char-offset-start
|
|
source-information-byte-offset-end
|
|
source-information-char-offset-end
|
|
source-information-position-line
|
|
source-information-position-column
|
|
source-information-type
|
|
provide-full-source-information)
|
|
(import (rnrs) (nanopass implementation-helpers))
|
|
|
|
(define-syntax datum
|
|
(syntax-rules ()
|
|
[(_ e) (syntax->datum #'e)]))
|
|
|
|
(define-syntax with-r6rs-quasiquote
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(k . body)
|
|
#`(let-syntax ([#,(datum->syntax #'k 'quasiquote) (syntax-rules () [(_ x) `x])])
|
|
. body)])))
|
|
|
|
(define-syntax extended-quasiquote
|
|
(lambda (x)
|
|
(define gather-unquoted-exprs
|
|
(lambda (body)
|
|
(let f ([body body] [t* '()] [e* '()])
|
|
(syntax-case body (unquote unquote-splicing)
|
|
[(unquote x)
|
|
(identifier? #'x)
|
|
(if (memp (lambda (t) (bound-identifier=? t #'x)) t*)
|
|
(values body t* e*)
|
|
(values body (cons #'x t*) (cons #'x e*)))]
|
|
[(unquote-splicing x)
|
|
(identifier? #'x)
|
|
(if (memp (lambda (t) (bound-identifier=? t #'x)) t*)
|
|
(values body t* e*)
|
|
(values body (cons #'x t*) (cons #'x e*)))]
|
|
[(unquote e)
|
|
(with-syntax ([(t) (generate-temporaries '(t))])
|
|
(values #'(unquote t) (cons #'t t*) (cons #'e e*)))]
|
|
[(unquote-splicing e)
|
|
(with-syntax ([(t) (generate-temporaries '(t))])
|
|
(values #'(unquote-splicing t) (cons #'t t*) (cons #'e e*)))]
|
|
[(tmpl0 . tmpl1)
|
|
(let-values ([(tmpl0 t* e*) (f #'tmpl0 t* e*)])
|
|
(let-values ([(tmpl1 t* e*) (f #'tmpl1 t* e*)])
|
|
(values #`(#,tmpl0 . #,tmpl1) t* e*)))]
|
|
[atom (values #'atom t* e*)]))))
|
|
(define build-list
|
|
(lambda (body orig-level)
|
|
(let loop ([body body] [level orig-level])
|
|
(syntax-case body (unquote unquote-splicing)
|
|
[(tmpl0 ... (unquote e))
|
|
(with-syntax ([(tmpl0 ...) (rebuild-body #'(tmpl0 ...) (fx- orig-level 1))])
|
|
(cond
|
|
[(fx=? level 0) #'(tmpl0 ... (unquote e))]
|
|
[(fx=? level 1) #'(tmpl0 ... (unquote-splicing e))]
|
|
[else (let loop ([level level] [e #'e])
|
|
(if (fx=? level 1)
|
|
#`(tmpl0 ... (unquote-splicing #,e))
|
|
(loop (fx- level 1) #`(apply append #,e))))]))]
|
|
[(tmpl0 ... (unquote-splicing e))
|
|
(with-syntax ([(tmpl0 ...) (rebuild-body #'(tmpl0 ...) (fx- orig-level 1))])
|
|
(cond
|
|
[(fx=? level 0) #'(tmpl0 ... (unquote-splicing e))]
|
|
[else (let loop ([level level] [e #'e])
|
|
(if (fx=? level 0)
|
|
#`(tmpl0 ... (unquote-splicing #,e))
|
|
(loop (fx- level 1) #`(apply append #,e))))]))]
|
|
[(tmpl0 ... tmpl1 ellipsis)
|
|
(eq? (datum ellipsis) '...)
|
|
(loop #'(tmpl0 ... tmpl1) (fx+ level 1))]
|
|
[(tmpl0 ... tmpl1)
|
|
(with-syntax ([(tmpl0 ...) (rebuild-body #'(tmpl0 ...) (fx- orig-level 1))])
|
|
(let-values ([(tmpl1 t* e*) (gather-unquoted-exprs #'tmpl1)])
|
|
(when (null? e*)
|
|
(syntax-violation 'extended-quasiquote
|
|
"no variables found in ellipsis expression" body))
|
|
(let loop ([level level]
|
|
[e #`(map (lambda #,t*
|
|
(extended-quasiquote
|
|
#,tmpl1))
|
|
. #,e*)])
|
|
(if (fx=? level 1)
|
|
#`(tmpl0 ... (unquote-splicing #,e))
|
|
(loop (fx- level 1) #`(apply append #,e))))))]))))
|
|
(define rebuild-body
|
|
(lambda (body level)
|
|
(syntax-case body (unquote unquote-splicing)
|
|
[(unquote e) #'(unquote e)]
|
|
[(unquote-splicing e) #'(unquote-splicing e)]
|
|
[(tmpl0 ... tmpl1 ellipsis)
|
|
(eq? (datum ellipsis) '...)
|
|
(with-syntax ([(tmpl0 ...) (build-list #'(tmpl0 ... tmpl1) (fx+ level 1))])
|
|
#'(tmpl0 ...))]
|
|
[(tmpl0 ... tmpl1 ellipsis . tmpl2)
|
|
(eq? (datum ellipsis) '...)
|
|
(with-syntax ([(tmpl0 ...) (build-list #'(tmpl0 ... tmpl1) (fx+ level 1))]
|
|
[tmpl2 (rebuild-body #'tmpl2 level)])
|
|
#'(tmpl0 ... . tmpl2))]
|
|
[(tmpl0 ... tmpl1)
|
|
(with-syntax ([(tmpl0 ...) (rebuild-body #'(tmpl0 ...) level)]
|
|
[tmpl1 (rebuild-body #'tmpl1 level)])
|
|
#'(tmpl0 ... tmpl1))]
|
|
[(tmpl0 ... tmpl1 . tmpl2)
|
|
(with-syntax ([(tmpl0 ...) (rebuild-body #'(tmpl0 ... tmpl1) level)]
|
|
[tmpl2 (rebuild-body #'tmpl2 level)])
|
|
#'(tmpl0 ... . tmpl2))]
|
|
[other #'other])))
|
|
(syntax-case x ()
|
|
[(k body)
|
|
(with-syntax ([body (rebuild-body #'body 0)])
|
|
#'(quasiquote body))])))
|
|
|
|
(define-syntax with-extended-quasiquote
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(k . body)
|
|
(with-implicit (k quasiquote)
|
|
#'(let-syntax ([quasiquote (syntax-rules ()
|
|
[(_ x) (extended-quasiquote x)])])
|
|
|
|
. body))])))
|
|
|
|
(define-syntax with-auto-unquote
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(k (x* ...) . body)
|
|
(with-implicit (k quasiquote)
|
|
#'(let-syntax ([quasiquote
|
|
(lambda (x)
|
|
(define replace-vars
|
|
(let ([vars (list #'x* ...)])
|
|
(lambda (b)
|
|
(let f ([b b])
|
|
(syntax-case b ()
|
|
[id (identifier? #'id)
|
|
(if (memp (lambda (var) (free-identifier=? var #'id)) vars)
|
|
#'(unquote id)
|
|
#'id)]
|
|
[(a . d) (with-syntax ([a (f #'a)] [d (f #'d)]) #'(a . d))]
|
|
[atom #'atom])))))
|
|
(syntax-case x ()
|
|
[(_ b)
|
|
(with-syntax ([b (replace-vars #'b)])
|
|
#'`b)]))])
|
|
. body))])))
|
|
|
|
(define all-unique-identifiers?
|
|
(lambda (ls)
|
|
(and (for-all identifier? ls)
|
|
(let f ([ls ls])
|
|
(if (null? ls)
|
|
#t
|
|
(let ([id (car ls)] [ls (cdr ls)])
|
|
(and (not (memp (lambda (x) (free-identifier=? x id)) ls))
|
|
(f ls))))))))
|
|
|
|
(define-syntax with-values
|
|
(syntax-rules ()
|
|
[(_ p c) (call-with-values (lambda () p) c)]))
|
|
|
|
(define-syntax rec
|
|
(syntax-rules ()
|
|
[(_ name proc) (letrec ([name proc]) name)]
|
|
[(_ (name . arg) body body* ...)
|
|
(letrec ([name (lambda arg body body* ...)]) name)]))
|
|
|
|
(define-syntax define-auxiliary-keyword
|
|
(syntax-rules ()
|
|
[(_ name)
|
|
(define-syntax name
|
|
(lambda (x)
|
|
(syntax-violation 'name "misplaced use of auxiliary keyword" x)))]))
|
|
|
|
(define-syntax define-auxiliary-keywords
|
|
(syntax-rules ()
|
|
[(_ name* ...)
|
|
(begin (define-auxiliary-keyword name*) ...)]))
|
|
|
|
(define-auxiliary-keywords extends definitions entry terminals nongenerative-id maybe)
|
|
|
|
(define-syntax define-who
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(k name expr)
|
|
(with-implicit (k who)
|
|
#'(define name (let () (define who 'name) expr)))]
|
|
[(k (name . fmls) expr exprs ...)
|
|
#'(define-who name (lambda (fmls) expr exprs ...))])))
|
|
|
|
(define-syntax trace-define-who
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(k name expr)
|
|
(with-implicit (k who)
|
|
#'(trace-define name (let () (define who 'name) expr)))]
|
|
[(k (name . fmls) expr exprs ...)
|
|
#'(trace-define-who name (lambda (fmls) expr exprs ...))])))
|
|
|
|
;;; moved from meta-syntax-dispatch.ss and nano-syntax-dispatch.ss
|
|
(define combine
|
|
(lambda (r* r)
|
|
(if (null? (car r*))
|
|
r
|
|
(cons (map car r*) (combine (map cdr r*) r)))))
|
|
|
|
;;; moved from meta-syntax-dispatch.ss and syntaxconvert.ss
|
|
(define ellipsis?
|
|
(lambda (x)
|
|
(and (identifier? x) (free-identifier=? x (syntax (... ...))))))
|
|
|
|
(define unquote?
|
|
(lambda (x)
|
|
(and (identifier? x) (free-identifier=? x (syntax unquote)))))
|
|
|
|
(define unquote-splicing?
|
|
(lambda (x)
|
|
(and (identifier? x) (free-identifier=? x (syntax unquote-splicing)))))
|
|
|
|
(define plus?
|
|
(lambda (x)
|
|
(and (identifier? x)
|
|
(or (free-identifier=? x #'+)
|
|
(eq? (syntax->datum x) '+)))))
|
|
|
|
(define minus?
|
|
(lambda (x)
|
|
(and (identifier? x)
|
|
(or (free-identifier=? x #'-)
|
|
(eq? (syntax->datum x) '-)))))
|
|
|
|
(define double-arrow?
|
|
(lambda (x)
|
|
(and (identifier? x)
|
|
(or (free-identifier=? x #'=>)
|
|
(eq? (syntax->datum x) '=>)))))
|
|
|
|
(define colon?
|
|
(lambda (x)
|
|
(and (identifier? x)
|
|
(or (free-identifier=? x #':)
|
|
(eq? (syntax->datum x) ':)))))
|
|
|
|
(define arrow?
|
|
(lambda (x)
|
|
(and (identifier? x)
|
|
(or (free-identifier=? x #'->)
|
|
(eq? (syntax->datum x) '->)))))
|
|
|
|
;;; unique-name produces a unique name derived the input name by
|
|
;;; adding a unique suffix of the form .<digit>+. creating a unique
|
|
;;; name from a unique name has the effect of replacing the old
|
|
;;; unique suffix with a new one.
|
|
|
|
(define unique-suffix
|
|
(let ((count 0))
|
|
(lambda ()
|
|
(set! count (+ count 1))
|
|
(number->string count))))
|
|
|
|
(define unique-name
|
|
(lambda (id . id*)
|
|
(string-append
|
|
(fold-right
|
|
(lambda (id str) (string-append str ":" (symbol->string (syntax->datum id))))
|
|
(symbol->string (syntax->datum id)) id*)
|
|
"."
|
|
(unique-suffix))))
|
|
|
|
; TODO: at some point we may want this to be a little bit more
|
|
; sophisticated, or we may want to have something like a regular
|
|
; expression style engine where we bail as soon as we can identify
|
|
; what the meta-var corresponds to.
|
|
(define meta-var->raw-meta-var
|
|
(lambda (sym)
|
|
(let ([s (symbol->string sym)])
|
|
(let f ([i (fx- (string-length s) 1)])
|
|
(cond
|
|
[(fx=? i -1) sym]
|
|
[(or (char=? #\* (string-ref s i))
|
|
(char=? #\^ (string-ref s i))
|
|
(char=? #\? (string-ref s i)))
|
|
(f (fx- i 1))]
|
|
[else (let f ([i i])
|
|
(cond
|
|
[(fx=? i -1) sym]
|
|
[(char-numeric? (string-ref s i)) (f (fx- i 1))]
|
|
[else (string->symbol (substring s 0 (fx+ i 1)))]))])))))
|
|
|
|
(define build-id
|
|
(lambda (who x x*)
|
|
(define ->str
|
|
(lambda (x)
|
|
(cond
|
|
[(string? x) x]
|
|
[(identifier? x) (symbol->string (syntax->datum x))]
|
|
[(symbol? x) (symbol->string x)]
|
|
[else (error who "invalid input ~s" x)])))
|
|
(apply string-append (->str x) (map ->str x*))))
|
|
|
|
(define $construct-id
|
|
(lambda (who str->sym tid x x*)
|
|
(unless (identifier? tid)
|
|
(error who "template argument ~s is not an identifier" tid))
|
|
(datum->syntax tid (str->sym (build-id who x x*)))))
|
|
|
|
(define-who construct-id
|
|
(lambda (tid x . x*)
|
|
($construct-id who string->symbol tid x x*)))
|
|
|
|
(define-who construct-unique-id
|
|
(lambda (tid x . x*)
|
|
($construct-id who gensym tid x x*)))
|
|
|
|
(define-syntax partition-syn
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(_ ls-expr () e0 e1 ...) #'(begin ls-expr e0 e1 ...)]
|
|
[(_ ls-expr ([set pred] ...) e0 e1 ...)
|
|
(with-syntax ([(pred ...)
|
|
(let f ([preds #'(pred ...)])
|
|
(if (null? (cdr preds))
|
|
(if (free-identifier=? (car preds) #'otherwise)
|
|
(list #'(lambda (x) #t))
|
|
preds)
|
|
(cons (car preds) (f (cdr preds)))))])
|
|
#'(let-values ([(set ...)
|
|
(let f ([ls ls-expr])
|
|
(if (null? ls)
|
|
(let ([set '()] ...) (values set ...))
|
|
(let-values ([(set ...) (f (cdr ls))])
|
|
(cond
|
|
[(pred (car ls))
|
|
(let ([set (cons (car ls) set)])
|
|
(values set ...))]
|
|
...
|
|
[else (error 'partition-syn
|
|
"no home for ~s"
|
|
(car ls))]))))])
|
|
e0 e1 ...))])))
|
|
|
|
(define gentemp
|
|
(lambda ()
|
|
(car (generate-temporaries '(#'t)))))
|
|
|
|
(define bound-id-member?
|
|
(lambda (id id*)
|
|
(and (not (null? id*))
|
|
(or (bound-identifier=? id (car id*))
|
|
(bound-id-member? id (cdr id*))))))
|
|
|
|
(define bound-id-union ; seems to be unneeded
|
|
(lambda (ls1 ls2)
|
|
(cond
|
|
[(null? ls1) ls2]
|
|
[(bound-id-member? (car ls1) ls2) (bound-id-union (cdr ls1) ls2)]
|
|
[else (cons (car ls1) (bound-id-union (cdr ls1) ls2))])))
|
|
|
|
(define syntax->source-info
|
|
(lambda (stx)
|
|
(let ([si (syntax->source-information stx)])
|
|
(and si
|
|
(cond
|
|
[(and (source-information-position-line si)
|
|
(source-information-position-column si))
|
|
(format "~s line ~s, char ~s of ~a"
|
|
(source-information-type si)
|
|
(source-information-position-line si)
|
|
(source-information-position-column si)
|
|
(source-information-source-file si))]
|
|
[(source-information-byte-offset-start si)
|
|
(format "~s byte position ~s of ~a"
|
|
(source-information-type si)
|
|
(source-information-byte-offset-start si)
|
|
(source-information-source-file si))]
|
|
[(source-information-char-offset-start si)
|
|
(format "~s character position ~s of ~a"
|
|
(source-information-type si)
|
|
(source-information-char-offset-start si)
|
|
(source-information-source-file si))]
|
|
[else (format "in ~a" (source-information-source-file si))]))))))
|