<commit_before>;; Write structures with cyclic references cut short.
;; E.g.:
;; > (let a (box<- 42))
;; > (a .^= a)
;; > (cycle-write a)
;; #1=<box #1>

;; TODO exclude known-to-be-acyclic types from the tags map

(to (cycle-write thing @(optional sink-arg))
  (let sink (or sink-arg out))          ;TODO fancier (optional ...)
  (let tags (map<-))
  (let buffer (flexarray<-))
  (let cycle-sink (cycle-sink<- tags buffer)) ;XXX better name?
  (cycle-sink .print thing)
  (hey sink @buffer.values)
  void)
;; TODO restore if I decide I don't like 'hey':
;;  (for each! ((writer buffer.values))
;;    (writer sink)))

;; The tags map keeps a tag for each object the cycle sink visits. The
;; tag is 0 after the first visit; then, on the second and thereafter,
;; a positive integer identifying the object.

;; The buffer accumulates a sequence of procedures to send the final
;; formatted text to the destination sink.
;; TODO I think this logic could be simpler

(to (cycle-sink<- tags buffer)
  (let counter (box<- 0))
  (make cycle-sink

    (to (_ .display atom)
      (buffer .push! (_ .display atom)))

    (to (_ .print thing)
      (if (or (symbol? thing) (self-evaluating? thing))  ;; TODO skip other atom types
          (thing .selfie cycle-sink)
          (may (tags .get thing)
            (be #no
              ;; First visit.
              (tags .set! thing 0)
              (buffer .push! (on (sink)
                               (let id (tags thing))
                               (unless (= 0 id)
                                 (format .to-sink sink "#~w=" id))))
              (thing .selfie cycle-sink))
            (be tag
              (let id (hm (when (= tag 0)
                            ;; Second visit.
                            (hey (counter .update _.+)
                                 (-> (tags .set! thing it))))
                          (else
                            ;; Thereafter.
                            tag)))
              (buffer .push! (on (sink)
                               (format .to-sink sink "#~w" id)))))))))

(export cycle-write)<commit_msg>tweak<commit_after>;; Write structures with cyclic references cut short.
;; E.g.:
;; > (let a (box<- 42))
;; > (a .^= a)
;; > (cycle-write a)
;; #1=<box #1>

;; TODO exclude known-to-be-acyclic types from the tags map

(to (cycle-write thing @(optional sink-arg))
  (let sink (or sink-arg out))          ;TODO fancier (optional ...)
  (let tags (map<-))
  (let buffer (flexarray<-))
  (let cycle-sink (cycle-sink<- tags buffer)) ;XXX better name?
  (cycle-sink .print thing)
  (hey sink @buffer.values)
  void)
;; TODO restore if I decide I don't like 'hey':
;;  (for each! ((writer buffer.values))
;;    (writer sink)))

;; The tags map keeps a tag for each object the cycle sink visits. The
;; tag is 0 after the first visit; then, on the second and thereafter,
;; a positive integer identifying the object.

;; The buffer accumulates a sequence of procedures to send the final
;; formatted text to the destination sink.
;; TODO I think this logic could be simpler

(to (cycle-sink<- tags buffer)
  (let counter (box<- 0))
  (make cycle-sink

    (to (_ .display atom)
      (buffer .push! (_ .display atom)))

    (to (_ .print thing)
      (if (or (symbol? thing) (self-evaluating? thing))  ;; TODO skip other atom types
          (thing .selfie cycle-sink)
          (may (tags .get thing)
            (be #no
              ;; First visit.
              (tags .set! thing 0)
              (buffer .push! (on (sink)
                               (let id (tags thing))
                               (unless (= 0 id)
                                 (format .to-sink sink "#~w=" id))))
              (thing .selfie cycle-sink))
            (be tag
              (let id (if (= tag 0)
                          ;; Second visit:
                          (hey (counter .update _.+)
                               (-> (tags .set! thing it)))
                          ;; Thereafter:
                          tag))
              (buffer .push! (on (sink)
                               (format .to-sink sink "#~w" id)))))))))

(export cycle-write)
{'fraction_non_alphanumeric': 0.13529128550794414, 'fraction_numerical': 0.004333172845450169, 'mean_word_length': 1.6583493282149713, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7418617', 'n_tokens_mistral': 1253, 'n_tokens_neox': 1181, 'n_words': 457}