diff --git a/collects/r6rs/info.ss b/collects/r6rs/info.ss new file mode 100644 index 0000000000..c14a2ca411 --- /dev/null +++ b/collects/r6rs/info.ss @@ -0,0 +1 @@ +#lang setup/infotab diff --git a/collects/r6rs/main.ss b/collects/r6rs/main.ss index 92fadb35b0..b9f7ba82ee 100644 --- a/collects/r6rs/main.ss +++ b/collects/r6rs/main.ss @@ -1,9 +1,381 @@ #lang scheme/base -(provide (rename-out [module-begin #%module-begin]) - quote) +#| +FIXME: + * (for ... expand) should shift all exports, not just run-time (requires a mzscheme change) + * need meta levels other than 0, 1, and -1 + * Check that each identifier is imported only once across phases. +|# -(define-syntax-rule (module-begin . stuff) - (#%module-begin - "The R6RS language just supplies a reader, so far." - 'stuff)) +(require (for-syntax scheme/base + syntax/kerncase + "private/find-version.ss")) + +(provide (rename-out [module-begin #%module-begin])) + +;; ---------------------------------------- +;; Library and top-level forms + +(define-for-syntax (syntax-list? stx) + (syntax->list stx)) + +(define-syntax (module-begin stx) + (syntax-case stx (library) + [(_ (library . rest)) + ;; A library + (with-syntax ([(_ orig) stx]) + #'(#%plain-module-begin + (library-body orig . rest)))] + [(_ (library . rest) . _) + (raise-syntax-error + #f + "allowed only in as a top-level module by itself" + (syntax-case stx () + [(_ lib . _) #'lib]))] + [(_ decl . rest) + (syntax-list? #'rest) + ;; A top-level program + #'(#%plain-module-begin + (top-level-body decl . rest))] + [(_) + (raise-syntax-error 'r6rs + (string-append + "must contain a `library' form (for a library)" + " or start with `import' (for a top-level program)") + stx)] + [_ + (raise-syntax-error #f "ill-formed module (misuse of `.')" stx)])) + +(define-syntax (top-level-body stx) + (syntax-case stx (import) + [(_ (import . im) . rest) + (syntax-list? #'im) + (with-syntax ([(_ im . _) stx]) + #'(begin + (r6rs-import im) + . rest))] + [(_ (import . im) . rest) + (raise-syntax-error #f + "ill-formed imports (misuse of `.')" + (syntax-case stx () + [(_ im . _) #'im]))] + [(_ thing . _) + (raise-syntax-error 'top-level-program + "expected an `import' declaration, found something else" + #'thing)])) + +(define-for-syntax (symbolic-identifier=? a b) + (eq? (syntax-e a) (syntax-e b))) + +(define-for-syntax (valid-library-name? n) + (syntax-case n () + [(id id2 ... (vers ...)) + (and (identifier? #'id) + (andmap identifier? (syntax->list #'(id2 ...))) + (andmap (lambda (v) + (exact-nonnegative-integer? (syntax-e v))) + (syntax->list #'(vers ...))))] + [(id id2 ...) + (valid-library-name? #'(id id2 ... ()))] + [_ #f])) + +(define-syntax (library-body stx) + (syntax-case* stx (export import) symbolic-identifier=? + [(_ orig name + (export . ex) + (import . im) + . body) + (and (valid-library-name? #'name) + (syntax-list? #'ex) + (syntax-list? #'im) + (syntax-list? #'body)) + (with-syntax ([(_ _ _ ex im . _) stx]) + #'(begin + (r6rs-import im) + (r6rs-export ex) + (library-body/defns . body)))] + [(_ orig name . _) + (not (valid-library-name? #'name)) + (raise-syntax-error #f + "invalid library name" + #'orig + #'name)] + [(_ orig name (export . ex) . _) + (not (syntax-list? #'ex)) + (raise-syntax-error #f + "ill-formed export sequence (misuse of `.')" + #'orig + (syntax-case stx () + [(_ _ _ ex . _) #'ex]))] + [(_ orig name (export . ex) (import . im) . _) + (not (syntax-list? #'im)) + (raise-syntax-error #f + "ill-formed import sequence (misuse of `.')" + #'orig + (syntax-case stx () + [(_ _ _ _ im . _) #'im]))] + [(_ orig name (export . ex) (import . im) . _) + (raise-syntax-error #f + "ill-formed body (misuse of `.')" + #'orig)] + [(_ orig name (export . ex)) + (raise-syntax-error #f + "missing `import' clause" + #'orig)] + [(_ orig name (export . ex) im . _) + (raise-syntax-error #f + "expected `import' clause, found something else" + #'orig + #'im)] + [(_ orig name) + (raise-syntax-error #f + "missing `export' and `import' clauses" + #'orig)] + [(_ orig name ex . _) + (raise-syntax-error #f + "expected `export' clause, found something else" + #'orig + #'ex)] + [(_ orig) + (raise-syntax-error #f + "missing name, `export' clauses, and `import' clause" + #'orig)] + [(_ orig . _) + (raise-syntax-error #f + "ill-formed library" + #'orig)])) + +(define-syntax (library-body/defns stx) + (syntax-case stx () + [(_ thing . more) + (let ([a (local-expand + #'thing + 'module + (kernel-form-identifier-list))]) + (syntax-case a (begin) + [(def . _) + (ormap (lambda (id) + (free-identifier=? id #'def)) + (list #'define-values + #'define-syntaxes + #'define-values-for-syntax)) + #`(begin #,a (library-body/defns . more))] + [(begin sub ...) + #`(library-body/defns sub ... . more)] + [else + #`(begin (let () #,a . more))]))] + [(_) #'(begin)])) + +;; ---------------------------------------- +;; Imports and exports + +(define-for-syntax (is-sub-version-reference? stx) + (syntax-case* stx (<= >= and or not) symbolic-identifier=? + [n (exact-nonnegative-integer? (syntax-e #'n)) #t] + [(>= n) (exact-nonnegative-integer? (syntax-e #'n))] + [(<= n) (exact-nonnegative-integer? (syntax-e #'n))] + [(and sv ...) (andmap is-sub-version-reference? (syntax->list #'(sv ...)))] + [(or sv ...) (andmap is-sub-version-reference? (syntax->list #'(sv ...)))] + [(not sv) (is-sub-version-reference? #'sv)] + [_ #f])) + +(define-for-syntax (is-version-reference? stx) + (syntax-case* stx (and or not) symbolic-identifier=? + [(and vr ...) + (andmap is-version-reference? (syntax->list #'(vr ...)))] + [(or vr ...) + (andmap is-version-reference? (syntax->list #'(vr ...)))] + [(not vr) + (is-version-reference? #'vr)] + [(sv ...) + (andmap is-sub-version-reference? (syntax->list #'(sv ...)))] + [_ #f])) + +(define-for-syntax (parse-library-reference orig stx) + (syntax-case stx () + [(id1 id2 ... (vers ...)) + (and (identifier? #'id1) + (andmap identifier? (syntax->list #'(id2 ...))) + (is-version-reference? #'(vers ...))) + (let-values ([(coll file) + (let ([strs (map (lambda (id) + (symbol->string (syntax-e id))) + (syntax->list #'(id1 id2 ...)))]) + (if (= 1 (length strs)) + (values (car strs) "main") + (values (reverse (cdr (reverse strs))) + (car (reverse strs)))))]) + (let ([base (build-path (with-handlers ([exn:fail? + (lambda (exn) + (raise-syntax-error + #f + (format + "cannot find suitable library installed (exception: ~a)" + (if (exn? exn) + (exn-message exn) + exn)) + orig + stx))]) + (apply collection-path coll)) + file)]) + (let ([vers (find-version (path->bytes base) (syntax->datum #'(vers ...)))]) + (if vers + (datum->syntax + stx + `(,#'lib ,(apply string-append + (car coll) + (append + (map (lambda (s) + (string-append "/" s)) + (append (cdr coll) (list file))) + (map (lambda (v) + (format "-~a" v)) + vers) + (list ".ss"))))) + (raise-syntax-error + #f + "cannot find suitable installed library" + orig + stx)))))] + [(id1 id2 ...) + (and (identifier? #'id1) + (andmap identifier? (syntax->list #'(id2 ...)))) + (parse-library-reference orig #'(id1 id2 ... ()))] + [_ + (raise-syntax-error #f + "ill-formed library reference" + orig + stx)])) + +(define-for-syntax (parse-import-set orig stx) + (define (bad) + (raise-syntax-error #f + (format "bad `~a' form" + (syntax-e (car (syntax-e stx)))) + orig + stx)) + (define (check-id id) + (unless (identifier? id) + (raise-syntax-error #f + (format "not an identifier in `~a' form" + (syntax-e (car (syntax-e stx)))) + orig + id))) + (syntax-case* stx (library only except prefix rename) symbolic-identifier=? + [(library lib) + (parse-library-reference orig #'lib)] + [(library . _) (bad)] + [(only im id ...) + (for-each check-id (syntax->list #'(id ...))) + #`(only-in #,(parse-import-set orig #'im) id ...)] + [(only . _) (bad)] + [(except im id ...) + (for-each check-id (syntax->list #'(id ...))) + #`(except-in #,(parse-import-set orig #'im) id ...)] + [(except . _) (bad)] + [(prefix im id) + (check-id #'id) + #`(prefix-in id #,(parse-import-set orig #'im))] + [(prefix . _) (bad)] + [(rename im (id id2) ...) + (for-each check-id + (apply + append + (map syntax->list + (syntax->list #'((id id2) ...))))) + #`(except-in #,(parse-import-set orig #'im) id ...)] + [(rename . _) (bad)] + [_ (parse-library-reference orig stx)])) + +(define-syntax (r6rs-import stx) + (let ([orig (syntax-case stx () + [(_ orig) #'orig])]) + (syntax-case stx (import) + [(_ (import im ...)) + (with-syntax ([((im ...) ...) + (map (lambda (im) + (syntax-case* im (for) symbolic-identifier=? + [(for base-im level ...) + (let* ([levels + (map (lambda (level) + (syntax-case* level (run expand meta) symbolic-identifier=? + [run #'except-in] + [expand #'for-syntax] + [(meta 0) #'except-in] + [(meta 1) #'for-syntax] + [(meta -1) #'for-template] + [(meta n) + (raise-syntax-error + #f + "meta level not supported" + orig + level)] + [_ + (raise-syntax-error + #f + "bad `for' level" + orig + level)])) + (syntax->list #'(level ...)))]) + (with-syntax ([is (parse-import-set orig #'im)] + [(level ...) (if (null? levels) + (list #'only-in) + null)]) + + #`((level is) ...)))] + [(for . _) + (raise-syntax-error + #f + "bad `for' import form" + orig + im)] + [_ (list (parse-import-set orig im))])) + (syntax->list #'(im ...)))]) + #'(require im ... ...))]))) + +(define-syntax (r6rs-export stx) + (let ([orig (syntax-case stx () + [(_ orig) #'orig])]) + (syntax-case stx (export) + [(_ (export ex ...)) + (let ([exs (syntax->list #'(ex ...))]) + (for-each (lambda (ex) + (unless (identifier? ex) + (syntax-case ex (rename) + [(rename thing ...) + (for-each (lambda (thing) + (syntax-case thing () + [(id1 id2) + (unless (and (identifier? #'id1) + (identifier? #'id2)) + (raise-syntax-error #f + "not an identifier" + orig + (if (identifier? #'id1) + #'id2 + #'id1)))] + [_ (raise-syntax-error + #f + "expected `( )' for rename, but found something else" + orig + thing)])) + (syntax->list #'(thing ...)))] + [(rename . _) + (raise-syntax-error #f + "bad syntax (misuse of `.')" + orig + ex)] + [_ + (raise-syntax-error #f + "not an identifier or `rename' clause" + orig + ex)]))) + exs) + (with-syntax ([(ex ...) + (map (lambda (ex) + (syntax-case ex () + [(rename . rest) + #'(rename-out . rest)] + [_ ex])) + exs)]) + #'(provide ex ...)))]))) diff --git a/collects/r6rs/private/find-version.ss b/collects/r6rs/private/find-version.ss new file mode 100644 index 0000000000..33801c31ce --- /dev/null +++ b/collects/r6rs/private/find-version.ss @@ -0,0 +1,77 @@ +#lang scheme/base + +(provide find-version) + +(define (find-version base-path vers) + (let-values ([(dir name _) (split-path (bytes->path base-path))]) + (let ([files (with-handlers ([exn:fail:filesystem? (lambda (exn) null)]) + (directory-list dir))]) + (and files + (let* ([p (path-element->bytes name)] + [len (bytes-length p)] + [candidate-versions + (filter + values + (map + (lambda (file) + (let ([s (path-element->bytes file)]) + (and (len . < . (bytes-length s)) + (regexp-match? #rx#"[.]ss$" s) + (bytes=? p (subbytes s 0 len)) + (or (and (= (bytes-length s) (+ len 3)) + null) + (let ([vers (subbytes s len (- (bytes-length s) 3))]) + (and (regexp-match #rx#"^(-[0-9]+)+$" vers) + (map string->number + (cdr + (map bytes->string/latin-1 + (regexp-split #rx#"-" vers)))))))))) + files))] + [versions + (sort candidate-versions + (lambda (a b) + (let loop ([a a][b b]) + (cond + [(null? a) #t] + [(null? b) #f] + [(> (car a) (car b)) #t] + [(< (car a) (car b)) #f] + [else (loop (cdr a) (cdr b))]))))]) + (ormap (lambda (candidate-version) + (and (version-match? candidate-version vers) + candidate-version)) + versions)))))) + +(define (version-match? cand vers) + (cond + [(null? vers) #t] + [(null? cand) #f] + [(eq? (car vers) 'and) + (andmap (lambda (v) + (version-match? cand v)) + (cdr vers))] + [(eq? (car vers) 'or) + (ormap (lambda (v) + (version-match? cand v)) + (cdr vers))] + [(eq? (car vers) 'not) + (not (version-match? (cadr vers)))] + [(sub-version-match? (car cand) (car vers)) + (version-match? (cdr cand) (cdr vers))] + [else #f])) + +(define (sub-version-match? cand subvers) + (cond + [(number? subvers) (= cand subvers)] + [else (case (car subvers) + [(>=) (>= cand (cadr subvers))] + [(<=) (<= cand (cadr subvers))] + [(and) (andmap (lambda (sv) + (sub-version-match? cand sv)) + (cdr subvers))] + [(or) (ormap (lambda (sv) + (sub-version-match? cand sv)) + (cdr subvers))] + [(not) (not (sub-version-match? cand (cadr subvers)))] + [else (error "bad subversion")])])) + diff --git a/collects/rnrs/base-6.ss b/collects/rnrs/base-6.ss new file mode 100644 index 0000000000..100bc107fb --- /dev/null +++ b/collects/rnrs/base-6.ss @@ -0,0 +1,22 @@ +#lang scheme/base + +(require (for-syntax scheme/base) + (prefix-in r5rs: r5rs)) + +(provide (rename-out [datum #%datum] + [r5rs:define define] + [r5rs:lambda lambda]) + #%app) + +;; ---------------------------------------- +;; Datum + +(define-syntax (datum stx) + (syntax-case stx () + [(_ . thing) + (if (vector? (syntax-e #'thing)) + (raise-syntax-error 'r6rs + "a vector is not an expression" + #'thing) + #`(quote thing))])) + diff --git a/collects/rnrs/info.ss b/collects/rnrs/info.ss new file mode 100644 index 0000000000..c14a2ca411 --- /dev/null +++ b/collects/rnrs/info.ss @@ -0,0 +1 @@ +#lang setup/infotab