use pascal::{interpreter::Interpreter, lexer::Lexer, parser::Parser};
use anyhow::Result;
#[test]
fn test_complex_arithmetic() -> Result<()> {
let source = r#"
program test;
var
a, b, c, d, e, f: integer;
begin
a := 10;
b := 5;
c := 3;
d := 2;
// Complex expression: 10 + 5 * 3 - 2 / 2
// Should be: 10 + 15 - 1 = 24
e := a + b * c - d div d;
// Nested parentheses: (10 + 5) * (3 - 2)
// Should be: 15 * 1 = 15
f := (a + b) * (c - d);
// Test modulo: 10 mod 3 = 1
a := a mod c;
// Test power-like behavior with multiplication
b := b * b; // 5 * 5 = 25
// Test unary minus
c := -c; // -3
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("a"), Some(&pascal::Value::Integer(1))); assert_eq!(scope.get("b"), Some(&pascal::Value::Integer(25))); assert_eq!(scope.get("c"), Some(&pascal::Value::Integer(-3))); assert_eq!(scope.get("d"), Some(&pascal::Value::Integer(2)));
assert_eq!(scope.get("e"), Some(&pascal::Value::Integer(24))); assert_eq!(scope.get("f"), Some(&pascal::Value::Integer(15)));
Ok(())
}
#[test]
fn test_nested_control_structures() -> Result<()> {
let source = r#"
program test;
var
i, j, k, sum, product: integer;
found: boolean;
begin
sum := 0;
product := 1;
found := false;
// Nested loops with conditionals
for i := 1 to 5 do
begin
for j := 1 to 3 do
begin
sum := sum + i * j;
// Break condition simulation
if (i = 3) and (j = 2) then
found := true;
product := product * (i + j);
end;
end;
// Test while loop
k := 0;
while k < 10 do
begin
k := k + 1;
if k > 5 then
sum := sum + k;
end;
// Test repeat until
i := 0;
repeat
i := i + 1;
until i >= 5;
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("sum"), Some(&pascal::Value::Integer(130)));
assert_eq!(scope.get("found"), Some(&pascal::Value::Boolean(true)));
assert_eq!(scope.get("i"), Some(&pascal::Value::Integer(5)));
Ok(())
}
#[test]
fn test_string_operations() -> Result<()> {
let source = r#"
program test;
var
ch: char;
success: boolean;
begin
// Test character assignments
ch := 'A';
success := true;
// Test character comparisons
if ch = 'A' then
ch := 'B';
if ch <> 'A' then
success := true;
// Test character operations
if ch > 'A' then
ch := 'C';
// Test boolean logic with characters
if (ch = 'C') and success then
success := true;
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("ch"), Some(&pascal::Value::Char('C')));
assert_eq!(scope.get("success"), Some(&pascal::Value::Boolean(true)));
Ok(())
}
#[test]
fn test_complex_boolean_expressions() -> Result<()> {
let source = r#"
program test;
var
a, b, c, d: boolean;
result: boolean;
begin
a := true;
b := false;
c := true;
d := false;
// Test AND operations
result := a and c; // true and true = true
result := result and (not b); // true and (not false) = true
// Test OR operations
result := b or d; // false or false = false
result := result or a; // false or true = true
// Test complex expression
result := (a and b) or (c and d); // (false) or (false) = false
result := (a or b) and (c or d); // (true) and (true) = true
// Test NOT with parentheses
result := not ((a and b) or (c and d)); // not (false) = true
// Test combination
result := (a and c) or (not b and not d); // true or true = true
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("result"), Some(&pascal::Value::Boolean(true)));
Ok(())
}
#[test]
fn test_scope_management() -> Result<()> {
let source = r#"
program test;
var
x, y, z: integer;
begin
x := 1;
y := 2;
z := 3;
// Simulate block scope with compound statements
begin
var x: integer; // Note: This syntax might not be supported
x := 10;
y := y + x; // y = 2 + 10 = 12
end;
// x should still be 1, y should be 12
x := x + y + z; // 1 + 12 + 3 = 16
// Test with while loop as a block
x := 5;
y := 0;
while x > 0 do
begin
y := y + x; // 5 + 4 + 3 + 2 + 1 = 15
x := x - 1;
end;
// Final test
x := 10;
if x > 5 then
begin
x := x * 2; // 20
end;
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
if result.is_err() {
let source2 = r#"
program test;
var
x, y, z: integer;
begin
x := 1;
y := 2;
z := 3;
x := x + y + z; // 1 + 2 + 3 = 6
// Test with while loop
x := 5;
y := 0;
while x > 0 do
begin
y := y + x; // 5 + 4 + 3 + 2 + 1 = 15
x := x - 1;
end;
// Final test
x := 10;
if x > 5 then
begin
x := x * 2; // 20
end;
end.
"#;
let mut interpreter2 = Interpreter::new(false);
let result2 = interpreter2.execute(source2);
assert!(result2.is_ok(), "Execution failed: {:?}", result2.err());
let scope = interpreter2.current_scope();
assert_eq!(scope.get("x"), Some(&pascal::Value::Integer(20)));
assert_eq!(scope.get("y"), Some(&pascal::Value::Integer(15)));
assert_eq!(scope.get("z"), Some(&pascal::Value::Integer(3)));
return Ok(());
}
let scope = interpreter.current_scope();
assert_eq!(scope.get("x"), Some(&pascal::Value::Integer(20)));
assert_eq!(scope.get("y"), Some(&pascal::Value::Integer(15)));
Ok(())
}
#[test]
fn test_real_number_operations() -> Result<()> {
let source = r#"
program test;
var
r1, r2, r3: real;
i: integer;
begin
r1 := 3.14;
r2 := 2.0;
r3 := r1 * r2; // 6.28
r1 := r3 / r2; // 3.14
r2 := r1 + r3; // 3.14 + 6.28 = 9.42
// Mix real and integer
i := 5;
r1 := r2 + i; // 9.42 + 5.0 = 14.42
r3 := r1 * 2.0; // 28.84
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
if let Some(&pascal::Value::Real(r)) = scope.get("r3") {
assert!((r - 28.84).abs() < 0.01, "r3 should be ~28.84, got {}", r);
} else {
panic!("r3 should be a Real value");
}
Ok(())
}
#[test]
fn test_assignment_chains() -> Result<()> {
let source = r#"
program test;
var
a, b, c, d, e: integer;
begin
// Simple chain
a := b := c := d := e := 5;
// Verify all are 5
a := a + b + c + d + e; // 25
// Complex expression with multiple operations
a := (b + c) * (d - e); // (5 + 5) * (5 - 5) = 10 * 0 = 0
// Reset and test different operations
a := 10;
b := 3;
c := a div b; // 3
d := a mod b; // 1
e := a - b * c; // 10 - 3 * 3 = 1
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
if result.is_err() {
let source2 = r#"
program test;
var
a, b, c, d, e: integer;
begin
// Individual assignments
a := 5;
b := 5;
c := 5;
d := 5;
e := 5;
// Verify all are 5
a := a + b + c + d + e; // 25
// Complex expression with multiple operations
a := (b + c) * (d - e); // (5 + 5) * (5 - 5) = 10 * 0 = 0
// Reset and test different operations
a := 10;
b := 3;
c := a div b; // 3
d := a mod b; // 1
e := a - b * c; // 10 - 3 * 3 = 1
end.
"#;
let mut interpreter2 = Interpreter::new(false);
let result2 = interpreter2.execute(source2);
assert!(result2.is_ok(), "Execution failed: {:?}", result2.err());
let scope = interpreter2.current_scope();
assert_eq!(scope.get("a"), Some(&pascal::Value::Integer(1)));
assert_eq!(scope.get("b"), Some(&pascal::Value::Integer(3)));
assert_eq!(scope.get("c"), Some(&pascal::Value::Integer(3)));
assert_eq!(scope.get("d"), Some(&pascal::Value::Integer(1)));
assert_eq!(scope.get("e"), Some(&pascal::Value::Integer(1)));
return Ok(());
}
let scope = interpreter.current_scope();
assert_eq!(scope.get("a"), Some(&pascal::Value::Integer(1)));
assert_eq!(scope.get("b"), Some(&pascal::Value::Integer(3)));
assert_eq!(scope.get("c"), Some(&pascal::Value::Integer(3)));
assert_eq!(scope.get("d"), Some(&pascal::Value::Integer(1)));
assert_eq!(scope.get("e"), Some(&pascal::Value::Integer(1)));
Ok(())
}
#[test]
fn test_boundary_conditions() -> Result<()> {
let source = r#"
program test;
var
min, max, zero: integer;
result: boolean;
begin
// Test zero
zero := 0;
result := (zero = 0);
// Test positive integers
max := 1000;
result := result and (max > 0);
// Test negative integers
min := -1000;
result := result and (min < 0);
// Test boundary comparisons
result := (max > min) and (min < zero) and (zero < max);
// Test arithmetic at boundaries
max := max + min; // 1000 + (-1000) = 0
min := min * -1; // -1000 * -1 = 1000
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("zero"), Some(&pascal::Value::Integer(0)));
assert_eq!(scope.get("max"), Some(&pascal::Value::Integer(0)));
assert_eq!(scope.get("min"), Some(&pascal::Value::Integer(1000)));
assert_eq!(scope.get("result"), Some(&pascal::Value::Boolean(true)));
Ok(())
}
#[test]
fn test_comprehensive_program() -> Result<()> {
let source = r#"
program test;
var
sum, i, j: integer;
product: integer;
flag: boolean;
count: integer;
begin
// Initialize
sum := 0;
product := 1;
flag := false;
count := 0;
// Calculate sum of squares from 1 to 5
for i := 1 to 5 do
begin
sum := sum + i * i;
// Count even numbers
if (i mod 2) = 0 then
count := count + 1;
end;
// Calculate product of first 3 numbers
for i := 1 to 3 do
begin
product := product * i;
end;
// Test nested loops
for i := 1 to 3 do
begin
for j := 1 to 3 do
begin
if i * j > 5 then
flag := true;
end;
end;
// Test while loop with condition
i := 10;
while i > 0 do
begin
i := i - 1;
if i = 5 then
sum := sum + 100; // Add bonus
end;
// Test boolean logic
flag := (sum > 0) and (product > 0) and (count = 2);
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("sum"), Some(&pascal::Value::Integer(155)));
assert_eq!(scope.get("product"), Some(&pascal::Value::Integer(6)));
assert_eq!(scope.get("count"), Some(&pascal::Value::Integer(2)));
assert_eq!(scope.get("i"), Some(&pascal::Value::Integer(0)));
assert_eq!(scope.get("flag"), Some(&pascal::Value::Boolean(true)));
Ok(())
}
#[test]
fn test_operator_precedence() -> Result<()> {
let source = r#"
program test;
var
a, b, c, d, result: integer;
begin
a := 10;
b := 5;
c := 2;
d := 3;
// Test precedence: * and div before + and -
result := a + b * c; // 10 + 10 = 20
// Test precedence with parentheses
result := (a + b) * c; // 15 * 2 = 30
// Test associativity (left to right)
result := a - b - c; // (10 - 5) - 2 = 3
// Test mixed operators
result := a + b * c - d div c; // 10 + 10 - 1 = 19
// Test complex expression
result := (a + b) * (c - d) div 2; // 15 * -1 div 2 = -7
// Test mod and div
result := a mod b + c div d; // 0 + 0 = 0
// Reset for final test
a := 20;
b := 3;
result := a div b * b + a mod b; // 6 * 3 + 2 = 20
end.
"#;
let mut interpreter = Interpreter::new(false);
let result = interpreter.execute(source);
assert!(result.is_ok(), "Execution failed: {:?}", result.err());
let scope = interpreter.current_scope();
assert_eq!(scope.get("result"), Some(&pascal::Value::Integer(20)));
Ok(())
}