diff --git a/quad/quad/dev.rkt b/quad/quad/dev.rkt new file mode 100644 index 00000000..b28ee734 --- /dev/null +++ b/quad/quad/dev.rkt @@ -0,0 +1,12 @@ +#lang racket/base +(require (for-syntax racket/base)) +(provide (except-out (all-from-out racket/base) #%module-begin) + (rename-out [~module-begin #%module-begin]) + (for-syntax (all-from-out racket/base))) + +(define-syntax-rule (~module-begin . args) + (#%module-begin + . args)) + +(module reader syntax/module-reader + #:language 'quad/dev) \ No newline at end of file diff --git a/quad/quad/foo.rkt b/quad/quad/foo.rkt index 95c45ef2..fa7ef132 100644 --- a/quad/quad/foo.rkt +++ b/quad/quad/foo.rkt @@ -1,14 +1,2 @@ -#lang sugar/debug racket - -(define 1-out - (block '(measure 240.0 font "Times New Roman" leading 16.0 vmeasure 300.0 size 13.5 x-align justify x-align-last-line left) (box '(width 15.0)) (block '() (block '(weight bold) "Hot " (word '(size 22.0) "D") "ang, My Fellow Americans.") " This " (block '(no-break #t) "is some truly") " nonsense generated from my typesetting system, which is called Quad. I’m writing this in a source file in DrRacket. When I click [Run], a PDF pops out. Not bad\u200a—\u200aand no LaTeX needed. Quad, however, does use the fancy linebreaking algorithm developed for TeX. (It also includes a faster linebreaking algorithm for when speed is more important than quality.) Of course, it can also handle " (block '(font "Courier") "different fonts,") (block '(style italic) " styles, ") (word '(size 14.0 weight bold) "and sizes-") " within the same line. As you can see, it can also justify paragraphs."))) - -(require quad/typeset quad/quads quad/render) - -;(time (send (new pdf-renderer%) render-to-file (typeset 1-out) "f1-test.pdf")) - -(require (prefix-in 2- "foo2.rkt")) -(time (send (new pdf-renderer%) render-to-file (typeset 2-out) "f2-test.pdf")) - -(require (prefix-in 3- "foo3.rkt")) -;(time (send (new pdf-renderer%) render-to-file (typeset 3-out) "f3-test.pdf")) +#lang quad +(q:zoo #f "hi" "there" "America") \ No newline at end of file diff --git a/quad/quad/main.rkt b/quad/quad/main.rkt index cb460cbf..679f2fe9 100644 --- a/quad/quad/main.rkt +++ b/quad/quad/main.rkt @@ -1,52 +1,8 @@ #lang racket/base -(provide (except-out (all-from-out racket/base) #%module-begin) - (rename-out [quad-module-begin #%module-begin])) -(require (for-syntax racket/base syntax/strip-context)) -(require quad/quads quad/typeset quad/world quad/render racket/class) +(require "top.rkt") +(provide (except-out (all-from-out racket/base) #%top) + (rename-out [~top #%top])) -(define-syntax (quad-module-begin stx) - (syntax-case stx () - [(_ expr ...) - (replace-context #'(expr ...) - #'(#%module-begin - (module outy racket/base - (require quad/quads) - (define out (block '(font "Times New Roman" measure 360.0 leading 14.0 column-count 1 column-gutter 10.0 size 11.5 x-align justify x-align-last-line left) expr ...)) - (provide out)) - (require 'outy) - (provide (all-from-out 'outy)) - (displayln out)))])) (module reader syntax/module-reader - quad/main - #:read quad-read - #:read-syntax quad-read-syntax - #:whole-body-readers? #t ;; need this to make at-reader work - #:info custom-get-info - (require scribble/reader) - - (define (quad-read p) - (syntax->datum (quad-read-syntax (object-name p) p))) - - (define quad-command-char #\@) - - (define (quad-read-syntax path-string p) - (define quad-at-reader (make-at-reader - #:command-char quad-command-char - #:syntax? #t - #:inside? #t)) - (define source-stx (quad-at-reader path-string p)) - source-stx) - - (define (custom-get-info key default [proc (λ _ #f)]) - (case key - [(color-lexer) - (define my-make-scribble-inside-lexer - (dynamic-require 'syntax-color/scribble-lexer 'make-scribble-inside-lexer (λ () #f))) - (cond [my-make-scribble-inside-lexer - (my-make-scribble-inside-lexer #:command-char quad-command-char)] - [else default])] - [(drracket:toolbar-buttons) - (define my-make-drracket-buttons (dynamic-require 'quad/buttons 'make-drracket-buttons)) - (my-make-drracket-buttons)] - [else default]))) \ No newline at end of file + #:language 'quad) \ No newline at end of file diff --git a/quad/quad/breaktester.rkt b/quad/quad/old-master/breaktester.rkt similarity index 100% rename from quad/quad/breaktester.rkt rename to quad/quad/old-master/breaktester.rkt diff --git a/quad/quad/buttons.rkt b/quad/quad/old-master/buttons.rkt similarity index 100% rename from quad/quad/buttons.rkt rename to quad/quad/old-master/buttons.rkt diff --git a/quad/quad/cmd-char.png b/quad/quad/old-master/cmd-char.png similarity index 100% rename from quad/quad/cmd-char.png rename to quad/quad/old-master/cmd-char.png diff --git a/quad/quad/experimental.rkt b/quad/quad/old-master/experimental.rkt similarity index 100% rename from quad/quad/experimental.rkt rename to quad/quad/old-master/experimental.rkt diff --git a/quad/quad/ffi/fc-bindings.rkt b/quad/quad/old-master/ffi/fc-bindings.rkt similarity index 100% rename from quad/quad/ffi/fc-bindings.rkt rename to quad/quad/old-master/ffi/fc-bindings.rkt diff --git a/quad/quad/ffi/pango-bindings.rkt b/quad/quad/old-master/ffi/pango-bindings.rkt similarity index 100% rename from quad/quad/ffi/pango-bindings.rkt rename to quad/quad/old-master/ffi/pango-bindings.rkt diff --git a/quad/quad/old-master/foo.rkt b/quad/quad/old-master/foo.rkt new file mode 100644 index 00000000..95c45ef2 --- /dev/null +++ b/quad/quad/old-master/foo.rkt @@ -0,0 +1,14 @@ +#lang sugar/debug racket + +(define 1-out + (block '(measure 240.0 font "Times New Roman" leading 16.0 vmeasure 300.0 size 13.5 x-align justify x-align-last-line left) (box '(width 15.0)) (block '() (block '(weight bold) "Hot " (word '(size 22.0) "D") "ang, My Fellow Americans.") " This " (block '(no-break #t) "is some truly") " nonsense generated from my typesetting system, which is called Quad. I’m writing this in a source file in DrRacket. When I click [Run], a PDF pops out. Not bad\u200a—\u200aand no LaTeX needed. Quad, however, does use the fancy linebreaking algorithm developed for TeX. (It also includes a faster linebreaking algorithm for when speed is more important than quality.) Of course, it can also handle " (block '(font "Courier") "different fonts,") (block '(style italic) " styles, ") (word '(size 14.0 weight bold) "and sizes-") " within the same line. As you can see, it can also justify paragraphs."))) + +(require quad/typeset quad/quads quad/render) + +;(time (send (new pdf-renderer%) render-to-file (typeset 1-out) "f1-test.pdf")) + +(require (prefix-in 2- "foo2.rkt")) +(time (send (new pdf-renderer%) render-to-file (typeset 2-out) "f2-test.pdf")) + +(require (prefix-in 3- "foo3.rkt")) +;(time (send (new pdf-renderer%) render-to-file (typeset 3-out) "f3-test.pdf")) diff --git a/quad/quad/foo2.rkt b/quad/quad/old-master/foo2.rkt similarity index 100% rename from quad/quad/foo2.rkt rename to quad/quad/old-master/foo2.rkt diff --git a/quad/quad/foo3.rkt b/quad/quad/old-master/foo3.rkt similarity index 100% rename from quad/quad/foo3.rkt rename to quad/quad/old-master/foo3.rkt diff --git a/quad/quad/info.rkt b/quad/quad/old-master/info.rkt similarity index 100% rename from quad/quad/info.rkt rename to quad/quad/old-master/info.rkt diff --git a/quad/quad/logger.rkt b/quad/quad/old-master/logger.rkt similarity index 100% rename from quad/quad/logger.rkt rename to quad/quad/old-master/logger.rkt diff --git a/quad/quad/old-master/main.rkt b/quad/quad/old-master/main.rkt new file mode 100644 index 00000000..cb460cbf --- /dev/null +++ b/quad/quad/old-master/main.rkt @@ -0,0 +1,52 @@ +#lang racket/base +(provide (except-out (all-from-out racket/base) #%module-begin) + (rename-out [quad-module-begin #%module-begin])) +(require (for-syntax racket/base syntax/strip-context)) +(require quad/quads quad/typeset quad/world quad/render racket/class) + +(define-syntax (quad-module-begin stx) + (syntax-case stx () + [(_ expr ...) + (replace-context #'(expr ...) + #'(#%module-begin + (module outy racket/base + (require quad/quads) + (define out (block '(font "Times New Roman" measure 360.0 leading 14.0 column-count 1 column-gutter 10.0 size 11.5 x-align justify x-align-last-line left) expr ...)) + (provide out)) + (require 'outy) + (provide (all-from-out 'outy)) + (displayln out)))])) + +(module reader syntax/module-reader + quad/main + #:read quad-read + #:read-syntax quad-read-syntax + #:whole-body-readers? #t ;; need this to make at-reader work + #:info custom-get-info + (require scribble/reader) + + (define (quad-read p) + (syntax->datum (quad-read-syntax (object-name p) p))) + + (define quad-command-char #\@) + + (define (quad-read-syntax path-string p) + (define quad-at-reader (make-at-reader + #:command-char quad-command-char + #:syntax? #t + #:inside? #t)) + (define source-stx (quad-at-reader path-string p)) + source-stx) + + (define (custom-get-info key default [proc (λ _ #f)]) + (case key + [(color-lexer) + (define my-make-scribble-inside-lexer + (dynamic-require 'syntax-color/scribble-lexer 'make-scribble-inside-lexer (λ () #f))) + (cond [my-make-scribble-inside-lexer + (my-make-scribble-inside-lexer #:command-char quad-command-char)] + [else default])] + [(drracket:toolbar-buttons) + (define my-make-drracket-buttons (dynamic-require 'quad/buttons 'make-drracket-buttons)) + (my-make-drracket-buttons)] + [else default]))) \ No newline at end of file diff --git a/quad/quad/measure.rkt b/quad/quad/old-master/measure.rkt similarity index 100% rename from quad/quad/measure.rkt rename to quad/quad/old-master/measure.rkt diff --git a/quad/quad/ocm.rkt b/quad/quad/old-master/ocm.rkt similarity index 100% rename from quad/quad/ocm.rkt rename to quad/quad/old-master/ocm.rkt diff --git a/quad/quad/py/SMAWK.py b/quad/quad/old-master/py/SMAWK.py similarity index 100% rename from quad/quad/py/SMAWK.py rename to quad/quad/old-master/py/SMAWK.py diff --git a/quad/quad/py/SMAWK.pyc b/quad/quad/old-master/py/SMAWK.pyc similarity index 100% rename from quad/quad/py/SMAWK.pyc rename to quad/quad/old-master/py/SMAWK.pyc diff --git a/quad/quad/py/smawktest.py b/quad/quad/old-master/py/smawktest.py similarity index 100% rename from quad/quad/py/smawktest.py rename to quad/quad/old-master/py/smawktest.py diff --git a/quad/quad/old-master/quads.rkt b/quad/quad/old-master/quads.rkt new file mode 100644 index 00000000..028f8a55 --- /dev/null +++ b/quad/quad/old-master/quads.rkt @@ -0,0 +1,239 @@ +#lang racket/base +(require (for-syntax racket/base racket/syntax racket/string) racket/string racket/contract racket/serialize sugar/list racket/format racket/list sugar/debug sugar/coerce racket/bool racket/function sugar/unstable/string) +(require "world.rkt") +(provide (all-defined-out)) + +;; struct implementation + +(serializable-struct quad (name attrs list) #:transparent + #:methods gen:custom-write + [(define write-proc (λ(b port mode) + (display (format "(~a)" (string-join (filter-not void? (list + (~a (quad-name b)) + (if (and (hash? (quad-attrs b)) (> (length (hash-keys (quad-attrs b))) 0)) (~v (flatten (hash->list (quad-attrs b)))) "#f") + (if (> (length (quad-list b)) 0) (~a (string-join (map ~v (quad-list b)) " ")) (void)))) " ")) port)))] + #:property prop:sequence (λ(q) (quad-list q))) + + + +;; vector implementation +#| +(define (quad-name q) (vector-ref q 0)) +(define (quad-attrs q) (vector-ref q 1)) +(define (quad-list q) (vector-ref q 2)) + +(define (quad? x) + (and (vector? x) + (symbol? (quad-name x)) + (or (false? (quad-attrs x)) (hash? (quad-attrs x))) + (list? (quad-list x)))) + +(define (quad name attrs xs) + (vector name attrs xs)) +|# +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; hash implementation +#| +(define (quad-name q) (hash-ref q 'name)) +(define (quad-attrs q) (hash-ref q 'attrs)) +(define (quad-list q) (hash-ref q 'list)) + +(define (quad? x) + (and (hash? x) + (andmap (λ(k) (hash-has-key? x k)) (list 'name 'attrs 'list)) + (symbol? (quad-name x)) + (ormap (λ(pred) (pred (quad-attrs x))) (list false? hash?)) + (list? (quad-list x)))) + +(define (quad name attrs xs) + (hash 'name name 'attrs attrs 'list xs)) +|# + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define (quad-name? x) (symbol? x)) +(define (hashable-list? x) (and (list? x) (even? (length x)))) +(define (quad-attrs? x) (or (false? x) (hash? x))) +(define (quad-list? x) (and (list? x) (andmap (λ(xi) (or (quad? xi) (and (string? xi) (< 0 (string-length xi))))) x))) +(define (quads? x) (and (list? x) (andmap quad? x))) +(define (lists-of-quads? x) (and (list? x) (andmap quads? x))) + +(define quad= equal?) + +(define token? quad?) + +(define (quad/c x) (λ(x) (and (quad? x) (symbol? (quad-name x)) (hash? (quad-attrs x)) + (andmap (λ(xi) (or (quad/c xi) (string? xi))) (quad-list x))))) + +(define quad-attr-ref + (case-lambda + [(q key) + (if (quad-attrs q) + (hash-ref (quad-attrs q) key) + (error 'quad-attr-ref (format "no attrs in quad ~a" q)))] + [(q key default) + (if (quad-attrs q) + (hash-ref (quad-attrs q) key default) + default)])) + +(define-syntax (quad-attr-ref/parameter stx) + (syntax-case stx () + [(_ q key) + (with-syntax ([world:key-default (format-id stx "~a-default" (string-trim (symbol->string (syntax->datum #'key)) "-key"))]) + #'(quad-attr-ref q key (world:key-default)))])) + + +(define (quad-has-attr? q key) + (define qa (quad-attrs q)) + (and qa (hash-has-key? qa key))) + +(define-syntax (define-quad-list-function stx) + (syntax-case stx () + [(_ proc) + (with-syntax ([quad-proc (format-id stx "quad-~a" #'proc)]) + #'(define (quad-proc q) (proc (quad-list q))))])) + +(define-quad-list-function first) +(define-quad-list-function car) +(define-quad-list-function cdr) +(define-quad-list-function last) +(define (quad-cons item q) + (quad (quad-name q) (quad-attrs q) (cons item (quad-list q)))) + +(define-syntax-rule (quad-ref q r) + (list-ref (quad-list q) r)) + +(define (quad-ends-with? q str) + (quad? string? . -> . boolean?) + (cond + [(not (empty? (quad-list q))) + (define last-item (last (quad-list q))) + (cond + [(string? last-item) (ends-with? last-item str)] + [(quad? last-item) (quad-ends-with? last-item str)])] + [else #f])) + + +(define (quad-append q new-item) + (quad? (or/c quad? string?) . -> . quad?) + (quad (quad-name q) (quad-attrs q) (append (quad-list q) (list new-item)))) + +(define (quad->string x) + (quad? . -> . string?) + (cond + [(quad? x) (string-append* (map quad->string (quad-list x)))] + [(string? x) x] + [else ""])) + +(define-syntax-rule (report-quadstring q) + (begin + (report (quad->string q) 'q) + q)) + +(define cannot-be-common-attrs '(width x y page)) ;; todo: how to specify these better? this-* prefix? + +;; make this a macro because qs-in is often huge +;; and the macro avoids allocation + garbage collection +(define attr-missing (gensym)) +(define (gather-common-attrs qs) + (let loop ([qs qs] + [common-attrs (if (quad-attrs (car qs)) + (for/list ([kv-pair (in-hash-pairs (quad-attrs (car qs)))] + #:unless (member (car kv-pair) cannot-be-common-attrs)) + kv-pair) + empty)]) + (cond + [(empty? common-attrs) empty] + [(empty? qs) (flatten common-attrs)] + [else (loop (cdr qs) + (filter (λ(ca) (equal? (quad-attr-ref (car qs) (car ca) attr-missing) (cdr ca))) + common-attrs))]))) + + +(define-syntax (define-box-type stx) + (syntax-case stx () + [(_ id) + (with-syntax ([id? (format-id #'id "~a?" #'id)] + [ids? (format-id #'id "~as?" #'id)] + [lists-of-ids? (format-id #'id "list-of-~as?" #'id)] + [quads->id (format-id #'id "quads->~a" #'id)] + [inline/quads->id (format-id #'id "inline/quads->~a" #'id)]) + #'(begin + ;; quad predicate - ok to be relaxed here if we're strict when making the struct + (define (id? x) + (and (quad? x) (equal? (quad-name x) 'id))) + ;; quad constructor + ;; put contract here rather than on struct, because this is the main interface + ;; and this contract is more liberal. + ;; but don't put a separate contract on struct, because it's superfluous. + (define (id [attrs empty] . xs) + (() ((or/c quad-attrs? hashable-list?)) #:rest quad-list? . ->* . id?) + (quad 'id (and attrs (if (hash? attrs) attrs (apply hash attrs))) xs)) + ;; quad list predicate and list-of-list predicate. + ;; These are faster than the listof contract combinator. + (define (ids? x) + (and (list? x) (andmap id? x))) + (define (lists-of-ids? x) + (and (list? x) (andmap ids? x))) + ;; quad converter macro + (define (quads->id qs) + (apply id (gather-common-attrs qs) qs))))])) + + +;; do not treat empty string as whitespace. +;; throws off tests that rely on adjacency to positive whitespace. +(define (whitespace? x [nbsp? #f]) + ((any/c)(boolean?) . ->* . coerce/boolean?) + (cond + [(quad? x) (whitespace? (quad-list x) nbsp?)] + [(string? x) (or (and (regexp-match #px"\\p{Zs}" x) ; Zs = unicode whitespace category + (or nbsp? (not (regexp-match #px"\u00a0" x)))))] ; 00a0: nbsp + [(list? x) (and (not (empty? x)) (andmap (curryr whitespace? nbsp?) x))] ; andmap returns #t for empty lists + [else #f])) + +(define (whitespace/nbsp? x) + (whitespace? x #t)) + + +(define-syntax (define-break-type stx) + (syntax-case stx () + [(_ id) + (with-syntax ([split-on-id-breaks (format-id #'id "split-on-~a-breaks" #'id)] + [id-break (format-id #'id "~a-break" #'id)] + [id-break? (format-id #'id "~a-break?" #'id)] + [multi-id (format-id #'id "multi~a" #'id)] + [multi-id? (format-id #'id "multi~a?" #'id)] + [quads->multi-id (format-id #'id "quads->multi~a" #'id)]) + #'(begin + (define-box-type id) + (define-box-type id-break) + (define-box-type multi-id) + ;; breaker + (define (split-on-id-breaks x) + (quads? . -> . lists-of-quads?) + ;; omit leading & trailing whitespace, because they're superfluous next to a break + (map (curryr trimf whitespace?) (filter-split x id-break?)))))])) + +(define-box-type box) + +(define-break-type word) +(define (word-string c) (car (quad-list c))) + +(define-box-type spacer) +(define-box-type kern) +(define-box-type optical-kern) +(define-box-type flag) +(define-box-type doc) +(define-box-type input) +(define-box-type piece) +(define-box-type run) + +(define-break-type page) +(define-break-type column) +(define-break-type block) +(define-break-type line) + +(define (->input q) (input empty q)) +(define coerce/input? (make-coercion-contract input)) + diff --git a/quad/quad/quick-sample.rkt b/quad/quad/old-master/quick-sample.rkt similarity index 100% rename from quad/quad/quick-sample.rkt rename to quad/quad/old-master/quick-sample.rkt diff --git a/quad/quad/quick-sample.rktd b/quad/quad/old-master/quick-sample.rktd similarity index 100% rename from quad/quad/quick-sample.rktd rename to quad/quad/old-master/quick-sample.rktd diff --git a/quad/quad/quick-test.rkt b/quad/quad/old-master/quick-test.rkt similarity index 100% rename from quad/quad/quick-test.rkt rename to quad/quad/old-master/quick-test.rkt diff --git a/quad/quad/render.rkt b/quad/quad/old-master/render.rkt similarity index 100% rename from quad/quad/render.rkt rename to quad/quad/old-master/render.rkt diff --git a/quad/quad/samples-base.rktd b/quad/quad/old-master/samples-base.rktd similarity index 100% rename from quad/quad/samples-base.rktd rename to quad/quad/old-master/samples-base.rktd diff --git a/quad/quad/samples.rkt b/quad/quad/old-master/samples.rkt similarity index 100% rename from quad/quad/samples.rkt rename to quad/quad/old-master/samples.rkt diff --git a/quad/quad/scribblings/manual-fonts.css b/quad/quad/old-master/scribblings/manual-fonts.css similarity index 100% rename from quad/quad/scribblings/manual-fonts.css rename to quad/quad/old-master/scribblings/manual-fonts.css diff --git a/quad/quad/scribblings/manual-racket.css b/quad/quad/old-master/scribblings/manual-racket.css similarity index 100% rename from quad/quad/scribblings/manual-racket.css rename to quad/quad/old-master/scribblings/manual-racket.css diff --git a/quad/quad/scribblings/manual-racket.js b/quad/quad/old-master/scribblings/manual-racket.js similarity index 100% rename from quad/quad/scribblings/manual-racket.js rename to quad/quad/old-master/scribblings/manual-racket.js diff --git a/quad/quad/scribblings/manual-style.css b/quad/quad/old-master/scribblings/manual-style.css similarity index 100% rename from quad/quad/scribblings/manual-style.css rename to quad/quad/old-master/scribblings/manual-style.css diff --git a/quad/quad/scribblings/quad.html b/quad/quad/old-master/scribblings/quad.html similarity index 100% rename from quad/quad/scribblings/quad.html rename to quad/quad/old-master/scribblings/quad.html diff --git a/quad/quad/scribblings/quad.scrbl b/quad/quad/old-master/scribblings/quad.scrbl similarity index 100% rename from quad/quad/scribblings/quad.scrbl rename to quad/quad/old-master/scribblings/quad.scrbl diff --git a/quad/quad/scribblings/racket.css b/quad/quad/old-master/scribblings/racket.css similarity index 100% rename from quad/quad/scribblings/racket.css rename to quad/quad/old-master/scribblings/racket.css diff --git a/quad/quad/scribblings/scribble-common.js b/quad/quad/old-master/scribblings/scribble-common.js similarity index 100% rename from quad/quad/scribblings/scribble-common.js rename to quad/quad/old-master/scribblings/scribble-common.js diff --git a/quad/quad/scribblings/scribble.css b/quad/quad/old-master/scribblings/scribble.css similarity index 100% rename from quad/quad/scribblings/scribble.css rename to quad/quad/old-master/scribblings/scribble.css diff --git a/quad/quad/segfault.rkt b/quad/quad/old-master/segfault.rkt similarity index 100% rename from quad/quad/segfault.rkt rename to quad/quad/old-master/segfault.rkt diff --git a/quad/quad/stats-data.txt b/quad/quad/old-master/stats-data.txt similarity index 100% rename from quad/quad/stats-data.txt rename to quad/quad/old-master/stats-data.txt diff --git a/quad/quad/stats.rkt b/quad/quad/old-master/stats.rkt similarity index 100% rename from quad/quad/stats.rkt rename to quad/quad/old-master/stats.rkt diff --git a/quad/quad/test-quadlang.rkt b/quad/quad/old-master/test-quadlang.rkt similarity index 100% rename from quad/quad/test-quadlang.rkt rename to quad/quad/old-master/test-quadlang.rkt diff --git a/quad/quad/test-quadlangmod.rkt b/quad/quad/old-master/test-quadlangmod.rkt similarity index 100% rename from quad/quad/test-quadlangmod.rkt rename to quad/quad/old-master/test-quadlangmod.rkt diff --git a/quad/quad/test-render.rkt b/quad/quad/old-master/test-render.rkt similarity index 100% rename from quad/quad/test-render.rkt rename to quad/quad/old-master/test-render.rkt diff --git a/quad/quad/tests-ocm.rkt b/quad/quad/old-master/tests-ocm.rkt similarity index 100% rename from quad/quad/tests-ocm.rkt rename to quad/quad/old-master/tests-ocm.rkt diff --git a/quad/quad/tests.rkt b/quad/quad/old-master/tests.rkt similarity index 100% rename from quad/quad/tests.rkt rename to quad/quad/old-master/tests.rkt diff --git a/quad/quad/texts/jude.txt b/quad/quad/old-master/texts/jude.txt similarity index 100% rename from quad/quad/texts/jude.txt rename to quad/quad/old-master/texts/jude.txt diff --git a/quad/quad/texts/jude0.txt b/quad/quad/old-master/texts/jude0.txt similarity index 100% rename from quad/quad/texts/jude0.txt rename to quad/quad/old-master/texts/jude0.txt diff --git a/quad/quad/texts/jude2.txt b/quad/quad/old-master/texts/jude2.txt similarity index 100% rename from quad/quad/texts/jude2.txt rename to quad/quad/old-master/texts/jude2.txt diff --git a/quad/quad/texts/judebig.txt b/quad/quad/old-master/texts/judebig.txt similarity index 100% rename from quad/quad/texts/judebig.txt rename to quad/quad/old-master/texts/judebig.txt diff --git a/quad/quad/texts/segfault.txt b/quad/quad/old-master/texts/segfault.txt similarity index 100% rename from quad/quad/texts/segfault.txt rename to quad/quad/old-master/texts/segfault.txt diff --git a/quad/quad/todo.rkt b/quad/quad/old-master/todo.rkt similarity index 100% rename from quad/quad/todo.rkt rename to quad/quad/old-master/todo.rkt diff --git a/quad/quad/typeset.rkt b/quad/quad/old-master/typeset.rkt similarity index 100% rename from quad/quad/typeset.rkt rename to quad/quad/old-master/typeset.rkt diff --git a/quad/quad/utils.rkt b/quad/quad/old-master/utils.rkt similarity index 100% rename from quad/quad/utils.rkt rename to quad/quad/old-master/utils.rkt diff --git a/quad/quad/world.rkt b/quad/quad/old-master/world.rkt similarity index 100% rename from quad/quad/world.rkt rename to quad/quad/old-master/world.rkt diff --git a/quad/quad/wrap.rkt b/quad/quad/old-master/wrap.rkt similarity index 100% rename from quad/quad/wrap.rkt rename to quad/quad/old-master/wrap.rkt diff --git a/quad/quad/quads.rkt b/quad/quad/quads.rkt index 028f8a55..a4dc1cfc 100644 --- a/quad/quad/quads.rkt +++ b/quad/quad/quads.rkt @@ -1,239 +1,29 @@ -#lang racket/base -(require (for-syntax racket/base racket/syntax racket/string) racket/string racket/contract racket/serialize sugar/list racket/format racket/list sugar/debug sugar/coerce racket/bool racket/function sugar/unstable/string) -(require "world.rkt") +#lang quad/dev (provide (all-defined-out)) +(require (for-syntax racket/string)) -;; struct implementation - -(serializable-struct quad (name attrs list) #:transparent - #:methods gen:custom-write - [(define write-proc (λ(b port mode) - (display (format "(~a)" (string-join (filter-not void? (list - (~a (quad-name b)) - (if (and (hash? (quad-attrs b)) (> (length (hash-keys (quad-attrs b))) 0)) (~v (flatten (hash->list (quad-attrs b)))) "#f") - (if (> (length (quad-list b)) 0) (~a (string-join (map ~v (quad-list b)) " ")) (void)))) " ")) port)))] - #:property prop:sequence (λ(q) (quad-list q))) - - - -;; vector implementation -#| (define (quad-name q) (vector-ref q 0)) (define (quad-attrs q) (vector-ref q 1)) (define (quad-list q) (vector-ref q 2)) + (define (quad? x) (and (vector? x) (symbol? (quad-name x)) - (or (false? (quad-attrs x)) (hash? (quad-attrs x))) + (or (not (quad-attrs x)) (hash? (quad-attrs x))) (list? (quad-list x)))) -(define (quad name attrs xs) - (vector name attrs xs)) -|# -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; hash implementation -#| -(define (quad-name q) (hash-ref q 'name)) -(define (quad-attrs q) (hash-ref q 'attrs)) -(define (quad-list q) (hash-ref q 'list)) - -(define (quad? x) - (and (hash? x) - (andmap (λ(k) (hash-has-key? x k)) (list 'name 'attrs 'list)) - (symbol? (quad-name x)) - (ormap (λ(pred) (pred (quad-attrs x))) (list false? hash?)) - (list? (quad-list x)))) +(define (quad-attrs? x) (list? x)) (define (quad name attrs xs) - (hash 'name name 'attrs attrs 'list xs)) -|# - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define (quad-name? x) (symbol? x)) -(define (hashable-list? x) (and (list? x) (even? (length x)))) -(define (quad-attrs? x) (or (false? x) (hash? x))) -(define (quad-list? x) (and (list? x) (andmap (λ(xi) (or (quad? xi) (and (string? xi) (< 0 (string-length xi))))) x))) -(define (quads? x) (and (list? x) (andmap quad? x))) -(define (lists-of-quads? x) (and (list? x) (andmap quads? x))) - -(define quad= equal?) - -(define token? quad?) - -(define (quad/c x) (λ(x) (and (quad? x) (symbol? (quad-name x)) (hash? (quad-attrs x)) - (andmap (λ(xi) (or (quad/c xi) (string? xi))) (quad-list x))))) - -(define quad-attr-ref - (case-lambda - [(q key) - (if (quad-attrs q) - (hash-ref (quad-attrs q) key) - (error 'quad-attr-ref (format "no attrs in quad ~a" q)))] - [(q key default) - (if (quad-attrs q) - (hash-ref (quad-attrs q) key default) - default)])) - -(define-syntax (quad-attr-ref/parameter stx) - (syntax-case stx () - [(_ q key) - (with-syntax ([world:key-default (format-id stx "~a-default" (string-trim (symbol->string (syntax->datum #'key)) "-key"))]) - #'(quad-attr-ref q key (world:key-default)))])) - - -(define (quad-has-attr? q key) - (define qa (quad-attrs q)) - (and qa (hash-has-key? qa key))) - -(define-syntax (define-quad-list-function stx) - (syntax-case stx () - [(_ proc) - (with-syntax ([quad-proc (format-id stx "quad-~a" #'proc)]) - #'(define (quad-proc q) (proc (quad-list q))))])) - -(define-quad-list-function first) -(define-quad-list-function car) -(define-quad-list-function cdr) -(define-quad-list-function last) -(define (quad-cons item q) - (quad (quad-name q) (quad-attrs q) (cons item (quad-list q)))) - -(define-syntax-rule (quad-ref q r) - (list-ref (quad-list q) r)) - -(define (quad-ends-with? q str) - (quad? string? . -> . boolean?) - (cond - [(not (empty? (quad-list q))) - (define last-item (last (quad-list q))) - (cond - [(string? last-item) (ends-with? last-item str)] - [(quad? last-item) (quad-ends-with? last-item str)])] - [else #f])) - - -(define (quad-append q new-item) - (quad? (or/c quad? string?) . -> . quad?) - (quad (quad-name q) (quad-attrs q) (append (quad-list q) (list new-item)))) - -(define (quad->string x) - (quad? . -> . string?) - (cond - [(quad? x) (string-append* (map quad->string (quad-list x)))] - [(string? x) x] - [else ""])) - -(define-syntax-rule (report-quadstring q) - (begin - (report (quad->string q) 'q) - q)) - -(define cannot-be-common-attrs '(width x y page)) ;; todo: how to specify these better? this-* prefix? - -;; make this a macro because qs-in is often huge -;; and the macro avoids allocation + garbage collection -(define attr-missing (gensym)) -(define (gather-common-attrs qs) - (let loop ([qs qs] - [common-attrs (if (quad-attrs (car qs)) - (for/list ([kv-pair (in-hash-pairs (quad-attrs (car qs)))] - #:unless (member (car kv-pair) cannot-be-common-attrs)) - kv-pair) - empty)]) - (cond - [(empty? common-attrs) empty] - [(empty? qs) (flatten common-attrs)] - [else (loop (cdr qs) - (filter (λ(ca) (equal? (quad-attr-ref (car qs) (car ca) attr-missing) (cdr ca))) - common-attrs))]))) - - -(define-syntax (define-box-type stx) - (syntax-case stx () - [(_ id) - (with-syntax ([id? (format-id #'id "~a?" #'id)] - [ids? (format-id #'id "~as?" #'id)] - [lists-of-ids? (format-id #'id "list-of-~as?" #'id)] - [quads->id (format-id #'id "quads->~a" #'id)] - [inline/quads->id (format-id #'id "inline/quads->~a" #'id)]) - #'(begin - ;; quad predicate - ok to be relaxed here if we're strict when making the struct - (define (id? x) - (and (quad? x) (equal? (quad-name x) 'id))) - ;; quad constructor - ;; put contract here rather than on struct, because this is the main interface - ;; and this contract is more liberal. - ;; but don't put a separate contract on struct, because it's superfluous. - (define (id [attrs empty] . xs) - (() ((or/c quad-attrs? hashable-list?)) #:rest quad-list? . ->* . id?) - (quad 'id (and attrs (if (hash? attrs) attrs (apply hash attrs))) xs)) - ;; quad list predicate and list-of-list predicate. - ;; These are faster than the listof contract combinator. - (define (ids? x) - (and (list? x) (andmap id? x))) - (define (lists-of-ids? x) - (and (list? x) (andmap ids? x))) - ;; quad converter macro - (define (quads->id qs) - (apply id (gather-common-attrs qs) qs))))])) - - -;; do not treat empty string as whitespace. -;; throws off tests that rely on adjacency to positive whitespace. -(define (whitespace? x [nbsp? #f]) - ((any/c)(boolean?) . ->* . coerce/boolean?) - (cond - [(quad? x) (whitespace? (quad-list x) nbsp?)] - [(string? x) (or (and (regexp-match #px"\\p{Zs}" x) ; Zs = unicode whitespace category - (or nbsp? (not (regexp-match #px"\u00a0" x)))))] ; 00a0: nbsp - [(list? x) (and (not (empty? x)) (andmap (curryr whitespace? nbsp?) x))] ; andmap returns #t for empty lists - [else #f])) - -(define (whitespace/nbsp? x) - (whitespace? x #t)) - - -(define-syntax (define-break-type stx) - (syntax-case stx () - [(_ id) - (with-syntax ([split-on-id-breaks (format-id #'id "split-on-~a-breaks" #'id)] - [id-break (format-id #'id "~a-break" #'id)] - [id-break? (format-id #'id "~a-break?" #'id)] - [multi-id (format-id #'id "multi~a" #'id)] - [multi-id? (format-id #'id "multi~a?" #'id)] - [quads->multi-id (format-id #'id "quads->multi~a" #'id)]) - #'(begin - (define-box-type id) - (define-box-type id-break) - (define-box-type multi-id) - ;; breaker - (define (split-on-id-breaks x) - (quads? . -> . lists-of-quads?) - ;; omit leading & trailing whitespace, because they're superfluous next to a break - (map (curryr trimf whitespace?) (filter-split x id-break?)))))])) - -(define-box-type box) - -(define-break-type word) -(define (word-string c) (car (quad-list c))) - -(define-box-type spacer) -(define-box-type kern) -(define-box-type optical-kern) -(define-box-type flag) -(define-box-type doc) -(define-box-type input) -(define-box-type piece) -(define-box-type run) - -(define-break-type page) -(define-break-type column) -(define-break-type block) -(define-break-type line) + (vector name attrs xs)) -(define (->input q) (input empty q)) -(define coerce/input? (make-coercion-contract input)) +(module+ test + (require rackunit) + (define q (quad 'foo #f '("bar"))) + (check-true (quad? q)) + (check-false (quad? 42)) + (check-equal? (quad-name q) 'foo) + (check-equal? (quad-attrs q) #f) + (check-equal? (quad-list q) '("bar"))) \ No newline at end of file diff --git a/quad/quad/split.rkt b/quad/quad/split.rkt new file mode 100644 index 00000000..683da33d --- /dev/null +++ b/quad/quad/split.rkt @@ -0,0 +1,79 @@ +#lang racket +(require "quads.rkt") + +;; push together multiple attr sources into one list of pairs. +;; mostly a helper function for the two attr functions below. +(define (join-attrs quads-or-attrs-or-lists) + (append-map hash->list (filter-not false? (map (λ(x) + (cond + [(quad? x) (quad-attrs x)] + [(quad-attrs? x) x] + #;[(hashable-list? x) (apply hash x)] + [else #f])) quads-or-attrs-or-lists)))) + + +;; flatten merges attributes, but applies special logic suitable to flattening +;; for instance, resolving x and y coordinates. +(define (flatten-attrs . quads-or-attrs-or-falses) + (define all-attrs (join-attrs quads-or-attrs-or-falses)) + (define-values (x-attrs y-attrs other-attrs-reversed) + (for/fold ([xas null][yas null][oas null])([attr (in-list all-attrs)]) + (cond + [(equal? (car attr) 'x) (values (cons attr xas) yas oas)] + [(equal? (car attr) 'y) (values xas (cons attr yas) oas)] + [else (values xas yas (cons attr oas))]))) + (define (make-cartesian-attr key attrs) (if (empty? attrs) empty (cons key (apply + (map cdr attrs))))) + (define-values (x-attr y-attr) (apply values (map make-cartesian-attr (list 'x 'y) (list x-attrs y-attrs)))) + (apply hash (flatten (list* x-attr y-attr (reverse other-attrs-reversed))))) + +;; pushes attributes down from parent quads to children, +;; resulting in a flat list of quads. +(define (flatten-quad q) + (flatten + (let loop ([x q][parent #f]) + (cond + [(quad? x) + (let ([x-with-parent-attrs (quad (quad-name x) + (flatten-attrs parent x) ; child positioned last so it overrides parent attributes + (quad-list x))]) + (if (empty? (quad-list x)) + x-with-parent-attrs ; no subelements, so stop here + (map (λ(xi) (loop xi x-with-parent-attrs)) (quad-list x))))] ; replace quad with its elements + [(string? x) (quad (quad-name parent) (quad-attrs parent) (list x))])))) + + + +(require sugar/debug) +;; flatten quad as above, +;; then dissolve it into individual character quads while copying attributes + +(define (split-quad q) + (letrec ([do-explode (λ(x [parent #f]) + (cond + [(quad? x) + (if (empty? (quad-list x)) + x ; no subelements, so stop here + (map (λ(xi) (do-explode xi x)) (quad-list x)))] ; replace quad with its elements, exploded + ;; todo: figure out why newlines foul up the input stream. Does it suffice to ignore them? + [else (map (λ(xc) (quad 'atom (quad-attrs parent) (list xc))) (regexp-match* #px"[^\r\n]" x))]))]) + (flatten (map do-explode (flatten-quad q))))) + +(require (for-syntax syntax/strip-context sugar/debug)) +(define-syntax (stx-quad stx) + (syntax-case stx () + [(_ ((ATTR-NAME ATTR-VAL) ...) XS) + (with-syntax ([(NEW-ATTR-NAME ...) (map (λ(an) (replace-context #'here an)) (syntax->list #'(ATTR-NAME ...)))]) + #'(let ([NEW-ATTR-NAME ATTR-VAL] ...) + (for-each (λ(x) (println (list size x))) XS)))])) + + +(require racket/generator) +#;(define y (generator () (stx-quad ((size 10)) (list "bar" (stx-quad () '("zam")))))) + +#| +(define x (quad 'foo (hash 'size 10) (list "bar" (quad 'foo (hash 'size 8) '("zam")) "qux"))) +(split-quad x) +|# + +(define x2 (stx-quad ((size 10)) (list "bar" (stx-quad ((size 8)) '("zam")) "qux"))) +x2 diff --git a/quad/quad/top.rkt b/quad/quad/top.rkt new file mode 100644 index 00000000..7d635fc1 --- /dev/null +++ b/quad/quad/top.rkt @@ -0,0 +1,12 @@ +#lang quad/dev +(provide (all-defined-out)) +(require "quads.rkt" (for-syntax racket/string racket/syntax)) + +(define-syntax (~top stx) + (syntax-case stx () + [(_ . id) + (let ([id-str (format "~a" (syntax->datum #'id))]) + (if (id-str . string-prefix? . "q:") + (with-syntax ([new-id (format-id #'id "~a" (string-trim id-str "q:" #:right? #f))]) + #'(λ args (apply quad 'new-id args))) + #'(#%top . id)))])) \ No newline at end of file