From c593bdd8b7c62ea857fb196ceeb003bb87caf9fa Mon Sep 17 00:00:00 2001 From: "Artyom V. Poptsov" Date: Tue, 6 Oct 2015 10:20:57 +0300 Subject: [PATCH] Provide means to 'read' an # object Currently there's no way to 'read' back an unspecified value, but in some cases such a feature might be handy (eg. in case of reading a vector filled with unspecified objects.) This patch changes the way the reader treats the returned value of a reader extension callback set by 'read-hash-extend': now when the callback returns a multiple values object and the second value is '#t', the 1st value returned as is, even if it is an unspecified object. If a callback returns an unspecified object alone, it is considered as an indication that the callback could not read an object -- in other words, the solution should be backward compatible with existing reader extenstions. * libguile/read.c (scm_read_sharp): Check whether the value returned by 'scm_read_sharp_extension' is a multiple values object and handle it appropriately if so. (read_inner_expression): Handle multiple values object returned by 'scm_read_sharp'. * test-suite/tests/reader.test: Add a test for 'read-hash-extend' to ensure that the callback can return an unspecified object. Add a test for the same procedure that ensures that the callback can indicate an unknown object by returning an unspecified value alone. * doc/ref/api-evaluation.texi: Update description of 'read-hash-extend'. --- doc/ref/api-evaluation.texi | 8 ++++++++ libguile/read.c | 19 +++++++++++++------ test-suite/tests/reader.test | 11 +++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/doc/ref/api-evaluation.texi b/doc/ref/api-evaluation.texi index 296f1da..8278ab9 100644 --- a/doc/ref/api-evaluation.texi +++ b/doc/ref/api-evaluation.texi @@ -298,6 +298,14 @@ starting with the character sequence @code{#} and @var{chr}. @var{proc} will be called with two arguments: the character @var{chr} and the port to read further data from. The object returned will be the return value of @code{read}. + +Returning of an unspecified value (value of the @code{*unspecified*} +constant) alone from @var{proc} means that the procedure cannot parse +the character sequence. Despite of this, in some cases you may want to +read and return an unspecified value as the correct value. This can be +achieved by returning of two values: an unspecified value as the first +value and @code{#t} as the second, see (@pxref{Multiple Values}). + Passing @code{#f} for @var{proc} will remove a previous setting. @end deffn diff --git a/libguile/read.c b/libguile/read.c index ecf27ff..e9bb6f0 100644 --- a/libguile/read.c +++ b/libguile/read.c @@ -1656,8 +1656,15 @@ scm_read_sharp (scm_t_wchar chr, SCM port, scm_t_read_opts *opts, chr = scm_getc_unlocked (port); result = scm_read_sharp_extension (chr, port, opts); - if (!scm_is_eq (result, SCM_UNSPECIFIED)) - return result; + if (! SCM_VALUESP (result)) + { + if (! scm_is_eq (result, SCM_UNSPECIFIED)) + return result; + } + else if (scm_is_true (scm_c_value_ref (result, 1))) + { + return result; + } switch (chr) { @@ -1713,7 +1720,7 @@ scm_read_sharp (scm_t_wchar chr, SCM port, scm_t_read_opts *opts, return (scm_read_nil (chr, port, opts)); default: result = scm_read_sharp_extension (chr, port, opts); - if (scm_is_eq (result, SCM_UNSPECIFIED)) + if ((! SCM_VALUESP (result)) && scm_is_eq (result, SCM_UNSPECIFIED)) { /* To remain compatible with 1.8 and earlier, the following characters have lower precedence than `read-hash-extend' @@ -1728,7 +1735,7 @@ scm_read_sharp (scm_t_wchar chr, SCM port, scm_t_read_opts *opts, } } else - return result; + return scm_c_value_ref (result, 0); } return SCM_UNSPECIFIED; @@ -1807,11 +1814,11 @@ read_inner_expression (SCM port, scm_t_read_opts *opts) long line = SCM_LINUM (port); int column = SCM_COL (port) - 1; SCM result = scm_read_sharp (chr, port, opts, line, column); - if (scm_is_eq (result, SCM_UNSPECIFIED)) + if ((! SCM_VALUESP (result)) && scm_is_eq (result, SCM_UNSPECIFIED)) /* We read a comment or some such. */ break; else - return result; + return scm_c_value_ref (result, 0); } case ')': scm_i_input_error (FUNC_NAME, port, "unexpected \")\"", SCM_EOL); diff --git a/test-suite/tests/reader.test b/test-suite/tests/reader.test index 5eb368d..b860abe 100644 --- a/test-suite/tests/reader.test +++ b/test-suite/tests/reader.test @@ -169,6 +169,17 @@ (pass-if "square brackets are parens" (equal? '() (read-string "[]"))) + (pass-if "reader callback can read an unspecified value" + (with-fluids ((%read-hash-procedures (fluid-ref %read-hash-procedures))) + (read-hash-extend #\< (lambda (c port) (values *unspecified* #t))) + (eq? *unspecified* (read-string (object->string *unspecified*))))) + + (pass-if-exception "reader callback can indicate an unknown object" + exception:unknown-sharp-object + (with-fluids ((%read-hash-procedures (fluid-ref %read-hash-procedures))) + (read-hash-extend #\< (lambda (c port) *unspecified*)) + (eq? *unspecified* (read-string (object->string *unspecified*))))) + (pass-if-exception "paren mismatch" exception:unexpected-rparen (read-string "'[)")) -- 2.4.6