peek-{char,byte}-or-special: add 'special option

The old reader used an internal option to short-circuit special-value
reading when a special value acts as a terminating "character". Expose
that shortcut by allowing 'special as an argument to
`peek-{char,byte}-or-special`, and update the reader to use it.
This commit is contained in:
Matthew Flatt 2018-02-27 16:46:21 -07:00
parent 613de748df
commit 4e1e91a34e
10 changed files with 2411 additions and 2179 deletions

View File

@ -410,20 +410,39 @@ character.}
@defproc[(peek-char-or-special [in input-port? (current-input-port)] @defproc[(peek-char-or-special [in input-port? (current-input-port)]
[skip-bytes-amt exact-nonnegative-integer? 0] [skip-bytes-amt exact-nonnegative-integer? 0]
[special-wrap (or/c (any/c -> any/c) #f) #f] [special-wrap (or/c (any/c -> any/c) #f 'special) #f]
[source-name any/c #f]) [source-name any/c #f])
(or/c char? eof-object? any/c)]{ (or/c char? eof-object? any/c)]{
Like @racket[peek-char], but if the input port returns a non-byte Like @racket[peek-char], but if the input port returns a non-byte
value after @racket[skip-bytes-amt] byte positions, then it is returned. value after @racket[skip-bytes-amt] byte positions, then the result
depends on @racket[special-wrap]:
@itemlist[
@item{If @racket[special-wrap] is @racket[#f], then the special value
is returned (as for @racket[read-char-or-special]).}
@item{If @racket[special-wrap] is a procedure, then it is applied the
special value to produce the result (as for
@racket[read-char-or-special]).}
@item{If @racket[special-wrap] is @racket['special], then
@racket['special] is returned in place of the special
value---without calling the special-value procedure that is
returned by the input-port implementation.}
]
@history[#:changed "6.8.0.2" @elem{Added the @racket[special-wrap] and @history[#:changed "6.8.0.2" @elem{Added the @racket[special-wrap] and
@racket[source-name] arguments.}]} @racket[source-name] arguments.}
#:changed "6.90.0.16" @elem{Added @racket['special] as an option
for @racket[special-wrap].}]}
@defproc[(peek-byte-or-special [in input-port? (current-input-port)] @defproc[(peek-byte-or-special [in input-port? (current-input-port)]
[skip-bytes-amt exact-nonnegative-integer? 0] [skip-bytes-amt exact-nonnegative-integer? 0]
[progress (or/c progress-evt? #f) #f] [progress (or/c progress-evt? #f) #f]
[special-wrap (or/c (any/c -> any/c) #f) #f] [special-wrap (or/c (any/c -> any/c) #f 'special) #f]
[source-name any/c #f]) [source-name any/c #f])
(or/c byte? eof-object? any/c)]{ (or/c byte? eof-object? any/c)]{
@ -432,7 +451,9 @@ instead of a character, and it supports a @racket[progress] argument
like @racket[peek-bytes-avail!]. like @racket[peek-bytes-avail!].
@history[#:changed "6.8.0.2" @elem{Added the @racket[special-wrap] and @history[#:changed "6.8.0.2" @elem{Added the @racket[special-wrap] and
@racket[source-name] arguments.}]} @racket[source-name] arguments.}
#:changed "6.90.0.16" @elem{Added @racket['special] as an option
for @racket[special-wrap].}]}
@defproc[(port-progress-evt [in (and/c input-port? port-provides-progress-evts?) @defproc[(port-progress-evt [in (and/c input-port? port-provides-progress-evts?)

View File

@ -368,6 +368,15 @@
(err/rt-test (read-char infinite-voids) exn:application:mismatch?) (err/rt-test (read-char infinite-voids) exn:application:mismatch?)
(test 'void read-byte-or-special infinite-voids) (test 'void read-byte-or-special infinite-voids)
(test 'void read-char-or-special infinite-voids) (test 'void read-char-or-special infinite-voids)
(test 'void peek-char-or-special infinite-voids 0)
(test 'special peek-char-or-special infinite-voids 0 'special)
(let ([p (make-input-port
'voids
(lambda (s) (lambda args 'void))
(lambda (skip s progress-evt) (lambda args (error "oops")))
void)])
(test 'special peek-char-or-special infinite-voids 0 'special)
(test 'void read-char-or-special infinite-voids))
(let ([go (let ([go
(lambda (get-avail!) (lambda (get-avail!)
(define (get) (define (get)

View File

@ -952,6 +952,49 @@
(test #\y read-char-or-special p) (test #\y read-char-or-special p)
(test 3 file-position p)) (test 3 file-position p))
;; Test read-char-or-special:
(let ([p (make-p (list #"x" a-special #"y") (lambda (x) 5) void)])
(test #\x peek-char-or-special p)
(test 0 file-position p)
(test #\x peek-char-or-special p 0)
(test a-special peek-char-or-special p 1)
(test #\y peek-char-or-special p 2)
(test 0 file-position p)
(test #\x read-char-or-special p)
(test 1 file-position p)
(test a-special peek-char-or-special p)
(test 1 file-position p)
(test a-special read-char-or-special p)
(test 2 file-position p)
(test #\y peek-char-or-special p)
(test 2 file-position p)
(test #\y read-char-or-special p)
(test 3 file-position p))
;; Reading somethign like a symbol should stop at a special
;; without calling the special-producing procedure:
(let* ([pos 0]
[p (make-input-port
'voids
(lambda (s)
(if (pos . < . 3)
(begin
(set! pos (add1 pos))
(bytes-set! s 0 (char->integer #\a))
1)
(lambda args (error "oops/read"))))
(lambda (s skip progress-evt)
(cond
[((+ skip pos) . < . 3)
(begin
(bytes-set! s 0 (char->integer #\a))
1)]
[((+ skip pos) . < . 4)
(lambda args (error "oops/peek"))]
[else eof-object]))
void)])
(test 'aaa read p))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test read-syntax offsets: ;; Test read-syntax offsets:

View File

@ -16,5 +16,9 @@
(define-inline (read-char/special in config [source (read-config-source config)]) (define-inline (read-char/special in config [source (read-config-source config)])
(read-char-or-special in special source)) (read-char-or-special in special source))
;; Returns `(special 'special)` for any special value:
(define-inline (peek-char/special in config [skip-count 0] [source (read-config-source config)]) (define-inline (peek-char/special in config [skip-count 0] [source (read-config-source config)])
(peek-char-or-special in skip-count special source)) (define c (peek-char-or-special in skip-count 'special source))
(if (eq? c 'special)
(special 'special)
c))

View File

@ -30,11 +30,11 @@ GENERATE_ARGS = -t main.rkt --submod main \
io-src-generate: io-src-generate:
$(RACKET) ../expander/bootstrap-run.rkt $(GENERATE_ARGS) $(RACKET) ../expander/bootstrap-run.rkt $(GENERATE_ARGS)
demo: compiled/rktio.rktl demo: $(RKTIO_DEP)
$(RACO) make demo.rkt $(RACO) make demo.rkt
$(RACKET) demo.rkt $(RACKET) demo.rkt
demo-thread: compiled/rktio.rktl demo-thread: $(RKTIO_DEP)
$(RACO) make demo-thread.rkt $(RACO) make demo-thread.rkt
$(RACKET) demo-thread.rkt $(RACKET) demo-thread.rkt

View File

@ -277,6 +277,8 @@
(test '#&(special src 1 1 2) (read-byte-or-special specialist box 'src)) (test '#&(special src 1 1 2) (read-byte-or-special specialist box 'src))
(test '(special #f #f #f #f) (peek-byte-or-special specialist)) (test '(special #f #f #f #f) (peek-byte-or-special specialist))
(test '#&(special src 1 2 3) (peek-byte-or-special specialist 0 #f box 'src)) (test '#&(special src 1 2 3) (peek-byte-or-special specialist 0 #f box 'src))
(test 'special (peek-byte-or-special specialist 0 #f 'special 'src))
(test 'special (peek-char-or-special specialist 0 'special 'src))
(let-values ([(i o) (make-pipe)]) (let-values ([(i o) (make-pipe)])
(struct my-i (i) #:property prop:input-port 0) (struct my-i (i) #:property prop:input-port 0)

View File

@ -78,7 +78,7 @@
(rktio_get_last_error rktio-name) (rktio_get_last_error rktio-name)
(rktio_get_last_error_step rktio-name)))) (rktio_get_last_error_step rktio-name))))
(include "../compiled/rktio.rktl") (include "../../rktio/rktio.rktl")
(define rktio_NULL #f) (define rktio_NULL #f)
@ -215,4 +215,4 @@
'rktio_do_install_os_signal_handler rktio_do_install_os_signal_handler 'rktio_do_install_os_signal_handler rktio_do_install_os_signal_handler
'rktio_get_ctl_c_handler rktio_get_ctl_c_handler] 'rktio_get_ctl_c_handler rktio_get_ctl_c_handler]
form ...)) form ...))
(include "../compiled/rktio.rktl"))) (include "../../rktio/rktio.rktl")))

View File

@ -35,7 +35,7 @@
(check who input-port? orig-in) (check who input-port? orig-in)
(check who exact-nonnegative-integer? skip-k) (check who exact-nonnegative-integer? skip-k)
(check who #:or-false evt? progress-evt) (check who #:or-false evt? progress-evt)
(check who #:or-false (procedure-arity-includes/c 1) special-wrap) (check who special-wrap-for-peek? #:contract special-wrap-for-peek/c-str special-wrap)
(when progress-evt (when progress-evt
(check-progress-evt who progress-evt orig-in)) (check-progress-evt who progress-evt orig-in))
(let ([in (->core-input-port orig-in)]) (let ([in (->core-input-port orig-in)])
@ -64,7 +64,7 @@
[source-name #f]) [source-name #f])
(check who input-port? in) (check who input-port? in)
(check who exact-nonnegative-integer? skip-k) (check who exact-nonnegative-integer? skip-k)
(check who #:or-false (procedure-arity-includes/c 1) special-wrap) (check who special-wrap-for-peek? #:contract special-wrap-for-peek/c-str special-wrap)
(extract-special-value (do-peek-char who in skip-k #:special-ok? #t) (extract-special-value (do-peek-char who in skip-k #:special-ok? #t)
in source-name skip-k in source-name skip-k
special-wrap)) special-wrap))
@ -74,21 +74,33 @@
(define (extract-special-value v in source-name delta special-wrap) (define (extract-special-value v in source-name delta special-wrap)
(cond (cond
[(procedure? v) [(procedure? v)
(define special (cond
(cond [(eq? special-wrap 'special)
[(not source-name) 'special]
[else
(define special
(cond (cond
[(procedure-arity-includes? v 0) [(not source-name)
(v)] (cond
[(procedure-arity-includes? v 0)
(v)]
[else
(v #f #f #f #f)])]
[else [else
(v #f #f #f #f)])] (define-values (line col pos) (port-next-location in))
[else (v source-name
(define-values (line col pos) (port-next-location in)) line
(v source-name (and col (+ col delta))
line (and pos (+ pos delta)))]))
(and col (+ col delta)) (if special-wrap
(and pos (+ pos delta)))])) (special-wrap special)
(if special-wrap special)])]
(special-wrap special)
special)]
[else v])) [else v]))
(define (special-wrap-for-peek? w)
(or (not w) (eq? w 'special) (and (procedure? w)
(procedure-arity-includes? w 1))))
(define special-wrap-for-peek/c-str
"(or/c (any/c -> any/c) #f 'special)")

View File

@ -143,6 +143,7 @@ ROSYM static Scheme_Object *lf_symbol;
ROSYM static Scheme_Object *crlf_symbol; ROSYM static Scheme_Object *crlf_symbol;
ROSYM static Scheme_Object *module_symbol; ROSYM static Scheme_Object *module_symbol;
ROSYM static Scheme_Object *string_symbol; ROSYM static Scheme_Object *string_symbol;
ROSYM static Scheme_Object *special_symbol;
READ_ONLY static Scheme_Object *default_read_handler; READ_ONLY static Scheme_Object *default_read_handler;
READ_ONLY static Scheme_Object *default_display_handler; READ_ONLY static Scheme_Object *default_display_handler;
@ -200,6 +201,7 @@ scheme_init_port_fun(Scheme_Startup_Env *env)
REGISTER_SO(crlf_symbol); REGISTER_SO(crlf_symbol);
REGISTER_SO(module_symbol); REGISTER_SO(module_symbol);
REGISTER_SO(string_symbol); REGISTER_SO(string_symbol);
REGISTER_SO(special_symbol);
any_symbol = scheme_intern_symbol("any"); any_symbol = scheme_intern_symbol("any");
any_one_symbol = scheme_intern_symbol("any-one"); any_one_symbol = scheme_intern_symbol("any-one");
@ -208,6 +210,7 @@ scheme_init_port_fun(Scheme_Startup_Env *env)
crlf_symbol = scheme_intern_symbol("return-linefeed"); crlf_symbol = scheme_intern_symbol("return-linefeed");
module_symbol = scheme_intern_symbol("module"); module_symbol = scheme_intern_symbol("module");
string_symbol = scheme_intern_symbol("string"); string_symbol = scheme_intern_symbol("string");
special_symbol = scheme_intern_symbol("special");
scheme_write_proc = scheme_make_noncm_prim(sch_write, "write", 1, 2); scheme_write_proc = scheme_make_noncm_prim(sch_write, "write", 1, 2);
scheme_display_proc = scheme_make_noncm_prim(display, "display", 1, 2); scheme_display_proc = scheme_make_noncm_prim(display, "display", 1, 2);
@ -2982,8 +2985,17 @@ do_read_char(char *name, int argc, Scheme_Object *argv[], int peek, int spec, in
spec_wrap = argv[pos]; spec_wrap = argv[pos];
if (SCHEME_FALSEP(spec_wrap)) if (SCHEME_FALSEP(spec_wrap))
spec_wrap = NULL; spec_wrap = NULL;
else if (!scheme_fast_check_arity(spec_wrap, 1)) else if (!(peek && SAME_OBJ(spec_wrap, special_symbol))
scheme_check_proc_arity2(name, 1, pos, argc, argv, 1); && !scheme_fast_check_arity(spec_wrap, 1)) {
if (!scheme_check_proc_arity2(NULL, 1, pos, argc, argv, 1)) {
scheme_wrong_contract(name,
(peek
? "(or/c (any/c -> any/c) #f 'special)"
: "(or/c (any/c -> any/c) #f)"),
pos, argc, argv);
return NULL;
}
}
pos++; pos++;
if (argc > pos) if (argc > pos)
src = argv[pos++]; src = argv[pos++];
@ -3021,6 +3033,8 @@ do_read_char(char *name, int argc, Scheme_Object *argv[], int peek, int spec, in
} }
if (ch == SCHEME_SPECIAL) { if (ch == SCHEME_SPECIAL) {
if (SAME_OBJ(spec_wrap, special_symbol))
return special_symbol;
src = scheme_get_ready_special(port, src, peek); src = scheme_get_ready_special(port, src, peek);
if (spec_wrap) { if (spec_wrap) {
Scheme_Object *a[1]; Scheme_Object *a[1];

File diff suppressed because it is too large Load Diff