Browser
A Playground and a browser adapter that run rules without installation, entirely in the browser.
In preparationOpen the Playground →Lispex, a language for house style
Lispex is a language for writing business rules, such as a publisher's house style. It cannot touch files or the network, so it is safe to run inside any program.
(module main
(import)
(export heading-place)
(entry heading-place)
(define heading-place
(lambda (lines-left)
(if (< lines-left 2)
"next-page"
"keep")))
)Input 1Answer "next-page"
A copy editor reads with that style beside the page. The questions repeat from book to book, and the answers should repeat as well.
3.4 Notes on sources
The sources for this chapter appear in the order the text cites them.
end of page · 1 line left below the heading
This rule moves a heading to the next page when fewer than 2 lines are left below it. Try it with a page that has 1 line left.
A Playground and a browser adapter that run rules without installation, entirely in the browser.
In preparationOpen the Playground →The lispex command from the npm package, for running rule files with Node.js.
In preparationRun Lispex in your application →A command line tool that runs rule files on your computer with work and memory limits and signs Vouch records.
In preparationDownloads →The evaluator that ships with Topaz, through which a Topaz application calls rules.
In preparationRun in Topaz ↗(module main
(import)
(export place-all)
(entry place-all)
(define styles
'((chapter 4 odd)
(section 2 any)
(figure 0 any)))
(define (place kind page lines-left)
(let ((row (assoc kind styles)))
(cond
((< lines-left (list-ref row 1))
'next-page)
((and (eq? (list-ref row 2) 'odd)
(even? page))
'next-odd-page)
(else 'keep))))
(define (place-all requests)
(map (lambda (request) (apply place request))
requests)))((chapter 12 30) (section 7 1) (figure 8 0))(next-odd-page next-page keep)The rule on the left keeps a publisher's house style as a table of three rows. A chapter heading needs 4 lines below it, and a chapter starts on an odd page. A section heading needs 2 lines. A figure stays where it is whatever the lines left.
place judges one element. It finds the row for that kind. When fewer lines are left than the row asks for, it returns next-page. When there are enough lines but the row asks for an odd page and the page number is even, it returns next-odd-page. Otherwise it returns keep.
place-all is the entry procedure. The application passes one list with the kind, page number and lines left of each element, and the rule returns its decisions in the same order.
(chapter 12 30)next-odd-page(section 7 1)next-page(figure 8 0)keepFor another publisher, change the numbers and the page condition in the table. The application lays out the pages, and the rule answers where each element goes, as data.
Read the first rule →A Topaz application can keep its calculations in Topaz code and its judgments in Lispex rule files. If you build a typesetting app with Topaz, breaking lines and laying out pages are calculations. Judgments that change from publisher to publisher, such as where a chapter starts or how many lines a heading needs below it, can live in rule files.
The example above receives the number of remaining lines through heading-place. An input of 1 returns "next-page", and an input of 2 returns "keep".
A rule cannot read files or reach the clock or the network. current-second is an operation removed from the language, so preparation refuses the embargo rule.
(module main
(import)
(export embargo-lifted?)
(entry embargo-lifted?)
(define (embargo-lifted? release)
(>= (current-second) release)))1793404800resolve
library.reference
name = current-second
reason = removed-operationWhen a rule reaches its work or memory limit, evaluation stops there and the app receives that result. This chapter-start rule reaches its work limit.
(module main
(import)
(export chapter-start)
(entry chapter-start)
(define (chapter-start page)
(if (odd? page)
page
; 12, 14, 16, ... always even
(chapter-start (+ page 2)))))12evaluate
resource.exhausted
axis = workWith the same evaluator, the same rule, input, and limits give the same answer on every run.
(module main
(import)
(export best-break)
(entry best-break)
(define (best-break breaks)
(fold-left
(lambda (best next)
(if (< (cdr next) (cdr best))
next
best))
(car breaks)
(cdr breaks))))((line-12 . 40) (line-13 . 25) (line-14 . 25))(line-13 . 25)When a publisher hands the final file to a printer, a Lispex Vouch record keeps the decision that page size, margins, embedded fonts, and page count meet the agreed specifications. If a printing problem occurs, both parties can check the same record.
The application checks the final file and supplies the page size, margin, embedded-font, and page-count results to the rule.
(module main
(import)
(export handoff)
(entry handoff)
(define (handoff checks)
(let ((page-size (list-ref checks 0))
(margins (list-ref checks 1))
(fonts (list-ref checks 2))
(page-count (list-ref checks 3)))
(if (and page-size margins fonts page-count)
'approve
'deny))))(#t #t #t #t)approveNative signs a record of the rule, input, and answer.
The recipient checks the signature with a trusted public key and reruns the rule with the source and input they reviewed. Those files must match the signed bytes.
The rerun answer is compared with the recorded answer and with the decision the recipient requires.
Execution evidence and signatures are separate from the portable receipt. The calling application decides what to do next.
A Lispex Image stores the exact bytes of a source file in a PNG. This image holds the first example on this page. Decoding the unchanged file with the current image codec returns the source with comments and spaces.

Only the exact original file decodes. A screenshot, a resized copy, or a re-saved file is rejected.
In preparationCreate and read Lispex images →