Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion impl/rust-cli/benches/operation_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use tempfile::tempdir;
use vsh::commands::{mkdir, rmdir, rm, touch};
use vsh::commands::{mkdir, rm, rmdir, touch};
use vsh::state::ShellState;

/// Benchmark: mkdir operation
Expand Down
45 changes: 25 additions & 20 deletions impl/rust-cli/benches/performance_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criteri
use std::fs;
use std::io::Write;
use tempfile::TempDir;
use vsh::commands::{mkdir, rm, touch, undo, redo};
use vsh::commands::{mkdir, redo, rm, touch, undo};
use vsh::glob::expand_glob;
use vsh::state::ShellState;

Expand Down Expand Up @@ -50,24 +50,28 @@ fn bench_undo_scaling(c: &mut Criterion) {

for num_ops in [10, 50, 100].iter() {
group.throughput(Throughput::Elements(*num_ops as u64));
group.bench_with_input(BenchmarkId::from_parameter(num_ops), num_ops, |b, &num_ops| {
b.iter(|| {
let temp = TempDir::new().unwrap();
let root = temp.path().to_str().unwrap();
let mut state = ShellState::new(root).unwrap();
group.bench_with_input(
BenchmarkId::from_parameter(num_ops),
num_ops,
|b, &num_ops| {
b.iter(|| {
let temp = TempDir::new().unwrap();
let root = temp.path().to_str().unwrap();
let mut state = ShellState::new(root).unwrap();

// Create operations
for i in 0..num_ops {
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
}
// Create operations
for i in 0..num_ops {
mkdir(&mut state, &format!("dir_{}", i), false).unwrap();
}

// Benchmark undoing all
for _ in 0..num_ops {
undo(&mut state, 1, false).unwrap();
}
black_box(&state);
});
});
// Benchmark undoing all
for _ in 0..num_ops {
undo(&mut state, 1, false).unwrap();
}
black_box(&state);
});
},
);
}

group.finish();
Expand Down Expand Up @@ -216,9 +220,10 @@ fn bench_checkpoint_creation(c: &mut Criterion) {
state
},
|mut state| {
state
.checkpoints
.insert("test".to_string(), (state.history.len(), chrono::Utc::now()));
state.checkpoints.insert(
"test".to_string(),
(state.history.len(), chrono::Utc::now()),
);
black_box(&state.checkpoints);
},
criterion::BatchSize::SmallInput,
Expand Down
211 changes: 168 additions & 43 deletions impl/rust-cli/src/arith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
//! assert_eq!(result, 15);
//! ```

use anyhow::{anyhow, Result};
use crate::state::ShellState;
use anyhow::{anyhow, Result};

/// Arithmetic operators
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -144,7 +144,8 @@ impl Tokenizer {
}
}

num_str.parse::<i64>()
num_str
.parse::<i64>()
.map_err(|_| anyhow!("Invalid number: {}", num_str))
}

Expand Down Expand Up @@ -295,7 +296,10 @@ impl Tokenizer {
}
}

Some(ch) => Err(anyhow!("Unexpected character in arithmetic expression: {}", ch)),
Some(ch) => Err(anyhow!(
"Unexpected character in arithmetic expression: {}",
ch
)),
}
}
}
Expand Down Expand Up @@ -508,7 +512,11 @@ impl Parser {
if matches!(self.peek(), Token::Op(ArithOp::Pow)) {
self.advance();
let right = self.parse_power()?; // Right-associative recursion
Ok(ArithExpr::BinaryOp(ArithOp::Pow, Box::new(left), Box::new(right)))
Ok(ArithExpr::BinaryOp(
ArithOp::Pow,
Box::new(left),
Box::new(right),
))
} else {
Ok(left)
}
Expand Down Expand Up @@ -554,7 +562,10 @@ impl Parser {
Ok(expr)
}

token => Err(anyhow!("Unexpected token in arithmetic expression: {:?}", token)),
token => Err(anyhow!(
"Unexpected token in arithmetic expression: {:?}",
token
)),
}
}
}
Expand Down Expand Up @@ -704,65 +715,149 @@ mod tests {
fn test_arith_basic_ops() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("5 + 3").unwrap(), &state).unwrap(), 8);
assert_eq!(eval_arith(&parse_arithmetic("10 - 4").unwrap(), &state).unwrap(), 6);
assert_eq!(eval_arith(&parse_arithmetic("6 * 7").unwrap(), &state).unwrap(), 42);
assert_eq!(eval_arith(&parse_arithmetic("20 / 4").unwrap(), &state).unwrap(), 5);
assert_eq!(eval_arith(&parse_arithmetic("17 % 5").unwrap(), &state).unwrap(), 2);
assert_eq!(
eval_arith(&parse_arithmetic("5 + 3").unwrap(), &state).unwrap(),
8
);
assert_eq!(
eval_arith(&parse_arithmetic("10 - 4").unwrap(), &state).unwrap(),
6
);
assert_eq!(
eval_arith(&parse_arithmetic("6 * 7").unwrap(), &state).unwrap(),
42
);
assert_eq!(
eval_arith(&parse_arithmetic("20 / 4").unwrap(), &state).unwrap(),
5
);
assert_eq!(
eval_arith(&parse_arithmetic("17 % 5").unwrap(), &state).unwrap(),
2
);
}

#[test]
fn test_arith_precedence() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("2 + 3 * 4").unwrap(), &state).unwrap(), 14);
assert_eq!(eval_arith(&parse_arithmetic("(2 + 3) * 4").unwrap(), &state).unwrap(), 20);
assert_eq!(eval_arith(&parse_arithmetic("2 ** 3 + 1").unwrap(), &state).unwrap(), 9);
assert_eq!(eval_arith(&parse_arithmetic("2 ** (3 + 1)").unwrap(), &state).unwrap(), 16);
assert_eq!(
eval_arith(&parse_arithmetic("2 + 3 * 4").unwrap(), &state).unwrap(),
14
);
assert_eq!(
eval_arith(&parse_arithmetic("(2 + 3) * 4").unwrap(), &state).unwrap(),
20
);
assert_eq!(
eval_arith(&parse_arithmetic("2 ** 3 + 1").unwrap(), &state).unwrap(),
9
);
assert_eq!(
eval_arith(&parse_arithmetic("2 ** (3 + 1)").unwrap(), &state).unwrap(),
16
);
}

#[test]
fn test_arith_power_right_associative() {
let state = ShellState::new("/tmp").unwrap();

// 2 ** 3 ** 2 = 2 ** (3 ** 2) = 2 ** 9 = 512
assert_eq!(eval_arith(&parse_arithmetic("2 ** 3 ** 2").unwrap(), &state).unwrap(), 512);
assert_eq!(
eval_arith(&parse_arithmetic("2 ** 3 ** 2").unwrap(), &state).unwrap(),
512
);
}

#[test]
fn test_arith_comparison() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("5 > 3").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("5 < 3").unwrap(), &state).unwrap(), 0);
assert_eq!(eval_arith(&parse_arithmetic("5 == 5").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("5 != 3").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("5 >= 5").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("5 <= 4").unwrap(), &state).unwrap(), 0);
assert_eq!(
eval_arith(&parse_arithmetic("5 > 3").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("5 < 3").unwrap(), &state).unwrap(),
0
);
assert_eq!(
eval_arith(&parse_arithmetic("5 == 5").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("5 != 3").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("5 >= 5").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("5 <= 4").unwrap(), &state).unwrap(),
0
);
}

#[test]
fn test_arith_logical() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("1 && 1").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("1 && 0").unwrap(), &state).unwrap(), 0);
assert_eq!(eval_arith(&parse_arithmetic("1 || 0").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("0 || 0").unwrap(), &state).unwrap(), 0);
assert_eq!(eval_arith(&parse_arithmetic("!0").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("!5").unwrap(), &state).unwrap(), 0);
assert_eq!(
eval_arith(&parse_arithmetic("1 && 1").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("1 && 0").unwrap(), &state).unwrap(),
0
);
assert_eq!(
eval_arith(&parse_arithmetic("1 || 0").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("0 || 0").unwrap(), &state).unwrap(),
0
);
assert_eq!(
eval_arith(&parse_arithmetic("!0").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("!5").unwrap(), &state).unwrap(),
0
);
}

#[test]
fn test_arith_bitwise() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("5 & 3").unwrap(), &state).unwrap(), 1);
assert_eq!(eval_arith(&parse_arithmetic("5 | 3").unwrap(), &state).unwrap(), 7);
assert_eq!(eval_arith(&parse_arithmetic("5 ^ 3").unwrap(), &state).unwrap(), 6);
assert_eq!(eval_arith(&parse_arithmetic("~5").unwrap(), &state).unwrap(), -6);
assert_eq!(eval_arith(&parse_arithmetic("8 << 2").unwrap(), &state).unwrap(), 32);
assert_eq!(eval_arith(&parse_arithmetic("8 >> 2").unwrap(), &state).unwrap(), 2);
assert_eq!(
eval_arith(&parse_arithmetic("5 & 3").unwrap(), &state).unwrap(),
1
);
assert_eq!(
eval_arith(&parse_arithmetic("5 | 3").unwrap(), &state).unwrap(),
7
);
assert_eq!(
eval_arith(&parse_arithmetic("5 ^ 3").unwrap(), &state).unwrap(),
6
);
assert_eq!(
eval_arith(&parse_arithmetic("~5").unwrap(), &state).unwrap(),
-6
);
assert_eq!(
eval_arith(&parse_arithmetic("8 << 2").unwrap(), &state).unwrap(),
32
);
assert_eq!(
eval_arith(&parse_arithmetic("8 >> 2").unwrap(), &state).unwrap(),
2
);
}

#[test]
Expand All @@ -771,16 +866,25 @@ mod tests {
state.set_variable("x", "5");
state.set_variable("y", "10");

assert_eq!(eval_arith(&parse_arithmetic("x + y").unwrap(), &state).unwrap(), 15);
assert_eq!(eval_arith(&parse_arithmetic("x * 2 + y").unwrap(), &state).unwrap(), 20);
assert_eq!(
eval_arith(&parse_arithmetic("x + y").unwrap(), &state).unwrap(),
15
);
assert_eq!(
eval_arith(&parse_arithmetic("x * 2 + y").unwrap(), &state).unwrap(),
20
);
}

#[test]
fn test_arith_undefined_variable() {
let state = ShellState::new("/tmp").unwrap();

// Undefined variables should be treated as 0
assert_eq!(eval_arith(&parse_arithmetic("undefined_var + 5").unwrap(), &state).unwrap(), 5);
assert_eq!(
eval_arith(&parse_arithmetic("undefined_var + 5").unwrap(), &state).unwrap(),
5
);
}

#[test]
Expand All @@ -795,24 +899,45 @@ mod tests {
fn test_arith_nested() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("((5 + 3) * 2) + 1").unwrap(), &state).unwrap(), 17);
assert_eq!(eval_arith(&parse_arithmetic("2 ** (3 ** 2)").unwrap(), &state).unwrap(), 512);
assert_eq!(
eval_arith(&parse_arithmetic("((5 + 3) * 2) + 1").unwrap(), &state).unwrap(),
17
);
assert_eq!(
eval_arith(&parse_arithmetic("2 ** (3 ** 2)").unwrap(), &state).unwrap(),
512
);
}

#[test]
fn test_arith_unary_minus() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic("-5").unwrap(), &state).unwrap(), -5);
assert_eq!(eval_arith(&parse_arithmetic("-(5 + 3)").unwrap(), &state).unwrap(), -8);
assert_eq!(eval_arith(&parse_arithmetic("10 + -5").unwrap(), &state).unwrap(), 5);
assert_eq!(
eval_arith(&parse_arithmetic("-5").unwrap(), &state).unwrap(),
-5
);
assert_eq!(
eval_arith(&parse_arithmetic("-(5 + 3)").unwrap(), &state).unwrap(),
-8
);
assert_eq!(
eval_arith(&parse_arithmetic("10 + -5").unwrap(), &state).unwrap(),
5
);
}

#[test]
fn test_arith_whitespace() {
let state = ShellState::new("/tmp").unwrap();

assert_eq!(eval_arith(&parse_arithmetic(" 5 + 3 ").unwrap(), &state).unwrap(), 8);
assert_eq!(eval_arith(&parse_arithmetic("( ( 5 + 3 ) * 2 )").unwrap(), &state).unwrap(), 16);
assert_eq!(
eval_arith(&parse_arithmetic(" 5 + 3 ").unwrap(), &state).unwrap(),
8
);
assert_eq!(
eval_arith(&parse_arithmetic("( ( 5 + 3 ) * 2 )").unwrap(), &state).unwrap(),
16
);
}
}
Loading
Loading