├── .gitignore
├── LICENSE
├── README.md
├── curryst.typ
├── examples
├── math-formula.svg
├── math-formula.typ
├── natural-deduction.svg
├── natural-deduction.typ
├── rule-as-premise.svg
├── rule-as-premise.typ
├── usage.svg
└── usage.typ
├── tests
├── README.md
├── test-1.png
├── test-10.png
├── test-11.png
├── test-12.png
├── test-13.png
├── test-14.png
├── test-15.png
├── test-16.png
├── test-17.png
├── test-18.png
├── test-19.png
├── test-2.png
├── test-20.png
├── test-21.png
├── test-22.png
├── test-23.png
├── test-24.png
├── test-25.png
├── test-26.png
├── test-27.png
├── test-28.png
├── test-29.png
├── test-3.png
├── test-30.png
├── test-31.png
├── test-32.png
├── test-33.png
├── test-34.png
├── test-35.png
├── test-36.png
├── test-37.png
├── test-38.png
├── test-39.png
├── test-4.png
├── test-40.png
├── test-41.png
├── test-42.png
├── test-43.png
├── test-44.png
├── test-45.png
├── test-46.png
├── test-47.png
├── test-48.png
├── test-49.png
├── test-5.png
├── test-50.png
├── test-51.png
├── test-52.png
├── test-53.png
├── test-54.png
├── test-55.png
├── test-56.png
├── test-57.png
├── test-58.png
├── test-59.png
├── test-6.png
├── test-60.png
├── test-7.png
├── test-8.png
├── test-9.png
└── tests.typ
└── typst.toml
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Rémi Hutin - Paul Adam - Malo <@MDLC01>
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Curryst
2 |
3 | A Typst package for typesetting proof trees.
4 |
5 |
6 | ## Import
7 |
8 | You can import the latest version of this package with:
9 |
10 | ```typst
11 | #import "@preview/curryst:0.5.1": rule, prooftree
12 | ```
13 |
14 | ## Basic usage
15 |
16 | To display a proof tree, you first need to create a tree, using the `rule` function. Its first argument is the conclusion, and the other positional arguments are the premises. It also accepts a `name` for the rule name, displayed on the right of the bar, as well as a `label`, displayed on the left of the bar.
17 |
18 | ```typ
19 | #let tree = rule(
20 | label: [Label],
21 | name: [Rule name],
22 | [Conclusion],
23 | [Premise 1],
24 | [Premise 2],
25 | [Premise 3]
26 | )
27 | ```
28 |
29 | Then, you can display the tree with the `prooftree` function:
30 |
31 | ```typ
32 | #prooftree(tree)
33 | ```
34 |
35 | In this case, we get the following result:
36 |
37 | 
38 |
39 | Proof trees can be part of mathematical formulas:
40 |
41 | ```typ
42 | Consider the following tree:
43 | $
44 | Pi quad = quad prooftree(
45 | rule(
46 | phi,
47 | Pi_1,
48 | Pi_2,
49 | )
50 | )
51 | $
52 | $Pi$ constitutes a derivation of $phi$.
53 | ```
54 |
55 | 
56 |
57 | You can specify a rule as the premises of a rule in order to create a tree:
58 |
59 | ```typ
60 | #prooftree(
61 | rule(
62 | name: $R$,
63 | $C_1 or C_2 or C_3$,
64 | rule(
65 | name: $A$,
66 | $C_1 or C_2 or L$,
67 | rule(
68 | $C_1 or L$,
69 | $Pi_1$,
70 | ),
71 | ),
72 | rule(
73 | $C_2 or overline(L)$,
74 | $Pi_2$,
75 | ),
76 | )
77 | )
78 | ```
79 |
80 | 
81 |
82 | As an example, here is a natural deduction proof tree generated with Curryst:
83 |
84 | 
85 |
86 |
87 | Show code
88 |
89 | ```typ
90 | #let ax = rule.with(name: [ax])
91 | #let and-el = rule.with(name: $and_e^ell$)
92 | #let and-er = rule.with(name: $and_e^r$)
93 | #let impl-i = rule.with(name: $scripts(->)_i$)
94 | #let impl-e = rule.with(name: $scripts(->)_e$)
95 | #let not-i = rule.with(name: $not_i$)
96 | #let not-e = rule.with(name: $not_e$)
97 |
98 | #prooftree(
99 | impl-i(
100 | $tack (p -> q) -> not (p and not q)$,
101 | not-i(
102 | $p -> q tack not (p and not q)$,
103 | not-e(
104 | $ underbrace(p -> q\, p and not q, Gamma) tack bot $,
105 | impl-e(
106 | $Gamma tack q$,
107 | ax($Gamma tack p -> q$),
108 | and-el(
109 | $Gamma tack p$,
110 | ax($Gamma tack p and not q$),
111 | ),
112 | ),
113 | and-er(
114 | $Gamma tack not q$,
115 | ax($Gamma tack p and not q$),
116 | ),
117 | ),
118 | ),
119 | )
120 | )
121 | ```
122 |
123 |
124 |
125 | ## Advanced usage
126 |
127 | The `prooftree` function accepts multiple named arguments that let you customize the tree:
128 |
129 |
130 |
min-premise-spacing
131 |
The minimum amount of space between two premises.
132 |
133 |
title-inset
134 |
The amount to extend the horizontal bar beyond the content. Also determines how far from the bar labels and names are displayed.
135 |
136 |
stroke
137 |
The stroke to use for the horizontal bars.
138 |
139 |
vertical-spacing
140 |
The space between the bottom of the bar and the conclusion, and between the top of the bar and the premises.
141 |
142 |
min-bar-height
143 |
The minimum height of the box containing the horizontal bar.
144 |
145 |
dir
146 |
The orientation of the proof tree (either btt or ttb, btt being the default).
147 |
148 |
149 | For more information, please refer to the documentation in [`curryst.typ`](curryst.typ).
150 |
--------------------------------------------------------------------------------
/curryst.typ:
--------------------------------------------------------------------------------
1 | /// Creates an inference rule.
2 | ///
3 | /// You can render a rule created with this function using the `prooftree`
4 | /// function.
5 | #let rule(
6 | /// The label of the rule, displayed on the left of the horizontal bar.
7 | label: none,
8 | /// The name of the rule, displayed on the right of the horizontal bar.
9 | name: none,
10 | /// The conclusion of the rule.
11 | conclusion,
12 | /// The premises of the rule. Might be other rules constructed with this
13 | /// function, or some content.
14 | ..premises
15 | ) = {
16 | assert.ne(
17 | type(conclusion),
18 | dictionary,
19 | message: "the conclusion of a rule must be some content (it cannot be another rule)",
20 | )
21 | assert.eq(
22 | premises.named().len(),
23 | 0,
24 | message: "unexpected named arguments to `rule`",
25 | )
26 | (
27 | label: label,
28 | name: name,
29 | conclusion: conclusion,
30 | premises: premises.pos()
31 | )
32 | }
33 |
34 | /// Lays out a proof tree.
35 | #let prooftree(
36 | /// The rule to lay out.
37 | ///
38 | /// Such a rule can be constructed using the `rule` function.
39 | rule,
40 | /// The minimum amount of space between two premises.
41 | min-premise-spacing: 15pt,
42 | /// The amount to extend the horizontal bar beyond the content. Also
43 | /// determines how far from the bar labels and names are displayed.
44 | title-inset: 2pt,
45 | /// The stroke to use for the horizontal bars.
46 | stroke: stroke(0.4pt),
47 | /// The space between the bottom of the bar and the conclusion, and between
48 | /// the top of the bar and the premises.
49 | ///
50 | /// Note that, in this case, "the bar" refers to the bounding box of the
51 | /// horizontal line and the rule name (if any).
52 | vertical-spacing: 0pt,
53 | /// The minimum height of the box containing the horizontal bar.
54 | ///
55 | /// The height of this box is normally determined by the height of the rule
56 | /// name because it is the biggest element of the box. This setting lets you
57 | /// set a minimum height. The default is 0.8em, is higher than a single line
58 | /// of content, meaning all parts of the tree will align properly by default,
59 | /// even if some rules have no name (unless a rule is higher than a single
60 | /// line).
61 | min-bar-height: 0.8em,
62 | /// The orientation of the proof tree.
63 | ///
64 | /// If set to ttb, the conclusion will be at the top, and the premises will
65 | /// be at the bottom. Defaults to btt, the conclusion being at the bottom
66 | /// and the premises at the top.
67 | dir: btt,
68 | ) = {
69 | /// Lays out some content.
70 | ///
71 | /// This function simply wraps the passed content in the usual
72 | /// `(content: .., left-blank: .., right-blank: ..)` dictionary.
73 | let layout-content(content) = {
74 | // We wrap the content in a box with fixed dimensions so that fractional units
75 | // don't come back to haunt us later.
76 | let dimensions = measure(content)
77 | (
78 | content: box(
79 | // stroke: yellow + 0.3pt, // DEBUG
80 | ..dimensions,
81 | content,
82 | ),
83 | left-blank: 0pt,
84 | right-blank: 0pt,
85 | )
86 | }
87 |
88 |
89 | /// Lays out multiple premises, spacing them properly.
90 | let layout-premises(
91 | /// Each laid out premise.
92 | ///
93 | /// Must be an array of ditionaries with `content`, `left-blank` and
94 | /// `right-blank` attributes.
95 | premises,
96 | /// The minimum amount between each premise.
97 | min-spacing,
98 | /// If the laid out premises have an inner width smaller than this, their
99 | /// spacing will be increased in order to reach this inner width.
100 | optimal-inner-width,
101 | ) = {
102 | let arity = premises.len()
103 |
104 | if arity == 0 {
105 | return layout-content(none)
106 | }
107 |
108 | if arity == 1 {
109 | return premises.at(0)
110 | }
111 |
112 | let left-blank = premises.at(0).left-blank
113 | let right-blank = premises.at(-1).right-blank
114 |
115 | let initial-content = stack(
116 | dir: ltr,
117 | spacing: min-spacing,
118 | ..premises.map(premise => premise.content),
119 | )
120 | let initial-inner-width = measure(initial-content).width - left-blank - right-blank
121 |
122 | if initial-inner-width >= optimal-inner-width {
123 | return (
124 | content: box(initial-content),
125 | left-blank: left-blank,
126 | right-blank: right-blank,
127 | )
128 | }
129 |
130 | // Conclusion is wider than the premises: they need to be spaced out.
131 | let remaining-space = optimal-inner-width - initial-inner-width
132 | let final-content = stack(
133 | dir: ltr,
134 | spacing: min-spacing + remaining-space / (arity + 1),
135 | ..premises.map(premise => premise.content),
136 | )
137 |
138 | (
139 | content: box(final-content),
140 | left-blank: left-blank,
141 | right-blank: right-blank,
142 | )
143 | }
144 |
145 |
146 | /// Lays out multiple leaf premises, onto multiple lines to respect the
147 | /// available horizontal space.
148 | ///
149 | /// This function is meant to be called when we already know the premises
150 | /// should be typeset onto multiple lines. In particular, if there is enough
151 | /// space to fit all the premises, they will not be spaced apart.
152 | let layout-leaf-premises(
153 | /// Each laid out premise.
154 | ///
155 | /// Must be an array of content-like (meaning `content`, `string`, etc.).
156 | premises,
157 | /// The minimum amount between each premise.
158 | min-spacing,
159 | /// The available width for the returned content.
160 | ///
161 | /// Ideally, the width of the returned content should be bounded by this
162 | /// value, although no guarantee is made.
163 | available-width,
164 | ) = {
165 | let line-builder = stack.with(
166 | dir: ltr,
167 | spacing: min-spacing,
168 | )
169 |
170 | let lines = ((),)
171 | for premise in premises {
172 | let augmented-line = lines.last() + (premise,)
173 | if measure(line-builder(..augmented-line)).width <= available-width {
174 | lines.last() = augmented-line
175 | } else {
176 | lines.push((premise,))
177 | }
178 | }
179 |
180 | layout-content({
181 | set align(center)
182 | stack(
183 | dir: ttb,
184 | spacing: 0.7em,
185 | ..lines
186 | .filter(line => line.len() != 0)
187 | .map(line => line-builder(..line)),
188 | )
189 | })
190 | }
191 |
192 |
193 | /// Lays out the horizontal bar of a rule.
194 | let layout-bar(
195 | /// The stroke to use for the bar.
196 | stroke,
197 | /// The length of the bar, without taking hangs into account.
198 | length,
199 | /// How much to extend the bar to the left and to the right.
200 | hang,
201 | /// The label of the rule, displayed on the left of the bar.
202 | ///
203 | /// If this is `none`, no label is displayed.
204 | label,
205 | /// The name of the rule, displayed on the right of the bar.
206 | ///
207 | /// If this is `none`, no name is displayed.
208 | name,
209 | /// The space to leave between the label and the bar, and between the bar
210 | /// and the name.
211 | margin,
212 | /// The minimum height of the content to return.
213 | min-height,
214 | ) = {
215 | let bar = line(
216 | start: (0pt, 0pt),
217 | length: length + 2 * hang,
218 | stroke: stroke,
219 | )
220 |
221 | let (width: label-width, height: label-height) = measure(label)
222 | let (width: name-width, height: name-height) = measure(name)
223 |
224 | let content = {
225 | show: box.with(
226 | // stroke: green + 0.3pt, // DEBUG
227 | height: calc.max(label-height, name-height, min-height),
228 | )
229 |
230 | set align(horizon)
231 |
232 | let bake(body) = if body == none {
233 | none
234 | } else {
235 | move(dy: -0.15em, box(body, ..measure(body)))
236 | }
237 |
238 | let parts = (
239 | bake(label),
240 | bar,
241 | bake(name),
242 | ).filter(p => p != none)
243 |
244 | stack(
245 | dir: ltr,
246 | spacing: margin,
247 | ..parts,
248 | )
249 | }
250 |
251 | (
252 | content: content,
253 | left-blank:
254 | if label == none {
255 | hang
256 | } else {
257 | hang + margin + label-width
258 | },
259 | right-blank:
260 | if name == none {
261 | hang
262 | } else {
263 | hang + margin + name-width
264 | },
265 | )
266 | }
267 |
268 |
269 | /// Lays out the application of a rule.
270 | let layout-rule(
271 | /// The laid out premises.
272 | ///
273 | /// This must be a dictionary with `content`, `left-blank`
274 | /// and `right-blank` attributes.
275 | premises,
276 | /// The conclusion, displayed below the bar.
277 | conclusion,
278 | /// The stroke of the bar.
279 | bar-stroke,
280 | /// The amount by which to extend the bar on each side.
281 | bar-hang,
282 | /// The label of the rule, displayed on the left of the bar.
283 | ///
284 | /// If this is `none`, no label is displayed.
285 | label,
286 | /// The name of the rule, displayed on the right of the bar.
287 | ///
288 | /// If this is `none`, no name is displayed.
289 | name,
290 | /// The space to leave between the label and the bar, and between the bar
291 | /// and the name.
292 | bar-margin,
293 | /// The spacing above and below the bar.
294 | vertical-spacing,
295 | /// The minimum height of the bar element.
296 | min-bar-height,
297 | ) = {
298 | // Fix the dimensions of the conclusion and name to prevent problems with
299 | // fractional units later.
300 | conclusion = box(conclusion, ..measure(conclusion))
301 |
302 | let premises-inner-width = measure(premises.content).width - premises.left-blank - premises.right-blank
303 | let conclusion-width = measure(conclusion).width
304 |
305 | let bar-length = calc.max(premises-inner-width, conclusion-width)
306 |
307 | let bar = layout-bar(bar-stroke, bar-length, bar-hang, label, name, bar-margin, min-bar-height)
308 |
309 | let left-start
310 | let right-start
311 |
312 | let premises-left-offset
313 | let conclusion-left-offset
314 |
315 | if premises-inner-width > conclusion-width {
316 | left-start = calc.max(premises.left-blank, bar.left-blank)
317 | right-start = calc.max(premises.right-blank, bar.right-blank)
318 | premises-left-offset = left-start - premises.left-blank
319 | conclusion-left-offset = left-start + (premises-inner-width - conclusion-width) / 2
320 | } else {
321 | let premises-left-hang = premises.left-blank - (bar-length - premises-inner-width) / 2
322 | let premises-right-hang = premises.right-blank - (bar-length - premises-inner-width) / 2
323 | left-start = calc.max(premises-left-hang, bar.left-blank)
324 | right-start = calc.max(premises-right-hang, bar.right-blank)
325 | premises-left-offset = left-start + (bar-length - premises-inner-width) / 2 - premises.left-blank
326 | conclusion-left-offset = left-start
327 | }
328 | let bar-left-offset = left-start - bar.left-blank
329 |
330 | let content = {
331 | // show: box.with(stroke: yellow + 0.3pt) // DEBUG
332 |
333 | let stack-dir = dir.inv()
334 | let align-y = dir.start()
335 |
336 | set align(align-y + left)
337 |
338 | stack(
339 | dir: stack-dir,
340 | spacing: vertical-spacing,
341 | h(premises-left-offset) + premises.content,
342 | h(bar-left-offset) + bar.content,
343 | h(conclusion-left-offset) + conclusion,
344 | )
345 | }
346 |
347 | (
348 | content: box(content),
349 | left-blank: left-start + (bar-length - conclusion-width) / 2,
350 | right-blank: right-start + (bar-length - conclusion-width) / 2,
351 | )
352 | }
353 |
354 |
355 | /// Lays out an entire proof tree.
356 | ///
357 | /// All lengths passed to this function must be resolved.
358 | let layout-tree(
359 | /// The rule containing the tree to lay out.
360 | rule,
361 | /// The available width for the tree.
362 | ///
363 | /// `none` is interpreted as infinite available width.
364 | ///
365 | /// Ideally, the width of the returned tree should be bounded by this value,
366 | /// although no guarantee is made.
367 | available-width,
368 | /// The minimum amount between each premise.
369 | min-premise-spacing,
370 | /// The stroke of the bar.
371 | bar-stroke,
372 | /// The amount by which to extend the bar on each side.
373 | bar-hang,
374 | /// The space to leave between the label and the bar, and between the bar
375 | /// and the name.
376 | bar-margin,
377 | /// The margin above and below the bar.
378 | vertical-spacing,
379 | /// The minimum height of the bar element.
380 | min-bar-height,
381 | ) = {
382 | if type(rule) != dictionary {
383 | return layout-content(rule)
384 | }
385 |
386 | let layout-with-baked-premises(premises) = {
387 | layout-rule(
388 | premises,
389 | rule.conclusion,
390 | bar-stroke,
391 | bar-hang,
392 | rule.label,
393 | rule.name,
394 | bar-margin,
395 | vertical-spacing,
396 | min-bar-height,
397 | )
398 | }
399 |
400 | let side-to-side-premises = layout-premises(
401 | rule.premises.map(premise => layout-tree(
402 | premise,
403 | none,
404 | min-premise-spacing,
405 | bar-stroke,
406 | bar-hang,
407 | bar-margin,
408 | vertical-spacing,
409 | min-bar-height,
410 | )),
411 | min-premise-spacing,
412 | measure(rule.conclusion).width,
413 | )
414 | let result = layout-with-baked-premises(side-to-side-premises)
415 |
416 | let premises-are-all-leaves = rule.premises.all(premise => type(premise) != dictionary)
417 | if available-width == none or measure(result.content).width <= available-width or not premises-are-all-leaves {
418 | return result
419 | }
420 |
421 | // If the premises are all leaves, they can be typeset in multiple lines
422 | // when there is not enough horizontal space.
423 | let used-width = bar-hang * 2
424 | if rule.name != none {
425 | used-width += bar-margin + measure(rule.name).width
426 | }
427 | if rule.label != none {
428 | used-width += bar-margin + measure(rule.label).width
429 | }
430 | let stacked-premises = layout-leaf-premises(
431 | rule.premises,
432 | min-premise-spacing,
433 | available-width - used-width,
434 | )
435 | layout-with-baked-premises(stacked-premises)
436 | }
437 |
438 |
439 | layout(available => {
440 | let tree = layout-tree(
441 | rule,
442 | available.width,
443 | min-premise-spacing.to-absolute(),
444 | stroke,
445 | title-inset.to-absolute(),
446 | title-inset.to-absolute(),
447 | vertical-spacing.to-absolute(),
448 | min-bar-height.to-absolute(),
449 | ).content
450 |
451 | block(
452 | // stroke : black + 0.3pt, // DEBUG
453 | ..measure(tree),
454 | breakable: false,
455 | tree,
456 | )
457 | })
458 | }
459 |
--------------------------------------------------------------------------------
/examples/math-formula.svg:
--------------------------------------------------------------------------------
1 |
259 |
--------------------------------------------------------------------------------
/examples/math-formula.typ:
--------------------------------------------------------------------------------
1 | #import "../curryst.typ": rule, prooftree
2 | #set document(date: none)
3 | #set page(width: auto, height: auto, margin: 0.5cm, fill: white)
4 |
5 | Consider the following tree:
6 | $
7 | Pi quad = quad prooftree(
8 | rule(
9 | phi,
10 | Pi_1,
11 | Pi_2,
12 | )
13 | )
14 | $
15 | $Pi$ constitutes a derivation of $phi$.
16 |
--------------------------------------------------------------------------------
/examples/natural-deduction.typ:
--------------------------------------------------------------------------------
1 | #import "../curryst.typ": rule, prooftree
2 | #set document(date: none)
3 | #set page(width: auto, height: auto, margin: 0.5cm, fill: white)
4 |
5 | #let ax = rule.with(name: [ax])
6 | #let and-el = rule.with(name: $and_e^ell$)
7 | #let and-er = rule.with(name: $and_e^r$)
8 | #let impl-i = rule.with(name: $scripts(->)_i$)
9 | #let impl-e = rule.with(name: $scripts(->)_e$)
10 | #let not-i = rule.with(name: $not_i$)
11 | #let not-e = rule.with(name: $not_e$)
12 |
13 | #prooftree(
14 | impl-i(
15 | $tack (p -> q) -> not (p and not q)$,
16 | not-i(
17 | $p -> q tack not (p and not q)$,
18 | not-e(
19 | $ underbrace(p -> q\, p and not q, Gamma) tack bot $,
20 | impl-e(
21 | $Gamma tack q$,
22 | ax($Gamma tack p -> q$),
23 | and-el(
24 | $Gamma tack p$,
25 | ax($Gamma tack p and not q$),
26 | ),
27 | ),
28 | and-er(
29 | $Gamma tack not q$,
30 | ax($Gamma tack p and not q$),
31 | ),
32 | ),
33 | ),
34 | )
35 | )
36 |
--------------------------------------------------------------------------------
/examples/rule-as-premise.svg:
--------------------------------------------------------------------------------
1 |
369 |
--------------------------------------------------------------------------------
/examples/rule-as-premise.typ:
--------------------------------------------------------------------------------
1 | #import "../curryst.typ": rule, prooftree
2 | #set document(date: none)
3 | #set page(width: auto, height: auto, margin: 0.5cm, fill: white)
4 |
5 | #prooftree(
6 | rule(
7 | name: $R$,
8 | $C_1 or C_2 or C_3$,
9 | rule(
10 | name: $A$,
11 | $C_1 or C_2 or L$,
12 | rule(
13 | $C_1 or L$,
14 | $Pi_1$,
15 | ),
16 | ),
17 | rule(
18 | $C_2 or overline(L)$,
19 | $Pi_2$,
20 | ),
21 | )
22 | )
23 |
--------------------------------------------------------------------------------
/examples/usage.svg:
--------------------------------------------------------------------------------
1 |
217 |
--------------------------------------------------------------------------------
/examples/usage.typ:
--------------------------------------------------------------------------------
1 | #import "../curryst.typ": rule, prooftree
2 | #set document(date: none)
3 | #set page(width: auto, height: auto, margin: 0.5cm, fill: white)
4 |
5 | #let tree = rule(
6 | label: [Label],
7 | name: [Rule name],
8 | [Conclusion],
9 | [Premise 1],
10 | [Premise 2],
11 | [Premise 3]
12 | )
13 |
14 | #prooftree(tree)
15 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | # Curryst test suite
2 |
3 | To run the tests, use
4 | ```
5 | $ typst compile tests.typ 'test-{p}.png' --root ..
6 | ```
7 |
8 | This will override the images. If no image changes, this means the tests passed.
9 |
--------------------------------------------------------------------------------
/tests/test-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-1.png
--------------------------------------------------------------------------------
/tests/test-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-10.png
--------------------------------------------------------------------------------
/tests/test-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-11.png
--------------------------------------------------------------------------------
/tests/test-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-12.png
--------------------------------------------------------------------------------
/tests/test-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-13.png
--------------------------------------------------------------------------------
/tests/test-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-14.png
--------------------------------------------------------------------------------
/tests/test-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-15.png
--------------------------------------------------------------------------------
/tests/test-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-16.png
--------------------------------------------------------------------------------
/tests/test-17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-17.png
--------------------------------------------------------------------------------
/tests/test-18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-18.png
--------------------------------------------------------------------------------
/tests/test-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-19.png
--------------------------------------------------------------------------------
/tests/test-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-2.png
--------------------------------------------------------------------------------
/tests/test-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-20.png
--------------------------------------------------------------------------------
/tests/test-21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-21.png
--------------------------------------------------------------------------------
/tests/test-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-22.png
--------------------------------------------------------------------------------
/tests/test-23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-23.png
--------------------------------------------------------------------------------
/tests/test-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-24.png
--------------------------------------------------------------------------------
/tests/test-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-25.png
--------------------------------------------------------------------------------
/tests/test-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-26.png
--------------------------------------------------------------------------------
/tests/test-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-27.png
--------------------------------------------------------------------------------
/tests/test-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-28.png
--------------------------------------------------------------------------------
/tests/test-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-29.png
--------------------------------------------------------------------------------
/tests/test-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-3.png
--------------------------------------------------------------------------------
/tests/test-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-30.png
--------------------------------------------------------------------------------
/tests/test-31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-31.png
--------------------------------------------------------------------------------
/tests/test-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-32.png
--------------------------------------------------------------------------------
/tests/test-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-33.png
--------------------------------------------------------------------------------
/tests/test-34.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-34.png
--------------------------------------------------------------------------------
/tests/test-35.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-35.png
--------------------------------------------------------------------------------
/tests/test-36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-36.png
--------------------------------------------------------------------------------
/tests/test-37.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-37.png
--------------------------------------------------------------------------------
/tests/test-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-38.png
--------------------------------------------------------------------------------
/tests/test-39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-39.png
--------------------------------------------------------------------------------
/tests/test-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-4.png
--------------------------------------------------------------------------------
/tests/test-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-40.png
--------------------------------------------------------------------------------
/tests/test-41.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-41.png
--------------------------------------------------------------------------------
/tests/test-42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-42.png
--------------------------------------------------------------------------------
/tests/test-43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-43.png
--------------------------------------------------------------------------------
/tests/test-44.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-44.png
--------------------------------------------------------------------------------
/tests/test-45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-45.png
--------------------------------------------------------------------------------
/tests/test-46.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-46.png
--------------------------------------------------------------------------------
/tests/test-47.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-47.png
--------------------------------------------------------------------------------
/tests/test-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-48.png
--------------------------------------------------------------------------------
/tests/test-49.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-49.png
--------------------------------------------------------------------------------
/tests/test-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-5.png
--------------------------------------------------------------------------------
/tests/test-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-50.png
--------------------------------------------------------------------------------
/tests/test-51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-51.png
--------------------------------------------------------------------------------
/tests/test-52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-52.png
--------------------------------------------------------------------------------
/tests/test-53.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-53.png
--------------------------------------------------------------------------------
/tests/test-54.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-54.png
--------------------------------------------------------------------------------
/tests/test-55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-55.png
--------------------------------------------------------------------------------
/tests/test-56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-56.png
--------------------------------------------------------------------------------
/tests/test-57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-57.png
--------------------------------------------------------------------------------
/tests/test-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-58.png
--------------------------------------------------------------------------------
/tests/test-59.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-59.png
--------------------------------------------------------------------------------
/tests/test-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-6.png
--------------------------------------------------------------------------------
/tests/test-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-60.png
--------------------------------------------------------------------------------
/tests/test-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-7.png
--------------------------------------------------------------------------------
/tests/test-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-8.png
--------------------------------------------------------------------------------
/tests/test-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pauladam94/curryst/acacb67eef5fc0a5d2e804f4182f939dbd244f30/tests/test-9.png
--------------------------------------------------------------------------------
/tests/tests.typ:
--------------------------------------------------------------------------------
1 | #import "../curryst.typ" : rule, prooftree
2 | #set document(date: none)
3 | #set page(margin: 0.5cm, width: auto, height: auto)
4 |
5 | #let test(width: auto, config: (:), ..args) = {
6 | for dir in (btt, ttb) {
7 | pagebreak(weak: true)
8 | block(
9 | stroke: 0.3pt + red,
10 | width: width,
11 | prooftree(dir: dir, rule(..args), ..config)
12 | )
13 | }
14 | }
15 |
16 |
17 | #test(
18 | [Conclusion],
19 | )
20 |
21 |
22 | #test(
23 | name: [Axiom],
24 | [Conclusion],
25 | )
26 |
27 |
28 | #test(
29 | label: [Label],
30 | name: [Axiom],
31 | [Conclusion],
32 | )
33 |
34 |
35 | #test(
36 | label: [Label],
37 | name: [Name],
38 | [Long conclusion],
39 | [Premise],
40 | )
41 |
42 |
43 | #test(
44 | label: [Label],
45 | name: [Name],
46 | [Conclusion],
47 | [Long premise],
48 | )
49 |
50 |
51 | #test(
52 | label: [Label],
53 | name: [Name],
54 | [Conclusion],
55 | [Premise 1],
56 | [Premise 2],
57 | )
58 |
59 |
60 | #test(
61 | label: [Label],
62 | name: [Name],
63 | [Very long conclusion],
64 | [Prem. 1],
65 | [Prem. 2],
66 | )
67 |
68 |
69 | #test(
70 | label: [Label],
71 | name: [Name],
72 | [Very long conclusion],
73 | rule(
74 | [Prem. 1],
75 | [Hypothesis 1],
76 | ),
77 | [Prem. 2],
78 | )
79 |
80 |
81 | #test(
82 | label: [Label],
83 | name: [Name],
84 | [Very long conclusion],
85 | rule(
86 | label: [Other label],
87 | name: [Other name],
88 | [Prem. 1],
89 | [Hypothesis 1],
90 | ),
91 | [Prem. 2],
92 | )
93 |
94 |
95 | #test(
96 | name: [Name],
97 | [Very long conclusion],
98 | rule(
99 | [Prem. 1],
100 | [Hypothesis 1],
101 | ),
102 | rule(
103 | name: [Other name],
104 | [Prem. 2],
105 | [Hypothesis 2],
106 | ),
107 | )
108 |
109 |
110 | #let ax(ccl) = rule(name: [ax], ccl)
111 | #let and-el(ccl, p) = rule(name: $and_e^l$, ccl, p)
112 | #let and-er(ccl, p) = rule(name: $and_e^r$, ccl, p)
113 | #let impl-i(ccl, p) = rule(name: $attach(->, br: i)$, ccl, p)
114 | #let impl-e(ccl, pi, p1) = rule(name: $attach(->, br: e)$, ccl, pi, p1)
115 | #let not-i(ccl, p) = rule(name: $not_i$, ccl, p)
116 | #let not-e(ccl, pf, pt) = rule(name: $not_e$, ccl, pf, pt)
117 |
118 | #test(
119 | [Conclusion],
120 | impl-i(
121 | $tack (p -> q) -> not (p and not q)$,
122 | not-i(
123 | $p -> q tack not (p and not q)$,
124 | not-e(
125 | $p -> q, p and not q tack bot$,
126 | impl-e(
127 | $Gamma tack q$,
128 | ax($Gamma tack p -> q$),
129 | and-el(
130 | $Gamma tack p$,
131 | ax($Gamma tack p and not q$),
132 | ),
133 | ),
134 | and-er(
135 | $Gamma tack not q$,
136 | ax($Gamma tack p and not q$),
137 | ),
138 | ),
139 | ),
140 | ),
141 | )
142 |
143 |
144 | #test(
145 | [This is a very wide conclusion, wider than all premises combined],
146 | [Premise],
147 | [Premise],
148 | [Premise],
149 | )
150 |
151 | #test(
152 | [Conclusion],
153 | rule(
154 | [Premise],
155 | [Short],
156 | ),
157 | rule(
158 | [Premise],
159 | [Short],
160 | ),
161 | rule(
162 | [Premise],
163 | [Very long premise to a premise in a tree],
164 | ),
165 | )
166 |
167 | #test(
168 | [Conclusion],
169 | rule(
170 | [Premise],
171 | [Very long premise to a premise in a tree],
172 | ),
173 | rule(
174 | [Premise],
175 | [Short],
176 | ),
177 | rule(
178 | [Premise],
179 | [Short],
180 | ),
181 | )
182 |
183 |
184 | #let ax(ccl) = rule(name: "aaaaaaaa", ccl)
185 | #let and-el(ccl, p) = rule(name: "aaaaaaaaaaaaaaaaaaaaaaaa", ccl, p)
186 | #let and-er(ccl, p) = rule(name: "aaaaaaaa", ccl, p)
187 | #let impl-i(ccl, p) = rule(name: "aaaaaaaa", ccl, p)
188 | #let impl-e(ccl, pi, p1) = rule(name: "aaaaaaaaaaaaaaaaa", ccl, pi, p1)
189 | #let not-i(ccl, p) = rule(name: "aaaaaaaa", ccl, p)
190 | #let not-e(ccl, pf, pt) = rule(name: "aaaaaaaa", ccl, pf, pt)
191 |
192 | #test(
193 | config: (min-premise-spacing: 8pt),
194 | [Conclusion],
195 | impl-i(
196 | $tack (p -> q) -> not (p and not q)$,
197 | not-i(
198 | $p -> q tack not (p and not q)$,
199 | not-e(
200 | $p -> q, p and not q tack bot$,
201 | impl-e(
202 | $Gamma tack q$,
203 | ax($Gamma tack p -> q$),
204 | and-el(
205 | $Gamma tack p$,
206 | ax($Gamma tack p and not q$),
207 | ),
208 | ),
209 | and-er(
210 | $Gamma tack not q$,
211 | ax($Gamma tack p and not q$),
212 | ),
213 | ),
214 | ),
215 | ),
216 | )
217 |
218 |
219 | #test(
220 | config: (stroke: stroke(paint: blue, thickness: 2pt, cap: "round", dash: "dashed")),
221 | name: [Name],
222 | [Conclusion],
223 | [Premise],
224 | )
225 |
226 |
227 | #test(
228 | config: (
229 | min-premise-spacing: 2cm,
230 | title-inset: 1cm,
231 | vertical-spacing: 0.2cm,
232 | min-bar-height: 0.3cm,
233 | ),
234 | name: [Name],
235 | [Conclusion],
236 | rule(
237 | label: [Label 1],
238 | [Premise 1],
239 | [Hypothesis 1],
240 | ),
241 | rule(
242 | name: [Name 2],
243 | [Premise 2],
244 | [Hypothesis 2],
245 | ),
246 | rule(
247 | label: [Label 3],
248 | name: [Name 3],
249 | [Premise 3],
250 | [Hypothesis 3],
251 | ),
252 | )
253 |
254 |
255 | #test(
256 | config: (
257 | min-premise-spacing: 0pt,
258 | title-inset: 0pt,
259 | vertical-spacing: 0pt,
260 | min-bar-height: 0pt,
261 | ),
262 | name: [Name],
263 | [Conclusion],
264 | rule(
265 | label: [Label 1],
266 | [Premise 1],
267 | [Hypothesis 1],
268 | ),
269 | rule(
270 | name: [Name 2],
271 | [Premise 2],
272 | [Hypothesis 2],
273 | ),
274 | rule(
275 | label: [Label 3],
276 | name: [Name 3],
277 | [Premise 3],
278 | [Hypothesis 3],
279 | ),
280 | )
281 |
282 |
283 | // Test leafs are shown on multiple lines when appropriate.
284 |
285 | #test(
286 | width: 5cm,
287 | name: $or_e$,
288 | $Gamma tack psi$,
289 | $Gamma tack phi_1 or phi_2$,
290 | $Gamma, phi_1 tack psi$,
291 | $Gamma, phi_2 tack psi$,
292 | )
293 |
294 | #test(
295 | width: 5cm,
296 | config: (
297 | min-premise-spacing: 1cm
298 | ),
299 | [The conclusion],
300 | rect(width: 1cm),
301 | rect(width: 1cm),
302 | rect(width: 1cm),
303 | )
304 |
305 | #test(
306 | width: 5cm,
307 | config: (
308 | min-premise-spacing: 1cm
309 | ),
310 | [The conclusion is a bit wide],
311 | rect(width: 1cm),
312 | rect(width: 1cm),
313 | rect(width: 1cm),
314 | )
315 |
316 | #test(
317 | width: 5cm,
318 | config: (
319 | min-premise-spacing: 1cm
320 | ),
321 | [The conclusion is hugely wide!!!],
322 | rect(width: 1cm),
323 | rect(width: 1cm),
324 | rect(width: 1cm),
325 | )
326 |
327 | #test(
328 | width: 5cm,
329 | config: (
330 | min-premise-spacing: 1cm,
331 | title-inset: 0pt,
332 | ),
333 | [The conclusion],
334 | rect(width: 1cm),
335 | rect(width: 1cm),
336 | rect(width: 1cm),
337 | )
338 |
339 | #test(
340 | width: 8cm,
341 | config: (
342 | min-premise-spacing: 1cm,
343 | title-inset: 0.5cm,
344 | ),
345 | name: rect(width: 0.5cm),
346 | label: rect(width: 0.5cm),
347 | [The conclusion],
348 | rect(width: 1cm),
349 | rect(width: 1cm),
350 | rect(width: 1cm),
351 | )
352 |
353 | #test(
354 | width: 7.9cm,
355 | config: (
356 | min-premise-spacing: 1cm,
357 | title-inset: 0.5cm,
358 | ),
359 | name: rect(width: 0.5cm),
360 | label: rect(width: 0.5cm),
361 | [The conclusion],
362 | rect(width: 1cm),
363 | rect(width: 1cm),
364 | rect(width: 1cm),
365 | )
366 |
367 | #{
368 | // This test triggers a very specific issue. I can't find a way to reproduce
369 | // it without using Libertinus Serif as the font.
370 | // The issue is that rules are incorrectly laid out vertically due to rounding
371 | // errors. Note that, for this test to work, the container in the `test`
372 | // function should be a `block` and not a `box`.
373 | set text(font: "Libertinus Serif")
374 | test(
375 | name: [......................],
376 | [...],
377 | [................],
378 | [................],
379 | )
380 | }
381 |
382 |
383 | #test(
384 | [This is a very wide conclusion, wider than all premises combined],
385 | [Premise],
386 | [Another premise.],
387 | )
388 |
389 | #test(
390 | [This is a very wide conclusion, wider than all premises combined],
391 | rule(
392 | [Premise],
393 | [Very, very wide hypothesis...]
394 | ),
395 | [Another premise.],
396 | )
397 |
398 | #test(
399 | [This is a very wide conclusion, wider than all premises combined],
400 | rule(
401 | [Premise],
402 | [Hyyyyyyyyyyyyyyyyyyypothesis]
403 | ),
404 | rule(
405 | [Premise],
406 | [Hyyyyyyyyyyyyyyyyyyypothesis as well]
407 | ),
408 | )
409 |
410 |
411 | #test(
412 | [This is a wide conclusion, but not the widest],
413 | rule(
414 | [Premise],
415 | [Hyyyyyyyyyyyyyyyyyyypothesis]
416 | ),
417 | rule(
418 | [Premise],
419 | [Hyyyyyyyyyyyyyyyyyyypothesis as well]
420 | ),
421 | )
422 |
--------------------------------------------------------------------------------
/typst.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "curryst"
3 | version = "0.5.1"
4 | entrypoint = "curryst.typ"
5 | authors = ["Rémi Hutin <@remih23>", "Paul Adam <@pauladam94>", "Malo <@MDLC01>"]
6 | license = "MIT"
7 | description = "Typeset trees of inference rules."
8 | repository = "https://github.com/pauladam94/curryst"
9 | keywords = ["proof tree", "proof trees", "prooftree", "prooftrees", "inference", "logic", "deduction"]
10 | categories = ["components", "visualization", "integration"]
11 | disciplines = ["computer-science", "mathematics"]
12 | compiler = "0.12.0"
13 | exclude = [".gitignore", "examples/*", "tests/*"]
14 |
--------------------------------------------------------------------------------