This commit is contained in:
Robby Findler 2013-01-17 20:43:15 -06:00
parent 0531412a8e
commit bb216d142c
9 changed files with 1725 additions and 0 deletions

View File

@ -0,0 +1,83 @@
#lang unstable/2d racket/base
(require unstable/2d/cond
rackunit)
(define (basic a b c d)
#2dcond
╔═══╦═══╦═══╗
a b
╠═══╬═══╬═══╣
c 1 2
╠═══╬═══╬═══╣
d 3 4
╚═══╩═══╩═══╝)
(define ((matches reg) exn) (regexp-match? reg (exn-message exn)))
(check-equal? (basic #t #t #t #t) 1)
(check-equal? (basic #t #f #t #f) 1)
(check-equal? (basic #f #t #t #t) 2)
(check-equal? (basic #f #t #t #f) 2)
(check-equal? (basic #t #t #f #t) 3)
(check-equal? (basic #t #f #f #t) 3)
(check-equal? (basic #f #t #f #t) 4)
(check-exn (matches #rx"x-direction questions")
(λ () (basic #f #f #f #f)))
(check-exn (matches #rx"y-direction questions.*x coordinate 1")
(λ () (basic #t #f #f #f)))
(check-exn (matches #rx"y-direction questions.*x coordinate 2")
(λ () (basic #f #t #f #f)))
(define (bot-right-cell a b c d)
#2dcond
╔═══╦═══╦═══╗
a b
╠═══╬═══╩═══╣
c 1
╠═══╣ ╔═══╣
d 2
╚═══╩═══╩═══╝)
(check-equal? (bot-right-cell #t #t #t #t) 1)
(check-equal? (bot-right-cell #t #f #t #f) 1)
(check-equal? (bot-right-cell #f #t #t #t) 1)
(check-equal? (bot-right-cell #f #t #t #f) 1)
(check-equal? (bot-right-cell #t #t #f #t) 1)
(check-equal? (bot-right-cell #t #f #f #t) 1)
(check-equal? (bot-right-cell #f #t #f #t) 2)
(define (top-left-cell a b c d)
#2dcond
╔═══╦═══╦═══╗
a b
╠═══╬═══╬═══╣
c 1
╠═══╬═══╝
d 2
╚═══╩═══════╝)
(check-equal? (top-left-cell #t #t #t #t) 1)
(check-equal? (top-left-cell #t #f #t #f) 1)
(check-equal? (top-left-cell #f #t #t #t) 2)
(check-equal? (top-left-cell #f #t #t #f) 2)
(check-equal? (top-left-cell #t #t #f #t) 2)
(check-equal? (top-left-cell #t #f #f #t) 2)
(check-equal? (top-left-cell #f #t #f #t) 2)
(let ([sp (open-output-string)])
(define (f x) (printf "~a\n" x) #f)
(parameterize ([current-output-port sp])
#2dcond
╔═════╦═══════╦═══════╦════╗
(f 1) (f 2) #t
╠═════╬═══════╩═══════╩════╣
(f 3)
╠═════╣
(f 4) 222
╠═════╣
#t
╚═════╩════════════════════╝)
(check-equal? (get-output-string sp)
"1\n2\n3\n4\n"))

View File

@ -0,0 +1,37 @@
#lang unstable/2d racket
(require unstable/2d/match
rackunit)
(check-equal?
#2dmatch
╔═══════╦══════╦══════════╗
1 'x x
2
╠═══════╬══════╬══════════╣
y #f (list x y)
╚═══════╩══════╩══════════╝
(list 1 2))
(define ( t1 t2)
#2dmatch
╔══════════╦══════╦═══════╦══════════╦═════════════════╗
t2 'Int 'Real 'Complex `(-> ,t2d
t1 ,t2r)
╠══════════╬══════╩═══════╩══════════╬═════════════════╣
'Int
╠══════════╬══════╗ #t
'Real #f
╠══════════╣ ╚═══════╗
'Complex
╠══════════╣ ╚══════════╬═════════════════╣
`(-> ,t1d #f (and ( t2d t1d)
,t1r) ( t1r t2r))
╚══════════╩═════════════════════════╩═════════════════╝)
(check-equal? ( 'Int 'Int) #t)
(check-equal? ( 'Int 'Real) #t)
(check-equal? ( 'Real 'Int) #f)
(check-equal? ( 'Complex 'Complex) #t)
(check-equal? ( 'Complex 'Int) #f)
(check-equal? ( '(-> Real Int) '(-> Int Real)) #t)
(check-equal? ( '(-> Int Complex) '(-> Int Real)) #f)

View File

@ -0,0 +1,664 @@
#lang racket/base
(require rackunit
unstable/2d/private/readtable
racket/set)
(define touched-lines-table (make-hash))
(check-equal? (chars->desc '(#\a) "or")
"a")
(check-equal? (chars->desc '(#\a #\b) "or")
"a or b")
(check-equal? (chars->desc '(#\a #\b #\c) "or")
"a, b, or c")
(check-equal? (chars->desc '(#\a #\b #\c #\d) "or")
"a, b, c, or d")
(check-equal? (chars->desc '(#\a #\b #\c #\d #\e) "or")
"a, b, c, d, or e")
(check-equal? (read (open-input-string "#2(x)"))
(parameterize ([current-readtable (make-2d-readtable)])
(read (open-input-string "#2(x)"))))
(check-equal? (with-handlers ((exn:fail? exn-message))
(read (open-input-string "#2x(x)")))
(with-handlers ((exn:fail? exn-message))
(parameterize ([current-readtable (make-2d-readtable)])
(read (open-input-string "#2x(x)")))))
(check-true (syntax? (read-syntax 'hi (open-input-string "#2(x)"))))
(check-equal? (read (open-input-string "#2(x)"))
(parameterize ([current-readtable (make-2d-readtable)])
(syntax->datum (read-syntax 'hi (open-input-string "#2(x)")))))
(parameterize ([current-readtable (make-2d-readtable)])
(define sp (open-input-string
(string-append "#2d\n"
"╔══╦══╗\n"
"║1 ║2 ║\n"
"╠══╬══╣\n"
"║4 ║3 ║\n"
"╚══╩══╝\n")))
(define wp
(make-input-port 'name sp sp void #f #f
(λ () (values #f #f #f))
void))
(port-count-lines! wp)
;; make sure that if there is no source location information,
;; we still get some result back.
(check-true (pair? (read wp))))
(define (get-err-locs inputs)
(with-handlers ([exn:fail:read? exn:fail:read-srclocs])
(define p (open-input-string (apply string-append inputs)))
(port-count-lines! p)
(parameterize ([current-readtable (make-2d-readtable)])
(read-syntax #f p))
#f))
(define (get-something inputs i)
(define p (open-input-string (apply string-append inputs)))
(port-count-lines! p)
;; account for the "#2d" that was read from the first line
(call-with-values (λ () (parse-2dcond p "source" 1 0 1 2))
(λ x (list-ref x i))))
(define (get-graph inputs) (get-something inputs 0))
(define (get-all-lines inputs) (get-something inputs 1))
(define (get-table-column-breaks inputs) (get-something inputs 2))
(define (get-initial-space-count inputs) (get-something inputs 3))
(check-equal? (get-err-locs
'("#2d\n"
"╔══╦══╗\n"
"║1 ║2 ║\n"
"╠══╬══╣\n"
"║4 ║3 ║\n"
"╚══╩══╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
"╔══╦══╗\n"
"║λ ║2 ║\n"
"╠══╬══╣\n"
"║1 ║黃 ║\n"
"╚══╩══╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║1 ║4 ║\n"
" ╠══╬══╣\n"
" ║2 ║3 ║\n"
" ╚══╩══╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦══╗\n"
" ║1 ║2 ║3 ║\n"
" ╠══╬══╬══╣\n"
" ║6 ║5 ║4 ║\n"
" ╠══╬══╬══╣\n"
" ║7 ║8 ║9 ║\n"
" ╚══╩══╩══╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦══╗\n"
" ║ 1║ 2║ 3║\n"
" ╠══╬══╩══╣\n"
" ║ 4║ ║\n"
" ╠══╣ 6 ║\n"
" ║ 5║ ║\n"
" ╚══╩═════╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦══╗\n"
" ║ 1║ 2║ 3║\n"
" ╠══╬══╩══╣\n"
" ║ 4║5 ║\n"
" ╠══╬═════╣\n"
" ║ 6║7 ║\n"
" ╚══╩═════╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦══╦══╗\n"
" ║ 1║ 2║ 3║ 4║\n"
" ╠══╬══╬══╩══╣\n"
" ║ 4║ 5║ 6 ║\n"
" ╚══╩══╩═════╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦══╗\n"
" ║1 ║2 ║3 ║\n"
" ╠══╬══╬══╣\n"
" ║4 ║ ║ ║\n"
" ╠══╣ ║ ║\n"
" ║5 ║6 ║7 ║\n"
" ╚══╩══╩══╝\n"))
#f)
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║1 ║4 ║ ;; comment\n"
" ╠══╬══╣ ;; comment \n"
" ║2 ║3 ║\n"
" ╚══╩══╝\n"))
#f)
(define lines-table (hash-copy all-line-of-interest))
(parameterize ([current-lines lines-table])
(check-regexp-match #rx"expected a newline"
(with-handlers ((exn:fail? exn-message))
(parameterize ([current-readtable (make-2d-readtable)])
(read (open-input-string "#2d")))))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║ ║\n"
" ╠══╬══╣\n"
" ║ ║ ║\n"
" ╚══╩══╝\n"))
(list (srcloc #f 3 2 17 1)
(srcloc #f 2 2 7 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║ ═║ ║\n"
" ╠══╬══╣\n"
" ║ ║ ║\n"
" ╚══╩══╝\n"))
(list (srcloc #f 3 4 19 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║ ║ ║\n"
" ╠══╬══╣\n"
" ║ ║ ║\n"
" ╚══╩══╝\n"))
(list (srcloc #f 3 1 16 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║ ║ ║\n"
" ╠══╬══\n"
" ║ ║ ║\n"
" ╚══╩══╝\n"))
(list (srcloc #f 4 8 33 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╗\n"
" ║ ║ ║\n"
" ╠═\n"
" ║ ║ ║\n"
" ╚══╩══╝\n"))
(list (srcloc #f 4 4 29 1)))
(check-equal? (get-err-locs
'("#2d\n"
" +----+\n"
" | |\n"
" +----+\n"))
(list (srcloc #f 2 2 7 1)))
(check-equal? (get-err-locs
'("#2d\n"
" \n"))
(list (srcloc #f 2 0 5 3)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══\n"))
(list (srcloc #f 2 8 13 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╬══╩═══╣\n"
" ║ ║ ║\n"
" ╠══╣ ═ ║\n"
" ║ ║ ║\n"
" ╚══╩══════╝\n"))
(list (srcloc #f 6 8 69 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╬══╩═══╣\n"
" ║ ║ ║\n"
" ╠══╬══╝═══╣\n"
" ║ ║ ║\n"
" ╚══╩══════╝\n"))
(list (srcloc #f 6 8 69 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╬═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╠══╣ ═ ╠═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 6 7 72 1)
(srcloc #f 6 5 70 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╬═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╠══╬═ ═╬═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 6 7 72 1)
(srcloc #f 6 5 70 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╬═══╬═══╣\n"
" ║ ║ ║ \n"
" ╠══╬═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 5 13 63 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ║ ║\n"
" ║ ║ ║\n"
" ╠══╦═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 6 9 74 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ║ ╩ ║ ║\n"
" ║ ║ ║\n"
" ╠══╦═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 6 5 70 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
"\n"
" ║ ║ ║\n"
" ╠══╦═══╬═══╣\n"
" ║ ║ ║ ║\n"
" ╚══╩═══╩═══╝\n"))
(list (srcloc #f 6 6 71 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ╚══════╩═\n"))
(list (srcloc #f 6 11 76 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ╚══════\n"))
(list (srcloc #f 6 9 74 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ╚══════╩═══X\n"))
(list (srcloc #f 6 13 78 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║\n"
" ╚══════╩══X╝\n"))
(list (srcloc #f 6 12 77 1)))
(check-equal? (get-err-locs
'("#2d\n"
" ╔══╦═══╦═══╗\n"
" ║ ║ ║ ║\n"
" ╠══╩═══╬═══╣\n"
" ║ ║ ║ NOT WHITESPACE\n"
" ╚══════╩═══╝\n"))
(list (srcloc #f 5 19 69 1))))
(let ([lines (hash-map lines-table (λ (x y) x))])
(unless (null? lines)
(eprintf "no test case for errors on lines: ~s\n"
(sort lines <))))
(check-equal? (get-graph
'(" ╔══╦══╦══╗\n"
" ║ 1║ 2║ 3║\n"
" ╠══╬══╩══╣\n"
" ║ 4║ ║\n"
" ╠══╣ 6 ║\n"
" ║ 5║ ║\n"
" ╚══╩═════╝\n"))
(make-hash
(list (cons (list 0 0) (set))
(cons (list 0 1) (set))
(cons (list 0 2) (set))
(cons (list 1 0) (set))
(cons (list 2 0) (set))
(cons (list 1 1) (set (list 1 2) (list 2 1)))
(cons (list 2 1) (set (list 1 1) (list 2 2)))
(cons (list 1 2) (set (list 1 1) (list 2 2)))
(cons (list 2 2) (set (list 1 2) (list 2 1))))))
(check-equal? (get-graph
'(" ╔══╦══╦══╗\n"
" ║1 ║2 ║3 ║\n"
" ╠══╬══╬══╣\n"
" ║6 ║5 ║4 ║\n"
" ╠══╬══╬══╣\n"
" ║7 ║8 ║9 ║\n"
" ╚══╩══╩══╝\n"))
(make-hash
(list (cons (list 0 0) (set))
(cons (list 0 1) (set))
(cons (list 0 2) (set))
(cons (list 1 0) (set))
(cons (list 1 1) (set))
(cons (list 1 2) (set))
(cons (list 2 0) (set))
(cons (list 2 1) (set))
(cons (list 2 2) (set)))))
(check-equal? (get-graph
'(" ╔══╦══╦══╦══╗\n"
" ║1 ║2 ║3 ║4 ║\n"
" ╠══╬══╩══╩══╣\n"
" ║6 ║5 ║\n"
" ╠══╣ ╔══╗ ║\n"
" ║7 ║ ║10║ ║\n"
" ╠══╣ ╚══╝ ║\n"
" ║7 ║ ║\n"
" ╚══╩════════╝\n"))
(make-hash
(list (cons (list 0 0) (set))
(cons (list 0 1) (set))
(cons (list 0 2) (set))
(cons (list 0 3) (set))
(cons (list 1 0) (set))
(cons (list 1 1) (set (list 1 2) (list 2 1)))
(cons (list 1 2) (set (list 1 1) (list 1 3)))
(cons (list 1 3) (set (list 1 2) (list 2 3)))
(cons (list 2 0) (set))
(cons (list 2 1) (set (list 1 1) (list 3 1)))
(cons (list 2 2) (set))
(cons (list 2 3) (set (list 1 3) (list 3 3)))
(cons (list 3 0) (set))
(cons (list 3 1) (set (list 2 1) (list 3 2)))
(cons (list 3 2) (set (list 3 1) (list 3 3)))
(cons (list 3 3) (set (list 3 2) (list 2 3))))))
(check-equal? (get-all-lines '(" ╔══╦══╗\n"
" ║1 ║ ║\r"
" ╠══╬══╣\r\n"
" ║2 ║ ║\r"
" ╠══╬══╣\n"
" ║3 ║ ║\n"
" ╚══╩══╝\n"))
'#((" ╔══╦══╗\n" " ║1 ║ ║\r")
(" ╠══╬══╣\r\n" " ║2 ║ ║\r")
(" ╠══╬══╣\n" " ║3 ║ ║\n")))
(check-equal? (get-table-column-breaks '(" ╔══╦══╗\n"
" ║1 ║ ║\n"
" ╠══╬══╣\n"
" ║2 ║ ║\n"
" ╠══╬══╣\n"
" ║3 ║ ║\n"
" ╚══╩══╝\n"))
(list 2 2))
(check-equal? (get-initial-space-count '(" ╔══╦══╗\n"
" ║1 ║ ║\n"
" ╠══╬══╣\n"
" ║2 ║ ║\n"
" ╠══╬══╣\n"
" ║3 ║ ║\n"
" ╚══╩══╝\n"))
2)
(check-equal? (close-cell-graph (make-hash) 2 2)
(set (set (list 0 0))
(set (list 0 1))
(set (list 1 0))
(set (list 1 1))))
(check-equal? (close-cell-graph (make-hash
(list
(cons (list 0 0) (set (list 0 1)))))
2 2)
(set (set (list 0 0) (list 0 1))
(set (list 1 0))
(set (list 1 1))))
(check-equal? (close-cell-graph (make-hash
(list
(cons (list 0 0) (set (list 0 1)))
(cons (list 0 1) (set (list 1 1)))))
2 2)
(set (set (list 0 0) (list 0 1) (list 1 1))
(set (list 1 0))))
(check-equal? (close-cell-graph (make-hash
(list
(cons (list 0 0) (set (list 0 1)))
(cons (list 0 1) (set (list 1 1)))
(cons (list 1 1) (set (list 1 0)))))
2 2)
(set (set (list 0 0) (list 0 1) (list 1 1) (list 1 0))))
(check-true (compare/xy (list 0 0) (list 1 1)))
(check-false (compare/xy (list 1 1) (list 0 0)))
(check-true (compare/xy (list 1 0) (list 1 1)))
(check-false (compare/xy (list 1 1) (list 1 0)))
(check-false (compare/xy (list 1 0) (list 1 0)))
(check-equal? (smallest-representative (set (list 0 0) (list 1 0) (list 0 1) (list 1 1)))
(list 0 0))
(check-equal? (smallest-representative (set (list 1 1) (list 0 1) (list 1 0) (list 0 0)))
(list 0 0))
(let ()
(define scratch (string-copy " "))
(fill-scratch-string (set '(0 0))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╬══╣\n" "║56║78║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n 12 \n \n \n"))
(let ()
(define scratch (string-copy " "))
(fill-scratch-string (set '(1 0))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╬══╣\n" "║56║78║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n 34 \n \n \n"))
(let ()
(define scratch (string-copy " "))
(fill-scratch-string (set '(0 1))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╬══╣\n" "║56║78║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n \n \n 56 \n"))
(let ()
(define scratch (string-copy " "))
(fill-scratch-string (set '(1 1))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╬══╣\n" "║56║78║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n \n \n 78 \n"))
(let ()
(define scratch (string-copy " \n 34 \n \n \n"))
(fill-scratch-string (set '(1 0) '(1 1))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╣56║\n" "║78║90║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n 34 \n 56 \n 90 \n"))
(let ()
(define scratch (string-copy " \n 34 \n \n \n"))
(fill-scratch-string (set '(0 1) '(1 1))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╩══╣\n" "║56789║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n \n \n 56789 \n"))
(let ()
(define scratch (string-copy " \n 34 \n \n \n"))
(fill-scratch-string (set '(0 1) '(1 0) '(1 1))
#(("╔══╦══╗\n" "║12║34║\n") ("╠══╝56║\n" "║7890A║\n"))
scratch
'(2 2)
0)
(check-equal? scratch
" \n 34 \n 56 \n 7890A \n"))
(let ()
(define scratch (string-copy " "))
(fill-scratch-string (set '(0 0))
#(("╔═════╗\n" "║12345║\n" "║67890║\n" "║ABCDE║\n"))
scratch
'(5)
0)
(check-equal? scratch
" \n 12345 \n 67890 \n ABCDE \n"))
(let ()
(define scratch (make-string 66 #\space))
(fill-scratch-string (set '(1 2) '(1 1) '(2 2) '(2 1))
#(("╔══╦══╦══╗\n" "║12║34║56║\n")
("╠══╬══╩══╣\n" "║78║90ABC║\n")
("╠══╣DEFGH║\n" "║IJ║KLMNO║\n"))
scratch
'(2 2 2)
0)
(check-equal? scratch
" \n \n \n 90ABC \n DEFGH \n KLMNO \n"))
(let ()
(define scratch (make-string 120 #\space))
(fill-scratch-string
(set '(1 2) '(1 3) '(2 3))
#(("╔═╦════╦═════╗\n" "║1║2345║67890║\n")
("╠═╬════╩═════╣\n" "║a║bcdefghijk║\n")
("╠═╬════╗lmnop║\n" "║q║rstu║vwxyz║\n")
("╠═╣ABCD╚═════╣\n" "║E║FGHIJKLMNO║\n"))
scratch
'(1 4 5)
0)
(check-equal? (string-append
" \n"
" \n"
" \n"
" \n"
" \n"
" rstu \n"
" ABCD \n"
" FGHIJKLMNO \n")
scratch))
(let ()
(define scratch (make-string 495 #\space))
(fill-scratch-string
(set '(1 2) '(1 3) '(1 4) '(2 3) '(2 4) '(3 4))
#(("╔════════╦════╦═════╦════════╦═════════════╗\n"
"║ ║'Int║'Real║'Complex║ `(-> ,c ,d) ║\n")
("╠════════╬════╩═════╩════════╬═════════════╣\n"
"║'Int ║ ║ ║\n")
("╠════════╬════╗ #t ║ ║\n"
"║'Real ║ ║ ║ #f ║\n")
("╠════════╣ ╚═════╗ ║ ║\n"
"║'Complex║ ║ ║ ║\n")
("╠════════╣ ╚════════╬═════════════╣\n"
"║`(-> ,a ║ #f ║(and (≤ c a) ║\n"
"║ ,b)║ ║ (≤ b d))║\n"))
scratch
'(8 4 5 8 13)
0)
;; just make sure there are no border characters in there.
(check-regexp-match #rx"^[\n#f ]*$" scratch))
(let ()
(define str (make-string 84 #\space))
(fill-scratch-string (set '(1 2) '(1 1) '(2 1))
'#(("╔═══╦═══╦═══╗\n"
"║ ║ a ║ b ║\n")
("╠═══╬═══╩═══╣\n"
"║ c ║ 1 ║\n")
("╠═══╣ ╔═══╣\n"
"║ d ║ ║ 2 ║\n"))
str
'(3 3 3)
0)
(check-equal? str
" \n \n \n 1 \n \n \n"))
(let ()
(define sp (open-input-string
(string-append "#(#2d\n"
" ╔══╦══╗\n"
" ║1 ║2 ║\n"
" ╠══╬══╣\n"
" ║4 ║3 ║\n"
" ╚══╩══╝)1\n")))
(parameterize ([current-readtable (make-2d-readtable)])
(check-true (vector? (read sp)))
(check-equal? (read sp) 1)))

View File

@ -0,0 +1,77 @@
#lang racket
(provide 2dcond)
(define-syntax (2dcond stx)
(syntax-case stx ()
[(_ widths heights
[(cell ...) rhs ...] ...)
(let ()
;; coord-to-content : hash[(list num num) -o> (listof syntax)]
(define coord-to-content (make-hash))
(define let-bindings '())
;; build up the coord-to-content mapping
;; side-effect: record need for let bindings to
;; cover the the situation where multiple cells
;; are joined together
(for ([cells-stx (in-list (syntax->list #'((cell ...) ...)))]
[rhses (in-list (syntax->list #'((rhs ...) ...)))])
(define cells (syntax->datum cells-stx))
(cond
[(member (list 0 0) cells)
(unless (null? (syntax-e rhses))
(raise-syntax-error '2dcond
"cell at 0,0 must be empty"
stx))]
[else
(when (null? (syntax-e rhses))
(raise-syntax-error '2dcond
(format "cell at ~a,~a must not be empty"
(list-ref (car cells) 0)
(list-ref (car cells) 1))
stx))])
(cond
[(member (list 0 0) cells) (void)]
[(and
;; only one cell:
(null? (cdr cells))
;; not in the left-edge (questions)
(not (= 0 (car (car cells)))))
;; then we don't need a let binding
(hash-set! coord-to-content
(car cells)
(syntax->list rhses))]
[else
(for ([cell (in-list cells)])
(define x (list-ref cell 0))
(define y (list-ref cell 1))
(with-syntax ([(id) (generate-temporaries (list (format "2dcond~a-~a" x y)))]
[(rhs ...) rhses])
(set! let-bindings (cons #`[id (λ () rhs ...)]
let-bindings))
(hash-set! coord-to-content cell (list #'(id)))))]))
(define num-of-cols (length (syntax->list #'widths)))
(define num-of-rows (length (syntax->list #'heights)))
#`(let #,let-bindings
#,(for/fold ([else-branch #'(2dcond-runtime-error #f)])
([x-flip (in-range 1 num-of-cols)])
(define x (- num-of-cols x-flip))
#`(if (let () #,@(hash-ref coord-to-content (list x 0)))
(cond
#,@(for/list ([y (in-range 1 num-of-rows)])
#`[(let () #,@(hash-ref coord-to-content (list 0 y)))
#,@(hash-ref coord-to-content (list x y))])
[else (2dcond-runtime-error #,x)])
#,else-branch))))]))
(define (2dcond-runtime-error dir)
(define str
(if dir
(format "all of the y-direction questions were false (x coordinate ~a was true)"
dir)
"all of the x-direction questions were false"))
(error '2dcond str))

View File

@ -0,0 +1,37 @@
#lang racket/base
(require syntax/module-reader
(only-in "../private/readtable.rkt" make-2d-readtable))
(provide (rename-out [2d-read read]
[2d-read-syntax read-syntax]
[2d-get-info get-info]))
(define 2d-readtable (make-2d-readtable))
(define (wrap-reader p)
(lambda args
(parameterize ([current-readtable 2d-readtable])
(apply p args))))
(define-values (2d-read 2d-read-syntax 2d-get-info)
(make-meta-reader
'2d
"language path"
(lambda (bstr)
(let* ([str (bytes->string/latin-1 bstr)]
[sym (string->symbol str)])
(and (module-path? sym)
(vector
;; try submod first:
`(submod ,sym reader)
;; fall back to /lang/reader:
(string->symbol (string-append str "/lang/reader"))))))
wrap-reader
wrap-reader
(lambda (proc)
(lambda (key defval)
(case key
#;
[(color-lexer)
(dynamic-require 'syntax-color/scribble-lexer 'scribble-lexer)]
[else (if proc (proc key defval) defval)])))))

View File

@ -0,0 +1,73 @@
#lang racket/base
(require (for-syntax racket/base)
racket/match)
(provide 2dmatch)
(define-syntax (2dmatch stx)
(syntax-case stx ()
[(_ widths heights
[(cell ...) rhs ...] ...)
(let ()
;; coord-to-content : hash[(list num num) -o> (listof syntax)]
(define coord-to-content (make-hash))
(define let-bindings '())
(define main-args #f)
;; build up the coord-to-content mapping
;; side-effect: record need for let bindings to
;; cover the the situation where multiple cells
;; are joined together
;; (this code is similar to that in cond.rkt, but
;; my attempt at abstracting between them was unsuccessful)
(for ([cells-stx (in-list (syntax->list #'((cell ...) ...)))]
[rhses (in-list (syntax->list #'((rhs ...) ...)))])
(define cells (syntax->datum cells-stx))
(define on-boundary? (ormap (λ (lst) (or (= 0 (list-ref lst 0))
(= 0 (list-ref lst 1))))
cells))
(define rhses-lst (syntax->list rhses))
(cond
[(member (list 0 0) cells)
(unless (and rhses-lst (= 2 (length rhses-lst)))
(raise-syntax-error '2dmatch "cell at 0,0 must contain two expressions"))
(set! main-args rhses)]
[on-boundary?
(unless (and rhses-lst (= 1 (length rhses-lst)))
(raise-syntax-error '2dmatch
(format "cell at ~a,~a must contain exactly one match pattern")
stx))
(hash-set! coord-to-content (car cells) (car (syntax->list rhses)))]
[else
(when (null? (syntax-e rhses))
(raise-syntax-error '2dmatch
(format "cell at ~a,~a should not be empty"
(list-ref (car cells) 0)
(list-ref (car cells) 1))
stx))
(cond
[(null? (cdr cells)) ;; only one cell:
;; => we don't need a let binding
(hash-set! coord-to-content
(car cells)
(syntax->list rhses))]
[else
(for ([cell (in-list cells)])
(define x (list-ref cell 0))
(define y (list-ref cell 1))
(with-syntax ([(id) (generate-temporaries (list (format "2dmatch~a-~a" x y)))]
[(rhs ...) rhses])
(set! let-bindings (cons #`[id (λ () rhs ...)]
let-bindings))
(hash-set! coord-to-content cell (list #'(id)))))])]))
(define num-of-cols (length (syntax->list #'widths)))
(define num-of-rows (length (syntax->list #'heights)))
#`(let #,let-bindings
(match* #,main-args
#,@(for*/list ([x (in-range 1 num-of-cols)]
[y (in-range 1 num-of-rows)])
#`[(#,(hash-ref coord-to-content (list x 0))
#,(hash-ref coord-to-content (list 0 y)))
(let () #,@(hash-ref coord-to-content (list x y)))]))))]))

View File

@ -0,0 +1,690 @@
#lang racket/base
#|
ideas:
- 2dcond
- 2dmatch
- literal tables in scribble layout?
- something for graphics?
example uses:
- unifier
- subtyping relation
|#
(require racket/port
syntax/readerr
racket/match
racket/set
;syntax/rect
framework/private/dir-chars
(for-syntax racket/base
racket/list))
(provide make-2d-readtable
;; provided for the test suite
chars->desc
smallest-representative
parse-2dcond
all-line-of-interest
current-lines
close-cell-graph
compare/xy
fill-scratch-string)
(define all-line-of-interest (make-hash))
(define current-lines (make-parameter #f))
(define-syntax (line-of-interest stx)
(with-syntax ([line (syntax-line stx)])
(syntax-local-lift-expression #'(hash-set! all-line-of-interest line #t))
#'(visited line)))
(define (visited line)
(define t (current-lines))
(when t
(hash-remove! t line)))
(define (make-2d-readtable)
(define previous-readtable (current-readtable))
(make-readtable
#f
#\2
'dispatch-macro
(case-lambda
[(char port)
(define-values (line col pos) (port-next-location port))
(dispatch-proc char port #f line col pos read/recursive previous-readtable)]
[(char port source _line _col _pos)
(dispatch-proc char port source _line _col _pos
(λ (a b c) (read-syntax/recursive source a b c))
previous-readtable)])))
(define (dispatch-proc char port source _line _col _pos /recursive previous-readtable)
(define next-char (peek-char port))
(cond
[(equal? next-char #\d)
(define chars-read 2) ;; account for the # and the 2
(define (rc)
(set! chars-read (+ chars-read 1))
(read-char port))
(rc) ;; get the #\d
(define kwd-chars
(let loop ()
(define c (rc))
(cond
[(eof-object? c)
(line-of-interest)
(raise (make-exn:fail:read:eof
"expected a newline to follow #2d"
(current-continuation-marks)
(list (srcloc source
_line _col _pos
(+ _pos chars-read)))))]
[(equal? c #\newline) '()]
[(equal? c #\return)
(when (equal? #\newline (peek-char port))
(rc))
'()]
[else (cons c (loop))])))
(define-values (post-2d-line post-2d-col post-2d-span) (port-next-location port))
(define-values (edges lines table-column-breaks initial-space-count)
(parse-2dcond port source _line _col _pos chars-read))
(define lhses (close-cell-graph edges (length table-column-breaks) (vector-length lines)))
(define scratch-string (make-string (for/sum ([ss (in-vector lines)])
(for/sum ([s (in-list ss)])
(string-length s)))
#\space))
(define heights
(for/list ([line (in-vector lines)])
(length line)))
`(,(string->symbol (string-append "2d" (apply string kwd-chars)))
,table-column-breaks
,heights
,@(for/list ([set-of-indicies (in-list (sort (set->list lhses) compare/xy
#:key smallest-representative))])
(fill-scratch-string set-of-indicies
lines
scratch-string
table-column-breaks
initial-space-count)
(define scratch-port (open-input-string scratch-string))
(when post-2d-line (port-count-lines! scratch-port))
(set-port-next-location! scratch-port post-2d-line post-2d-col post-2d-span)
`[,(sort (set->list set-of-indicies) compare/xy)
,@(read-subparts source scratch-port
initial-space-count table-column-breaks heights set-of-indicies
previous-readtable /recursive)]))]
[else
(/recursive
(input-port-append #f (open-input-string "#2") port)
#f
previous-readtable)]))
(define (read-subparts source scratch-port
initial-space-count table-column-breaks heights lhs
previous-readtable /recursive)
(with-handlers (#;
[exn:fail:read?
(λ (exn)
(define constructor
(cond
[(exn:fail:read:eof? exn) exn:fail:read:eof/rects]
[(exn:fail:read:non-char? exn) exn:fail:read:non-char/rects]
[else exn:fail:read/rects]))
(raise
(constructor (exn-message exn)
(exn-continuation-marks exn)
(exn:fail:read-srclocs exn)
(build-rectangles
source
initial-space-count table-column-breaks heights lhs))))])
(let loop ()
(define o (/recursive scratch-port #f previous-readtable))
(cond
[(eof-object? o) '()]
[else (cons o (loop))]))))
#;
(define (build-rectangles source table-column-breaks heights set-of-indicies)
(for/list ([pr (in-set set-of-indicies)])
(define x (list-ref pr 0))
(define y (list-ref pr 1))
(srcloc-rect source
?-start-position
(list-ref table-column-breaks x)
(list-ref heights y))))
(define (fill-scratch-string set-of-indicies
lines
scratch-string
table-column-breaks
initial-space-count)
(define scratch-pos 0)
(define-syntax-rule
(set-scratch! c)
(let ([x c])
;(unless (char-whitespace? x) (printf "putting ~s @ ~s\n" x scratch-pos))
(string-set! scratch-string scratch-pos x)))
(define-syntax-rule
(inc-scratch-pos! e)
(set! scratch-pos (+ scratch-pos e)))
(for ([lines (in-vector lines)]
[y (in-naturals)])
(for ([line (in-list lines)]
[l-num (in-naturals)])
(define first-line? (zero? l-num))
;; skip over initial spaces: we know that the string is already right here
;; because it is initialized with spaces and never changed
;; the +1 is for the first character (in the current line)
;; of the table, which is always a table edge character
(inc-scratch-pos! (+ initial-space-count 1))
(define end-of-table-position
(for/fold ([start-pos-in-line (+ initial-space-count 1)])
([table-column-break (in-list table-column-breaks)]
[x (in-naturals)])
(cond
[(and (set-member? set-of-indicies (list x y))
(or (not first-line?)
(set-member? set-of-indicies (list x (- y 1)))))
(for ([j (in-range table-column-break)])
(set-scratch! (string-ref line (+ j start-pos-in-line)))
(inc-scratch-pos! 1))
;(printf "first-line? ~s x ~s y ~s\n" first-line? x y)
(set-scratch! (if (if first-line?
(and (set-member? set-of-indicies (list (+ x 1) (- y 1)))
(set-member? set-of-indicies (list (+ x 1) y))
(set-member? set-of-indicies (list x (- y 1))))
(set-member? set-of-indicies (list (+ x 1) y)))
(string-ref line (+ table-column-break start-pos-in-line))
#\space))
;(printf "set\n")
(inc-scratch-pos! 1)]
[else
(for ([j (in-range table-column-break)])
(set-scratch! #\space)
(inc-scratch-pos! 1))
(set-scratch! #\space)
(inc-scratch-pos! 1)])
(+ start-pos-in-line table-column-break 1)))
(for ([j (in-range end-of-table-position (string-length line))])
(set-scratch! (string-ref line j))
(inc-scratch-pos! 1)))))
(define (compare/xy p1 p2)
(cond
[(= (list-ref p1 0) (list-ref p2 0))
(< (list-ref p1 1) (list-ref p2 1))]
[else
(< (list-ref p1 0) (list-ref p2 0))]))
(define (smallest-representative set)
(define lst (set->list set))
(let loop ([best (car lst)]
[rest (cdr lst)])
(cond
[(null? rest) best]
[else
(cond
[(compare/xy best (car rest))
(loop best (cdr rest))]
[else
(loop (car rest) (cdr rest))])])))
(define (close-cell-graph edges width height)
(define res (make-hash))
(for ([x (in-range width)])
(for ([y (in-range height)])
(hash-set! res (list x y) (set (list x y)))))
(let loop ()
(define something-changed? #f)
(define (add-all n1 n2)
(define in-n1 (hash-ref res n1))
(define in-n2 (hash-ref res n2))
(for ([new-node (in-set in-n1)])
(unless (set-member? in-n2 new-node)
(set! something-changed? #t)
(hash-set! res n2 (set-add in-n2 new-node)))))
(for ([(node-src nodes) (in-hash edges)])
(for ([node-dest (in-set nodes)])
(add-all node-dest node-src)
(add-all node-src node-dest)))
(when something-changed? (loop)))
(apply set (hash-map res (λ (x y) y))))
;; parse-2dcond returns three values:
;; - a hash table encoding a graph that shows where the
;; broken walls are in the 2d
;; - a vector of lists of strings containing the all of the line
;; of the table except the last one; the first string in each
;; list is the boundary line between the two rows
;; - a list of numbers showing the size of each column, not
;; counting the separator character (and not taking into
;; acount broken walls)
;; - the number of spaces to the left of the 2d (same for all lines)
(define (parse-2dcond port source _line _col _pos chars-read)
(define current-line-number _line)
(define current-line-start-position (+ (or _pos 0) chars-read))
(define current-line #f)
(define current-line-length 0)
(define initial-space-count 0)
(define initial-column-guide #f)
(define newline-char-count 0)
(define table-column-breaks '())
(define table-column-guides '())
(define right-edge-column #f)
;; saving the previous lines to build
;; the result for this function
(define pending-row '())
(define rows '())
(define current-row 0)
(define cell-connections (make-hash))
(define (add-node col row)
(define k (list col row))
(unless (hash-ref cell-connections k #f)
(hash-set! cell-connections k (set))))
(define (add-edge col1 row1 col2 row2)
(define (add-->edge col1 row1 col2 row2)
(add-node col1 row1)
(define k (list col1 row1))
(hash-set! cell-connections k (set-add (hash-ref cell-connections k) (list col2 row2))))
(add-->edge col1 row1 col2 row2)
(add-->edge col2 row2 col1 row1))
(define (fetch-next-line)
(when current-line
(set! pending-row (cons current-line pending-row)))
(set! current-line-start-position
(+ current-line-start-position
current-line-length
newline-char-count))
(when current-line-number
(set! current-line-number (+ current-line-number 1)))
(define chars
(let loop ([chars-read 0])
(define c (read-char port))
(cond
[(eof-object? c)
(raise-read-eof-error
"expected eof; "
source _line _col _pos
(and _pos (- _pos (+ current-line-start-position chars-read))))]
[(equal? c #\return)
(cond
[(equal? #\newline (peek-char port))
(set! newline-char-count 2)
(list c (read-char port))]
[else
(set! newline-char-count 1)
(list c)])]
[(equal? c #\newline)
(set! newline-char-count 1)
(list c)]
[(and (equal? c #\╝) (equal? right-edge-column chars-read))
;; if we find a ╝ at the width of the table,
;; then we don't want
;; to consume any more characters and
;; instead to allow subsequent characters
;; to be part of some other thing that's
;; being read (presumably a close paren)
(set! newline-char-count 0)
(list c)]
[else
(cons c (loop (+ chars-read 1)))])))
(set! current-line (apply string chars))
(set! current-line-length (- (string-length current-line) newline-char-count)))
(define (process-first-line)
(fetch-next-line)
(let loop ([pos 0])
(cond
[(< pos current-line-length)
(cond
[(equal? #\space (string-ref current-line pos))
(loop (+ pos 1))]
[(equal? #\╔ (string-ref current-line pos))
(set! initial-column-guide (make-a-guide pos))
(set! initial-space-count pos)]
[else
(line-of-interest)
(readerr "expected the first non-whitespace character in the table to be ╔"
pos)])]
[else
(line-of-interest)
(readerr "expected some non-whitespace characters in the first line of the table"
0
pos)]))
(let loop ([pos (+ initial-space-count 1)]
[current-column-width 0]
[column 0]
[column-breaks '()]
[column-guides '()])
(cond
[(< pos current-line-length)
(case (string-ref current-line pos)
[(#\╦)
(add-node column 0)
(loop (+ pos 1) 0 (+ column 1)
(cons current-column-width column-breaks)
(cons (make-a-guide pos) column-guides))]
[(#\═) (loop (+ pos 1) (+ current-column-width 1) column
column-breaks column-guides)]
[(#\╗)
(add-node column 0)
(whitespace-to-end (+ pos 1))
(set! table-column-breaks (reverse (cons current-column-width column-breaks)))
(set! right-edge-column pos)
(set! table-column-guides (reverse (cons (make-a-guide pos) column-guides)))])]
[else
(line-of-interest)
(readerr "expected ╗ to terminate the first line" pos)])))
(define (process-a-line current-map)
(fetch-next-line)
;; check leading space
(let loop ([n 0])
(cond
[(= n initial-space-count) (void)]
[(and (< n current-line-length)
(equal? #\space (string-ref current-line n)))
(loop (+ n 1))]
[else
(line-of-interest)
(readerr "expected leading space" n)]))
(case (string-ref current-line initial-space-count)
[(#\║) (continue-line current-map)]
[(#\╠) (start-new-block current-map)]
[(#\╚) (finish-table current-map)]
[else
(line-of-interest)
(readerr/expected '(#\║ #\╠ #\╚)
initial-space-count
#:guides (list initial-column-guide))]))
(define (start-new-block previous-map)
(set! current-row (+ current-row 1))
(add-node 0 current-row)
(set! rows (cons (reverse pending-row) rows))
(set! pending-row '())
(let loop ([current-cell-size (car table-column-breaks)]
[table-column-breaks (cdr table-column-breaks)]
[pos (+ initial-space-count 1)]
;; whether or not the section of the line
;; we're currently traversing is there (or not)
[cell-wall-broken? #f]
;; the srcloc of the spot that led us to the decision
;; of which boolean that cell-wall-broken? should be
[cell-wall-guide (make-a-guide initial-space-count)]
;; this is the result, being built up backwards
[map '()]
;; this is the map from the previous cell;
;; it tells us which characters here have to point upwards
[previous-map previous-map]
[current-column 0])
(cond
[(zero? current-cell-size)
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended too soon" pos))
(define sep (string-ref current-line pos))
(cond
[(and cell-wall-broken? (not (car previous-map)))
(unless (equal? sep #\╔)
(when (double-barred-char? sep)
(line-of-interest)
(readerr "expected not to find a cell boundary character" pos)))]
[else
(define allowed-chars
(if (null? table-column-breaks)
(list (get-one (not cell-wall-broken?) (car previous-map) #f #f)
(get-one (not cell-wall-broken?) (car previous-map) #f #t))
(list (get-one (not cell-wall-broken?) (car previous-map) #f #f)
(get-one (not cell-wall-broken?) (car previous-map) #f #t)
(get-one (not cell-wall-broken?) (car previous-map) #t #f)
(get-one (not cell-wall-broken?) (car previous-map) #t #t))))
(unless (member sep allowed-chars)
(line-of-interest)
(readerr/expected (filter values allowed-chars) pos))])
(cond
[(null? table-column-breaks)
(whitespace-to-end (+ pos 1))
(reverse (cons #t map))]
[else
(define next-cell-wall-broken? (not (member sep rt-chars)))
(define edge-going-down? (and (member sep dn-chars) #t))
(define next-column (+ current-column 1))
(add-node next-column current-row)
(when next-cell-wall-broken?
(add-edge next-column current-row
next-column (- current-row 1)))
(unless edge-going-down?
(add-edge next-column current-row
(- next-column 1) current-row))
(loop (car table-column-breaks)
(cdr table-column-breaks)
(+ pos 1)
next-cell-wall-broken?
(make-a-guide pos)
(cons edge-going-down? map)
(cdr previous-map)
next-column)])]
[else
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended in the middle of a cell" pos))
(cond
[cell-wall-broken?
(when (double-barred-char? (string-ref current-line pos))
(line-of-interest)
(readerr
(format "expected not to find a cell boundary character (based on earlier ~a)"
(guide-char cell-wall-guide))
pos
#:guides (list cell-wall-guide)))]
[else
(unless (equal? (string-ref current-line pos) #\═)
(line-of-interest)
(readerr/expected '(#\═) pos #:guides (list cell-wall-guide)))])
(loop (- current-cell-size 1)
table-column-breaks
(+ pos 1)
cell-wall-broken?
cell-wall-guide
map
previous-map
current-column)])))
(define (continue-line map)
(let loop ([current-cell-size (car table-column-breaks)]
[table-column-breaks (cdr table-column-breaks)]
[map map]
[pos (+ initial-space-count 1)]
[column-number 0])
(cond
[(zero? current-cell-size)
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended at the boundary of a cell, expected the edge of the cell" pos))
(cond
[(car map)
(unless (equal? (string-ref current-line pos) #\║)
(line-of-interest)
(readerr/expected '(#\║) pos))]
[else
(when (double-barred-char? (string-ref current-line pos))
(line-of-interest)
(readerr "expected not to find a cell boundary character" pos))])
(cond
[(null? table-column-breaks)
(whitespace-to-end (+ pos 1))]
[else
(loop (car table-column-breaks)
(cdr table-column-breaks)
(cdr map)
(+ pos 1)
(+ column-number 1))])]
[else
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended in the middle of a cell" pos))
(when (double-barred-char? (string-ref current-line pos))
(line-of-interest)
(readerr "expected not to find a cell boundary character" pos))
(loop (- current-cell-size 1)
table-column-breaks
map
(+ pos 1)
column-number)]))
map)
(define (finish-table map)
(set! rows (cons (reverse pending-row) rows))
(let loop ([current-cell-size (car table-column-breaks)]
[table-column-breaks (cdr table-column-breaks)]
[map map]
[pos (+ initial-space-count 1)])
(cond
[(zero? current-cell-size)
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended in the middle of a cell" pos))
(define expected-char
(cond
[(null? table-column-breaks) #\╝]
[(car map) #\╩]
[else #\═]))
(unless (equal? (string-ref current-line pos) expected-char)
(line-of-interest)
(readerr/expected (list expected-char) pos))
(cond
[(null? table-column-breaks)
#f]
[else
(loop (car table-column-breaks)
(cdr table-column-breaks)
(cdr map)
(+ pos 1))])]
[else
(unless (< pos current-line-length)
(line-of-interest)
(readerr "line ended in the middle of a cell" pos))
(unless (equal? (string-ref current-line pos) #\═)
(line-of-interest)
(readerr/expected '(#\═) pos))
(loop (- current-cell-size 1)
table-column-breaks
map
(+ pos 1))])))
(define (whitespace-to-end pos)
(let loop ([pos pos])
(when (< pos current-line-length)
(define c (string-ref current-line pos))
(cond
[(equal? #\space c)
(loop (+ pos 1))]
[(equal? #\; c)
(void)]
[else
(line-of-interest)
(readerr "expected only whitespace outside of the table" pos)]))))
(struct guide (char srcloc))
(define (make-a-guide pos-in-line)
(guide (string-ref current-line pos-in-line)
(srcloc source current-line-number pos-in-line
(+ current-line-start-position pos-in-line)
1)))
(define (readerr/expected chars pos-in-line #:guides [guides '()])
(readerr (format "expected ~a~a~a"
(if (null? (cdr chars))
""
"one of ")
(chars->desc chars "or")
(if (null? guides)
""
(format " (based on earlier ~a)"
(chars->desc (map guide-char guides)
"and"))))
pos-in-line
#:guides guides))
(define (readerr msg pos-in-line [span 1] #:guides [guides '()])
(raise-read-error msg
source
current-line-number
pos-in-line
(+ current-line-start-position pos-in-line)
span
#:extra-srclocs (map guide-srcloc guides)))
(process-first-line)
(let loop ([map (map (λ (x) #t) table-column-breaks)])
(define next-map (process-a-line map))
(cond
[next-map (loop next-map)]
[else
(values cell-connections
(apply vector (reverse rows))
table-column-breaks
initial-space-count)])))
;; chars : non-empty-list-of-char -> string
(define (chars->desc chars sep)
(cond
[(null? (cdr chars))
(format "~a" (car chars))]
[else
(define commas? (pair? (cddr chars)))
(apply
string-append
(let loop ([chars chars]
[first? #t])
(cond
[(null? (cdr chars))
(list (format "~a~a ~a"
(if first? "" " ")
sep
(car chars)))]
[else
(cons (format "~a~a~a"
(if first? "" " ")
(car chars)
(if commas? "," ""))
(loop (cdr chars) #f))])))]))
(define (double-barred-char? c) (member c double-barred-chars))
(define (get-one lt? up? rt? dn?)
(define (cmb a b) (if a b (not b)))
(for/or ([c (in-list double-barred-chars)])
(and (cmb lt? (member c lt-chars))
(cmb up? (member c up-chars))
(cmb rt? (member c rt-chars))
(cmb dn? (member c dn-chars))
c)))

View File

@ -0,0 +1,63 @@
#lang scribble/doc
@(require scribble/base
scribble/manual
"utils.rkt"
(for-label ;unstable/2d/cond
;unstable/2d/match
racket/file
racket/contract
racket/base))
@title[#:tag "2d"]{2D Cond and Match}
@defmodulelang[unstable/2d]{The @racketmodname[unstable/2d] language installs
@litchar{#2d} reader support in the readtable, and then chains to the reader of
another language that is specified immediately after
@racketmodname[unstable/2d].}
The @litchar{#2d} syntax extension adds the ability use
two dimensional grid syntax. That is, you can drawn an ASCII-art
grid and then treat that as a conditional expression. For example,
here is a simple equality function that operates on pairs and
numbers:
@codeblock{
#lang unstable/2d racket
(define (same? a b)
#2dcond
╔═════════════╦═══════════════════════╦═════════════╗
║ ║ (pair? a) ║ (number? a) ║
╠═════════════╬═══════════════════════╬═════════════╣
║ (pair? b) ║ (and (same? (car a) ║ #f ║
║ ║ (car b)) ║ ║
║ ║ (same? (cdr a) ║ ║
║ ║ (cdr b))) ║ ║
╠═════════════╬═══════════════════════╬═════════════╣
║ (number? b) ║ #f ║ (= a b) ║
╚═════════════╩═══════════════════════╩═════════════╝)
}
This notation works in two stages: reading, and parsing (just as in
Racket in general). The reading stage converts anything that begins
with @litchar{#2d} into a parenthesized expression (possibly signaling
errors if the @litchar{═} and @litchar{║} and @litchar{╬}
characters do not line up in the right places).
Since the first line contains @litchar{#2dcond}, the reader will
produce a sequence whose first position is the identifier @racket[2dcond].
That macro will take over and then expand into ordinary conditional
expressions, in this case figuring out whether or not the inputs
are pairs or numbers and selecting the appropriate cell.
@section{2D Cond}
@defmodule[unstable/2d/cond]
@defform[(2dcond . stuff)]{}
@section{2D Match}
@defmodule[unstable/2d/match]
@defform[(2dmatch . stuff)]{}

View File

@ -104,6 +104,7 @@ Keep documentation and tests up to date.
@include-section["custom-write.scrbl"] ;; Struct Printing
@include-section["syntax.scrbl"]
@include-section["../temp-c/scribblings/temp-c.scrbl"]
@include-section["2d.scrbl"]
@;{--------}