#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use crate::parser::Parser;
use perl_tdd_support::must;
#[test]
fn test_variable_division() {
let code = "my $res = $x / $y;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division: {sexp}");
assert!(!sexp.contains("regex"), "Should not be regex: {sexp}");
}
#[test]
fn test_division_after_paren() {
let code = "my $avg = ($sum) / $count;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division after paren: {sexp}");
assert!(!sexp.contains("regex"), "Should not be regex: {sexp}");
}
#[test]
fn test_division_in_condition() {
let code = "if ($x / 2 > 0) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division in condition: {sexp}");
}
#[test]
fn test_division_after_hash_deref() {
let code = "$hash{key} / 10;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division after hash deref: {sexp}");
}
#[test]
fn test_division_assign() {
let code = "$x /= 2;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("assignment_/assign"), "Should be division-assign: {sexp}");
}
#[test]
fn test_chained_division() {
let code = "$x / $y / $z;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.matches("binary_/").count() >= 2, "Should be two divisions: {sexp}");
assert!(!sexp.contains("regex"), "Should not be regex: {sexp}");
}
#[test]
fn test_division_after_number() {
let code = "my $x = 10 / 3;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division after number: {sexp}");
}
#[test]
fn test_division_after_closing_bracket() {
let code = "$arr[0] / 2;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division after closing bracket: {sexp}");
}
#[test]
fn test_regex_after_if() {
let code = "if (/pattern/) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after if: {sexp}");
}
#[test]
fn test_regex_in_grep() {
let code = "grep /abc/, @list;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in grep: {sexp}");
}
#[test]
fn test_regex_in_binding() {
let code = "$str =~ /foo/;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex") || sexp.contains("match"), "Should be regex match: {sexp}");
}
#[test]
fn test_regex_in_void_context() {
let code = "/pattern/;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in void context: {sexp}");
}
#[test]
fn test_regex_with_interpolation() {
let code = "/$x/;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex with interpolation: {sexp}");
}
#[test]
fn test_regex_in_split() {
let code = "split /,/, $str;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split: {sexp}");
}
#[test]
fn test_regex_after_builtin_argument_separator() {
let code = "print /foo/;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after builtin: {sexp}");
}
#[test]
fn test_regex_in_map() {
let code = "map /abc/, @list;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in map: {sexp}");
}
#[test]
fn test_regex_after_while() {
let code = "while (/abc/) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after while: {sexp}");
}
#[test]
fn test_regex_after_unless() {
let code = "unless (/abc/) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after unless: {sexp}");
}
#[test]
fn test_division_after_function_call() {
let code = "time / 60;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division after nullary function: {sexp}");
}
#[test]
fn test_defined_or() {
let code = "my $val = $x // $y;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_//"), "Should be defined-or: {sexp}");
}
#[test]
fn test_defined_or_assign() {
let code = "$x //= $y;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("assignment_//assign"), "Should be defined-or-assign: {sexp}");
}
#[test]
fn test_complex_expression_with_division_and_regex() {
let code = "my $avg = $sum / $count; if (/done/) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should contain division: {sexp}");
assert!(sexp.contains("regex"), "Should also contain regex: {sexp}");
}
#[test]
fn test_regex_after_comma() {
let code = "my @r = (1, /abc/);";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after comma: {sexp}");
}
#[test]
fn test_regex_after_open_paren() {
let code = "(/pattern/);";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex after open paren: {sexp}");
}
#[test]
fn test_addition_then_division() {
let code = "my $avg = ($x + $y) / 2;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division: {sexp}");
assert!(sexp.contains("binary_+"), "Should have addition: {sexp}");
}
#[test]
fn test_split_regex_in_assignment_rhs() {
let code = "my @p = split /,/, $s;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split assignment RHS: {sexp}");
assert!(!sexp.contains("binary_/"), "Should not contain division: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_after_return() {
let code = "return split /\\s+/, $line;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split after return: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_as_push_argument() {
let code = "push @r, split /;/, $v;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split as push arg: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_inside_map_block() {
let code = "my @x = map { split /,/ } @lines;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside map block: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_hash_value_assignment() {
let code = "$hash{key} = split /=/, $pair;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split hash value assignment: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_parenthesized_call() {
let code = "foo(split /,/, $s);";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside call: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_ternary() {
let code = "$cond ? split /,/, $a : split /;/, $b;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.matches("regex").count() >= 2, "Should have two regexes: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_logical_or() {
let code = "my @p = split(/,/, $s) || die;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split with parens: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_grep_regex_in_assignment_rhs() {
let code = "my @m = grep /abc/, @list;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in grep assignment RHS: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_array_element() {
let code = "$arr[0] = split /,/, $str;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split for array elem assign: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_for_list() {
let code = "for my $part (split /,/, $str) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside for list: {sexp}");
assert!(!sexp.contains("binary_/"), "Should not contain division: {sexp}");
}
#[test]
fn test_split_regex_in_while_condition() {
let code = "while (split /\\n/, $text) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside while: {sexp}");
}
#[test]
fn test_split_regex_nested_in_join() {
let code = "join \"-\", split /,/, $str;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split as arg to join: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_array_ref() {
let code = "[split /,/, $str];";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside array ref: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_if_condition() {
let code = "if (split /,/, $str) { 1; }";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split inside if: {sexp}");
}
#[test]
fn test_map_regex_in_assignment_rhs() {
let code = "my @m = map /abc/, @list;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in map assignment RHS: {sexp}");
assert!(!sexp.contains("ERROR"), "Should not contain parse errors: {sexp}");
}
#[test]
fn test_split_regex_in_hash_ref_value() {
let code = "my $h = { key => split /,/, $str };";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split as hash ref value: {sexp}");
assert!(!sexp.contains("binary_/"), "Should not contain division: {sexp}");
}
#[test]
fn test_split_regex_chained_with_method() {
let code = "(split /,/, $str)[0];";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("regex"), "Should be regex in split with subscript: {sexp}");
}
#[test]
fn test_sort_followed_by_non_regex_division() {
let code = "my $x = scalar(@list) / 2;";
let mut parser = Parser::new(code);
let ast = must(parser.parse());
let sexp = ast.to_sexp();
assert!(sexp.contains("binary_/"), "Should be division: {sexp}");
assert!(!sexp.contains("regex"), "Should not be regex: {sexp}");
}
}