;; ;; table-style.scm ;; ;; Converts and expands table border styles. ;; ;; ISC License ;; ;; Copyright 2023 Dominik Pantůček ;; ;; Permission to use, copy, modify, and/or distribute this software ;; for any purpose with or without fee is hereby granted, provided ;; that the above copyright notice and this permission notice appear ;; in all copies. ;; ;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL ;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED ;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE ;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR ;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS ;; OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, ;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN ;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ;; (declare (unit table-style)) (module table-style ( expand-table-style table-style-tests! ) (import scheme (chicken base) (chicken keyword) box-drawing testing template-list-expander) ;; Consumes single border specification from cell borders. Returns: ;; sides, line-style-spec and rest. (define (table-border-style-consume lst) (let loop ((sides '()) (sides-done? #f) (line-style-spec '()) (lst lst)) (if (null? lst) ;; Last style in the list, just finish (values (reverse sides) (reverse line-style-spec) lst) (let ((tk (car lst))) (cond ((and sides-done? (keyword? tk)) ;; Next side spec continues (values (reverse sides) (reverse line-style-spec) lst)) ((keyword? tk) ;; Still adding sides (loop (cons tk sides) sides-done? ;; Should be always #f line-style-spec ;; Should always be '() (cdr lst))) (else ;; Line specification (loop sides #t (cons tk line-style-spec) (cdr lst)))))))) ;; Compiles correct box drawing cell and merges it with given cell (define (combine-cell-border cell sides line-style-spec) (let ((line-style (spec->line-style line-style-spec))) ;; Overide given sides (if (null? sides) (make-line-cell line-style line-style line-style line-style) (let loop ((sides sides) (cell cell)) (if (null? sides) cell (loop (cdr sides) (case (car sides) ((#:north #:top #:up) (set-line-cell-north cell line-style)) ((#:west #:left) (set-line-cell-west cell line-style)) ((#:east #:right) (set-line-cell-east cell line-style)) ((#:south #:bottom #:down) (set-line-cell-south cell line-style)) (else cell)))))))) ;; Parses border style specification for single cell, returns ;; box-drawing cell with slightly different meaning of NWES sides. (define (parse-table-cell-border-style spec-arg) (let ((spec (if (list? spec-arg) spec-arg (list spec-arg)))) (let loop ((spec spec) (cell line-cell-none)) (if (null? spec) ;; Finished, return, whatever we accumulated cell (let-values (((sides line-style-spec rest) (table-border-style-consume spec))) (loop rest (combine-cell-border cell sides line-style-spec))))))) ;; Converts all "cells" using parse-table-cell-border-style (define (compile-table-style-spec spec) (map (lambda (row) (if (template-expansion-token? row) row (map (lambda (cell) (if (template-expansion-token? cell) cell (parse-table-cell-border-style cell))) row))) spec)) ;; Converts the template skipping dots, expands the result (define (expand-table-style spec width height) (let ((cspec (compile-table-style-spec spec))) (expand-template-list (map (lambda (row) (if (template-expansion-token? row) row (expand-template-list row width))) cspec) height))) ;; Module self-tests (define (table-style-tests!) (run-tests table-style (test-equal? parse-table-cell-border-style (parse-table-cell-border-style 'light) #b1001100110011001) (test-equal? parse-table-cell-border-style (parse-table-cell-border-style '(light dashed)) #b101010101010101) (test-equal? parse-table-cell-border-style (parse-table-cell-border-style '(light #:top none)) #b1001100110010000) (test-equal? compile-table-style-spec (compile-table-style-spec '(((heavy dashed) ...) ((light #:left #:right none) ...) ... (dashed ...))) '((#b0110011001100110 ...) (#b1001000000001001 ...) ... (#b0101010101010101 ...))) (test-equal? expand-table-style (expand-table-style '(((heavy dashed) ...) ((light #:left #:right none) ...) ... (dashed ...)) 4 4) '((#b0110011001100110 #b0110011001100110 #b0110011001100110 #b0110011001100110) (#b1001000000001001 #b1001000000001001 #b1001000000001001 #b1001000000001001) (#b1001000000001001 #b1001000000001001 #b1001000000001001 #b1001000000001001) (#b0101010101010101 #b0101010101010101 #b0101010101010101 #b0101010101010101))) )) )