Skip to main content

rudb_parse/
generate.rs

1//! Writing statements out of the rule table, which is the matcher run in the other direction.
2//!
3//! The matcher walks the table over a token vector and decides where each rule started and stopped.
4//! This walks the same table with no tokens in hand and makes them up: at a choice it picks an
5//! alternative, at a repeat it picks a count, at an identifier it asks a catalog for a name. What
6//! comes out is a statement the grammar can produce, which is a much larger set than the statements
7//! anybody has written down.
8//!
9//! It lives in the library rather than in a test because two things outside this crate need it. The
10//! compatibility harness generates statements and runs them through both engines, and it depends on
11//! `rudb` and on nothing else, so anything it cannot reach through the facade is a hole in the
12//! facade. `spec/sql/duckdb/10-generation-and-fuzzing.md` section 10.2 is the argument for building
13//! it at all.
14//!
15//! Three things keep it from producing either a novel or the same four tokens forever.
16//!
17//! Every node has a cost, which is the smallest number of tokens it can be written down in, and it
18//! is a fixpoint over the table computed once for the process. A choice weights its alternatives by
19//! that cost, so the cheap ones are picked more often and the expression grammar does not run away
20//! down its own left hand side. It is also what guarantees termination: once a statement is over
21//! budget or a rule has come round too often, every choice takes its cheapest alternative, every
22//! optional is skipped and every repeat goes round once, and the cheapest expansion of anything is
23//! finite by construction.
24//!
25//! The recursion bound counts how many times one rule is on the path rather than how long the path
26//! is, which is not the same thing in this grammar and the difference is not subtle. The expression
27//! rules are a chain of about twenty, one per precedence level, and a plain `a + b` walks the whole
28//! chain, so a depth bound tight enough to stop nesting is spent before it reaches a leaf and every
29//! leaf comes out as the cheapest literal there is. The first version of this had one and wrote
30//! several hundred expressions without a single column reference in any of them.
31//!
32//! Names come from a catalog rather than from a pool of letters, and one statement draws its
33//! columns from one table. Neither of those makes the statement mean anything, since a PEG walk has
34//! no idea what a scope is, but both of them mean a generated statement has a real chance of
35//! binding rather than dying on the first name, and a statement that dies on the first name tests
36//! the tokenizer and nothing else.
37//!
38//! What it does not promise is that everything it writes parses. Ordered choice is the reason: this
39//! can pick the fifth alternative of a choice and write text the matcher settles on the second
40//! alternative of, and then the rest of the sequence has nothing to match against. That is a
41//! property of every PEG generator and not a bug here. The share that parses is measured rather
42//! than assumed, by `the_generator_writes_statements_that_parse`, and the interesting inputs are
43//! the ones that do not, because our answer and DuckDB's answer on those is exactly the level two
44//! statement number the harness reports.
45
46use std::sync::OnceLock;
47
48use rudb_common::{Error, Result};
49
50use crate::generated::keywords::KEYWORDS;
51use crate::generated::rules::{CHILDREN, NODES, RULES, SYMBOLS};
52use crate::matcher::SUGGESTIONS;
53use crate::rules::{Node, Op, Suggestion};
54
55/// A node with no finite expansion, which is what a rule that can only refer to itself comes out
56/// as. Nothing is ever generated from one.
57const UNREACHABLE: u32 = u32::MAX;
58
59/// The rule a statement is written from when nothing says otherwise.
60const START: &str = "Statement";
61
62/// How many tokens a statement gets before every remaining decision takes the cheap way out.
63const BUDGET: u32 = 60;
64
65/// How many times one rule may be on the path from the root before the same thing happens.
66const REPEATS: u32 = 3;
67
68/// How many rules may be on that path at once, whatever they are.
69///
70/// This one is not about the shape of the output. The walk is Rust recursion, so a grammar that
71/// went round a cycle of rules that write nothing would run out of thread stack rather than
72/// producing a bad statement, and a number that no real statement comes near is cheap insurance.
73const STACK: u32 = 512;
74
75/// Where a name position gets its name.
76///
77/// Owned strings rather than borrowed ones, because the useful catalog is the one a harness reads
78/// off a live database rather than the one written here, and the default is only what makes the
79/// tests runnable and the examples readable.
80#[derive(Debug, Clone)]
81pub struct Catalog {
82    /// The tables, with their columns. One statement picks one of these and takes its column names
83    /// from it, so `SELECT a FROM t` is far likelier than `SELECT a FROM u`.
84    pub tables: Vec<Table>,
85    pub functions: Vec<String>,
86    pub table_functions: Vec<String>,
87    pub types: Vec<String>,
88    pub schemas: Vec<String>,
89    pub catalogs: Vec<String>,
90    pub pragmas: Vec<String>,
91    pub settings: Vec<String>,
92    /// File names, which are the one name position where a single quoted string is what a person
93    /// would write, so these carry their quotes.
94    pub files: Vec<String>,
95    pub variables: Vec<String>,
96}
97
98/// One table in the catalog.
99#[derive(Debug, Clone)]
100pub struct Table {
101    pub name: String,
102    pub columns: Vec<String>,
103}
104
105impl Default for Catalog {
106    fn default() -> Self {
107        Self {
108            tables: vec![
109                Table {
110                    name: "t".into(),
111                    columns: ["a", "b", "c"].iter().map(|name| (*name).into()).collect(),
112                },
113                Table {
114                    name: "u".into(),
115                    columns: ["x", "y"].iter().map(|name| (*name).into()).collect(),
116                },
117            ],
118            functions: names(&["abs", "length", "upper", "count", "coalesce"]),
119            table_functions: names(&["range", "generate_series"]),
120            types: names(&["INTEGER", "VARCHAR", "DOUBLE", "BOOLEAN", "DATE"]),
121            schemas: names(&["main"]),
122            catalogs: names(&["memory"]),
123            pragmas: names(&["database_list", "show_tables"]),
124            settings: names(&["threads", "memory_limit"]),
125            files: names(&["'data.parquet'", "'out.csv'"]),
126            variables: names(&["v"]),
127        }
128    }
129}
130
131fn names(from: &[&str]) -> Vec<String> {
132    from.iter().map(|name| (*name).to_string()).collect()
133}
134
135/// Numbers a `Number` position can be filled with.
136///
137/// All of them are positive, because a minus sign is a token of its own and a literal carrying one
138/// would be two tokens where the grammar asked for one.
139const NUMBERS: [&str; 7] = ["0", "1", "2", "42", "1.5", "1e3", "9223372036854775807"];
140
141/// Strings a `String` position can be filled with, including an escaped quote and a character
142/// outside ASCII, which are the two shapes a tokenizer gets wrong.
143const STRINGS: [&str; 4] = ["'a'", "''", "'it''s'", "'é'"];
144
145/// Operators for the generic `Operator` node, which is only ever the multi character ones.
146///
147/// Every single character operator in the language is spelled by a rule of its own, and the ones
148/// the grammar spells for itself are refused by `OperatorMatcher`, so this is the set that is left.
149const OPERATORS: [&str; 5] = ["||", "<<", ">>", "@>", "&&"];
150
151/// A statement writer.
152///
153/// Cheap to build and cheap to clone, and a run is a seed, so the same generator with the same seed
154/// writes the same statement on any machine and in any order.
155#[derive(Debug, Clone)]
156pub struct Generator {
157    catalog: Catalog,
158    budget: u32,
159    repeats: u32,
160}
161
162impl Default for Generator {
163    fn default() -> Self {
164        Self { catalog: Catalog::default(), budget: BUDGET, repeats: REPEATS }
165    }
166}
167
168impl Generator {
169    /// A generator over the default catalog.
170    #[must_use]
171    pub fn new() -> Self {
172        Self::default()
173    }
174
175    /// A generator over a catalog somebody else built, which is the case that matters.
176    #[must_use]
177    pub fn with_catalog(catalog: Catalog) -> Self {
178        Self { catalog, ..Self::default() }
179    }
180
181    /// How many tokens to write before finishing as cheaply as possible.
182    #[must_use]
183    pub fn budget(mut self, tokens: u32) -> Self {
184        self.budget = tokens.max(1);
185        self
186    }
187
188    /// How many times one rule may appear on the path from the root before the same thing happens.
189    ///
190    /// One is every statement written with no nesting in it at all. Three is a couple of levels of
191    /// subquery and of arithmetic, which is where the interesting shapes are.
192    #[must_use]
193    pub fn repeats(mut self, times: u32) -> Self {
194        self.repeats = times.max(1);
195        self
196    }
197
198    /// One statement, from this seed.
199    ///
200    /// # Panics
201    ///
202    /// Never, in the sense that matters: the rule it writes from is one of the 1088 in the table
203    /// and `every_rule_in_the_table_can_be_written_down` says every one of those has a finite text.
204    #[must_use]
205    pub fn statement(&self, seed: u64) -> String {
206        self.from_rule(START, seed).expect("Statement is a rule")
207    }
208
209    /// One piece of a statement, from a named rule.
210    ///
211    /// For a harness that wants expressions rather than statements, and for the tests, which are
212    /// much easier to read over `Expression` than over `Statement`.
213    ///
214    /// # Errors
215    ///
216    /// There is no rule with that name, or there is and nothing finite can be written from it.
217    pub fn from_rule(&self, rule: &str, seed: u64) -> Result<String> {
218        let index = RULES
219            .binary_search_by(|candidate| candidate.name.cmp(rule))
220            .map_err(|_| Error::parser(format!("no rule named {rule}")))?;
221        let root = RULES[index].root;
222        if costs()[root as usize] == UNREACHABLE {
223            return Err(Error::parser(format!("nothing finite can be written from {rule}")));
224        }
225        let mut run = Run {
226            catalog: &self.catalog,
227            random: Random::new(seed),
228            pieces: Vec::new(),
229            spent: 0,
230            budget: self.budget,
231            repeats: self.repeats,
232            path: vec![0; RULES.len()],
233            stack: 0,
234            tight: false,
235            table: 0,
236        };
237        run.table = run.random.below(self.catalog.tables.len().max(1));
238        run.node(root);
239        Ok(run.pieces.join(" "))
240    }
241}
242
243/// One statement being written.
244struct Run<'a> {
245    catalog: &'a Catalog,
246    random: Random,
247    /// The tokens so far, joined with a space at the end. A space between every pair is not
248    /// prettiness, it is the only way to be sure two tokens do not become a third: `-` then `-`
249    /// written without one is a comment to the end of the line, and `/` then `*` is a comment to
250    /// the end of the statement.
251    pieces: Vec<String>,
252    spent: u32,
253    budget: u32,
254    repeats: u32,
255    /// How many times each rule is on the path from the root to here.
256    path: Vec<u32>,
257    /// How many rules are on that path, which is what keeps the Rust stack out of it.
258    stack: u32,
259    /// Whether the rest of this subtree is being finished as cheaply as it can be.
260    tight: bool,
261    /// Which table of the catalog this statement is about.
262    table: usize,
263}
264
265impl Run<'_> {
266    /// Whether it is time to stop making the statement bigger.
267    fn cheap(&self) -> bool {
268        self.tight || self.spent >= self.budget
269    }
270
271    fn emit(&mut self, text: impl Into<String>) {
272        self.pieces.push(text.into());
273        self.spent += 1;
274    }
275
276    fn node(&mut self, index: u32) {
277        let node = NODES[index as usize];
278        match node.op {
279            Op::Rule => self.rule(node.a, node.b),
280            Op::Sequence => {
281                for child in node.children() {
282                    self.node(*child);
283                }
284            }
285            Op::Choice => {
286                let child = self.alternative(node.children(), self.cheap());
287                self.node(child);
288            }
289            Op::Optional => {
290                // One in three rather than one in two. An optional is usually a clause and a
291                // statement is mostly optionals, so an even coin gives every statement half the
292                // clauses in the grammar and nothing else.
293                if !self.cheap() && self.random.chance(3) {
294                    self.node(node.a);
295                }
296            }
297            Op::Repeat => {
298                let times = if self.cheap() { 1 } else { self.random.count(1, 3) };
299                for _ in 0..times {
300                    self.node(node.a);
301                }
302            }
303            Op::Keyword => {
304                let word = KEYWORDS[node.a as usize].0.to_uppercase();
305                self.emit(word);
306            }
307            Op::KeywordClass => self.keyword_in(node.a),
308            Op::Symbol => self.emit(SYMBOLS[node.a as usize]),
309            Op::Identifier => self.name(SUGGESTIONS[node.a as usize]),
310            Op::Number => {
311                let number = self.random.pick(&NUMBERS);
312                self.emit(number);
313            }
314            Op::String => {
315                let text = self.random.pick(&STRINGS);
316                self.emit(text);
317            }
318            Op::Operator => {
319                let operator = self.random.pick(&OPERATORS);
320                self.emit(operator);
321            }
322            // It is the end of the input, so writing anything at all would be wrong.
323            Op::EndOfInput => {}
324        }
325    }
326
327    /// A reference to a rule, which is the only place the walk can go round in a circle.
328    ///
329    /// The bound is on how many times one rule may be on the path rather than on how long the path
330    /// is. A depth bound sounds like the same thing and is not, because the expression grammar is a
331    /// chain of about twenty rules, one per precedence level, that a single `a + b` goes all the way
332    /// down. A depth of fourteen spends itself somewhere around multiplication and every leaf under
333    /// it comes out as whatever the cheapest literal is, which is exactly what the first version of
334    /// this did: it wrote several hundred expressions and not one of them contained a column.
335    fn rule(&mut self, rule: u32, root: u32) {
336        let index = rule as usize;
337        let was = self.tight;
338        // Sticky, and restored on the way out. Once a subtree is being finished cheaply the whole
339        // of it is, because that is what makes the walk terminate: the cheapest expansion of
340        // anything is finite, and a subtree that went back to choosing freely could go round again.
341        self.tight = was || self.path[index] >= self.repeats || self.stack >= STACK;
342        self.path[index] += 1;
343        self.stack += 1;
344        self.node(root);
345        self.stack -= 1;
346        self.path[index] -= 1;
347        self.tight = was;
348    }
349
350    /// Which alternative of a choice to take.
351    ///
352    /// Cheap means the cheapest, which is what makes the walk terminate. Otherwise the weight is
353    /// `16 / (cost + 1)`, so a two token alternative is picked about five times as often as a
354    /// fifteen token one. Uniform would be wrong in a way that is easy to miss: the expensive
355    /// alternatives in this grammar are the recursive ones, so a fair coin at every choice point
356    /// walks down them nearly every time and a generated statement is a hundred nested casts.
357    fn alternative(&mut self, children: &[u32], cheap: bool) -> u32 {
358        let costs = costs();
359        if cheap {
360            let mut best = children[0];
361            for child in children {
362                if costs[*child as usize] < costs[best as usize] {
363                    best = *child;
364                }
365            }
366            return best;
367        }
368        let weights: Vec<u64> =
369            children.iter().map(|child| weight(costs[*child as usize])).collect();
370        let total: u64 = weights.iter().sum();
371        // Every alternative is unreachable, so the choice is too, so nothing picked it and this
372        // cannot happen. Taking the first one is still a better answer than dividing by zero.
373        if total == 0 {
374            return children[0];
375        }
376        let mut pick = self.random.next() % total;
377        for (child, weight) in children.iter().zip(&weights) {
378            if pick < *weight {
379                return *child;
380            }
381            pick -= *weight;
382        }
383        children[children.len() - 1]
384    }
385
386    /// A word from one of the five keyword classes.
387    fn keyword_in(&mut self, mask: u32) {
388        let words = keywords_in(mask as u8);
389        if words.is_empty() {
390            return;
391        }
392        let index = self.random.below(words.len());
393        let word = KEYWORDS[words[index] as usize].0.to_uppercase();
394        self.emit(word);
395    }
396
397    /// A name for whichever of the eleven positions the grammar is at.
398    fn name(&mut self, suggestion: Suggestion) {
399        let catalog = self.catalog;
400        let table = catalog.tables.get(self.table);
401        let pool = match suggestion {
402            Suggestion::TableName => {
403                let name = table.map_or("t", |table| table.name.as_str()).to_string();
404                self.emit(name);
405                return;
406            }
407            Suggestion::ColumnName => {
408                let columns = table.map(|table| table.columns.as_slice()).unwrap_or_default();
409                if columns.is_empty() {
410                    self.emit("a");
411                } else {
412                    let index = self.random.below(columns.len());
413                    let name = columns[index].clone();
414                    self.emit(name);
415                }
416                return;
417            }
418            Suggestion::Variable => &catalog.variables,
419            Suggestion::ScalarFunctionName => &catalog.functions,
420            Suggestion::TableFunctionName => &catalog.table_functions,
421            Suggestion::TypeName => &catalog.types,
422            Suggestion::SchemaName => &catalog.schemas,
423            Suggestion::CatalogName => &catalog.catalogs,
424            Suggestion::PragmaName => &catalog.pragmas,
425            Suggestion::SettingName => &catalog.settings,
426            Suggestion::FileName => &catalog.files,
427        };
428        if pool.is_empty() {
429            self.emit("a");
430            return;
431        }
432        let index = self.random.below(pool.len());
433        let name = pool[index].clone();
434        self.emit(name);
435    }
436}
437
438/// How much of a choice's weight an alternative of this cost gets.
439fn weight(cost: u32) -> u64 {
440    if cost == UNREACHABLE {
441        return 0;
442    }
443    (16 / (u64::from(cost) + 1)).max(1)
444}
445
446/// The smallest number of tokens each node can be written down in, computed once for the process.
447///
448/// A fixpoint rather than a walk, because the grammar is recursive and a walk would not terminate.
449/// Costs start at unreachable and only ever fall, and a pass that moves nothing is the answer.
450/// Around ten passes settle this table, which is a few hundred microseconds once ever.
451fn costs() -> &'static [u32] {
452    static COSTS: OnceLock<Box<[u32]>> = OnceLock::new();
453    COSTS.get_or_init(build_costs)
454}
455
456fn build_costs() -> Box<[u32]> {
457    let mut costs = vec![UNREACHABLE; NODES.len()];
458    loop {
459        let mut moved = false;
460        for (index, node) in NODES.iter().enumerate() {
461            let value = cost_of(*node, &costs);
462            if value < costs[index] {
463                costs[index] = value;
464                moved = true;
465            }
466        }
467        if !moved {
468            return costs.into_boxed_slice();
469        }
470    }
471}
472
473fn cost_of(node: Node, costs: &[u32]) -> u32 {
474    match node.op {
475        // Matching the end of the input writes nothing, and an optional that is skipped writes
476        // nothing either, so both are free and neither can make a node unreachable.
477        Op::EndOfInput | Op::Optional => 0,
478        Op::Keyword
479        | Op::KeywordClass
480        | Op::Symbol
481        | Op::Identifier
482        | Op::Number
483        | Op::String
484        | Op::Operator => 1,
485        Op::Rule => costs[node.b as usize],
486        Op::Repeat => costs[node.a as usize],
487        Op::Sequence => CHILDREN[node.a as usize..(node.a + node.b) as usize]
488            .iter()
489            .fold(0, |total, child| total.saturating_add(costs[*child as usize])),
490        Op::Choice => CHILDREN[node.a as usize..(node.a + node.b) as usize]
491            .iter()
492            .map(|child| costs[*child as usize])
493            .min()
494            .unwrap_or(UNREACHABLE),
495    }
496}
497
498/// The words in each keyword class, by index into `KEYWORDS`, built once.
499///
500/// Five classes and 514 words, so this is five short lists and not worth being clever about. The
501/// alternative is scanning the whole table on every `KeywordClass` node, and those are the nodes a
502/// grammar full of keyword lists is mostly made of.
503fn keywords_in(mask: u8) -> &'static [u16] {
504    static BY_CLASS: OnceLock<[Vec<u16>; 8]> = OnceLock::new();
505    let by_class = BY_CLASS.get_or_init(|| {
506        let mut lists: [Vec<u16>; 8] = Default::default();
507        for (index, (_, classes)) in KEYWORDS.iter().enumerate() {
508            for (bit, list) in lists.iter_mut().enumerate() {
509                if classes & (1 << bit) != 0 {
510                    list.push(index as u16);
511                }
512            }
513        }
514        lists
515    });
516    // A mask names one class in every node the generator has ever seen, and the lowest set bit is
517    // as good an answer as any if that ever stops being true.
518    match (0..8).find(|bit| mask & (1 << bit) != 0) {
519        Some(bit) => &by_class[bit],
520        None => &[],
521    }
522}
523
524/// A xorshift, so that a seed is the whole of a run.
525///
526/// Not `rand`. This crate has one dependency and the thing being tested here is a grammar walk, so
527/// the quality that matters is that the same seed gives the same statement and not that the bits
528/// pass a statistical suite.
529struct Random(u64);
530
531impl Random {
532    fn new(seed: u64) -> Self {
533        // Zero is the one state a xorshift cannot leave, and seed zero is the one a person types.
534        Self(seed.wrapping_mul(0x2545_f491_4f6c_dd1d) | 1)
535    }
536
537    fn next(&mut self) -> u64 {
538        self.0 ^= self.0 << 13;
539        self.0 ^= self.0 >> 7;
540        self.0 ^= self.0 << 17;
541        self.0
542    }
543
544    fn below(&mut self, bound: usize) -> usize {
545        (self.next() % bound.max(1) as u64) as usize
546    }
547
548    fn count(&mut self, low: usize, high: usize) -> usize {
549        low + self.below(high - low + 1)
550    }
551
552    fn chance(&mut self, one_in: u64) -> bool {
553        self.next() % one_in == 0
554    }
555
556    fn pick<T: Copy>(&mut self, from: &[T]) -> T {
557        from[self.below(from.len())]
558    }
559}
560
561#[cfg(test)]
562mod tests {
563    use super::{Catalog, Generator, Random, Run, Table, UNREACHABLE, costs};
564    use crate::generated::rules::RULES;
565    use crate::matcher::parse_from;
566    use crate::tokenize::tokenize;
567
568    /// How many seeds the tests that measure a share run over when nothing says otherwise.
569    ///
570    /// Three thousand statements and three thousand parses are under a second, so the measurement
571    /// sits in the ordinary test run. `RUDB_GRAMMAR_SEED` sets where a run starts and
572    /// `RUDB_GRAMMAR_SEEDS` how many it does, which is how the numbers in
573    /// `spec/sql/duckdb/10-generation-and-fuzzing.md` section 10.2.1 were taken.
574    const SEEDS: u64 = 3000;
575
576    fn seeds() -> std::ops::RangeInclusive<u64> {
577        let first = setting("RUDB_GRAMMAR_SEED", 1);
578        let count = setting("RUDB_GRAMMAR_SEEDS", SEEDS).max(1);
579        first..=first.saturating_add(count - 1)
580    }
581
582    fn setting(name: &str, fallback: u64) -> u64 {
583        match std::env::var(name) {
584            Ok(text) => text
585                .trim()
586                .parse()
587                .unwrap_or_else(|_| panic!("{name} is {text}, which is not a number")),
588            Err(_) => fallback,
589        }
590    }
591
592    fn statements(generator: &Generator, rule: &str) -> Vec<String> {
593        seeds().map(|seed| generator.from_rule(rule, seed).expect("a rule")).collect()
594    }
595
596    /// The shortest text a rule can be written as, with no seed in it.
597    ///
598    /// A run that is tight from the first node takes the cheapest alternative, skips every optional
599    /// and goes round every repeat once, so there is nothing left for the random numbers to decide
600    /// and the answer is a property of the table.
601    fn cheapest(rule: &str) -> String {
602        let catalog = Catalog::default();
603        let root = RULES[RULES.binary_search_by(|r| r.name.cmp(rule)).expect("a rule")].root;
604        let mut run = Run {
605            catalog: &catalog,
606            random: Random::new(1),
607            pieces: Vec::new(),
608            spent: 0,
609            budget: 1,
610            repeats: 1,
611            path: vec![0; RULES.len()],
612            stack: 0,
613            tight: true,
614            table: 0,
615        };
616        run.node(root);
617        run.pieces.join(" ")
618    }
619
620    #[test]
621    fn the_same_seed_writes_the_same_statement() {
622        let generator = Generator::new();
623        for seed in [1, 2, 99, 10_000] {
624            assert_eq!(generator.statement(seed), generator.statement(seed));
625        }
626        // And two seeds do not, which is the other half of the claim and the one that fails if the
627        // seed stops reaching the walk.
628        assert_ne!(generator.statement(1), generator.statement(2));
629    }
630
631    #[test]
632    fn every_rule_in_the_table_can_be_written_down() {
633        let costs = costs();
634        let unreachable: Vec<&str> = RULES
635            .iter()
636            .filter(|rule| costs[rule.root as usize] == UNREACHABLE)
637            .map(|rule| rule.name)
638            .collect();
639        assert!(unreachable.is_empty(), "no finite text exists for {unreachable:?}");
640    }
641
642    #[test]
643    fn the_cheapest_text_a_rule_has_is_as_long_as_the_cost_table_says() {
644        // The cost table is the whole termination argument, so it is checked against the walk it is
645        // supposed to describe rather than against a second copy of its own arithmetic. A run that
646        // is tight from the first node takes the cheapest alternative, skips every optional and
647        // goes round every repeat once, which is exactly what the cost of a node is defined as, so
648        // the two numbers have to be the same number for all 1088 rules.
649        for rule in &RULES {
650            let text = cheapest(rule.name);
651            let written = if text.is_empty() { 0 } else { text.split(' ').count() as u32 };
652            assert_eq!(written, costs()[rule.root as usize], "{} wrote {text:?}", rule.name);
653        }
654    }
655
656    #[test]
657    fn what_it_writes_always_tokenizes() {
658        // Weaker than parsing and it holds every time rather than nearly every time, because the
659        // pieces are written with a space between every pair and no piece is a fragment of a token.
660        // A failure here is the generator writing something that is not text, which is a different
661        // and worse thing than writing a statement that does not parse.
662        for text in statements(&Generator::new(), "Statement") {
663            tokenize(&text).unwrap_or_else(|error| panic!("{text}\n{error}"));
664        }
665    }
666
667    #[test]
668    fn the_generator_writes_statements_that_parse() {
669        // Not all of them, and the reason is ordered choice: this writes the fifth alternative of a
670        // choice and the matcher settles on the second, which then leaves the rest of the sequence
671        // with nothing to match. The share is measured so that it cannot quietly collapse, and the
672        // floor is well under what it does today, because the number that matters about a generator
673        // is that it keeps working and not that it hits a target.
674        let written = statements(&Generator::new(), "Statement");
675        let parsed =
676            written.iter().filter(|text| parse_from(text, "Statement", true).is_ok()).count();
677        let total = written.len();
678        // Printed rather than only asserted, because this is where the share in section 10.2.1 of
679        // `spec/sql/duckdb/10-generation-and-fuzzing.md` comes from: run the test with a seed count
680        // and `--nocapture` and the number it prints is the number that goes in the document.
681        println!("{parsed} of {total} statements parse");
682        assert!(parsed * 100 / total >= 85, "{parsed} of {total} parse");
683    }
684
685    #[test]
686    fn the_filter_and_the_walk_without_it_agree_on_what_it_writes() {
687        // The FIRST filter claims to be a superset, so turning it off can only ever make the
688        // matcher slower and never make it accept more. Generated statements are the widest set of
689        // inputs there is to ask that over, and they reach parts of the rule table no corpus does.
690        for text in statements(&Generator::new(), "Statement") {
691            let filtered = parse_from(&text, "Statement", true);
692            let whole = parse_from(&text, "Statement", false);
693            assert_eq!(filtered.is_ok(), whole.is_ok(), "the filter changed the answer on {text}");
694        }
695    }
696
697    #[test]
698    fn the_names_it_writes_are_the_catalog_it_was_handed() {
699        // Every word it writes is either a keyword, which comes out upper case, or a name, which
700        // comes out exactly as the catalog spelled it. So a lower case word that is not in the
701        // catalog is a name the generator invented, and a generator that invents names is testing
702        // the tokenizer rather than the binder.
703        let catalog = Catalog {
704            tables: vec![Table { name: "zork".into(), columns: vec!["quux".into()] }],
705            functions: vec!["frob".into()],
706            ..Catalog::default()
707        };
708        let generator = Generator::with_catalog(catalog.clone());
709        let written = statements(&generator, "Statement");
710        let total = written.len();
711        let mut seen_a_column = false;
712        for text in written {
713            for piece in text.split(' ') {
714                if !piece.starts_with(|first: char| first.is_ascii_lowercase()) {
715                    continue;
716                }
717                seen_a_column |= piece == "quux";
718                assert!(known(&catalog, piece), "{piece} is not a name the catalog has, in {text}");
719            }
720        }
721        assert!(seen_a_column, "no statement in {total} mentioned a column");
722    }
723
724    fn known(catalog: &Catalog, piece: &str) -> bool {
725        catalog
726            .tables
727            .iter()
728            .any(|table| table.name == piece || table.columns.iter().any(|column| column == piece))
729            || [
730                &catalog.functions,
731                &catalog.table_functions,
732                &catalog.types,
733                &catalog.schemas,
734                &catalog.catalogs,
735                &catalog.pragmas,
736                &catalog.settings,
737                &catalog.files,
738                &catalog.variables,
739            ]
740            .iter()
741            .any(|pool| pool.iter().any(|name| name == piece))
742    }
743
744    #[test]
745    fn a_query_is_what_comes_out_of_the_rule_that_writes_queries() {
746        // `Statement` is a choice of thirty six and a query is one of them, so a caller that wants
747        // queries asks for the rule that writes them rather than filtering what it gets.
748        let generator = Generator::new();
749        let queries = statements(&generator, "SelectStatement");
750        let parsed =
751            queries.iter().filter(|text| parse_from(text, "SelectStatement", true).is_ok()).count();
752        let total = queries.len();
753        println!("{parsed} of {total} queries parse");
754        assert!(parsed * 100 / total >= 80, "{parsed} of {total} parse");
755        assert!(queries.iter().any(|text| text.contains("SELECT")));
756    }
757
758    #[test]
759    fn a_budget_of_one_token_still_writes_a_whole_statement() {
760        // The budget is where to stop growing and not where to stop, so what comes out is the
761        // cheapest whole statement the grammar has rather than a truncated one.
762        // One word, and it is a query, because `SELECT` on its own is a statement in this dialect
763        // and a tie in the cost table goes to the alternative the grammar writes first.
764        assert_eq!(cheapest("Statement"), "SELECT");
765        assert!(parse_from("SELECT", "Statement", true).is_ok());
766    }
767
768    #[test]
769    fn the_shortest_explain_there_is_does_not_parse_and_that_is_the_grammar() {
770        // Worth one test of its own, because it is the clearest example of why the share that
771        // parses is not one. The cheapest thing `EXPLAIN` can be followed by is the `ANALYZE`
772        // statement, so the shortest explain in the grammar is those two words. The matcher reads
773        // them the other way round: `ExplainStatement <- 'EXPLAIN' AnalyzeKeyword? ...` takes the
774        // word as the optional keyword, nothing is left for the statement that has to follow, and a
775        // PEG does not give a taken optional back. DuckDB's parser is this grammar and this
776        // algorithm, so it refuses the same two words for the same reason.
777        assert_eq!(cheapest("ExplainStatement"), "EXPLAIN ANALYZE");
778        assert!(parse_from("EXPLAIN ANALYZE", "ExplainStatement", true).is_err());
779        assert!(parse_from("EXPLAIN ANALYZE SELECT 1", "Statement", true).is_ok());
780    }
781
782    #[test]
783    fn an_unknown_rule_says_so() {
784        let error = Generator::new().from_rule("NoSuchRule", 1).unwrap_err();
785        assert!(error.to_string().contains("no rule named NoSuchRule"), "{error}");
786    }
787}