;; TEST code for read2-dev (import (scheme base) (scheme write) (scheme char)) ; (inline ; in-port:get-cnum ; in-port:get-lnum ; in-port:get-buf ; ) (define read cyc-read) ;; Extended information for each input port (define *in-port-table* '()) (define (reg-port fp) (let ((r (assoc fp *in-port-table*))) (cond ((not r) ;(write `(ADDED NEW ENTRY TO in port table!!)) (set! r (list fp #f ; Buffered char, if any 1 ; Line number 0)) ; Char number (set! *in-port-table* (cons r *in-port-table*)) r) (else r)))) ;; TODO: unreg-port - delete fp entry from *in-port-table* ;; would want to do this when port is closed (define (in-port:get-buf ptbl) (cadr ptbl)) (define (in-port:get-lnum ptbl) (caddr ptbl)) (define (in-port:get-cnum ptbl) (cadddr ptbl)) ;(define in-port:get-buf cadr) ;(define in-port:get-lnum caddr) ;(define in-port:get-cnum cadddr) (define (in-port:set-buf! ptbl buf) (set-car! (cdr ptbl) buf)) (define (in-port:set-lnum! ptbl lnum) (set-car! (cddr ptbl) lnum)) (define (in-port:set-cnum! ptbl cnum) (set-car! (cdddr ptbl) cnum)) ;(define-syntax in-port:set-buf! ; (er-macro-transformer ; (lambda (e r c) ; `(set-car! (cdr ,(cadr e)) ,(caddr e))))) ;(define-syntax in-port:set-lnum! ; (er-macro-transformer ; (lambda (e r c) ; `(set-car! (cddr ,(cadr e)) ,(caddr e))))) ;(define-syntax in-port:set-cnum! ; (er-macro-transformer ; (lambda (e r c) ; `(set-car! (cdddr ,(cadr e)) ,(caddr e))))) (define (in-port:read-buf! ptbl) (let ((result (cadr ptbl))) (in-port:set-buf! ptbl #f) result)) ;; END input port table ;; Helper functions (define (add-tok tok toks) (cons tok toks)) ;; Get completed list of tokens (define (get-toks tok toks) (if (null? tok) toks (add-tok (->tok tok) toks))) ;; Add a token to the list, quoting it if necessary (define (->tok lst) (parse-atom (reverse lst))) ;; Did we read a dotted list (define (dotted? lst) (and (> (length lst) 2) (equal? (cadr (reverse lst)) (string->symbol ".")))) ;; Convert a list read by the reader into an improper list (define (->dotted-list lst) (cond ((null? lst) '()) ((equal? (car lst) (string->symbol ".")) (cadr lst)) (else (cons (car lst) (->dotted-list (cdr lst)))))) (define (parse-error msg lnum cnum) (error (string-append "Error (line " (number->string lnum) ", char " (number->string cnum) "): " msg))) ;; Add finished token, if there is one, and continue parsing (define (parse/tok fp tok toks all? comment? parens ptbl curr-char) (cond ((null? tok) (parse fp '() toks all? comment? parens ptbl)) (all? (parse fp '() (add-tok (->tok tok) toks) all? comment? parens ptbl)) (else ;; Reached a terminating char, return current token and ;; save term char for the next (read). ;; Note: never call set-buf! if in "all?" mode, since ;; that mode builds a list of tokens (in-port:set-buf! ptbl curr-char) ;(write `(DEBUG ,tok ,ptbl)) ;(write "\n") (car (add-tok (->tok tok) toks))))) ;; Parse input from stream ;; ;; Input: ;; - Port object ;; - Current token ;; - List of tokens read (if applicable) ;; - Bool - Read-all mode, or just read the next object? ;; - Bool - Are we inside a comment? ;; - Level of nested parentheses ;; - Entry in the in-port table for this port ;; ;; Output: next object, or list of objects (if read-all mode) ;; (define (parse fp tok toks all? comment? parens ptbl) (in-port:set-cnum! ptbl (+ 1 (in-port:get-cnum ptbl))) (let ((c (if (in-port:get-buf ptbl) (in-port:read-buf! ptbl) ;; Already buffered (read-char fp)))) ;; DEBUGGING ;(write `(DEBUG read ,tok ,c)) ;(write (newline)) ;; END DEBUG (cond ((eof-object? c) (if (> parens 0) (parse-error "missing closing parenthesis" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (if all? (reverse (get-toks tok toks)) (let ((last (get-toks tok toks))) (if (> (length last) 0) (car last) c)))) ;; EOF (comment? (if (eq? c #\newline) (begin (in-port:set-lnum! ptbl (+ 1 (in-port:get-lnum ptbl))) (in-port:set-cnum! ptbl 0) (parse fp '() toks all? #f parens ptbl)) (parse fp '() toks all? #t parens ptbl))) ((eq? c #\newline) (in-port:set-lnum! ptbl (+ 1 (in-port:get-lnum ptbl))) (in-port:set-cnum! ptbl 0) (parse/tok fp tok toks all? #f parens ptbl c)) ((char-whitespace? c) (parse/tok fp tok toks all? #f parens ptbl c)) ((eq? c #\;) (parse/tok fp tok toks all? #t parens ptbl c)) ((eq? c #\') (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token ;; TODO: would also need to do this if previous char was ;; not a quote! ;; EG: 'a'b ==> (quote a) (quote b), NOT (quote (quote b)) (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else ;; Read the next expression and wrap it in a quote (let ((sub (parse fp '() '() #f ;all? #f ;comment? 0 ;parens ptbl))) (define new-toks (add-tok (list 'quote sub) ;(if (and (pair? sub) (dotted? sub)) ; (->dotted-list sub) ; sub)) (get-toks tok toks))) ;; Keep going (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))))) ((eq? c #\`) ;; TODO: should consolidate this with above (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else ;; Read the next expression and wrap it in a quote (let ((sub (parse fp '() '() #f #f 0 ptbl))) (define new-toks (add-tok (list 'quasiquote sub) (get-toks tok toks))) ;; Keep going (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))))) ((eq? c #\,) (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else ;; TODO: ; buffer must be empty now since it is only 1 char, so ; call read-char. then: ; - @ - unquote-splicing processing ; - eof - error ; - otherwise, add char back to buffer and do unquote processing ;; Read the next expression and wrap it in a quote (letrec ((sub #f) (next-c (read-char fp)) (unquote-sym (if (equal? next-c #\@) 'unquote-splicing 'unquote)) (new-toks #f)) ;; Buffer read-ahead char, if unused (cond ((eof-object? next-c) (parse-error "unexpected end of file" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) ((not (equal? next-c #\@)) (in-port:set-buf! ptbl next-c)) (else #f)) (set! sub (parse fp '() '() #f #f 0 ptbl)) (set! new-toks (add-tok (list unquote-sym sub) (get-toks tok toks))) ;; Keep going (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))))) ((eq? c #\() (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else (let ((sub ;(_cyc-read-all fp (+ parens 1))) (parse fp '() '() #t #f (+ parens 1) ptbl)) (toks* (get-toks tok toks))) (define new-toks (add-tok (if (and (pair? sub) (dotted? sub)) (->dotted-list sub) sub) toks*)) ;(write `(DEBUG incrementing paren level ,parens ,sub)) (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))))) ((eq? c #\)) (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) ((= parens 0) (parse-error "unexpected closing parenthesis" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (else (reverse (get-toks tok toks))))) ((eq? c #\") (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else (let ((str (read-str fp '() ptbl)) (toks* (get-toks tok toks))) (define new-toks (add-tok str toks*)) (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))))) ((eq? c #\#) (if (null? tok) ;; # reader (let ((next-c (read-char fp))) (in-port:set-cnum! ptbl (+ 1 (in-port:get-cnum ptbl))) (cond ;; Block comments ((eq? #\| next-c) (read-block-comment fp ptbl) (parse fp '() toks all? #f parens ptbl)) ;; Booleans ;; Do not use add-tok below, no need to quote a bool ((eq? #\t next-c) ;; read in rest of #true if it is there (when (eq? #\r (peek-char fp)) (if (not (and (eq? #\r (read-char fp)) (eq? #\u (read-char fp)) (eq? #\e (read-char fp)))) (parse-error "Invalid syntax for boolean true" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)))) (if all? (parse fp '() (cons #t toks) all? #f parens ptbl) #t)) ((eq? #\f next-c) ;; read in rest of #false if it is there (when (eq? #\a (peek-char fp)) (if (not (and (eq? #\a (read-char fp)) (eq? #\l (read-char fp)) (eq? #\s (read-char fp)) (eq? #\e (read-char fp)))) (parse-error "Invalid syntax for boolean false" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)))) (if all? (parse fp '() (cons #f toks) all? #f parens ptbl) #f)) ;; Numbers ((eq? #\e next-c) (parse-number fp toks all? parens ptbl 10 (lambda (num) (exact (string->number (list->string num)))))) ((eq? #\i next-c) (parse-number fp toks all? parens ptbl 10 (lambda (num) (inexact (string->number (list->string num)))))) ((eq? #\b next-c) (parse-number fp toks all? parens ptbl 2 (lambda (num) (string->number (list->string num) 2)))) ((eq? #\o next-c) (parse-number fp toks all? parens ptbl 8 (lambda (num) (string->number (list->string num) 8)))) ((eq? #\x next-c) (parse-number fp toks all? parens ptbl 16 (lambda (num) (string->number (list->string num) 16)))) ;; Bytevector ((eq? #\u next-c) (set! next-c (read-char fp)) (if (not (eq? #\8 next-c)) (parse-error "Unhandled input sequence" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (set! next-c (read-char fp)) (if (not (eq? #\( next-c)) (parse-error "Unhandled input sequence" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (let ((sub (parse fp '() '() #t #f (+ parens 1) ptbl)) (toks* (get-toks tok toks))) (define new-toks (add-tok (if (and (pair? sub) (dotted? sub)) (parse-error "Invalid vector syntax" ;(->dotted-list sub) (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)) (apply bytevector sub)) toks*)) (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))) ;; Vector ((eq? #\( next-c) (let ((sub (parse fp '() '() #t #f (+ parens 1) ptbl)) (toks* (get-toks tok toks))) (define new-toks (add-tok (if (and (pair? sub) (dotted? sub)) (parse-error "Invalid vector syntax" ;(->dotted-list sub) (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)) (list->vector sub)) toks*)) (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))) ;; Character ((eq? #\\ next-c) (let ((new-toks (cons (read-pound fp ptbl) toks))) (if all? (parse fp '() new-toks all? #f parens ptbl) (car new-toks)))) ;; Datum comment ((eq? #\; next-c) ; Read and discard next datum (parse fp '() '() #f #f 0 ptbl) (cond ((and (not all?) (not (null? tok))) ;; Reached a terminal char, read out previous token (in-port:set-buf! ptbl c) (car (add-tok (->tok tok) toks))) (else (parse fp tok toks all? #f parens ptbl)))) (else (parse-error "Unhandled input sequence" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))))) ;; just another char... (parse fp (cons c tok) toks all? #f parens ptbl))) ((eq? c #\|) (parse-literal-identifier fp toks all? parens ptbl)) (else (parse fp (cons c tok) toks all? #f parens ptbl))))) ;; Read chars past a leading #\ (define (read-pound fp ptbl) (define (done raw-buf) (let ((buf (reverse raw-buf))) (cond ((= 0 (length buf)) (parse-error "missing character" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) ((= 1 (length buf)) (car buf)) ((equal? buf '(#\a #\l #\a #\r #\m)) (integer->char 7)) ((equal? buf '(#\b #\a #\c #\k #\s #\p #\a #\c #\e)) (integer->char 8)) ((equal? buf '(#\d #\e #\l #\e #\t #\e)) (integer->char 127)) ((equal? buf '(#\e #\s #\c #\a #\p #\e)) (integer->char 27)) ((equal? buf '(#\n #\e #\w #\l #\i #\n #\e)) (integer->char 10)) ((equal? buf '(#\n #\u #\l #\l)) (integer->char 0)) ((equal? buf '(#\r #\e #\t #\u #\r #\n)) (integer->char 13)) ((equal? buf '(#\s #\p #\a #\c #\e)) (integer->char 32)) ((equal? buf '(#\t #\a #\b)) (integer->char 9)) ((and (> (length buf) 1) (equal? (car buf) #\x)) (integer->char (string->number (list->string (cdr buf)) 16))) (else (parse-error (string-append "unable to parse character: " (list->string buf)) (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)))))) (define (loop buf) (let ((c (peek-char fp))) (cond ((or (eof-object? c) (and (char-whitespace? c) (> (length buf) 0)) (and (> (length buf) 0) (equal? c #\)))) (done buf)) (else (loop (cons (read-char fp) buf)))))) (loop '())) (define (read-hex-scalar-value fp ptbl) (define (done buf) (cond ((= 0 (length buf)) (parse-error "missing character" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (else (integer->char (string->number (list->string buf) 16))))) (define (loop buf) (let ((c (read-char fp))) (cond ((or (eof-object? c) (eq? #\; c)) (done (reverse buf))) (else (loop (cons c buf)))))) (loop '())) (define (read-str fp buf ptbl) (let ((c (read-char fp))) (cond ((eof-object? c) (parse-error "missing closing double-quote" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) ((equal? #\\ c) (read-str fp (read-str-esc fp buf ptbl) ptbl)) ((equal? #\" c) (list->string (reverse buf))) (else (read-str fp (cons c buf) ptbl))))) ;; Read an escaped character within a string ;; The escape '\' has already been read at this point (define (read-str-esc fp buf ptbl) (let ((c (read-char fp))) (cond ((eof-object? c) (parse-error "missing escaped character within string" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) ((or (equal? #\" c) (equal? #\' c) (equal? #\? c) (equal? #\| c) (equal? #\\ c)) (cons c buf)) ((equal? #\a c) (cons #\alarm buf)) ((equal? #\b c) (cons #\backspace buf)) ((equal? #\n c) (cons #\newline buf)) ((equal? #\r c) (cons #\return buf)) ((equal? #\t c) (cons #\tab buf)) ((equal? #\x c) (cons (read-hex-scalar-value fp ptbl) buf)) (else (parse-error (string-append "invalid escape character [" (list->string (list c)) "] in string") (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)))))) (define (sign? c) (or (equal? c #\+) (equal? c #\-))) ;; token-numeric? -> [chars] -> boolean (define (token-numeric? a) (or (char-numeric? (car a)) (and (> (length a) 1) (eq? #\. (car a)) (char-numeric? (cadr a))) (and (> (length a) 1) (or (char-numeric? (cadr a)) (eq? #\. (cadr a))) (sign? (car a))))) ;; parse-atom -> [chars] -> literal (define (parse-atom a) (if (token-numeric? a) (string->number (list->string a)) (if (or (equal? a '(#\+ #\i #\n #\f #\. #\0)) (equal? a '(#\- #\i #\n #\f #\. #\0))) (expt 2.0 1000000) (if (or (equal? a '(#\+ #\n #\a #\n #\. #\0)) (equal? a '(#\- #\n #\a #\n #\. #\0))) (/ 0.0 0.0) (string->symbol (list->string a)))))) ;;;;; ;; Read next character from port, using buffered char if available (define (get-next-char fp ptbl) (if (in-port:get-buf ptbl) (in-port:read-buf! ptbl) ;; Already buffered (read-char fp))) ;; Read chars in the middle of a block comment (define (read-block-comment fp ptbl) (let ((c (get-next-char fp ptbl))) (cond ((eq? #\| c) (read-block-terminator fp ptbl)) (else (read-block-comment fp ptbl))))) ;; Read (possibly) the end of a block comment (define (read-block-terminator fp ptbl) (let ((c (get-next-char fp ptbl))) (cond ((eq? #\# c) #t) ((eq? #\| c) (read-block-terminator fp ptbl)) (else (read-block-comment fp ptbl))))) ;; Parse literal identifier encountered within pipes (define (parse-literal-identifier fp toks all? parens ptbl) (let ((sym (parse-li-rec fp '() ptbl))) (if all? (parse fp '() (cons sym toks) all? #f parens ptbl) sym))) ;; Helper for parse-literal-identifier (define (parse-li-rec fp tok ptbl) (let ((c (get-next-char fp ptbl)) (next (lambda (c) (parse-li-rec fp (cons c tok) ptbl)))) (cond ((eq? #\| c) (let ((str (if (null? tok) "" (list->string (reverse tok))))) (string->symbol str))) ((eof-object? c) (parse-error "EOF encountered parsing literal identifier" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (else (next c))))) (define (parse-number fp toks all? parens ptbl base tok->num) ; (parse-number-rec base fp '() ptbl)) (let ((num (parse-number-rec base fp '() ptbl))) ;(write `(DEBUG2 ,num ,(string? num))) (cond ((and (not (null? num)) (or (token-numeric? num) (and (> (length num) 0) (= base 16) (hex-digit? (car num))))) (let ((result (tok->num num))) (if all? (parse fp '() (cons result toks) all? #f parens ptbl) result))) (else (parse-error "Illegal number syntax" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl)))))) (define (parse-number-rec base fp tok ptbl) (let ((c (get-next-char fp ptbl)) (next (lambda (c) (parse-number-rec base fp (cons c tok) ptbl)))) (cond ((sign? c) (next c)) ((eq? #\. c) (next c)) ((char-numeric? c) (if (or (and (= base 2) (char>? c #\1)) (and (= base 8) (char>? c #\7))) (parse-error "Illegal digit" (in-port:get-lnum ptbl) (in-port:get-cnum ptbl))) (next c)) ((and (= base 16) (hex-digit? c)) (next c)) (else ;; We are done parsing a number (in-port:set-buf! ptbl c) ;; rebuffer unprocessed char (reverse tok))))) ;; Return token (define (hex-digit? c) (or (and (char>=? c #\a) (char<=? c #\f)) (and (char>=? c #\A) (char<=? c #\F)))) ;;;;; ;; Main lexer/parser (define cyc-read (lambda args (let ((fp (if (null? args) (current-input-port) (car args)))) (parse fp '() '() #f #f 0 (reg-port fp))))) ; ;; Test code ; ;(let ((fp (open-input-file "tests/begin.scm"))) ; ;(let ((fp (open-input-file "tests/strings.scm"))) ; (let ((fp (open-input-file "test.scm"))) ; (let ((fp (open-input-file "tests/unit-tests.scm"))) ; (write (read-all fp))) ;(define (repl) ; (let ((fp (current-input-port))) ; (write (cyc-read fp))) ; (repl)) ;(repl) (define-c reading-from-file? "(void *data, int argc, closure _, object k, object port)" " object result = boolean_f; Cyc_check_port(data, port); if (((port_type *)port)->flags == 1) { result = boolean_t; } return_closcall1(data, k, result);") (define-c read-token "(void *data, int argc, closure _, object k, object port)" " Cyc_io_read_token(data, k, port);") (define-c Cyc-opaque-eq? "(void *data, int argc, closure _, object k, object opq, object obj)" " if (Cyc_is_opaque(opq) == boolean_f) return_closcall1(data, k, boolean_f); return_closcall1(data, k, equalp( opaque_ptr(opq), obj ));") (define-c Cyc-opaque-unsafe-eq? "(void *data, int argc, closure _, object k, object opq, object obj)" " return_closcall1(data, k, equalp( opaque_ptr(opq), obj ));") (write ;(call-parse2 (open-input-file "scheme/cyclone/cgen.sld"))) (call-parse2 (open-input-file "test.out"))) ;(read-token (open-input-file "test.scm"))) ;TODO: getting there, but still not parsed correctly: ;(eq? c #\)) ;; Notes on writing a fast parser: ; - Interface to the user is (read). This needs to be fast ; - Could read input a line at a time, but then need to buffer in the port_type object ; - Thinking fread would be most efficient ; - Port would need an array, size (could be known instead of storing), and current index ; - Need a way to indicate EOF ; - One drawback - will not integrate nicely with other I/O on same port (read-char, read-line, etc) until those functions are updated to work with buffered I/O ; ; - Shouldn't chars for comments be immediately read? Not sure why existing code attempts to pass state ; - Not sure if we need the (all?) var. Can't we just loop and quit when closing paren is seen? ; ; - could main parsing code be written in C? this could save a lot of time ; maybe have a scheme layer to handle nested parens (anything else?) and call C to ; get the next token (define cyc-read2 (lambda args (let ((fp (if (null? args) (current-input-port) (car args)))) ;(parse fp '() '() #f #f 0 (reg-port fp)) ;; TODO: anything else? will track line/char nums within the C code (parse2 fp) ))) (define (call-parse2 fp) (let ((result (parse2 fp))) (if (Cyc-opaque? result) (error "unexpected closing parenthesis") result))) (define (parse2 fp) (let ((token (read-token fp))) ;; TODO: this will be a C call ;(write `(token ,token)) (cond ((Cyc-opaque? token) (cond ;; Open paren, start read loop ((Cyc-opaque-unsafe-eq? token #\() (let loop ((lis '()) (t (parse2 fp))) (cond ((eof-object? t) (error "missing closing parenthesis")) ((Cyc-opaque-eq? t #\)) (if (and (> (length lis) 2) (equal? (cadr lis) (string->symbol "."))) (->dotted-list (reverse lis)) (reverse lis))) (else (loop (cons t lis) (parse2 fp)))))) ((Cyc-opaque-unsafe-eq? token #\') (list 'quote (parse2 fp))) ((Cyc-opaque-unsafe-eq? token #\`) (list 'quasiquote (parse2 fp))) ((Cyc-opaque-unsafe-eq? token #\,) (list 'unquote (parse2 fp))) (else token))) ;; TODO: error if this is returned to original caller of parse2 ((vector? token) (cond ((= (vector-length token) 2) ;; Special case: number (string->number (vector-ref token 0) (vector-ref token 1))) ((= (vector-length token) 3) ;; Special case: exact/inexact number (if (vector-ref token 2) (exact (string->number (vector-ref token 0) (vector-ref token 1))) (inexact (string->number (vector-ref token 0) (vector-ref token 1))))) ((= (vector-length token) 1) ;; Special case: error (error (vector-ref token 0))) (else (let loop ((lis '()) (t (parse2 fp))) (cond ((eof-object? t) (error "missing closing parenthesis")) ((Cyc-opaque-eq? t #\)) (list->vector (reverse lis))) (else (loop (cons t lis) (parse2 fp)))))))) ((bytevector? token) (let loop ((lis '()) (t (parse2 fp))) (cond ((eof-object? t) (error "missing closing parenthesis")) ((Cyc-opaque-eq? t #\)) (apply bytevector (reverse lis))) (else (loop (cons t lis) (parse2 fp)))))) ((eq? token (string->symbol ",@")) (list 'unquote-splicing (parse2 fp))) ((eq? token (string->symbol "#;")) (parse2 fp) ;; Ignore next datum (parse2 fp)) ;; Other special cases? (else token))))