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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
//! A `Slab` is a pre-allocated block of memory, used during the
//! parse/compile/eval phases to reduce memory allocation/deallocation.
//!
//! A `Slab` enables you to perform one single, large allocation at the
//! beginning of the parse-compile-eval process, rather than many small
//! allocations.  You can also re-use a `Slab` for multiple expression
//! parse-compile-eval cycles, greatly reducing the amount of memory
//! operations.  The `Slab` is the main key to `fasteval`'s excellent
//! performance.
//!
//! You use `ExpressionI`, `ValueI`, and `InstructionI` index types to refer to
//! elements within the `Slab`.  These special index types are necessary to
//! side-step the Rust borrow checker, which is not able to understand
//! borrow-splitting of contiguous allocations (like arrays).
//! (In other words, these special index types allows `fasteval` to mutate a
//! `Slab` while simultaneously holding references to its contents.)
//!
//! You usually won't use any of the `Slab` method directly.  Instead, you'll
//! just pass a reference to other functions like [`parse()`](../parser/index.html),
//! [`compile()`](../compiler/trait.Compiler.html) and [`eval()`](../evaler/trait.Evaler.html).
//! We treat a `Slab` sort of like a Context in other programming systems.
//!
//! The `Slab` contains two fields: `ps` ("Parse Slab") and `cs`
//! ("Compile Slab").  It is structured like this because of Rust's borrowing
//! rules, so that the two fields can be borrowed and mutated independently.
//!
//! If you use the [`ez_eval()`](../ez/fn.ez_eval.html) function, it allocates
//! a Slab for you.  If you are performing the parse/compile/eval process
//! yourself, then you'll need to allocate a Slab at the beginning.
//!
//! # Examples
//!
//! Here is an example of re-using one `Slab` for multiple parse/eval cycles:
//! ```
//! use fasteval::Evaler;  // import this trait so we can call eval().
//! fn main() -> Result<(), fasteval::Error> {
//!     let parser = fasteval::Parser::new();
//!     let mut slab = fasteval::Slab::new();
//!
//!     let val = parser.parse("1+2*3-4", &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut fasteval::EmptyNamespace)?;
//!     assert_eq!(val, 3.0);
//!
//!     // Let's re-use the same slab again to save memory operations.
//!
//!     // `parse()` will clear the Slab's data.  It is important that you
//!     // do not use an old expression after the Slab has been cleared.
//!     let val = parser.parse("5+6*7-8", &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut fasteval::EmptyNamespace)?;
//!     assert_eq!(val, 39.0);
//!
//!     Ok(())
//! }
//! ```

use crate::error::Error;
use crate::parser::{ExpressionI, ValueI,
                    Expression,  Value};
use crate::compiler::{Instruction::{self, IConst}, InstructionI};

use std::fmt;
use std::mem;

#[cfg(feature="unsafe-vars")]
use std::collections::BTreeMap;


// Eliminate function call overhead:
macro_rules! get_expr {
    ($pslab:expr, $i_ref:ident) => {
        match $pslab.exprs.get($i_ref.0) {
            Some(expr_ref) => expr_ref,
            None => &$pslab.def_expr,
        }
    };
}
macro_rules! get_val {
    ($pslab:expr, $i_ref:ident) => {
        match $pslab.vals.get($i_ref.0) {
            Some(val_ref) => val_ref,
            None => &$pslab.def_val,
        }
    };
}
// The CompileSlab::get_instr method is in the hot path of compiled evaluation:
macro_rules! get_instr {
    ($cslab:expr, $i_ref:ident) => {
        match $cslab.instrs.get($i_ref.0) {
            Some(instr_ref) => instr_ref,
            None => &$cslab.def_instr,
        }
    };
}


impl ExpressionI {
    /// Gets an Expression reference from the ParseSlab.
    ///
    /// This is actually just a convenience function built on top of
    /// `ParseSlab.get_expr`, but it enables you to perform the entire
    /// parse/compile/eval process in one line without upsetting the Rust
    /// borrow checker.  (If you didn't have this function, the borrow checker
    /// would force you to split the process into at least two lines.)
    ///
    #[inline]
    pub fn from(self, ps:&ParseSlab) -> &Expression {
        get_expr!(ps,self)
    }
}
impl ValueI {
    /// Gets a Value reference from the ParseSlab.
    ///
    /// See the comments on [ExpressionI::from](struct.ExpressionI.html#method.from).
    ///
    #[inline]
    pub fn from(self, ps:&ParseSlab) -> &Value {
        get_val!(ps,self)
    }
}

/// [See the `slab module` documentation.](index.html)
pub struct Slab {
    pub ps:ParseSlab,
    pub cs:CompileSlab,
}

/// `ParseSlab` is where `parse()` results are stored, located at `Slab.ps`.
///
/// # Unsafe Variable Registration with `add_unsafe_var()`
///
/// (This is documented here because the
/// [`add_unsafe_var()`](#method.add_unsafe_var) method and its documentation
/// only appears if `fasteval` is built with the `unsafe-vars` feature (`cargo
/// build --features unsafe-vars`).  I want this documentation to appear
/// regardless of the build mode, so I'm putting it here.)
///
/// Here is the function signature of the `add_unsafe_var()` method:
///
/// ```text
/// pub unsafe fn add_unsafe_var(&mut self, name: String, ptr: &f64)
/// ```
///
/// If you are using [Unsafe Variables](../index.html#unsafe-variables), you
/// need to pre-register the unsafe variable names and pointers *before*
/// calling `parse()`.  This is because Unsafe Variables are represented
/// specially in the parse AST; therefore, `parse()` needs to know what
/// variables are unsafe and which ones are normal so that it can produce the
/// correct AST.
///
/// If you forget to pre-register an unsafe variable before `parse()`, the
/// variable will be treated like a Normal Variable, and you'll probably get an
/// [`Undefined`](../error/enum.Error.html#variant.Undefined) error during evaluation.
///
/// ## Safety
///
/// You must guarantee that Unsafe Variable pointers remain valid for the
/// lifetime of the resulting expression.  If you continue to use an expression
/// after the memory of an unsafe variable has been reclaimed, you will have
/// undefined behavior.
///
///
/// ## Examples
///
/// Here is an example of correct and incorrect use of unsafe variable pointers:
///
/// ```
/// use fasteval::Evaler;    // use this trait so we can call eval().
/// use fasteval::Compiler;  // use this trait so we can call compile().
///
/// // Here is an example of INCORRECT registration.  DO NOT DO THIS!
/// fn bad_unsafe_var(slab_mut:&mut fasteval::Slab) {
///     let bad : f64 = 0.0;
///
///     // Saves a pointer to 'bad':
///     unsafe { slab_mut.ps.add_unsafe_var("bad".to_string(), &bad); }  // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars`
///
///     // 'bad' goes out-of-scope here, and the pointer we registered is no longer valid!
///     // This will result in undefined behavior.
/// }
///
/// fn main() -> Result<(), fasteval::Error> {
///     let mut slab = fasteval::Slab::new();
///
///     // The Unsafe Variable will use a pointer to read this memory location:
///     // You must make sure that this variable stays in-scope as long as the
///     // expression is in-use.
///     let mut deg : f64 = 0.0;
///
///     // Unsafe Variables must be registered before 'parse()'.
///     // (Normal Variables only need definitions during the 'eval' phase.)
///     unsafe { slab.ps.add_unsafe_var("deg".to_string(), &deg); }  // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars`
///
///     // bad_unsafe_var(&mut slab);  // Don't do it this way.
///
///     let expr_str = "sin(deg/360 * 2*pi())";
///     let expr_ref = fasteval::Parser::new().parse(expr_str, &mut slab.ps)?.from(&slab.ps);
///
///     // The main reason people use Unsafe Variables is to maximize performance.
///     // Compilation also helps performance, so it is usually used together with Unsafe Variables:
///     let compiled = expr_ref.compile(&slab.ps, &mut slab.cs);
///
///     let mut ns = fasteval::EmptyNamespace;  // We only define unsafe variables, not normal variables,
///                                             // so EmptyNamespace is fine.
///
///     for d in 0..360 {
///         deg = d as f64;
///         let val = fasteval::eval_compiled!(compiled, &slab, &mut ns);
///         eprintln!("sin({}°) = {}", deg, val);
///     }
///
///     Ok(())
/// }
/// 
/// ```
pub struct ParseSlab {
    pub(crate) exprs      :Vec<Expression>,
    pub(crate) vals       :Vec<Value>,
    pub(crate) def_expr   :Expression,
    pub(crate) def_val    :Value,
    pub(crate) char_buf   :String,
    #[cfg(feature="unsafe-vars")]
    pub(crate) unsafe_vars:BTreeMap<String, *const f64>,
}

/// `CompileSlab` is where `compile()` results are stored, located at `Slab.cs`.
pub struct CompileSlab {
    pub(crate) instrs   :Vec<Instruction>,
    pub(crate) def_instr:Instruction,
}

impl ParseSlab {
    /// Returns a reference to the [`Expression`](../parser/struct.Expression.html)
    /// located at `expr_i` within the `ParseSlab.exprs'.
    ///
    /// If `expr_i` is out-of-bounds, a reference to a default `Expression` is returned.
    ///
    #[inline]
    pub fn get_expr(&self, expr_i:ExpressionI) -> &Expression {
        // I'm using this non-panic match structure to boost performance:
        match self.exprs.get(expr_i.0) {
            Some(expr_ref) => expr_ref,
            None => &self.def_expr,
        }
    }

    /// Returns a reference to the [`Value`](../parser/enum.Value.html)
    /// located at `val_i` within the `ParseSlab.vals'.
    ///
    /// If `val_i` is out-of-bounds, a reference to a default `Value` is returned.
    ///
    #[inline]
    pub fn get_val(&self, val_i:ValueI) -> &Value {
        match self.vals.get(val_i.0) {
            Some(val_ref) => val_ref,
            None => &self.def_val,
        }
    }

    /// Appends an `Expression` to `ParseSlab.exprs`.
    ///
    /// # Errors
    ///
    /// If `ParseSlab.exprs` is already full, a `SlabOverflow` error is returned.
    ///
    #[inline]
    pub(crate) fn push_expr(&mut self, expr:Expression) -> Result<ExpressionI,Error> {
        let i = self.exprs.len();
        if i>=self.exprs.capacity() { return Err(Error::SlabOverflow); }
        self.exprs.push(expr);
        Ok(ExpressionI(i))
    }

    /// Appends a `Value` to `ParseSlab.vals`.
    ///
    /// # Errors
    ///
    /// If `ParseSlab.vals` is already full, a `SlabOverflow` error is returned.
    ///
    #[inline]
    pub(crate) fn push_val(&mut self, val:Value) -> Result<ValueI,Error> {
        let i = self.vals.len();
        if i>=self.vals.capacity() { return Err(Error::SlabOverflow); }
        self.vals.push(val);
        Ok(ValueI(i))
    }

    /// Clears all data from `ParseSlab.exprs` and `ParseSlab.vals`.
    #[inline]
    pub fn clear(&mut self) {
        self.exprs.clear();
        self.vals.clear();
    }

    /// [See the `add_unsafe_var()` documentation above.](#unsafe-variable-registration-with-add_unsafe_var)
    #[cfg(feature="unsafe-vars")]
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub unsafe fn add_unsafe_var(&mut self, name:String, ptr:&f64) {
        self.unsafe_vars.insert(name, ptr as *const f64);
    }
}

impl CompileSlab {
    /// Returns a reference to the [`Instruction`](../compiler/enum.Instruction.html)
    /// located at `instr_i` within the `CompileSlab.instrs'.
    ///
    /// If `instr_i` is out-of-bounds, a reference to a default `Instruction` is returned.
    ///
    #[inline]
    pub fn get_instr(&self, instr_i:InstructionI) -> &Instruction {
        match self.instrs.get(instr_i.0) {
            Some(instr_ref) => instr_ref,
            None => &self.def_instr,
        }
    }

    /// Appends an `Instruction` to `CompileSlab.instrs`.
    pub(crate) fn push_instr(&mut self, instr:Instruction) -> InstructionI {
        if self.instrs.capacity()==0 { self.instrs.reserve(32); }
        let i = self.instrs.len();
        self.instrs.push(instr);
        InstructionI(i)
    }

    /// Removes an `Instruction` from `CompileSlab.instrs` as efficiently as possible.
    pub(crate) fn take_instr(&mut self, i:InstructionI) -> Instruction {
        if i.0==self.instrs.len()-1 {
            match self.instrs.pop() {
                Some(instr) => instr,
                None => IConst(std::f64::NAN),
            }
        } else {
            match self.instrs.get_mut(i.0) {
                Some(instr_ref) => mem::replace(instr_ref, IConst(std::f64::NAN)),  // Replace with a conspicuous value in case we use it by accident.
                None => IConst(std::f64::NAN),
            }
        }
    }

    /// Clears all data from `CompileSlab.instrs`.
    #[inline]
    pub fn clear(&mut self) {
        self.instrs.clear();
    }
}

impl Slab {
    /// Creates a new default-sized `Slab`.
    #[inline]
    pub fn new() -> Self { Self::with_capacity(64) }

    /// Creates a new `Slab` with the given capacity.
    #[inline]
    pub fn with_capacity(cap:usize) -> Self {
        Self{
            ps:ParseSlab{
                exprs      :Vec::with_capacity(cap),
                vals       :Vec::with_capacity(cap),
                def_expr   :Default::default(),
                def_val    :Default::default(),
                char_buf   :String::with_capacity(64),
                #[cfg(feature="unsafe-vars")]
                unsafe_vars:BTreeMap::new(),
            },
            cs:CompileSlab{
                instrs   :Vec::new(),  // Don't pre-allocate for compilation.
                def_instr:Default::default(),
            },
        }
    }

    /// Clears all data from [`Slab.ps`](struct.ParseSlab.html) and [`Slab.cs`](struct.CompileSlab.html).
    #[inline]
    pub fn clear(&mut self) {
        self.ps.exprs.clear();
        self.ps.vals.clear();
        self.cs.instrs.clear();
    }
}


fn write_indexed_list<T>(f:&mut fmt::Formatter, lst:&[T]) -> Result<(), fmt::Error> where T:fmt::Debug {
    write!(f, "{{")?;
    let mut nonempty = false;
    for (i,x) in lst.iter().enumerate() {
        if nonempty { write!(f, ",")?; }
        nonempty = true;
        write!(f, " {}:{:?}",i,x)?;
    }
    if nonempty { write!(f, " ")?; }
    write!(f, "}}")?;
    Ok(())
}
impl fmt::Debug for Slab {
    fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Slab{{ exprs:")?;
        write_indexed_list(f, &self.ps.exprs)?;
        write!(f, ", vals:")?;
        write_indexed_list(f, &self.ps.vals)?;
        write!(f, ", instrs:")?;
        write_indexed_list(f, &self.cs.instrs)?;
        write!(f, " }}")?;
        Ok(())
    }
}
impl fmt::Debug for ParseSlab {
    fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "ParseSlab{{ exprs:")?;
        write_indexed_list(f, &self.exprs)?;
        write!(f, ", vals:")?;
        write_indexed_list(f, &self.vals)?;
        write!(f, " }}")?;
        Ok(())
    }
}
impl fmt::Debug for CompileSlab {
    fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "CompileSlab{{ instrs:")?;
        write_indexed_list(f, &self.instrs)?;
        write!(f, " }}")?;
        Ok(())
    }
}

impl Default for Slab {
    fn default() -> Self { Self::with_capacity(64) }
}