/* Read the object itself. */
Lisp_Object tem = read0 (readcharfun, locate_syms);
+ if (CONSP (tem))
+ {
+ if (BASE_EQ (tem, placeholder))
+ /* Catch silly games like #1=#1# */
+ invalid_syntax ("nonsensical self-reference",
+ readcharfun);
+
+ /* Optimisation: since the placeholder is already
+ a cons, repurpose it as the actual value.
+ This allows us to skip the substition below,
+ since the placeholder is already referenced
+ inside TEM at the appropriate places. */
+ Fsetcar (placeholder, XCAR (tem));
+ Fsetcdr (placeholder, XCDR (tem));
+
+ struct Lisp_Hash_Table *h2
+ = XHASH_TABLE (read_objects_completed);
+ ptrdiff_t i = hash_lookup (h2, placeholder, &hash);
+ eassert (i < 0);
+ hash_put (h2, placeholder, Qnil, hash);
+ return placeholder;
+ }
+
/* If it can be recursive, remember it for
future substitutions. */
if (! SYMBOLP (tem)
}
/* Now put it everywhere the placeholder was... */
- if (CONSP (tem))
- {
- Fsetcar (placeholder, XCAR (tem));
- Fsetcdr (placeholder, XCDR (tem));
- return placeholder;
- }
- else
- {
- Flread__substitute_object_in_subtree
- (tem, placeholder, read_objects_completed);
+ Flread__substitute_object_in_subtree
+ (tem, placeholder, read_objects_completed);
- /* ...and #n# will use the real value from now on. */
- i = hash_lookup (h, number, &hash);
- eassert (i >= 0);
- set_hash_value_slot (h, i, tem);
+ /* ...and #n# will use the real value from now on. */
+ i = hash_lookup (h, number, &hash);
+ eassert (i >= 0);
+ set_hash_value_slot (h, i, tem);
- return tem;
- }
+ return tem;
}
/* #n# returns a previously read object. */
(should (equal (read "-0.e-5") -0.0))
)
+(defun lread-test-read-and-print (str)
+ (let* ((read-circle t)
+ (print-circle t)
+ (val (read-from-string str)))
+ (if (consp val)
+ (prin1-to-string (car val))
+ (error "reading %S failed: %S" str val))))
+
+(defconst lread-test-circle-cases
+ '("#1=(#1# . #1#)"
+ "#1=[#1# a #1#]"
+ "#1=(#2=[#1# #2#] . #1#)"
+ "#1=(#2=[#1# #2#] . #2#)"
+ "#1=[#2=(#1# . #2#)]"
+ "#1=(#2=[#3=(#1# . #2#) #4=(#3# . #4#)])"
+ ))
+
+(ert-deftest lread-circle ()
+ (dolist (str lread-test-circle-cases)
+ (ert-info (str :prefix "input: ")
+ (should (equal (lread-test-read-and-print str) str))))
+ (should-error (read-from-string "#1=#1#") :type 'invalid-read-syntax))
;;; lread-tests.el ends here