quarb 0.54.0

Query engine for arbors (arboreal graphs - tree-spanned or tree-dominated graphs)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
//! The signature notation of the inventory (spec: Signatures, draft
//! 203): one braced form per command, `f{a}(b; c?; -k|--kind
//! --from=PATH --weight=UNIT?)`. The braced parameter is the stdin
//! slot: the pipe fills it when the command is piped, the first
//! operand fills it at the head of an expression. The parenthesis
//! holds the operands by position, then the option segment. `?`
//! marks an optional operand (never before a required one), `…` zero
//! or more (`x; x…` one or more); a flag is always optional; a valued
//! option is required written bare and optional with `?`; the short
//! spelling stands beside the long one, `-k|--kind`. A nullary
//! command has no braced slot (`now()`); a reducer's slot is the
//! stream (`count{stream}(-o|--of=expr?)`). Both call spellings and
//! the refusal text derive from the one form.

/// One parsed form.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
    pub name: String,
    /// The stdin slot's name, when the command has one.
    pub slot: Option<String>,
    pub operands: Vec<Param>,
    pub options: Vec<OptSpec>,
}

/// One positional operand.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Param {
    pub name: String,
    /// `c?`
    pub optional: bool,
    /// `c…`
    pub variadic: bool,
}

/// One option of the segment.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptSpec {
    /// `--kind`
    pub long: Option<String>,
    /// `-k`
    pub short: Option<String>,
    /// The value's placeholder (`PATH`, `always|structures|off`);
    /// `None` for a flag.
    pub value: Option<String>,
    /// A valued option written bare is required; `?` makes it
    /// optional. A flag is never required.
    pub required: bool,
    /// `--by=col…`: the value takes several — a list, or the option
    /// repeated (ruling 2026-11-06); zero or more, never `…?`.
    pub many: bool,
}

impl OptSpec {
    /// Whether an option as written (`-k`, `--kind`) is this one.
    pub fn answers_to(&self, written: &str) -> bool {
        self.long.as_deref() == Some(written) || self.short.as_deref() == Some(written)
    }

    /// The spelling for a message: `-k|--kind`, `--from=PATH`.
    pub fn spelled(&self) -> String {
        let names: Vec<&str> = [self.short.as_deref(), self.long.as_deref()]
            .into_iter()
            .flatten()
            .collect();
        let mut s = names.join("|");
        if let Some(v) = &self.value {
            s.push('=');
            s.push_str(v);
            if self.many {
                s.push('…');
            } else if !self.required {
                s.push('?');
            }
        }
        s
    }
}

impl Signature {
    /// The operands a call must write.
    pub fn min_operands(&self) -> usize {
        self.operands
            .iter()
            .filter(|p| !p.optional && !p.variadic)
            .count()
    }

    /// The operands a call may write at most; `None` where a `…`
    /// leaves it open.
    pub fn max_operands(&self) -> Option<usize> {
        if self.operands.iter().any(|p| p.variadic) {
            None
        } else {
            Some(self.operands.len())
        }
    }

    /// The option an option as written names.
    pub fn option(&self, written: &str) -> Option<&OptSpec> {
        self.options.iter().find(|o| o.answers_to(written))
    }

    /// The form as the notation writes it.
    pub fn text(&self) -> String {
        let mut s = self.name.clone();
        if let Some(slot) = &self.slot {
            s.push('{');
            s.push_str(slot);
            s.push('}');
        }
        s.push('(');
        let mut fields: Vec<String> = self
            .operands
            .iter()
            .map(|p| {
                let mut f = p.name.clone();
                if p.variadic {
                    f.push('…');
                } else if p.optional {
                    f.push('?');
                }
                f
            })
            .collect();
        if !self.options.is_empty() {
            fields.push(
                self.options
                    .iter()
                    .map(OptSpec::spelled)
                    .collect::<Vec<_>>()
                    .join(" "),
            );
        }
        s.push_str(&fields.join("; "));
        s.push(')');
        s
    }
}

/// An inventory cell: one form, or genuinely different signatures
/// separated by ` / `.
pub fn parse_cell(cell: &str) -> Result<Vec<Signature>, String> {
    cell.split(" / ").map(parse_form).collect()
}

/// One form of the notation.
pub fn parse_form(form: &str) -> Result<Signature, String> {
    let form = form.trim();
    let name_end = form
        .find(|c: char| !(c.is_alphanumeric() || c == '_' || c == ':'))
        .unwrap_or(form.len());
    let name = &form[..name_end];
    if name.is_empty() {
        return Err(format!(
            "'{form}': a signature opens with the command's name"
        ));
    }
    let mut rest = &form[name_end..];
    let mut slot = None;
    if let Some(after) = rest.strip_prefix('{') {
        let Some(close) = after.find('}') else {
            return Err(format!("'{form}': the braced slot is not closed"));
        };
        let word = after[..close].trim();
        if word.is_empty() || !word.chars().all(|c| c.is_alphanumeric() || c == '_') {
            return Err(format!("'{form}': the braced slot is one word"));
        }
        slot = Some(word.to_string());
        rest = &after[close + 1..];
    }
    let Some(inner) = rest.strip_prefix('(').and_then(|r| r.strip_suffix(')')) else {
        return Err(format!(
            "'{form}': the parentheses are always written, empty for a command without operands"
        ));
    };
    let fields: Vec<&str> = inner.split(';').map(str::trim).collect();
    let fields: Vec<&str> = if fields == [""] { Vec::new() } else { fields };
    let mut operands = Vec::new();
    let mut options = Vec::new();
    for (i, field) in fields.iter().enumerate() {
        if field.is_empty() {
            return Err(format!("'{form}': an empty field"));
        }
        if field.starts_with('-') {
            if i + 1 != fields.len() {
                return Err(format!("'{form}': the option segment stands last"));
            }
            for word in field.split_whitespace() {
                let opt = parse_option(word).map_err(|e| format!("'{form}': {e}"))?;
                for seen in &options {
                    let seen: &OptSpec = seen;
                    if seen.long.is_some() && seen.long == opt.long {
                        return Err(format!(
                            "'{form}': '{}' is declared twice",
                            opt.long.as_deref().unwrap_or("")
                        ));
                    }
                    if seen.short.is_some() && seen.short == opt.short {
                        return Err(format!(
                            "'{form}': the short option '{}' is declared twice",
                            opt.short.as_deref().unwrap_or("")
                        ));
                    }
                }
                options.push(opt);
            }
            continue;
        }
        let (text, optional, variadic) = if let Some(t) = field.strip_suffix('…') {
            (t, false, true)
        } else if let Some(t) = field.strip_suffix('?') {
            (t, true, false)
        } else {
            (*field, false, false)
        };
        if text.is_empty() || !text.chars().all(|c| c.is_alphanumeric() || c == '_') {
            return Err(format!("'{form}': '{field}' is no operand name"));
        }
        if operands.iter().any(|p: &Param| p.variadic) {
            return Err(format!(
                "'{form}': '{field}' follows a '…' operand, which stands last"
            ));
        }
        if !optional && !variadic && operands.iter().any(|p: &Param| p.optional) {
            return Err(format!(
                "'{form}': the required operand '{field}' follows an optional one"
            ));
        }
        operands.push(Param {
            name: text.to_string(),
            optional,
            variadic,
        });
    }
    Ok(Signature {
        name: name.to_string(),
        slot,
        operands,
        options,
    })
}

/// `-k|--kind`, `--from=PATH`, `--weight=UNIT?`, `-p|--pretty=a|b`.
fn parse_option(word: &str) -> Result<OptSpec, String> {
    let (names, value) = match word.find('=') {
        Some(eq) => (&word[..eq], Some(&word[eq + 1..])),
        None => (word, None),
    };
    let (value, required, many) = match value {
        None => (None, false, false),
        Some(v) => match (v.strip_suffix('…'), v.strip_suffix('?')) {
            (Some(v), _) => {
                if v.ends_with('?') {
                    return Err(format!(
                        "'{word}': a many-valued option is marked '…' alone, never '…?' or '?…'"
                    ));
                }
                (Some(v), false, true)
            }
            (None, Some(v)) => (Some(v), false, false),
            (None, None) => (Some(v), true, false),
        },
    };
    if let Some(v) = value
        && v.ends_with("…")
    {
        return Err(format!(
            "'{word}': a many-valued option is marked '…' alone, never '?…'"
        ));
    }
    if let Some(v) = value
        && v.is_empty()
    {
        return Err(format!("'{word}': the value's placeholder is missing"));
    }
    if value.is_none() && names.ends_with('?') {
        return Err(format!(
            "'{word}': a flag is always optional, never written with '?'"
        ));
    }
    let mut long = None;
    let mut short = None;
    for name in names.split('|') {
        if let Some(l) = name.strip_prefix("--") {
            if l.is_empty()
                || !l
                    .chars()
                    .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
            {
                return Err(format!("'{word}': '{name}' is no option name"));
            }
            if long.replace(name.to_string()).is_some() {
                return Err(format!("'{word}': two long spellings"));
            }
        } else if let Some(s) = name.strip_prefix('-') {
            let mut chars = s.chars();
            if !(chars.next().is_some_and(char::is_alphabetic) && chars.next().is_none()) {
                return Err(format!(
                    "'{word}': a short option is one letter, '{name}' given"
                ));
            }
            if short.replace(name.to_string()).is_some() {
                return Err(format!("'{word}': two short spellings"));
            }
        } else {
            return Err(format!("'{word}': '{name}' is no option"));
        }
    }
    Ok(OptSpec {
        long,
        short,
        value: value.map(str::to_string),
        required,
        many,
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn a_many_valued_option_carries_the_mark() {
        let f = parse_form("sum{stream}(--of=expr? -b|--by=col…)").unwrap();
        let by = f
            .options
            .iter()
            .find(|o| o.long.as_deref() == Some("--by"))
            .unwrap();
        assert!(by.many && !by.required);
        let of = f
            .options
            .iter()
            .find(|o| o.long.as_deref() == Some("--of"))
            .unwrap();
        assert!(!of.many && !of.required);
        assert_eq!(by.spelled(), "-b|--by=col…");
        assert!(parse_form("x{stream}(--by=col…?)").is_err());
        assert!(parse_form("x{stream}(--by=col?…)").is_err());
    }

    #[test]
    fn the_examples_of_the_notation_parse() {
        let s = parse_form("f{a}(b; c?; -k|--kind --from=PATH --weight=UNIT?)").unwrap();
        assert_eq!(s.name, "f");
        assert_eq!(s.slot.as_deref(), Some("a"));
        assert_eq!(s.min_operands(), 1);
        assert_eq!(s.max_operands(), Some(2));
        assert_eq!(s.options.len(), 3);
        let kind = s.option("-k").unwrap();
        assert_eq!(kind.long.as_deref(), Some("--kind"));
        assert!(kind.value.is_none() && !kind.required);
        let from = s.option("--from").unwrap();
        assert_eq!(from.value.as_deref(), Some("PATH"));
        assert!(from.required && from.short.is_none());
        let weight = s.option("--weight").unwrap();
        assert!(!weight.required);
        assert_eq!(
            s.text(),
            "f{a}(b; c?; -k|--kind --from=PATH --weight=UNIT?)"
        );

        let now = parse_form("now()").unwrap();
        assert!(now.slot.is_none() && now.operands.is_empty() && now.options.is_empty());
        let count = parse_form("count{stream}(-o|--of=expr?)").unwrap();
        assert_eq!(count.slot.as_deref(), Some("stream"));
        assert_eq!(count.max_operands(), Some(0));
        let lev = parse_form("levenshtein{text}(other)").unwrap();
        assert_eq!((lev.min_operands(), lev.max_operands()), (1, Some(1)));
        let group = parse_form("group{stream}(key; key…)").unwrap();
        assert_eq!((group.min_operands(), group.max_operands()), (1, None));
        let tree = parse_form(
            "tree{context}(expr…; -d|--depth=n? -k|--kind -t|--traits -p|--props -r|--record -s|--sample=n?)",
        )
        .unwrap();
        assert_eq!((tree.min_operands(), tree.max_operands()), (0, None));
        assert!(!tree.option("--depth").unwrap().required);
        // alternatives after the first `=`
        let session = parse_form("session(-p|--pretty=always|structures|off?)").unwrap();
        assert_eq!(
            session.option("--pretty").unwrap().value.as_deref(),
            Some("always|structures|off")
        );
        // a cell of two signatures
        assert_eq!(parse_cell("f(a) / f(a; b; c)").unwrap().len(), 2);
    }

    #[test]
    fn the_notation_refuses_what_it_rules_out() {
        for (form, why) in [
            ("round", "parentheses"),
            ("f{a}", "parentheses"),
            ("f{}(a)", "one word"),
            ("f(a?; b)", "follows an optional"),
            ("f(a…; b)", "stands last"),
            ("f(-k|--kind; b)", "option segment stands last"),
            ("f(a; -k|--kind?)", "always optional"),
            ("f(a; -kk|--kind)", "one letter"),
            ("f(a; -k|--kind -k|--key)", "declared twice"),
            ("f(a; --kind --kind=n)", "declared twice"),
            ("f(a; --from=)", "placeholder"),
            ("f(a; b;)", "empty field"),
        ] {
            let e = parse_form(form).unwrap_err();
            assert!(e.contains(why), "{form}: {e}");
        }
    }
}