Skip to main content

max / quasi

Let a region say it repeats, and what takes one away `Slot::removes` takes an `Act` and `Repeating::new` takes an `Act`, so audiofiles' rule editor was writing four controls as `Act::new(..)` inside a call. That is the expression in argument position this grammar refuses, and the alternative was not a longer spelling: it was building the controls in the read, where no guard reaches them and nothing in the description says their labels or their addresses. `removes <arg> to <action>` is `act`'s shape with a different setter. `repeats <arg> adds <arg> to <action>` puts the add control in the header because `Repeating::new` takes it, and leaves the body for that type's own settings, which is the only arrangement that needs no container with a required member. Spelled `repeats` and not `repeating`: `Field::repeating` takes a `Repeat` and `Slot::repeating` takes a `Repeating`, and a member and a setting cannot share a name. Third time after `underway` and `proportion`.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
Author: Max Johnson <me@maxj.phd> · 2026-09-04 23:58 UTC
Signed with PGP, not checked
Commit: 56b0605bb868d290b7c298c1359160e0549bbf82
Parent: cd555a2
4 files changed, +138 insertions, -2 deletions
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-declare"
3 - version = "0.1.5"
3 + version = "0.1.6"
4 4 description = "The declare! form: a screen description compiled to Rust at build time."
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -262,6 +262,35 @@
262 262 action: Action,
263 263 body: Vec<Item>,
264 264 },
265 + /// `removes <arg> to <action> ( "{" { item } "}" | ";" )` -- the control one
266 + /// slot of a repeating question carries to take itself away.
267 + ///
268 + /// `act`'s shape and `Slot::removes`'s meaning, which is one word apart for
269 + /// the reason `offers` was: a control that takes its own region away is told
270 + /// the same things every other control is, and written as an expression in
271 + /// argument position it can be told none of them. audiofiles' rule editor is
272 + /// the site, twice.
273 + Removes {
274 + label: Arg,
275 + action: Action,
276 + body: Vec<Item>,
277 + },
278 + /// `repeats <arg> adds <arg> to <action> ( "{" { item } "}" | ";" )`
279 + ///
280 + /// A region that is a repeating question: what one of them is called, the
281 + /// control that adds another, and how few may be left standing. The add
282 + /// control is in the header rather than the body because `Repeating::new`
283 + /// takes it, and the body is that type's own settings.
284 + ///
285 + /// Spelled `repeats` and not `repeating`, which is the setting it calls,
286 + /// because `Field::repeating` takes a different type and one name cannot
287 + /// mean both. Same rule and same remedy as `underway` and `proportion`.
288 + Repeats {
289 + one: Arg,
290 + label: Arg,
291 + action: Action,
292 + body: Vec<Item>,
293 + },
265 294 /// `activate to <action>;` -- what opening this row or cell does.
266 295 ///
267 296 /// A setting in shape, and a member in the tree, because the thing it sets
@@ -58,6 +58,9 @@
58 58 Meter,
59 59 /// One figure: its body says what it is like, and it emits nothing.
60 60 Figure,
61 + /// One repeating question: its body says how few may be left, and it emits
62 + /// nothing.
63 + Repeats,
61 64 }
62 65
63 66 pub fn declaration(declaration: &Declaration) -> Result<TokenStream> {
@@ -576,6 +579,51 @@
576 579 panel_member(inner)?,
577 580 quote!(.with_ranked(#held, ::quasi_router::layout::Priority::#priority)),
578 581 )),
582 + // The control one slot of a repeating question carries to take itself
583 + // away, and the question the slots are of. Both are `Act`s the region is
584 + // told, and neither could be said at all: written where the vocabulary
585 + // takes them they are expressions in argument position, which no guard
586 + // reaches and no reader of the description sees. audiofiles' rule editor
587 + // is the site for both, twice each.
588 + (
589 + Container::Slot,
590 + Emission::Removes {
591 + label,
592 + action,
593 + body,
594 + },
595 + ) => {
596 + let label = arg(label)?;
597 + let called = self::action(action)?;
598 + let taken = accrete(
599 + body,
600 + Container::Act,
601 + &quote!(::quasi_router::Act::new(#label, #called)),
602 + )?;
603 + Ok((taken, quote!(.removes(#held))))
604 + }
605 + (
606 + Container::Slot,
607 + Emission::Repeats {
608 + one,
609 + label,
610 + action,
611 + body,
612 + },
613 + ) => {
614 + let one = arg(one)?;
615 + let label = arg(label)?;
616 + let called = self::action(action)?;
617 + let question = accrete(
618 + body,
619 + Container::Repeats,
620 + &quote!(::quasi_router::Repeating::new(
621 + #one,
622 + ::quasi_router::Act::new(#label, #called)
623 + )),
624 + )?;
625 + Ok((question, quote!(.repeating(#held))))
626 + }
579 627 (Container::Slot, other) => Ok((panel_member(other)?, quote!(.with(#held)))),
580 628 (Container::Screen, Emission::Region { name, kind, body }) => {
581 629 Ok((slot(name, kind, body)?, quote!(.with(#held))))
@@ -654,6 +702,10 @@
654 702 emission_span(other),
655 703 "a meter holds nothing: its body says what it is like",
656 704 )),
705 + (Container::Repeats, other) => Err(syn::Error::new(
706 + emission_span(other),
707 + "a repeating question holds nothing: its body says how few may be left",
708 + )),
657 709 (Container::Run, Emission::Beside { priority, inner }) => Ok((
658 710 node(inner)?,
659 711 quote!(.beside(#held, ::quasi_router::layout::Priority::#priority)),
@@ -1111,6 +1163,13 @@
1111 1163 emission_span(inner),
1112 1164 "`at` places a row on an axis; it needs a `timeline` in scope",
1113 1165 )),
1166 + Emission::Removes { action, .. } | Emission::Repeats { action, .. } => {
1167 + Err(syn::Error::new(
1168 + action.verb.span(),
1169 + "a repeating question and the control that takes one away are a \
1170 + region's, not a node's",
1171 + ))
1172 + }
1114 1173 }
1115 1174 }
1116 1175
@@ -1790,7 +1849,10 @@
1790 1849 Emission::Row { .. } | Emission::List(_) => Span::call_site(),
1791 1850 Emission::Form { action, .. } | Emission::Chip { action, .. } => action.verb.span(),
1792 1851 Emission::Field { kind, .. } => kind.span(),
1793 - Emission::Act { action, .. } | Emission::Offers { action, .. } => action.verb.span(),
1852 + Emission::Act { action, .. }
1853 + | Emission::Offers { action, .. }
1854 + | Emission::Removes { action, .. }
1855 + | Emission::Repeats { action, .. } => action.verb.span(),
1794 1856 Emission::Guarded { guard, .. } => guard.span,
1795 1857 Emission::Given { .. } => Span::call_site(),
1796 1858 Emission::Region { kind, .. } => match kind {
@@ -74,6 +74,8 @@
74 74 "offers",
75 75 "region",
76 76 "removable",
77 + "removes",
78 + "repeats",
77 79 "row",
78 80 "screen",
79 81 "table",
@@ -610,6 +612,49 @@
610 612 },
611 613 ))
612 614 }
615 + "removes" => {
616 + let label: Arg = input.parse()?;
617 + preposition(input, "to")?;
618 + let action: Action = input.parse()?;
619 + let guard = guard(input)?;
620 + let body = if input.peek(token::Brace) {
621 + block(input)?
622 + } else {
623 + input.parse::<Token![;]>()?;
624 + Vec::new()
625 + };
626 + Ok(guarded(
627 + guard,
628 + Self::Removes {
629 + label,
630 + action,
631 + body,
632 + },
633 + ))
634 + }
635 + "repeats" => {
636 + let one: Arg = input.parse()?;
637 + preposition(input, "adds")?;
638 + let label: Arg = input.parse()?;
639 + preposition(input, "to")?;
640 + let action: Action = input.parse()?;
641 + let guard = guard(input)?;
642 + let body = if input.peek(token::Brace) {
643 + block(input)?
644 + } else {
645 + input.parse::<Token![;]>()?;
646 + Vec::new()
647 + };
648 + Ok(guarded(
649 + guard,
650 + Self::Repeats {
651 + one,
652 + label,
653 + action,
654 + body,
655 + },
656 + ))
657 + }
613 658 "include" => {
614 659 let supplier: Hole = input.parse()?;
615 660 let guard = guard(input)?;