Skip to main content

macroonz_compiler/token/generation/
compose.rs

1//! Composing the Rust a renderer writes, out of the tokens that spell it.
2//!
3//! A renderer states what it means — a path, a call, a binding, an attribute — and never assembles punctuation by hand.
4//! A path stated as segments cannot be mis-spaced and cannot lose a colon; a call stated as a path and its arguments cannot lose a parenthesis; and neither can be built out of a string somebody supplied.
5//!
6//! Every helper here composes and never bounds.
7//! The declared magnitude bites only where a group closes, so exactly the helpers that write a group return [`Overflow`] and the rest are total.
8
9use super::{GeneratedDelimiter, GeneratedToken};
10use crate::bounded::Overflow;
11
12/// One delimited group.
13///
14/// # Errors
15///
16/// Returns [`Overflow`] where the group carries more tokens than the declared magnitude admits.
17pub fn group(
18    delimiter: GeneratedDelimiter,
19    tokens: Vec<GeneratedToken>,
20) -> Result<GeneratedToken, Overflow> {
21    GeneratedToken::group(delimiter, tokens)
22}
23
24/// One macro metavariable, as the two tokens that spell it.
25///
26/// The `$` is written joint, so the projection a person reads is `$name` rather than `$ name`.
27#[must_use]
28pub fn metavariable(name: &str) -> Vec<GeneratedToken> {
29    vec![GeneratedToken::joint('$'), GeneratedToken::word(name)]
30}
31
32/// The absolute path `::a::b::c`.
33#[must_use]
34pub fn absolute_path(segments: &[&str]) -> Vec<GeneratedToken> {
35    let mut tokens = Vec::new();
36    extend_path(&mut tokens, segments);
37    tokens
38}
39
40/// The path `root::a::b::c`, rooted at a crate the caller named.
41///
42/// The root is written as a plain word, so a caller that renamed its dependency is named the way it named itself.
43#[must_use]
44pub fn bound_path(root: &str, segments: &[&str]) -> Vec<GeneratedToken> {
45    let mut tokens = vec![GeneratedToken::word(root)];
46    extend_path(&mut tokens, segments);
47    tokens
48}
49
50/// The path `$binding::a::b::c`, rooted at a metavariable a macro shell will bind.
51#[must_use]
52pub fn twin_path(binding: &str, segments: &[&str]) -> Vec<GeneratedToken> {
53    let mut tokens = metavariable(binding);
54    extend_path(&mut tokens, segments);
55    tokens
56}
57
58/// The path `$root $(:: $segment)*::a::b`, rooted at the complete segmented path a macro matcher captured.
59///
60/// The repetition group has fixed arity, so its fit under the generated-token magnitude is settled at compile time.
61#[must_use]
62pub(crate) fn segmented_twin_path(
63    root_binding: &str,
64    segment_binding: &str,
65    segments: &[&str],
66) -> Vec<GeneratedToken> {
67    let mut tokens = metavariable(root_binding);
68    tokens.push(GeneratedToken::joint('$'));
69    tokens.push(GeneratedToken::fixed_group(
70        GeneratedDelimiter::Parenthesis,
71        [
72            GeneratedToken::joint(':'),
73            GeneratedToken::alone(':'),
74            GeneratedToken::joint('$'),
75            GeneratedToken::word(segment_binding),
76        ],
77    ));
78    tokens.push(GeneratedToken::alone('*'));
79    extend_path(&mut tokens, segments);
80    tokens
81}
82
83/// Write `::segment` for each segment onto a path being built.
84fn extend_path(tokens: &mut Vec<GeneratedToken>, segments: &[&str]) {
85    for segment in segments {
86        tokens.push(GeneratedToken::joint(':'));
87        tokens.push(GeneratedToken::alone(':'));
88        tokens.push(GeneratedToken::word(segment));
89    }
90}
91
92/// One call `path(arguments)`.
93///
94/// # Errors
95///
96/// Returns [`Overflow`] where the argument list outgrows the declared magnitude.
97pub fn call(
98    mut path: Vec<GeneratedToken>,
99    arguments: Vec<GeneratedToken>,
100) -> Result<Vec<GeneratedToken>, Overflow> {
101    path.push(group(GeneratedDelimiter::Parenthesis, arguments)?);
102    Ok(path)
103}
104
105/// One call `receiver.method(arguments)`.
106///
107/// # Errors
108///
109/// Returns [`Overflow`] where the argument list outgrows the declared magnitude.
110pub fn method_call(
111    mut receiver: Vec<GeneratedToken>,
112    method: &str,
113    arguments: Vec<GeneratedToken>,
114) -> Result<Vec<GeneratedToken>, Overflow> {
115    receiver.push(GeneratedToken::alone('.'));
116    receiver.push(GeneratedToken::word(method));
117    call(receiver, arguments)
118}
119
120/// One chain `receiver.first().second().third()`, every method taking no argument.
121///
122/// # Errors
123///
124/// Returns [`Overflow`] where a call in the chain outgrows the declared magnitude.
125pub fn method_chain(
126    mut receiver: Vec<GeneratedToken>,
127    methods: &[&str],
128) -> Result<Vec<GeneratedToken>, Overflow> {
129    for method in methods {
130        receiver = method_call(receiver, method, Vec::new())?;
131    }
132    Ok(receiver)
133}
134
135/// One statement `let name = expression;`.
136///
137/// A value a rendered block needs twice is bound once, which makes the agreement between its two readers structural rather than a comparison of two separately built values.
138#[must_use]
139pub fn bound_local(name: &str, expression: Vec<GeneratedToken>) -> Vec<GeneratedToken> {
140    let mut tokens = vec![
141        GeneratedToken::word("let"),
142        GeneratedToken::word(name),
143        GeneratedToken::alone('='),
144    ];
145    tokens.extend(expression);
146    tokens.push(GeneratedToken::alone(';'));
147    tokens
148}
149
150/// The type `::core::result::Result<ok, error>`.
151#[must_use]
152pub fn result_type(ok: Vec<GeneratedToken>, error: Vec<GeneratedToken>) -> Vec<GeneratedToken> {
153    let mut tokens = absolute_path(&["core", "result", "Result"]);
154    tokens.push(GeneratedToken::alone('<'));
155    tokens.extend(ok);
156    tokens.push(GeneratedToken::alone(','));
157    tokens.extend(error);
158    tokens.push(GeneratedToken::alone('>'));
159    tokens
160}
161
162/// One item `const name: kind = value;`.
163///
164/// The visibility is the caller's and is written before this.
165#[must_use]
166pub fn constant(
167    name: &str,
168    kind: Vec<GeneratedToken>,
169    value: Vec<GeneratedToken>,
170) -> Vec<GeneratedToken> {
171    let mut tokens = vec![GeneratedToken::word("const"), GeneratedToken::word(name)];
172    tokens.push(GeneratedToken::alone(':'));
173    tokens.extend(kind);
174    tokens.push(GeneratedToken::alone('='));
175    tokens.extend(value);
176    tokens.push(GeneratedToken::alone(';'));
177    tokens
178}
179
180/// One comparison `left == right`.
181#[must_use]
182pub fn equality(mut left: Vec<GeneratedToken>, right: Vec<GeneratedToken>) -> Vec<GeneratedToken> {
183    left.push(GeneratedToken::joint('='));
184    left.push(GeneratedToken::alone('='));
185    left.extend(right);
186    left
187}
188
189/// Every comparison joined by `&&`.
190#[must_use]
191pub fn and_all(comparisons: Vec<Vec<GeneratedToken>>) -> Vec<GeneratedToken> {
192    let mut tokens = Vec::new();
193    for (position, comparison) in comparisons.into_iter().enumerate() {
194        if position > 0 {
195            tokens.push(GeneratedToken::joint('&'));
196            tokens.push(GeneratedToken::alone('&'));
197        }
198        tokens.extend(comparison);
199    }
200    tokens
201}
202
203/// Two token runs separated by a comma.
204#[must_use]
205pub fn comma(mut left: Vec<GeneratedToken>, right: Vec<GeneratedToken>) -> Vec<GeneratedToken> {
206    left.push(GeneratedToken::alone(','));
207    left.extend(right);
208    left
209}
210
211/// Every token run separated by a comma, with no trailing one.
212#[must_use]
213pub fn comma_many(parts: Vec<Vec<GeneratedToken>>) -> Vec<GeneratedToken> {
214    let mut tokens = Vec::new();
215    for (position, part) in parts.into_iter().enumerate() {
216        if position > 0 {
217            tokens.push(GeneratedToken::alone(','));
218        }
219        tokens.extend(part);
220    }
221    tokens
222}
223
224/// Two spellings as the two comma-separated text literals a two-argument parser takes.
225#[must_use]
226pub fn text_pair(first: &str, second: &str) -> Vec<GeneratedToken> {
227    vec![
228        GeneratedToken::text(first),
229        GeneratedToken::alone(','),
230        GeneratedToken::text(second),
231    ]
232}
233
234/// One attribute `#[body]`.
235///
236/// # Errors
237///
238/// Returns [`Overflow`] where the body outgrows the declared magnitude.
239pub fn attribute(body: Vec<GeneratedToken>) -> Result<Vec<GeneratedToken>, Overflow> {
240    Ok(vec![
241        GeneratedToken::alone('#'),
242        group(GeneratedDelimiter::Bracket, body)?,
243    ])
244}
245
246/// One documentation attribute over one sentence.
247///
248/// # Errors
249///
250/// Returns [`Overflow`] where the attribute outgrows the declared magnitude.
251pub fn documentation(sentence: &str) -> Result<Vec<GeneratedToken>, Overflow> {
252    attribute(vec![
253        GeneratedToken::word("doc"),
254        GeneratedToken::alone('='),
255        GeneratedToken::text(sentence),
256    ])
257}
258
259/// The `::std::vec![…]` a roster-taking constructor is handed.
260///
261/// # Errors
262///
263/// Returns [`Overflow`] where the roster outgrows the declared magnitude.
264pub fn roster(items: Vec<GeneratedToken>) -> Result<Vec<GeneratedToken>, Overflow> {
265    let mut tokens = absolute_path(&["std", "vec"]);
266    tokens.push(GeneratedToken::alone('!'));
267    tokens.push(group(GeneratedDelimiter::Bracket, items)?);
268    Ok(tokens)
269}
270
271/// One owned `Vec` value, using `Vec::new()` when empty and `Vec::from([members])` otherwise.
272///
273/// # Errors
274///
275/// Returns [`Overflow`] where either generated group outgrows the declared token magnitude.
276pub(crate) fn vector(members: Vec<Vec<GeneratedToken>>) -> Result<Vec<GeneratedToken>, Overflow> {
277    if members.is_empty() {
278        return call(absolute_path(&["std", "vec", "Vec", "new"]), Vec::new());
279    }
280    let array = group(GeneratedDelimiter::Bracket, comma_many(members))?;
281    call(absolute_path(&["std", "vec", "Vec", "from"]), vec![array])
282}