curiouser

main
Matthew Butterick 2 years ago
parent d3d4c4e8e4
commit 6fa16add86

@ -1,25 +0,0 @@
#lang debug racket/base
(require racket/match
"compile.rkt"
"quad.rkt")
(provide atomize)
#|
My idea here is that instead of comparing attrs by using eq? with a hash,
we can use eq? (not equal?) on an association list
the idea being that if only update the attrs by consing onto the front,
then the process is strictly accretive, that is:
we are never allocating a fresh list to hold existing values
For instance, the top-level attrs represent a list object
that will eventually be the tail of the attrs in every atomized quad.
|#
(define-pass (atomize q)
#:pre quad?
#:post (list-of quad?)
(match (quad-elems q)
[(cons str _)
(for/list ([c (in-string str)])
(make-quad #:attrs (make-quad-attrs (list (cons 'char c)))))]
[_ (error 'atomize-branch-unimplemented)]))

@ -1,31 +0,0 @@
#lang debug racket/base
(require racket/match
"quad.rkt")
(provide (all-defined-out))
(struct compiler (passes)
#:constructor-name make-compiler
#:guard (λ (procs name)
(unless ((list-of procedure?) procs)
(raise-argument-error 'bad-input-to-compiler-constructor "list of procedures" procs))
procs)
#:property prop:procedure
(λ (self input) ((apply compose1 (reverse (compiler-passes self))) input)))
(define (compiler-append c passes)
(make-compiler (append (compiler-passes c) passes)))
(define-syntax-rule (define-pass (PASS-NAME ARG OTHER-ARG ...)
#:pre PRECOND-PROC
#:post POSTCOND-PROC
EXPRS ...)
(define PASS-NAME
(make-compiler
(list (procedure-rename
(λ (ARG OTHER-ARG ...)
(unless (PRECOND-PROC ARG)
(raise-argument-error 'PASS-NAME (format "~a" 'PRECOND-PROC) ARG))
(define res (let () EXPRS ...))
(unless (POSTCOND-PROC res)
(raise-argument-error 'PASS-NAME (format "~a" 'POSTCOND-PROC) res))
res) 'PASS-NAME)))))

@ -4,7 +4,7 @@
racket/format
racket/match
"quad.rkt"
"compile.rkt"
"pipeline.rkt"
"struct.rkt"
"layout.rkt")
(provide (all-defined-out))

@ -1,6 +1,6 @@
#lang debug racket/base
(require racket/contract
"compile.rkt"
"pipeline.rkt"
"quad.rkt")
(provide layout)

@ -0,0 +1,87 @@
#lang debug racket/base
(require racket/match
racket/hash
racket/list
racket/string
"pipeline.rkt"
"quad.rkt")
(provide (all-defined-out))
(define (simple-quad? x) (and (quad? x) (<= (length (quad-elems x)) 1)))
(define-pass (linearize q)
;; convert a single quad into a list of quads, with the attributes propagated downward
;; every resulting quad should have at most one element
#:pre quad?
#:post (list-of simple-quad?)
(let loop ([q q][attrs-context (make-quad-attrs)]) ;; returns (list-of quad?)
(define current-attrs (let ([qas (make-quad-attrs)])
(hash-union! #:combine (λ (v1 v2) v2) qas attrs-context (quad-attrs q))
qas))
(define (mq es) (make-quad #:tag (quad-tag q) #:attrs current-attrs #:elems es))
(match (quad-elems q)
[(? null?) (list (mq null))]
[(? pair? elems)
(apply append (for/list ([e (in-list elems)])
(cond
[(quad? e) (loop e current-attrs)]
[else (list (mq (list e)))])))])))
(module+ test
(define q (make-quad #:attrs (hasheq 'foo 42) #:elems (list (make-quad #:elems (list "Hi" " idiot" (make-quad #:attrs (hasheq 'bar 84) #:elems '("There")) " Eve" "ry" "one" (make-quad #:attrs (hasheq 'zam 108) #:elems null))))))
(define lqs (linearize q))
lqs)
(define-pass (merge-adjacent-strings sqs)
;; merge quads with the same attrs, and one or zero string elements,
;; into a single quad with one string element
#:pre (list-of simple-quad?)
#:post (list-of simple-quad?)
(let merge ([sqs sqs])
(match sqs
[_ #:when (<= (length sqs) 1) ; nothing to merge
sqs]
[(cons e0 rest)
;; because we copied attrs downward in linearize, we can use eq? to compare if they're the same
;; (instead of a key-by-key comparison)
(define (attrs-same? e) (eq? (quad-attrs e0) (quad-attrs e)))
(define-values (head tail) (splitf-at rest attrs-same?))
(cons
(cond
[(null? head) e0]
[else
(define qs-to-merge (cons e0 head))
(make-quad #:tag (quad-tag e0)
#:attrs (quad-attrs e0)
#:elems (list (string-join (append-map quad-elems qs-to-merge) "")))])
(merge tail))])))
(module+ test
(define mlqs (merge-adjacent-strings lqs))
mlqs)
(define-pass (split-whitespace qs)
#:pre (list-of simple-quad?)
#:post (list-of simple-quad?)
(define whitespace-pat #px"\\s+")
(define word-space " ")
(apply append
(for/list ([q (in-list qs)])
(match (quad-elems q)
[(list (? string? str))
(define tag (quad-tag q))
(define attrs (quad-attrs q))
;; the "gaps" (parts that don't match pattern) are guaranteed to be at even indexes
;; If string starts with a "gap", a zero-length separator is appended to the start.
;; so we just ignore those.
(for/list ([(substr idx) (in-indexed (regexp-match* whitespace-pat str #:gap-select? #t))]
#:unless (zero? (string-length substr)))
(make-quad #:tag tag
#:attrs attrs
#:elems (list (if (even? idx) substr word-space))))]
[_ (list q)]))))
(module+ test
(split-whitespace mlqs))

@ -2,8 +2,8 @@
(require "layout.rkt"
"render.rkt"
"quad.rkt"
"compile.rkt"
"atomize.rkt"
"pipeline.rkt"
"linearize.rkt"
"layout.rkt"
"draw.rkt"
racket/string
@ -17,17 +17,32 @@
[(and (list (? quad?) ...) qs) (make-quad #:elems qs)]
[other (make-quad #:elems (list other))]))
(define quad-compile (make-compiler (list
(define-pass (make-weirdo-char-quads qs)
#:pre (list-of simple-quad?)
#:post (list-of simple-quad?)
(apply append
(for/list ([q (in-list qs)])
(match q
[(quad _ _ (list (? string? str)))
(for/list ([c (in-string str)])
(define new-attrs (make-hasheq (cons (cons 'char c) (hash->list (quad-attrs q)))))
(make-quad #:tag (quad-tag q)
#:attrs new-attrs
#:elems null))]
[_ (list q)]))))
(define quad-compile (make-pipeline (list
bootstrap
atomize
linearize
merge-adjacent-strings
split-whitespace
make-weirdo-char-quads
layout
make-drawing-insts)))
(define quad-compile-to-stack (compiler-append quad-compile
(list stackify)))
make-drawing-insts
stackify)))
(define drawing-insts (parameterize ([current-wrap-width 13])
(quad-compile-to-stack "Hello this is the earth")))
(quad-compile "Hello this is the earth")))
(displayln drawing-insts)

@ -0,0 +1,37 @@
#lang debug racket/base
(require racket/match
(for-syntax racket/base)
"quad.rkt")
(provide (all-defined-out))
(struct pipeline (passes)
#:constructor-name make-pipeline
#:guard (λ (procs name)
(unless ((list-of procedure?) procs)
(raise-argument-error 'bad-input-to-compiler-constructor "list of procedures" procs))
procs)
#:property prop:procedure
(λ args (apply (apply compose1 (reverse (pipeline-passes (car args)))) (cdr args))))
(define (compiler-append c passes)
(make-pipeline (append (pipeline-passes c) passes)))
(define-syntax (define-pass stx)
(syntax-case stx ()
[(_ (PASS-NAME ARG OTHER-ARG ...)
#:pre PRECOND-PROC
#:post POSTCOND-PROC
EXPRS ...)
#`(define PASS-NAME
(make-pipeline
(list
(procedure-rename
#,(syntax/loc stx
(λ (ARG OTHER-ARG ...)
(unless (PRECOND-PROC ARG)
(raise-argument-error 'PASS-NAME (format "~a" 'PRECOND-PROC) ARG))
(define res (let () EXPRS ...))
(unless (POSTCOND-PROC res)
(raise-argument-error 'PASS-NAME (format "~a" 'POSTCOND-PROC) res))
res))
'PASS-NAME))))]))

@ -25,7 +25,7 @@
(define (quad-tag? x) (match x
[(or (? symbol?) #false) #true]
[_ #false]))
(define (make-quad-attrs alist) (make-hasheq alist))
(define (make-quad-attrs [alist null]) (make-hasheq alist))
(define (quad-attrs? x) (hash-eq? x))
(define (quad-elems? x) (list? x))

@ -1,5 +1,7 @@
#lang debug racket/base
(require "compile.rkt" "draw.rkt" "layout.rkt")
(require "pipeline.rkt"
"draw.rkt"
"layout.rkt")
(provide (all-defined-out))
(struct $renderer (doc-start-func

Loading…
Cancel
Save