Skip to main content

yash_arith/
lib.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2022 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! This crate implements shell arithmetic expansion.
18//!
19//! Arithmetic expansion evaluates an expression that may contain some operators
20//! that perform basic maths. The expression is given as a string and parsed
21//! like an expression in C. The expression can include variables that interact
22//! with the [environment](Env).
23//!
24//! To evaluate an expression, you call the [`eval()`] function with a string
25//! and an environment.
26//!
27//! ```
28//! use std::collections::HashMap;
29//! use yash_arith::{eval, Value};
30//! let mut env = HashMap::new();
31//! env.insert("a".to_owned(), "2".to_owned());
32//! let result = eval("1 + a", &mut env);
33//! assert_eq!(result, Ok(Value::Integer(3)));
34//! ```
35
36use std::fmt::Debug;
37use std::ops::Range;
38use thiserror::Error;
39
40mod token;
41
42use token::PeekableTokens;
43pub use token::TokenError;
44pub use token::Value;
45
46mod ast;
47
48pub use ast::PortabilityError;
49pub use ast::SyntaxError;
50
51mod env;
52
53pub use env::Env;
54
55mod eval;
56
57pub use eval::EvalError;
58
59/// Configuration for arithmetic expansion
60#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
61#[non_exhaustive]
62pub struct Config {
63    /// Restrict arithmetic expansion to portable constructs only.
64    ///
65    /// If this option is on, the increment and decrement operators (`++` and
66    /// `--`) are rejected with a [`PortabilityError::IncrementDecrement`]
67    /// error.
68    pub portable: bool,
69}
70
71impl Config {
72    /// Creates a new configuration with the default settings.
73    #[inline]
74    #[must_use]
75    pub fn new() -> Self {
76        Self::default()
77    }
78}
79
80/// Cause of an arithmetic expansion error
81#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
82#[error(transparent)]
83#[non_exhaustive]
84pub enum ErrorCause<E1, E2> {
85    /// Syntax error parsing the expression
86    SyntaxError(#[from] SyntaxError),
87    /// Error because the parsed expression contains a non-portable construct
88    PortabilityError(#[from] PortabilityError),
89    /// Error evaluating the parsed expression
90    EvalError(#[from] EvalError<E1, E2>),
91}
92
93impl<E1, E2> From<TokenError> for ErrorCause<E1, E2> {
94    fn from(e: TokenError) -> Self {
95        ErrorCause::SyntaxError(e.into())
96    }
97}
98
99/// Description of an error that occurred during expansion
100#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
101#[error("{cause}")]
102pub struct Error<E1, E2> {
103    /// Cause of the error
104    pub cause: ErrorCause<E1, E2>,
105    /// Range of the substring in the evaluated expression string where the error occurred
106    pub location: Range<usize>,
107}
108
109impl<E1, E2> From<ast::Error> for Error<E1, E2> {
110    fn from(e: ast::Error) -> Self {
111        Error {
112            cause: e.cause.into(),
113            location: e.location,
114        }
115    }
116}
117
118impl<E1, E2> From<ast::portability::Error> for Error<E1, E2> {
119    fn from(e: ast::portability::Error) -> Self {
120        Error {
121            cause: e.cause.into(),
122            location: e.location,
123        }
124    }
125}
126
127impl<E1, E2> From<eval::Error<E1, E2>> for Error<E1, E2> {
128    fn from(e: eval::Error<E1, E2>) -> Self {
129        Error {
130            cause: e.cause.into(),
131            location: e.location,
132        }
133    }
134}
135
136/// Performs arithmetic expansion with the specified configuration.
137pub fn eval_with_config<E: Env>(
138    expression: &str,
139    env: &mut E,
140    config: Config,
141) -> Result<Value, Error<E::GetVariableError, E::AssignVariableError>> {
142    let tokens = PeekableTokens::from(expression);
143    let ast = ast::parse(tokens)?;
144    if config.portable {
145        ast::portability::check(&ast)?;
146    }
147    let term = eval::eval(&ast, env)?;
148    let value = eval::into_value(term, env)?;
149    Ok(value)
150}
151
152/// Performs arithmetic expansion
153pub fn eval<E: Env>(
154    expression: &str,
155    env: &mut E,
156) -> Result<Value, Error<E::GetVariableError, E::AssignVariableError>> {
157    eval_with_config(expression, env, Config::default())
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use std::collections::HashMap;
164
165    #[test]
166    fn eval_with_portable_config_rejects_before_evaluation() {
167        let mut env = HashMap::from([("foo".to_owned(), "41".to_owned())]);
168        let config = Config { portable: true };
169
170        assert_eq!(
171            eval_with_config("foo++", &mut env, config),
172            Err(Error {
173                cause: ErrorCause::PortabilityError(PortabilityError::IncrementDecrement),
174                location: 3..5,
175            })
176        );
177        assert_eq!(env["foo"], "41");
178    }
179
180    #[test]
181    fn decimal_integer_constants() {
182        let env = &mut HashMap::new();
183        assert_eq!(eval("1", env), Ok(Value::Integer(1)));
184        assert_eq!(eval("42", env), Ok(Value::Integer(42)));
185    }
186
187    #[test]
188    fn octal_integer_constants() {
189        let env = &mut HashMap::new();
190        assert_eq!(eval("0", env), Ok(Value::Integer(0)));
191        assert_eq!(eval("01", env), Ok(Value::Integer(1)));
192        assert_eq!(eval("07", env), Ok(Value::Integer(7)));
193        assert_eq!(eval("0123", env), Ok(Value::Integer(0o123)));
194    }
195
196    #[test]
197    fn invalid_digit_in_octal_constant() {
198        let env = &mut HashMap::new();
199        assert_eq!(
200            eval("08", env),
201            Err(Error {
202                cause: TokenError::InvalidNumericConstant.into(),
203                location: 0..2,
204            })
205        );
206        assert_eq!(
207            eval("0192", env),
208            Err(Error {
209                cause: TokenError::InvalidNumericConstant.into(),
210                location: 0..4,
211            })
212        );
213    }
214
215    #[test]
216    fn space_around_token() {
217        let env = &mut HashMap::new();
218        assert_eq!(eval(" 12", env), Ok(Value::Integer(12)));
219        assert_eq!(eval("12 ", env), Ok(Value::Integer(12)));
220        assert_eq!(eval("\n 123 \t", env), Ok(Value::Integer(123)));
221        // TODO Test with more complex expressions
222    }
223
224    #[test]
225    fn unset_variable() {
226        let env = &mut HashMap::new();
227        assert_eq!(eval("foo", env), Ok(Value::Integer(0)));
228        assert_eq!(eval("bar", env), Ok(Value::Integer(0)));
229    }
230
231    #[test]
232    fn integer_variable() {
233        let env = &mut HashMap::new();
234        env.insert("foo".to_string(), "42".to_string());
235        env.insert("bar".to_string(), "123".to_string());
236        assert_eq!(eval("foo", env), Ok(Value::Integer(42)));
237        assert_eq!(eval("bar", env), Ok(Value::Integer(123)));
238    }
239
240    // TODO Variables (floats, infinities, & NaNs)
241
242    #[test]
243    fn invalid_variable_value() {
244        let env = &mut HashMap::new();
245        env.insert("foo".to_string(), "".to_string());
246        env.insert("bar".to_string(), "*".to_string());
247        env.insert("oops".to_string(), "foo".to_string());
248        assert_eq!(
249            eval("foo", env),
250            Err(Error {
251                cause: EvalError::InvalidVariableValue("".to_string()).into(),
252                location: 0..3,
253            })
254        );
255        assert_eq!(
256            eval("bar", env),
257            Err(Error {
258                cause: EvalError::InvalidVariableValue("*".to_string()).into(),
259                location: 0..3,
260            })
261        );
262        assert_eq!(
263            eval("  oops ", env),
264            Err(Error {
265                cause: EvalError::InvalidVariableValue("foo".to_string()).into(),
266                location: 2..6,
267            })
268        );
269    }
270
271    #[test]
272    fn unevaluated_variable_value() {
273        let env = &mut HashMap::new();
274        env.insert("empty".to_string(), "".to_string());
275        assert_eq!(eval("1 || empty", env), Ok(Value::Integer(1)));
276        assert_eq!(eval("0 && empty++", env), Ok(Value::Integer(0)));
277        assert_eq!(eval("1 ? 2 : --empty", env), Ok(Value::Integer(2)));
278        assert_eq!(eval("0 ? empty /= 1 : 3", env), Ok(Value::Integer(3)));
279    }
280
281    #[test]
282    fn simple_assignment_operator() {
283        let env = &mut HashMap::new();
284        env.insert("foo".to_string(), "#ignored_value#".to_string());
285
286        assert_eq!(eval("a=1", env), Ok(Value::Integer(1)));
287        assert_eq!(eval(" foo = 42 ", env), Ok(Value::Integer(42)));
288
289        assert_eq!(env["a"], "1");
290        assert_eq!(env["foo"], "42");
291        assert_eq!(env.len(), 2);
292    }
293
294    #[test]
295    fn compound_assignment_operators() {
296        let env = &mut HashMap::new();
297        assert_eq!(eval("a|=1", env), Ok(Value::Integer(1)));
298        assert_eq!(eval("a^=7", env), Ok(Value::Integer(6)));
299        assert_eq!(eval("a&=3", env), Ok(Value::Integer(2)));
300        assert_eq!(eval("a<<=4", env), Ok(Value::Integer(32)));
301        assert_eq!(eval("a>>=2", env), Ok(Value::Integer(8)));
302        assert_eq!(eval("a+=1", env), Ok(Value::Integer(9)));
303        assert_eq!(eval("a-=4", env), Ok(Value::Integer(5)));
304        assert_eq!(eval("a*=21", env), Ok(Value::Integer(105)));
305        assert_eq!(eval("a/=8", env), Ok(Value::Integer(13)));
306        assert_eq!(eval("a%=8", env), Ok(Value::Integer(5)));
307        assert_eq!(env["a"], "5");
308    }
309
310    #[test]
311    fn combining_assignment_operators() {
312        let env = &mut HashMap::new();
313        assert_eq!(eval("a = b -= c = 7", env), Ok(Value::Integer(-7)));
314        assert_eq!(env["a"], "-7");
315        assert_eq!(env["b"], "-7");
316        assert_eq!(env["c"], "7");
317    }
318
319    #[test]
320    fn conditional_operator() {
321        let env = &mut HashMap::new();
322        assert_eq!(eval("1?a=10:(b=20)", env), Ok(Value::Integer(10)));
323        assert_eq!(env["a"], "10");
324        assert_eq!(env.get("b"), None);
325
326        assert_eq!(eval("0 ? x = 30 : (y = 40)", env), Ok(Value::Integer(40)));
327        assert_eq!(env.get("x"), None);
328        assert_eq!(env["y"], "40");
329
330        assert_eq!(eval("9 ? 1 : 0 ? 2 : 3", env), Ok(Value::Integer(1)));
331        assert_eq!(eval("0 ? 1 : 0 ? 2 : 3", env), Ok(Value::Integer(3)));
332    }
333
334    #[test]
335    fn conditional_evaluation_in_conditional_operators() {
336        let env = &mut HashMap::new();
337        assert_eq!(
338            eval("1 ? 2 : (a = 3) ? b = 4 : (c = 5)", env),
339            Ok(Value::Integer(2))
340        );
341        assert!(env.is_empty(), "expected empty env: {env:?}");
342
343        assert_eq!(
344            eval("0 ? (a = 1) ? b = 2 : (c = 3) : 4", env),
345            Ok(Value::Integer(4))
346        );
347        assert!(env.is_empty(), "expected empty env: {env:?}");
348    }
349
350    #[test]
351    fn boolean_logic_operators() {
352        let env = &mut HashMap::new();
353        assert_eq!(eval("0||0", env), Ok(Value::Integer(0)));
354        assert_eq!(eval(" 1 || 0 ", env), Ok(Value::Integer(1)));
355        assert_eq!(eval(" 0 || 1 ", env), Ok(Value::Integer(1)));
356        assert_eq!(eval("2 || 3", env), Ok(Value::Integer(1)));
357
358        assert_eq!(eval("0&&0", env), Ok(Value::Integer(0)));
359        assert_eq!(eval(" 1 && 0 ", env), Ok(Value::Integer(0)));
360        assert_eq!(eval(" 0 && 1 ", env), Ok(Value::Integer(0)));
361        assert_eq!(eval("2 && 3", env), Ok(Value::Integer(1)));
362    }
363
364    #[test]
365    fn conditional_evaluation_in_boolean_logic_operators() {
366        let env = &mut HashMap::new();
367        assert_eq!(eval("(a = 0) || (b = 2)", env), Ok(Value::Integer(1)));
368        assert_eq!(env["a"], "0");
369        assert_eq!(env["b"], "2");
370
371        let env = &mut HashMap::new();
372        assert_eq!(eval("(a = 3) || (b = 2)", env), Ok(Value::Integer(1)));
373        assert_eq!(env["a"], "3");
374        assert_eq!(env.get("b"), None);
375
376        let env = &mut HashMap::new();
377        assert_eq!(eval("(a = 0) && (b = 2)", env), Ok(Value::Integer(0)));
378        assert_eq!(env["a"], "0");
379        assert_eq!(env.get("b"), None);
380
381        let env = &mut HashMap::new();
382        assert_eq!(eval("(a = 3) && (b = 2)", env), Ok(Value::Integer(1)));
383        assert_eq!(env["a"], "3");
384        assert_eq!(env["b"], "2");
385
386        let env = &mut HashMap::new();
387        env.insert("x".to_string(), "@".to_string());
388        assert_eq!(eval("0 && (x || x)", env), Ok(Value::Integer(0)));
389        assert_eq!(eval("1 || x && x", env), Ok(Value::Integer(1)));
390
391        let env = &mut HashMap::new();
392        assert_eq!(eval("0 && ++x", env), Ok(Value::Integer(0)));
393        assert_eq!(env.get("x"), None);
394
395        let env = &mut HashMap::new();
396        assert_eq!(eval("0 && x++", env), Ok(Value::Integer(0)));
397        assert_eq!(env.get("x"), None);
398    }
399
400    #[test]
401    fn bitwise_logic_operators() {
402        let env = &mut HashMap::new();
403        assert_eq!(eval("3|5", env), Ok(Value::Integer(7)));
404        assert_eq!(eval(" 5 | 3 ", env), Ok(Value::Integer(7)));
405        assert_eq!(eval(" 10 | 10 ", env), Ok(Value::Integer(10)));
406        assert_eq!(eval(" 7 | 14 | 28 ", env), Ok(Value::Integer(31)));
407
408        assert_eq!(eval("3^5", env), Ok(Value::Integer(6)));
409        assert_eq!(eval(" 5 ^ 3 ", env), Ok(Value::Integer(6)));
410        assert_eq!(eval(" 10 ^ 10 ", env), Ok(Value::Integer(0)));
411        assert_eq!(eval(" 7 ^ 14 ^ 28 ", env), Ok(Value::Integer(21)));
412
413        assert_eq!(eval("3&5", env), Ok(Value::Integer(1)));
414        assert_eq!(eval(" 5 & 3 ", env), Ok(Value::Integer(1)));
415        assert_eq!(eval(" 10 & 10 ", env), Ok(Value::Integer(10)));
416        assert_eq!(eval(" 7 & 14 & 28 ", env), Ok(Value::Integer(4)));
417    }
418
419    #[test]
420    fn equality_comparison_operators() {
421        let env = &mut HashMap::new();
422        assert_eq!(eval("1==2", env), Ok(Value::Integer(0)));
423        assert_eq!(eval(" 2 == 1 ", env), Ok(Value::Integer(0)));
424        assert_eq!(eval(" 5 == 5 ", env), Ok(Value::Integer(1)));
425        assert_eq!(eval(" 1 == 2 == 2 ", env), Ok(Value::Integer(0)));
426
427        assert_eq!(eval("1!=2", env), Ok(Value::Integer(1)));
428        assert_eq!(eval(" 2 != 1 ", env), Ok(Value::Integer(1)));
429        assert_eq!(eval(" 5 != 5 ", env), Ok(Value::Integer(0)));
430        assert_eq!(eval(" 1 != 1 != 2 ", env), Ok(Value::Integer(1)));
431    }
432
433    #[test]
434    fn inequality_comparison_operators() {
435        let env = &mut HashMap::new();
436        assert_eq!(eval("1<2", env), Ok(Value::Integer(1)));
437        assert_eq!(eval(" 2 < 1 ", env), Ok(Value::Integer(0)));
438        assert_eq!(eval(" 5 < 5 ", env), Ok(Value::Integer(0)));
439        assert_eq!(eval(" 3 < 3 < 3 ", env), Ok(Value::Integer(1)));
440
441        assert_eq!(eval("1<=2", env), Ok(Value::Integer(1)));
442        assert_eq!(eval(" 2 <= 1 ", env), Ok(Value::Integer(0)));
443        assert_eq!(eval(" 5 <= 5 ", env), Ok(Value::Integer(1)));
444        assert_eq!(eval(" 3 <= 3 <= 3 ", env), Ok(Value::Integer(1)));
445
446        assert_eq!(eval("1>2", env), Ok(Value::Integer(0)));
447        assert_eq!(eval(" 2 > 1 ", env), Ok(Value::Integer(1)));
448        assert_eq!(eval(" 5 > 5 ", env), Ok(Value::Integer(0)));
449        assert_eq!(eval(" 3 > 3 > 3 ", env), Ok(Value::Integer(0)));
450
451        assert_eq!(eval("1>=2", env), Ok(Value::Integer(0)));
452        assert_eq!(eval(" 2 >= 1 ", env), Ok(Value::Integer(1)));
453        assert_eq!(eval(" 5 >= 5 ", env), Ok(Value::Integer(1)));
454        assert_eq!(eval(" 3 >= 3 >= 3 ", env), Ok(Value::Integer(0)));
455    }
456
457    #[test]
458    fn bit_shift_operators() {
459        let env = &mut HashMap::new();
460        assert_eq!(eval("5<<3", env), Ok(Value::Integer(40)));
461        assert_eq!(eval(" 3 << 5 ", env), Ok(Value::Integer(96)));
462        assert_eq!(eval(" 2 << 2 << 2 ", env), Ok(Value::Integer(32)));
463
464        assert_eq!(eval("64>>3", env), Ok(Value::Integer(8)));
465        assert_eq!(eval(" 63 >> 3 ", env), Ok(Value::Integer(7)));
466        assert_eq!(eval(" 2 >> 2 >> 2 ", env), Ok(Value::Integer(0)));
467    }
468
469    #[test]
470    fn overflow_in_bit_shifting() {
471        let env = &mut HashMap::new();
472        assert_eq!(
473            eval("0x4000000000000000<<1", env),
474            Err(Error {
475                cause: EvalError::Overflow.into(),
476                location: 18..20,
477            })
478        );
479        assert_eq!(
480            eval("0<<1000", env),
481            Err(Error {
482                cause: EvalError::Overflow.into(),
483                location: 1..3,
484            })
485        );
486        assert_eq!(
487            eval("0<<0x100000000", env),
488            Err(Error {
489                cause: EvalError::Overflow.into(),
490                location: 1..3,
491            })
492        );
493
494        assert_eq!(
495            eval("0>>1000", env),
496            Err(Error {
497                cause: EvalError::Overflow.into(),
498                location: 1..3,
499            })
500        );
501        assert_eq!(
502            eval("0>>0x100000000", env),
503            Err(Error {
504                cause: EvalError::Overflow.into(),
505                location: 1..3,
506            })
507        );
508    }
509
510    #[test]
511    fn bit_shifting_of_negative_values() {
512        let env = &mut HashMap::new();
513
514        // Left-shifting a negative value is undefined in C.
515        assert_eq!(
516            eval("-1<<1", env),
517            Err(Error {
518                cause: EvalError::LeftShiftingNegative.into(),
519                location: 2..4,
520            })
521        );
522        assert_eq!(
523            eval("(-0x7FFFFFFFFFFFFFFF-1)<<1", env),
524            Err(Error {
525                cause: EvalError::LeftShiftingNegative.into(),
526                location: 23..25,
527            })
528        );
529
530        // Right-shifting a negative value is implementation-defined in C.
531        assert_eq!(eval("-4>>1", env), Ok(Value::Integer(-4 >> 1)));
532        assert_eq!(eval("-1>>1", env), Ok(Value::Integer(-1 >> 1)));
533    }
534
535    #[test]
536    fn reverse_bit_shifting() {
537        let env = &mut HashMap::new();
538        assert_eq!(
539            eval("1 << -1", env),
540            Err(Error {
541                cause: EvalError::ReverseShifting.into(),
542                location: 2..4,
543            })
544        );
545
546        assert_eq!(
547            eval("1 >> -1", env),
548            Err(Error {
549                cause: EvalError::ReverseShifting.into(),
550                location: 2..4,
551            })
552        );
553    }
554
555    #[test]
556    fn addition_operator() {
557        let env = &mut HashMap::new();
558        assert_eq!(eval("1+2", env), Ok(Value::Integer(3)));
559        assert_eq!(eval(" 12 + 34 ", env), Ok(Value::Integer(46)));
560        assert_eq!(eval(" 3 + 16 + 5 ", env), Ok(Value::Integer(24)));
561    }
562
563    #[test]
564    fn overflow_in_addition() {
565        let env = &mut HashMap::new();
566        assert_eq!(
567            eval("9223372036854775807+1", env),
568            Err(Error {
569                cause: EvalError::Overflow.into(),
570                location: 19..20,
571            })
572        );
573    }
574
575    #[test]
576    fn subtraction_operator() {
577        let env = &mut HashMap::new();
578        assert_eq!(eval("2-1", env), Ok(Value::Integer(1)));
579        assert_eq!(eval(" 42 - 15 ", env), Ok(Value::Integer(27)));
580        assert_eq!(eval(" 10 - 7 - 5 ", env), Ok(Value::Integer(-2)));
581    }
582
583    #[test]
584    fn overflow_in_subtraction() {
585        let env = &mut HashMap::new();
586        assert_eq!(
587            eval("0-9223372036854775807-2", env),
588            Err(Error {
589                cause: EvalError::Overflow.into(),
590                location: 21..22,
591            })
592        );
593    }
594
595    #[test]
596    fn multiplication_operator() {
597        let env = &mut HashMap::new();
598        assert_eq!(eval("3*6", env), Ok(Value::Integer(18)));
599        assert_eq!(eval(" 5 * 11 ", env), Ok(Value::Integer(55)));
600        assert_eq!(eval(" 2 * 3 * 4 ", env), Ok(Value::Integer(24)));
601    }
602
603    #[test]
604    fn overflow_in_multiplication() {
605        let env = &mut HashMap::new();
606        assert_eq!(
607            eval("0x100000000 * 0x80000000", env),
608            Err(Error {
609                cause: EvalError::Overflow.into(),
610                location: 12..13,
611            })
612        );
613    }
614
615    #[test]
616    fn division_operator() {
617        let env = &mut HashMap::new();
618        assert_eq!(eval("6/2", env), Ok(Value::Integer(3)));
619        assert_eq!(eval(" 120 / 24 ", env), Ok(Value::Integer(5)));
620        assert_eq!(eval(" 120/10/5 ", env), Ok(Value::Integer(2)));
621    }
622
623    #[test]
624    fn division_by_zero() {
625        let env = &mut HashMap::new();
626        assert_eq!(
627            eval("1/0", env),
628            Err(Error {
629                cause: EvalError::DivisionByZero.into(),
630                location: 1..2,
631            })
632        );
633        assert_eq!(
634            eval("0/0", env),
635            Err(Error {
636                cause: EvalError::DivisionByZero.into(),
637                location: 1..2,
638            })
639        );
640        assert_eq!(
641            eval("10/0", env),
642            Err(Error {
643                cause: EvalError::DivisionByZero.into(),
644                location: 2..3,
645            })
646        );
647    }
648
649    #[test]
650    fn overflow_in_division() {
651        let env = &mut HashMap::new();
652        assert_eq!(
653            eval("(-0x7FFFFFFFFFFFFFFF-1)/-1", env),
654            Err(Error {
655                cause: EvalError::Overflow.into(),
656                location: 23..24,
657            })
658        );
659    }
660
661    #[test]
662    fn remainder_operator() {
663        let env = &mut HashMap::new();
664        assert_eq!(eval("6%2", env), Ok(Value::Integer(0)));
665        assert_eq!(eval(" 17 % 5 ", env), Ok(Value::Integer(2)));
666        assert_eq!(eval(" 42 % 11 % 5 ", env), Ok(Value::Integer(4)));
667    }
668
669    #[test]
670    fn remainder_by_zero() {
671        let env = &mut HashMap::new();
672        assert_eq!(
673            eval("1%0", env),
674            Err(Error {
675                cause: EvalError::DivisionByZero.into(),
676                location: 1..2,
677            })
678        );
679        assert_eq!(
680            eval("0%0", env),
681            Err(Error {
682                cause: EvalError::DivisionByZero.into(),
683                location: 1..2,
684            })
685        );
686        assert_eq!(
687            eval("10%0", env),
688            Err(Error {
689                cause: EvalError::DivisionByZero.into(),
690                location: 2..3,
691            })
692        );
693    }
694
695    #[test]
696    fn overflow_in_remainder() {
697        let env = &mut HashMap::new();
698        assert_eq!(
699            eval("(-0x7FFFFFFFFFFFFFFF-1)%-1", env),
700            Err(Error {
701                cause: EvalError::Overflow.into(),
702                location: 23..24,
703            })
704        );
705    }
706
707    #[test]
708    fn plus_prefix_operator() {
709        let env = &mut HashMap::new();
710        assert_eq!(eval("+0", env), Ok(Value::Integer(0)));
711        assert_eq!(eval(" + 10 ", env), Ok(Value::Integer(10)));
712        assert_eq!(eval(" + + 57", env), Ok(Value::Integer(57)));
713    }
714
715    #[test]
716    fn numeric_negation_operator() {
717        let env = &mut HashMap::new();
718        assert_eq!(eval("-0", env), Ok(Value::Integer(0)));
719        assert_eq!(eval(" - 12 ", env), Ok(Value::Integer(-12)));
720        assert_eq!(eval(" - - 49", env), Ok(Value::Integer(49)));
721        assert_eq!(eval(" - - - 49", env), Ok(Value::Integer(-49)));
722    }
723
724    #[test]
725    fn overflow_in_numeric_negation() {
726        let env = &mut HashMap::new();
727        assert_eq!(
728            eval("-0x7FFFFFFFFFFFFFFF-1", env),
729            Ok(Value::Integer(i64::MIN))
730        );
731        assert_eq!(
732            eval(" - (-0x7FFFFFFFFFFFFFFF-1)", env),
733            Err(Error {
734                cause: EvalError::Overflow.into(),
735                location: 1..2
736            })
737        );
738    }
739
740    #[test]
741    fn bitwise_negation_operator() {
742        let env = &mut HashMap::new();
743        assert_eq!(eval("~0", env), Ok(Value::Integer(-1)));
744        assert_eq!(eval(" ~ 3 ", env), Ok(Value::Integer(!3)));
745        assert_eq!(eval(" ~ ~ 42", env), Ok(Value::Integer(42)));
746        assert_eq!(eval(" ~ ~ ~ 0x38E7", env), Ok(Value::Integer(!0x38E7)));
747    }
748
749    #[test]
750    fn logical_negation_operator() {
751        let env = &mut HashMap::new();
752        assert_eq!(eval("!0", env), Ok(Value::Integer(1)));
753        assert_eq!(eval(" ! 1 ", env), Ok(Value::Integer(0)));
754        assert_eq!(eval(" ! 2 ", env), Ok(Value::Integer(0)));
755        assert_eq!(eval(" ! ! 3", env), Ok(Value::Integer(1)));
756    }
757
758    #[test]
759    fn prefix_increment_operator() {
760        let env = &mut HashMap::new();
761        assert_eq!(eval("++a", env), Ok(Value::Integer(1)));
762        assert_eq!(eval("++a", env), Ok(Value::Integer(2)));
763        assert_eq!(eval("++a", env), Ok(Value::Integer(3)));
764        assert_eq!(eval("a", env), Ok(Value::Integer(3)));
765    }
766
767    #[test]
768    fn prefix_incrementing_non_variable() {
769        let env = &mut HashMap::new();
770        assert_eq!(
771            eval(" ++ +a ", env),
772            Err(Error {
773                cause: EvalError::AssignmentToValue.into(),
774                location: 1..3,
775            })
776        );
777    }
778
779    #[test]
780    fn overflow_in_increment() {
781        let env = &mut HashMap::new();
782        env.insert("i".to_string(), "9223372036854775807".to_string());
783        assert_eq!(
784            eval("  ++ i", env),
785            Err(Error {
786                cause: EvalError::Overflow.into(),
787                location: 2..4,
788            })
789        );
790    }
791
792    #[test]
793    fn prefix_decrement_operator() {
794        let env = &mut HashMap::new();
795        assert_eq!(eval("--d", env), Ok(Value::Integer(-1)));
796        assert_eq!(eval("--d", env), Ok(Value::Integer(-2)));
797        assert_eq!(eval("--d", env), Ok(Value::Integer(-3)));
798        assert_eq!(eval("d", env), Ok(Value::Integer(-3)));
799    }
800
801    #[test]
802    fn overflow_in_decrement() {
803        let env = &mut HashMap::new();
804        env.insert("i".to_string(), "-9223372036854775808".to_string());
805        assert_eq!(
806            eval(" -- i", env),
807            Err(Error {
808                cause: EvalError::Overflow.into(),
809                location: 1..3,
810            })
811        );
812    }
813
814    #[test]
815    fn prefix_decrementing_non_variable() {
816        let env = &mut HashMap::new();
817        assert_eq!(
818            eval("  -- +a ", env),
819            Err(Error {
820                cause: EvalError::AssignmentToValue.into(),
821                location: 2..4,
822            })
823        );
824    }
825
826    #[test]
827    fn postfix_increment_operator() {
828        let env = &mut HashMap::new();
829        assert_eq!(eval("a++", env), Ok(Value::Integer(0)));
830        assert_eq!(eval("a++", env), Ok(Value::Integer(1)));
831        assert_eq!(eval("a++", env), Ok(Value::Integer(2)));
832        assert_eq!(eval("a", env), Ok(Value::Integer(3)));
833    }
834
835    #[test]
836    fn postfix_incrementing_non_variable() {
837        let env = &mut HashMap::new();
838        assert_eq!(
839            eval("5++", env),
840            Err(Error {
841                cause: EvalError::AssignmentToValue.into(),
842                location: 1..3,
843            })
844        );
845    }
846
847    #[test]
848    fn postfix_decrement_operator() {
849        let env = &mut HashMap::new();
850        assert_eq!(eval("a--", env), Ok(Value::Integer(0)));
851        assert_eq!(eval("a--", env), Ok(Value::Integer(-1)));
852        assert_eq!(eval("a--", env), Ok(Value::Integer(-2)));
853        assert_eq!(eval("a", env), Ok(Value::Integer(-3)));
854    }
855
856    #[test]
857    fn postfix_decrementing_non_variable() {
858        let env = &mut HashMap::new();
859        assert_eq!(
860            eval("7 --", env),
861            Err(Error {
862                cause: EvalError::AssignmentToValue.into(),
863                location: 2..4,
864            })
865        );
866    }
867
868    #[test]
869    fn combining_operators_of_same_precedence() {
870        let env = &mut HashMap::new();
871        assert_eq!(eval("2+5-3", env), Ok(Value::Integer(4)));
872    }
873
874    #[test]
875    fn combining_operators_of_different_precedences() {
876        let env = &mut HashMap::new();
877        assert_eq!(eval("2+3*4", env), Ok(Value::Integer(14)));
878        assert_eq!(eval("2*3+4", env), Ok(Value::Integer(10)));
879    }
880
881    #[test]
882    fn combining_prefix_and_postfix_operators() {
883        let env = &mut HashMap::new();
884        assert_eq!(eval("+a++", env), Ok(Value::Integer(0)));
885        assert_eq!(eval("-a++", env), Ok(Value::Integer(-1)));
886        assert_eq!(eval("~a--", env), Ok(Value::Integer(-3)));
887        assert_eq!(eval("!a--", env), Ok(Value::Integer(0)));
888    }
889
890    #[test]
891    fn parentheses() {
892        let env = &mut HashMap::new();
893        assert_eq!(eval("(42)", env), Ok(Value::Integer(42)));
894        assert_eq!(eval("(1+2)", env), Ok(Value::Integer(3)));
895        assert_eq!(eval("(2+3)*4", env), Ok(Value::Integer(20)));
896        assert_eq!(eval("2*(3+4)", env), Ok(Value::Integer(14)));
897        assert_eq!(eval(" ( 6 - ( 7 - 3 ) ) * 2 ", env), Ok(Value::Integer(4)));
898        assert_eq!(eval(" 4 | ( ( 2 && 2 ) & 3 )", env), Ok(Value::Integer(5)));
899    }
900
901    #[test]
902    fn combining_postfix_operator_and_parentheses() {
903        let env = &mut HashMap::new();
904        assert_eq!(eval("(a)++", env), Ok(Value::Integer(0)));
905        assert_eq!(eval("(a) --", env), Ok(Value::Integer(1)));
906        assert_eq!(eval("a", env), Ok(Value::Integer(0)));
907    }
908
909    #[test]
910    fn unmatched_parenthesis() {
911        let env = &mut HashMap::new();
912        assert_eq!(
913            eval(" ( 1 ", env),
914            Err(Error {
915                cause: ErrorCause::SyntaxError(SyntaxError::UnclosedParenthesis {
916                    opening_location: 1..2,
917                }),
918                location: 5..5,
919            })
920        );
921    }
922}