Skip to main content

max / quasi

17.6 KB · 454 lines History Blame Raw
1 //! The declared form's syntax tree.
2 //!
3 //! This is the grammar of wiki `quasi-declare-form` section 4, narrowed to what
4 //! a real screen has actually demanded. It grows one production at a time, and
5 //! every production here exists because a conversion needed it: nothing is
6 //! specified ahead of a screen that wants it.
7 //!
8 //! What the form refuses is as load-bearing as what it admits. There is no
9 //! expression node, no block in argument position, no closure and no struct
10 //! literal, because those four are what keep `Node: Eq` intact and keep an
11 //! argument from reopening into a sub-grammar. A shape that needs one of them
12 //! calls a supplier function beside the declaration instead.
13
14 use proc_macro2::Span;
15 use syn::{Ident, Type};
16
17 /// One declared shape: a header and the items its body emits.
18 pub struct Declaration {
19 pub docs: Vec<String>,
20 /// The bare attributes the generated function carries, `must_use` and
21 /// `inline`. Nothing that takes an argument: an attribute with a body is a
22 /// second grammar, and none of the converted screens has wanted one.
23 pub flags: Vec<Ident>,
24 pub vis: Option<syn::Visibility>,
25 pub name: Ident,
26 pub params: Vec<Param>,
27 pub returns: Type,
28 pub items: Vec<Item>,
29 }
30
31 #[derive(Clone)]
32 pub struct Param {
33 pub name: Ident,
34 pub ty: Type,
35 }
36
37 #[derive(Clone)]
38 pub enum Item {
39 /// `let name = <source>;`
40 Bind { name: Ident, source: Source },
41 /// `<builder> <arg>* <guard>?;` -- one setting on the enclosing container.
42 ///
43 /// The ident is the **builder method's** name and not the struct field's,
44 /// which is what ATTRIBUTE NAMING settles: `Slot::named` writes `name`, and
45 /// a form that spelled the field would be naming something no caller can
46 /// reach.
47 ///
48 /// A setting takes a guard for the same reason a member does: the rule is
49 /// that a guard is allowed where something may be absent, and a setting
50 /// that is sometimes not made is exactly that. `git_explore`'s `more` is
51 /// the live instance -- a table says what it has not shown only when there
52 /// is another page.
53 Attribute {
54 name: Ident,
55 args: Vec<Arg>,
56 guard: Option<Guard>,
57 },
58 /// `for <binder> in <hole> { .. }` -- the same body once per element.
59 ///
60 /// A loop is the one item that cannot be hoisted above its container: its
61 /// members name the binder, which does not exist until the loop does.
62 For {
63 /// `&name` where the elements are references to values the members
64 /// take by value. Written where the code writes it, as R8 requires,
65 /// and a pattern rather than an expression.
66 dereferenced: bool,
67 binder: Ident,
68 iterable: Hole,
69 body: Vec<Item>,
70 },
71 /// Anything that puts something into the enclosing container.
72 Emit(Emission),
73 }
74
75 #[derive(Clone)]
76 pub enum Source {
77 /// A string literal, which may carry `{hole}` interpolations.
78 Str(Interpolated),
79 /// A hole: one eager evaluation whose owned result lands in a field.
80 Hole(Hole),
81 /// `given <hole> { <literal> -> <source>, .. }` in value position.
82 ///
83 /// This is the production the record's amendment 9 was reaching for and
84 /// missed: it put `dispatch` on the statement side, where an arm is an
85 /// emission and no emission is a value. Here a value dispatch is its own
86 /// node whose arms are sources, so it produces a value by construction and
87 /// cannot admit a block.
88 Choose {
89 scrutinee: Hole,
90 arms: Vec<(Pattern, Source)>,
91 otherwise: Box<Source>,
92 },
93 }
94
95 /// A pattern in value-dispatch position. Literals only: a binding pattern would
96 /// need a scope, and a scope is how an arm becomes a block.
97 #[derive(Clone)]
98 pub enum Pattern {
99 Int(i64),
100 Str(String),
101 Bool(bool),
102 /// `Enum::Variant`, emitted verbatim so rustc judges exhaustiveness.
103 Path(syn::Path),
104 }
105
106 /// A string with `{hole}` interpolations, compiled to a literal push or a
107 /// `format!` depending on whether it has any.
108 #[derive(Clone)]
109 pub struct Interpolated {
110 pub parts: Vec<StrPart>,
111 pub span: Span,
112 }
113
114 #[derive(Clone)]
115 pub enum StrPart {
116 Lit(String),
117 Hole(Hole),
118 }
119
120 /// One evaluation: a path, optionally called, then field and method steps.
121 ///
122 /// A hole is deliberately not an expression. It cannot contain an operator, a
123 /// closure, a turbofish, an index or a block, so the macro can place its result
124 /// in a field without reasoning about evaluation order.
125 #[derive(Clone)]
126 pub struct Hole {
127 pub root: HoleRoot,
128 pub steps: Vec<Step>,
129 }
130
131 #[derive(Clone)]
132 pub enum HoleRoot {
133 /// A bare lowercase ident: a binding, innermost first.
134 Binding(Ident),
135 /// A `::`-qualified or uppercase-initial path: a Rust path, binding nothing.
136 Path(syn::Path),
137 /// A module function called at the root of a hole.
138 Call { path: syn::Path, args: Vec<Arg> },
139 }
140
141 #[derive(Clone)]
142 pub enum Step {
143 Field(Ident),
144 Method { name: Ident, args: Vec<Arg> },
145 }
146
147 #[derive(Clone)]
148 pub enum Arg {
149 Str(Interpolated),
150 /// `[ <arg>, .. ]`, which is an array literal and never a collection: what
151 /// it can hold is an arg, so it cannot reopen into a sub-grammar.
152 List(Vec<Arg>),
153 Hole(Hole),
154 Int(i64),
155 Bool(bool),
156 /// `&<hole>`. Nothing borrows implicitly, so the ampersand is written where
157 /// the code writes it.
158 Borrow(Box<Arg>),
159 }
160
161 /// Everything that emits into the enclosing container.
162 #[derive(Clone)]
163 pub enum Emission {
164 /// `<member> <arg>* ( "{" { item } "}" | ";" )` -- one of `Node`'s own
165 /// constructors, by its own name, and whatever it is told afterwards.
166 ///
167 /// The body is what `forum_memberships` demanded: `Node::empty` carries a
168 /// way out (`offering`) on the library's pane and not on the settings pane,
169 /// so an empty state is a member with a setting rather than a bare one.
170 Simple {
171 member: Ident,
172 args: Vec<Arg>,
173 body: Vec<Item>,
174 },
175 /// `chip <value> to <action> { .. }`, and `removable <value> to <action>`.
176 ///
177 /// Its own emission rather than a `Simple` member because `Tag::chip` takes
178 /// an `Action`, and an action is spelled `to <verb>` rather than as an arg.
179 ///
180 /// One emission for both because they differ by one constructor and nothing
181 /// else. `Tag` has three constructors and the form now says all three:
182 /// `badge` is the inert one, `chip` goes somewhere, and `removable` goes
183 /// somewhere and says the going takes it off.
184 Chip {
185 value: Arg,
186 action: Action,
187 /// Whether the tag says it can be taken off, which is `Tag::removable`.
188 removable: bool,
189 body: Vec<Item>,
190 },
191 /// `screen <arrangement> <arg> { .. }`
192 Screen {
193 arrangement: Ident,
194 /// What the arrangement is told. Every one takes a title; `list_detail`
195 /// takes whether it is tabbed as well, which is why this is a list
196 /// rather than the one title it was until wave 9.
197 args: Vec<Arg>,
198 body: Vec<Item>,
199 },
200 /// `row <arg> { .. }`
201 Row { primary: Arg, body: Vec<Item> },
202 /// `form <action> { submit <arg>; field .. }`
203 Form { action: Action, body: Vec<Item> },
204 /// `field <kind> <name> <label> ( { .. } | ; )`
205 Field {
206 kind: Ident,
207 name: Arg,
208 label: Arg,
209 body: Vec<Item>,
210 },
211 /// `list { .. }` -- the rows built in place rather than supplied.
212 ///
213 /// Its own member rather than a container in the general sense: a `Vec`
214 /// does not chain, so the one thing that accretes by pushing is kept where
215 /// it can be read instead of bending every other container's shape.
216 List(Vec<Item>),
217 /// `act <arg> to <action> ( { .. } | ; )`
218 Act {
219 label: Arg,
220 action: Action,
221 body: Vec<Item>,
222 },
223 /// `offers <arg> to <action> ( { .. } | ; )` -- `act`'s held-back twin.
224 ///
225 /// A control a row carries but does not show: `Row::offers` pushes onto the
226 /// menu a host reveals when it is asked, where `Row::act` puts the control
227 /// in the row. One word apart because that is the whole of the difference,
228 /// and a member rather than a setting because a menu control is told the
229 /// same things an inline one is -- audiofiles' sidebar greys a vault's
230 /// Delete when it is the last vault, which a control written as an
231 /// expression cannot say.
232 Offers {
233 label: Arg,
234 action: Action,
235 body: Vec<Item>,
236 },
237 /// `table { column ..; cells { .. } }`
238 ///
239 /// Columns and rows accrete rather than arriving as two lists, which is
240 /// what `Table::column` and `Row::cell` were added to `quasi-router` for.
241 /// The form has no expression to hold a list in, and a production that
242 /// admitted one would be the door this grammar exists to keep shut.
243 Table(Vec<Item>),
244 /// `column <name> ( "{" .. "}" | ";" )` -- one column and how it narrows.
245 Column { name: Arg, body: Vec<Item> },
246 /// `cells { cell ..; }` -- one row of a table.
247 ///
248 /// A row's cells are all positional or all named; mixing the two is what
249 /// `Row::at`'s own docs warn against.
250 Cells(Vec<Item>),
251 /// `cell [ "at" <column> ] <value> ( "{" .. "}" | ";" )`
252 ///
253 /// Positional without the column, named with it. `item_sales` demanded the
254 /// named form: its columns are module consts and its cells are a function
255 /// away, so a row written by position would be lined up against headings
256 /// nobody reading the row can see. Naming is `Row::at`, and the two are
257 /// not mixed in one row -- that warning is `at`'s own.
258 Cell {
259 column: Option<Arg>,
260 value: Arg,
261 body: Vec<Item>,
262 },
263 /// `offering <arg> to <action> ( "{" { item } "}" | ";" )` -- the way out an
264 /// empty or failed state offers.
265 ///
266 /// Spelled like `act` and placed like a setting, because `Node::offering`
267 /// takes an `Act` and a setting's arguments are args. Its own member rather
268 /// than an `act` the container reinterprets: a body holding a control and a
269 /// body holding a way out would otherwise read the same and mean different
270 /// things.
271 Offering {
272 label: Arg,
273 action: Action,
274 body: Vec<Item>,
275 },
276 /// `removes <arg> to <action> ( "{" { item } "}" | ";" )` -- the control one
277 /// slot of a repeating question carries to take itself away.
278 ///
279 /// `act`'s shape and `Slot::removes`'s meaning, which is one word apart for
280 /// the reason `offers` was: a control that takes its own region away is told
281 /// the same things every other control is, and written as an expression in
282 /// argument position it can be told none of them. audiofiles' rule editor is
283 /// the site, twice.
284 Removes {
285 label: Arg,
286 action: Action,
287 body: Vec<Item>,
288 },
289 /// `repeats <arg> adds <arg> to <action> ( "{" { item } "}" | ";" )`
290 ///
291 /// A region that is a repeating question: what one of them is called, the
292 /// control that adds another, and how few may be left standing. The add
293 /// control is in the header rather than the body because `Repeating::new`
294 /// takes it, and the body is that type's own settings.
295 ///
296 /// Spelled `repeats` and not `repeating`, which is the setting it calls,
297 /// because `Field::repeating` takes a different type and one name cannot
298 /// mean both. Same rule and same remedy as `underway` and `proportion`.
299 Repeats {
300 one: Arg,
301 label: Arg,
302 action: Action,
303 body: Vec<Item>,
304 },
305 /// `activate to <action>;` -- what opening this row or cell does.
306 ///
307 /// A setting in shape, and a member in the tree, because the thing it sets
308 /// is an action and a setting's arguments are args. Rows, rows of cells and
309 /// single cells all carry one.
310 Activate(Action),
311 /// `include <hole>;` -- whatever another shape built.
312 Include(Hole),
313 /// `include each <shape>`: a shape answering many members, spliced whole.
314 ///
315 /// The plural of [`Include`](Self::Include), and a separate emission rather
316 /// than a flag on it because what it places is a run and not a node: only
317 /// a container that holds many members can take one.
318 IncludeEach(Hole),
319 /// `link <arg> to <action>;`
320 Link { text: Arg, action: Action },
321 /// `region <arg> as <kind> { .. }`
322 Region {
323 name: Arg,
324 kind: RegionKind,
325 body: Vec<Item>,
326 },
327 /// `across <fallback> { .. }`
328 Across { fallback: Ident, body: Vec<Item> },
329 /// `beside <priority> <emission>`
330 ///
331 /// An `Arg` rather than an `Ident`, which is rule R1(4) reaching the last
332 /// vocabulary slot that did not have it: `tone`, `width`, `priority` and
333 /// `fit` have all taken a hole since wave 2, and a bare uppercase ident
334 /// still reads as the variant, so every existing site is unchanged.
335 /// audiofiles' toolbar is what asked -- its six panel toggles each carry
336 /// what they are worth, and a rank that has to be a literal cannot say it.
337 /// `framed <label> <emission>` -- a member placed as a named frame.
338 ///
339 /// The placement modifier for a region that shows one member at a time. The
340 /// name belongs to the placement rather than to the member, because it names
341 /// the control that reveals it (quasicoherent `2cdc6761`), and a member
342 /// written in another `declare!` cannot carry one at all: a shape answers a
343 /// `Slot`, and since that ruling a `Slot` has no label.
344 ///
345 /// Inside one invocation the macro hoists a child region's own `label`
346 /// instead, so the common spelling is unchanged. This is for the case a
347 /// hoist cannot reach, which is exactly the case that went wrong: goingson's
348 /// `summary_body` is its own shape and its parent includes it.
349 Framed { label: Arg, inner: Box<Emission> },
350 Beside {
351 priority: Arg,
352 /// How much of the row it asks for, where it said.
353 ///
354 /// Optional because most members have no opinion: a strip's members and
355 /// a band's take what they need, which is what a run already drew.
356 /// Written between the rank and the member (`beside Essential Fill
357 /// include pane(x)`) and told from the member by R1's rule -- a member
358 /// is one of the known lowercase words, so anything else in that
359 /// position is the width.
360 ///
361 /// A row only. The body of a region is a stack and its members each get
362 /// the whole width, so a width there is refused rather than ignored.
363 width: Option<Arg>,
364 inner: Box<Emission>,
365 },
366 /// `at <arg> <emission>` -- one thing on an axis, and where it sits.
367 ///
368 /// Only inside a `timeline`, where the argument is a `Placement` and the
369 /// emission is the row that sits at it. The keyword is `cell at <arg>`'s,
370 /// and the container is the whole of how the two are told apart, exactly as
371 /// it is for `beside` and for `include`. A timeline holds nothing but
372 /// entries and a row of cells holds nothing but cells, so no body can be
373 /// read both ways.
374 At { at: Arg, inner: Box<Emission> },
375 /// `<emission> when <predicate>` / `unless <predicate>`.
376 ///
377 /// A guard suppresses the emission and nothing else. R9: the holes inside
378 /// it are still evaluated, because a hole is one eager evaluation and a
379 /// guard is not a scope.
380 Guarded { guard: Guard, inner: Box<Emission> },
381 /// `given <hole> { <pattern> -> <emission> .. }` in member position.
382 ///
383 /// Every arm emits exactly one member, which is what makes the dispatch a
384 /// member itself rather than a statement. R7: this is a real `match`, so
385 /// exhaustiveness and arm order are rustc's.
386 Given {
387 scrutinee: Hole,
388 arms: Vec<(Pattern, Box<Emission>)>,
389 otherwise: Option<Box<Emission>>,
390 },
391 }
392
393 /// What kind of region this is: a variant, or a hole that answers with one.
394 ///
395 /// `RegionKind::Widget` carries a name, so it is the one kind a bare ident
396 /// cannot spell. `media_picker` has three widget regions and a `widget(name)`
397 /// helper beside them, which is the deferred table's own remedy for a value the
398 /// form cannot say; this lets the declaration call it.
399 ///
400 /// Told apart by R1's rule and not by a keyword: a bare uppercase ident is a
401 /// variant, and anything else is a hole.
402 #[derive(Clone)]
403 pub enum RegionKind {
404 Variant(Ident),
405 Supplied(Hole),
406 }
407
408 /// `when <predicate>`, or `unless <predicate>`, which is its negation.
409 #[derive(Clone)]
410 pub struct Guard {
411 pub negated: bool,
412 pub predicate: Predicate,
413 pub span: Span,
414 }
415
416 #[derive(Clone)]
417 pub enum Predicate {
418 /// `<clause> and <clause>`, or `<clause> or <clause>`. One connective per
419 /// predicate: mixing them would need precedence, and precedence is how a
420 /// predicate becomes an expression.
421 Joined {
422 connective: Ident,
423 clauses: Vec<Predicate>,
424 },
425 /// `not <clause>`.
426 Not(Box<Predicate>),
427 /// A hole that is already a `bool`.
428 Truth(Hole),
429 /// `<hole> <compare> <arg>`. The comparison is a word rather than an
430 /// operator, so nothing in the form is an expression.
431 Comparison {
432 left: Hole,
433 compare: Ident,
434 right: Arg,
435 },
436 }
437
438 #[derive(Clone)]
439 pub struct Action {
440 /// One of the verbs, or `doing`, which takes the whole action from a
441 /// supplier. Amendment 6: a screen whose action is decided by something
442 /// the form cannot say gets a function beside it rather than a production.
443 pub verb: Ident,
444 pub target: Option<Arg>,
445 pub modifiers: Vec<Modifier>,
446 }
447
448 /// One word after the verb, and whatever it needs to say it.
449 #[derive(Clone)]
450 pub struct Modifier {
451 pub name: Ident,
452 pub args: Vec<Arg>,
453 }
454