redex icon indicating copy to clipboard operation
redex copied to clipboard

compound rewriter inserts 'spring, gobbling up line breaks

Open shhyou opened this issue 7 years ago • 2 comments

The program

#lang racket
(require redex/reduction-semantics redex/pict)
(define-language empty)
(with-compound-rewriter 'R
  (λ (lws) `(,(list-ref lws 2) "•" ,(list-ref lws 3)))
  (term->pict empty
              (R 0 ;; line 7
                 1))) ;; line 8

produces a pict with 0 and 1 overlapped. After some tracking, I think this because the 'spring inserted during the conversion from "•" to lw gobbled up the new line:

;; debugging output
After apply-rewrites, before build-lines:
(lw
 (list
  (lw "0" 7 0 17 1 #f #f)
  (lw "•" 7 0 14 0 #f #f)
  'spring
  (lw . 7 1 14 3 #f #f)
  'spring
  (lw . 8 0 17 0 #f #f)
  (lw "1" 8 0 17 1 #f #f))
 7
 1
 14
 5
 #f
 #f)
eject (current=7, gobbled=0) 7:17-7:18 "0"
eject (current=7, gobbled=0) 7:14-7:14 "•"
eject (current=7, gobbled=0) 7:14-8:17 #<pict>
eject (current=7, gobbled=0) 8:17-8:17 #<pict>
eject (current=8, gobbled=1) 8:17-8:18 "1"


After build-lines, before setup-lines:
(list
 (line
  7
  (list (spacer-token 0 3) (pict-token 3 0 .) (string-token 3 1 "1" 'roman)))
 (line
  7
  (list
   (string-token 0 3 "   " 'roman)
   (string-token 3 1 "0" 'roman)
   (string-token 0 0 "•" 'roman)
   (pict-token 0 3 .))))

I find it surprising that inserting elements ("•") in the rewriter would gobble line breaks. Maybe apply-rewrites should not insert 'spring when the two lws surrounding the non-lw are at different lines. If this is intended, I think the column counts need some fix to avoid overlapping.

shhyou avatar Nov 26 '18 20:11 shhyou

I believe I just didn't think about newlines and being smart about them when gobbling up space. It would be better to do something more sophisticated in that case. It isn't obvious which line the • belongs on, but picking a default seems like a nice thing.

Indeed, I have written code in rewriters that examines the arguments and has multiple cases based on whether or not two arguments are on the same line or not. But I'll point out that in this case, if there is one line in the input, then you probably want to use " • " and if there are two lines and the dot is on the first line, you probably want " •" (dropping the last space so the pict doesn't get too wide in the case that the thing on the second line is especially wide).

Another approach is to use just-after or just-before, but this doesn't avoid the problem of the inter-"word" space.

rfindler avatar Nov 27 '18 14:11 rfindler

I tried using just-after for the inserted "•". Indeed, the inter-"word" space problem exists.

          (λ (lws) (list (list-ref lws 2)
                         (just-after "•" (list-ref lws 2))
                         (list-ref lws 3)))

I tried removing the second 'spring between the two blank images inserted by Redex provided they are not on the same line. This seems to work. For the case where the two lws are on the same line, I leave the output as-is (though I think the space is already being gobbled up by the 'spring in the first place).

Somehow removing the second 'spring in all cases also work but I don't know why.

Spaces, before and after

+++ b/redex-pict-lib/redex/private/core-layout.rkt
@@ -434,11 +434,13 @@
                                   (- next-lw-line line)
                                   new-lw-col 
                                   new-lw-col-span)
-                        'spring
-                        (build-lw to-wrap2 next-lw-line 0 (+ new-lw-col new-lw-col-span) 0)
-                        (if after-next-lw
-                            (cons next-lw (loop after-next-lw next-line next-column))
-                            '()))))])))))
+                        (append
+                         (cond [(= line next-lw-line) '(spring)]
+                               [else '()])
+                         (list (build-lw to-wrap2 next-lw-line 0 (+ new-lw-col new-lw-col-span) 0))
+                         (if after-next-lw
+                             (cons next-lw (loop after-next-lw next-line next-column))
+                             '())))))])))))
   
   (define (extract-pieces-to-wrap who lst)
     (let ([fst (car lst)])

Testing program:

#lang racket
(require redex/reduction-semantics redex/pict pict)
(define-language empty)

(define (pic thunk)
  (begin0
    (begin
      (printf "========================================\n")
      (frame
       (scale
        (with-compound-rewriter 'R
          (λ (lws) (list (list-ref lws 2)
                         "X" "V"
                         (list-ref lws 3)))
          (thunk))
        2)))
    (printf "========================================\n")))

(lt-superimpose
 (filled-rectangle 250 200
                   #:color "white"
                   #:draw-border? #f)

 (inset
  (vl-append
   10
   (pic
    (λ ()
      (term->pict empty
                  (R 0 1))))
   (pic
    (λ ()
      (term->pict empty
                  (R 0
                     1))))
   (pic
    (λ ()
      (term->pict empty
                  (R 0
                     1234567890)))))
  10))

shhyou avatar Dec 10 '18 03:12 shhyou

Thank you for taking the time to look into this. Looking at the code again, I don't fully understand what's going on in the interaction between multiple lines and inserted items but this change is definitely an improvement and seems quite conservative so I pushed it.

rfindler avatar Dec 27 '22 02:12 rfindler