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
//! 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)]
pub enum Error {
    /// Parsing options
    Options(String),
    /// Parsing expressions
    Expr(String),
    /// Query structure error
    ParseQuery(String),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Options(e) => write!(f, "options: {e}"),
            Error::Expr(e) => write!(f, "expresions: {e}"),
            Error::ParseQuery(e) => write!(f, "query structure: {e}"),
        }
    }
}

impl std::error::Error for Error {}