//! The declared form's syntax tree. //! //! This is the grammar of wiki `quasi-declare-form` section 4, narrowed to what //! a real screen has actually demanded. It grows one production at a time, and //! every production here exists because a conversion needed it: nothing is //! specified ahead of a screen that wants it. //! //! What the form refuses is as load-bearing as what it admits. There is no //! expression node, no block in argument position, no closure and no struct //! literal, because those four are what keep `Node: Eq` intact and keep an //! argument from reopening into a sub-grammar. A shape that needs one of them //! calls a supplier function beside the declaration instead. use proc_macro2::Span; use syn::{Ident, Type}; /// One declared shape: a header and the items its body emits. pub struct Declaration { pub docs: Vec, /// The bare attributes the generated function carries, `must_use` and /// `inline`. Nothing that takes an argument: an attribute with a body is a /// second grammar, and none of the converted screens has wanted one. pub flags: Vec, pub vis: Option, pub name: Ident, pub params: Vec, pub returns: Type, pub items: Vec, } #[derive(Clone)] pub struct Param { pub name: Ident, pub ty: Type, } #[derive(Clone)] pub enum Item { /// `let name = ;` Bind { name: Ident, source: Source }, /// ` * ?;` -- one setting on the enclosing container. /// /// The ident is the **builder method's** name and not the struct field's, /// which is what ATTRIBUTE NAMING settles: `Slot::named` writes `name`, and /// a form that spelled the field would be naming something no caller can /// reach. /// /// A setting takes a guard for the same reason a member does: the rule is /// that a guard is allowed where something may be absent, and a setting /// that is sometimes not made is exactly that. `git_explore`'s `more` is /// the live instance -- a table says what it has not shown only when there /// is another page. Attribute { name: Ident, args: Vec, guard: Option, }, /// `for in { .. }` -- the same body once per element. /// /// A loop is the one item that cannot be hoisted above its container: its /// members name the binder, which does not exist until the loop does. For { /// `&name` where the elements are references to values the members /// take by value. Written where the code writes it, as R8 requires, /// and a pattern rather than an expression. dereferenced: bool, binder: Ident, iterable: Hole, body: Vec, }, /// Anything that puts something into the enclosing container. Emit(Emission), } #[derive(Clone)] pub enum Source { /// A string literal, which may carry `{hole}` interpolations. Str(Interpolated), /// A hole: one eager evaluation whose owned result lands in a field. Hole(Hole), /// `given { -> , .. }` in value position. /// /// This is the production the record's amendment 9 was reaching for and /// missed: it put `dispatch` on the statement side, where an arm is an /// emission and no emission is a value. Here a value dispatch is its own /// node whose arms are sources, so it produces a value by construction and /// cannot admit a block. Choose { scrutinee: Hole, arms: Vec<(Pattern, Source)>, otherwise: Box, }, } /// A pattern in value-dispatch position. Literals only: a binding pattern would /// need a scope, and a scope is how an arm becomes a block. #[derive(Clone)] pub enum Pattern { Int(i64), Str(String), Bool(bool), /// `Enum::Variant`, emitted verbatim so rustc judges exhaustiveness. Path(syn::Path), } /// A string with `{hole}` interpolations, compiled to a literal push or a /// `format!` depending on whether it has any. #[derive(Clone)] pub struct Interpolated { pub parts: Vec, pub span: Span, } #[derive(Clone)] pub enum StrPart { Lit(String), Hole(Hole), } /// One evaluation: a path, optionally called, then field and method steps. /// /// A hole is deliberately not an expression. It cannot contain an operator, a /// closure, a turbofish, an index or a block, so the macro can place its result /// in a field without reasoning about evaluation order. #[derive(Clone)] pub struct Hole { pub root: HoleRoot, pub steps: Vec, } #[derive(Clone)] pub enum HoleRoot { /// A bare lowercase ident: a binding, innermost first. Binding(Ident), /// A `::`-qualified or uppercase-initial path: a Rust path, binding nothing. Path(syn::Path), /// A module function called at the root of a hole. Call { path: syn::Path, args: Vec }, } #[derive(Clone)] pub enum Step { Field(Ident), Method { name: Ident, args: Vec }, } #[derive(Clone)] pub enum Arg { Str(Interpolated), /// `[ , .. ]`, which is an array literal and never a collection: what /// it can hold is an arg, so it cannot reopen into a sub-grammar. List(Vec), Hole(Hole), Int(i64), Bool(bool), /// `&`. Nothing borrows implicitly, so the ampersand is written where /// the code writes it. Borrow(Box), } /// Everything that emits into the enclosing container. #[derive(Clone)] pub enum Emission { /// ` * ( "{" { item } "}" | ";" )` -- one of `Node`'s own /// constructors, by its own name, and whatever it is told afterwards. /// /// The body is what `forum_memberships` demanded: `Node::empty` carries a /// way out (`offering`) on the library's pane and not on the settings pane, /// so an empty state is a member with a setting rather than a bare one. Simple { member: Ident, args: Vec, body: Vec, }, /// `chip to { .. }`, and `removable to `. /// /// Its own emission rather than a `Simple` member because `Tag::chip` takes /// an `Action`, and an action is spelled `to ` rather than as an arg. /// /// One emission for both because they differ by one constructor and nothing /// else. `Tag` has three constructors and the form now says all three: /// `badge` is the inert one, `chip` goes somewhere, and `removable` goes /// somewhere and says the going takes it off. Chip { value: Arg, action: Action, /// Whether the tag says it can be taken off, which is `Tag::removable`. removable: bool, body: Vec, }, /// `screen { .. }` Screen { arrangement: Ident, /// What the arrangement is told. Every one takes a title; `list_detail` /// takes whether it is tabbed as well, which is why this is a list /// rather than the one title it was until wave 9. args: Vec, body: Vec, }, /// `row { .. }` Row { primary: Arg, body: Vec }, /// `form { submit ; field .. }` Form { action: Action, body: Vec }, /// `field