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
//! Small query language for pseudorandomness
//!
//! See <https://github.com/Zheoni/rng-query> for the syntax and CLI.
//!
//! ## Usage as a lib
//!
//! `rng-query` is mainly the CLI app, so this lib is not the main objective. I
//! will try to follow [semver](https://semver.org/) but for the lib it's not a
//! guarantee, so you may want to pin a specific version.
//!
//! Run a whole input with [`run_query`] or have more control with [`State`] and
//! its methods.
//!
//! All [`Display`](std::fmt::Display) implementations of the crate *may* output ANSI color codes.
//! Use something like [anstream](https://docs.rs/anstream/) if you dont want
//! colors.

mod ast;
mod eval;
mod expr;
mod parse;

use ast::Entry;
use eval::Eval;
pub use eval::Sample;
use parse::parse_query;
use rand::SeedableRng;
use rand_pcg::Pcg64 as Pcg;

macro_rules! regex {
    ($re:literal $(,)?) => {{
        static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
        RE.get_or_init(|| regex::Regex::new($re).unwrap())
    }};
}
pub(crate) use regex;

/// Run a query
pub fn run_query(input: &str) -> Result<Vec<Sample>, Error> {
    let mut state = State::new();
    state.run_query(input)
}

/// Query interpreter
#[derive(Debug, Clone)]
pub struct State {
    rng: Pcg,
    data: Vec<(usize, Entry)>,
}

impl State {
    /// Create a new state
    ///
    /// Seed is autogenerated form entropy.
    pub fn new() -> Self {
        Self::from_rng(Pcg::from_entropy())
    }
    /// Create a new state with a seed
    pub fn with_seed(seed: u64) -> Self {
        Self::from_rng(Pcg::seed_from_u64(seed))
    }
    fn from_rng(rng: Pcg) -> Self {
        Self {
            rng,
            data: Vec::new(),
        }
    }
}

impl Default for State {
    fn default() -> Self {
        Self::new()
    }
}

impl State {
    /// Runs a query
    ///
    /// It will consume entries from the state if any.
    pub fn run_query(&mut self, input: &str) -> Result<Vec<Sample>, Error> {
        let mut ast = parse_query(input)?;
        if !self.data.is_empty() {
            let mut entries = std::mem::take(&mut self.data);
            let last_id = entries.last().map(|(id, _)| *id).unwrap_or(0);

            entries.reserve(ast.root.entries.len());
            for (id, e) in ast.root.entries {
                entries.push((id + last_id + 1, e));
            }
            debug_assert!(entries.windows(2).all(|w| w[0].0 + 1 == w[1].0));
            ast.root.entries = entries;
        }
        let res = ast.eval(&mut self.rng);
        Ok(res)
    }

    fn push_entry(&mut self, entry: Entry) {
        let id = self.data.len();
        self.data.push((id, entry));
    }

    /// Adds data entries for the next query
    pub fn add_data(&mut self, entry: &str) {
        self.push_entry(Entry::data(entry.trim()));
    }

    /// Adds a regular entry for the next query
    pub fn add_entry(&mut self, entry: &str) -> Result<(), Error> {
        let entry = Entry::parse(entry.trim())?;
        self.push_entry(entry);
        Ok(())
    }
}

/// Query error
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// Parsing options
    #[error("options: {0}")]
    Options(String),
    /// Parsing expressions
    #[error("expression: {0}")]
    Expr(String),
    /// Query structure error
    #[error("parsing query structure: {0}")]
    ParseQuery(String),
}