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