# Some predicate functions used in testing starts_with_a <- function(x) grepl("^a", x[1]) starts_with_b <- function(x) grepl("^b", x[1]) starts_with_hash <- function(x) grepl("^#", x[1]) get_numbers <- function(x) { if (length(x) == 0) { list() } else { m <- gregexpr("[[:digit:]]+", x) matches <- as.numeric(regmatches(x, m)[[1]]) if (length(matches) == 0) list() else matches } } test_that("'succeed' works in standard cases", { expect_equal(succeed("abc")(c("xyz", "def")), list(L = list("abc"), R = c("xyz", "def"))) expect_equal( succeed(data.frame(title = "The beginning", author = "J. Doe"))(c("Unconsumed", "text")), list(L = list(data.frame(title = "The beginning", author = "J. Doe")), R = c("Unconsumed", "text")) ) }) test_that("'fail' works in standard cases", { expect_true(failed(fail()(c("xyz", "def")))) }) test_that("'satisfy' works in standard cases", { expect_equal(satisfy(starts_with_a)(c("abc", "def")), list(L = list("abc"), R = "def")) expect_true(failed(satisfy(starts_with_a)(c("bca", "def")))) expect_true(failed(satisfy(starts_with_a)(character(0)))) }) test_that("'satisfy' validates that 'b' argument is a function", { # Test with character string instead of function expect_error( satisfy("not a function"), "'b' must be a function" ) # Test with numeric value expect_error( satisfy(42), "'b' must be a function" ) # Test with NULL expect_error( satisfy(NULL), "'b' must be a function" ) # Test with list expect_error( satisfy(list(a = 1)), "'b' must be a function" ) }) test_that("'literal' works in standard cases", { expect_equal(literal("ab")(c("ab", "cdef")), list(L = list("ab"), R = "cdef")) expect_true(failed(literal("ab")(c("abc", "cdef")))) }) test_that("'eof' works in standard conditions", { expect_true(failed(eof()("a"))) expect_equal(eof()(character(0)), list(L = list(), R = list())) expect_equal((literal("a") %then% eof())("a"), list(L = list("a"), R = list())) }) test_that("`eof` works in repetition parsers", { expect_equal(zero_or_one(eof())(character(0)), list(L = list(), R = list())) expect_equal(zero_or_more(eof())(character(0)), list(L = list(), R = list())) expect_equal(one_or_more(eof())(character(0)), list(L = list(), R = list())) expect_equal(match_n(1, eof())(character(0)), list(L = list(), R = list())) expect_equal(exactly(1, eof())(character(0)), list(L = list(), R = list())) }) test_that("'%then%' works in standard cases", { expect_equal((satisfy(starts_with_a) %then% satisfy(starts_with_b))(c("ab", "bc", "de")), list(L = c(list("ab"), list("bc")), R = "de")) expect_true(failed((satisfy(starts_with_a) %then% satisfy(starts_with_b))(c("bb", "bc", "de")))) expect_true(failed((satisfy(starts_with_a) %then% satisfy(starts_with_b))(c("ab", "ac", "de")))) expect_equal((literal("ab") %then% literal("ac"))(c("ab", "ac", "de")), list(L = c(list("ab"), list("ac")), R = "de")) expect_true(failed((literal("ab") %then% literal("ac"))(as.character(0)))) }) test_that("'%xthen%' and '%thenx%' perform as they should", { expect_equal((satisfy(starts_with_a) %xthen% satisfy(starts_with_b))(c("ab", "bc", "de")), list(L = list("ab"), R = "de")) expect_equal((satisfy(starts_with_a) %thenx% satisfy(starts_with_b))(c("ab", "bc", "de")), list(L = list("bc"), R = "de")) }) test_that("'%or%' works in standard cases", { expect_equal((literal("A") %or% literal("a"))(LETTERS[1:5]), list(L = list("A"), R = LETTERS[2:5])) expect_equal((literal("A") %or% literal("a"))(letters[1:5]), list(L = list("a"), R = letters[2:5])) expect_true(failed((literal("A") %or% literal("a"))(letters[2:5]))) expect_equal((literal("a") %or% satisfy(starts_with_a))(letters[1:5]), list(L = list("a"), R = letters[2:5])) expect_true(failed((literal("a") %or% satisfy(starts_with_a))(as.character(0)))) }) test_that("We can create a composite parser", { expect_equal((literal("a") %or% satisfy(starts_with_a) %or% literal("a"))(letters[1:5]), list(L = list("a"), R = letters[2:5])) expect_equal(((literal("a") %or% satisfy(starts_with_a)) %then% (literal("b")))(letters[1:5]), list(L = c(list("a"), list("b")), R = letters[3:5])) }) test_that("'zero_or_more' works in standard cases", { expect_equal(zero_or_more(literal("A"))(LETTERS[1:5]), list(L = list("A"), R = LETTERS[2:5])) expect_equal(zero_or_more(literal("A"))(LETTERS[2:5]), list(L = list(), R = LETTERS[2:5])) }) test_that("'one_or_more' works in standard cases", { expect_equal(one_or_more(literal("A"))(LETTERS[1:5]), list(L = list("A"), R = LETTERS[2:5])) expect_true(failed(one_or_more(literal("A"))(LETTERS[2:5]))) }) test_that("'exactly' works in standard cases", { expect_equal(exactly(2, literal("A"))(c("A", LETTERS[1:5])), list(L = c(list("A"), list("A")), R = LETTERS[2:5])) expect_true(failed(exactly(2, literal("A"))(c(rep("A", 2), LETTERS[1:5])))) expect_equal(exactly(0, literal("A"))(LETTERS[2:5]), list(L = list(), R = LETTERS[2:5])) expect_equal(exactly(2, (literal("A") %ret% NULL))(rep("A", 2)), list(L = list(), R = character(0))) }) test_that("'zero_or_one' works in standard cases", { expect_equal(zero_or_one(literal("A"))(LETTERS[2:5]), list(L = list(), R = LETTERS[2:5])) expect_equal(zero_or_one(literal("A"))(LETTERS[1:5]), list(L = list("A"), R = LETTERS[2:5])) expect_true(failed(zero_or_one(literal("A"))(c("A", LETTERS[1:5])))) }) test_that("'zero_or_one' is greedy", { ABblock <- function() { zero_or_one(literal("A")) %then% literal("B") } expect_equal(one_or_more(ABblock())(c("A", "B", "B")), list(L = list("A", "B", "B"), R = character(0))) }) test_that("'match_n' works in standard cases", { expect_equal(match_n(2, literal("A"))(c("A", LETTERS[1:5])), list(L = c(list("A"), list("A")), R = LETTERS[2:5])) expect_equal(match_n(2, literal("A"))(c(rep("A", 2), LETTERS[1:5])), list(L = c(list("A"), list("A")), R = LETTERS[1:5])) expect_true(failed(match_n(2, literal("A"))(LETTERS[1:5]))) }) test_that("'match_n' and 'exactly' wor when n=0", { expect_equal(match_n(0, literal("A"))("A"), list(L = list(), R = "A")) expect_equal(exactly(0, literal("A"))("B"), list(L = list(), R = "B")) }) test_that("'match_s' works in standard cases", { expect_equal(match_s(get_numbers)("12 13 14"), list(L = list(c(12, 13, 14)), R = character(0))) expect_true(failed(match_s(get_numbers)("ab cd ef"))) }) test_that("All parsers accept character(0) input and return failure", { expect_true(failed(satisfy(is_empty_atom)(character(0)))) expect_true(failed(literal(character(0))(character(0)))) expect_true(failed((literal(character(0)) %or% literal(character(0)))(character(0)))) expect_true(failed((literal("A") %then% literal(character(0)))("A")), list()) expect_true(failed((literal(character(0)) %then% literal(character(0)))(character(0)))) expect_true(failed((literal("A") %xthen% literal(character(0)))("A"))) expect_true(failed((literal(character(0)) %xthen% literal(character(0)))(character(0)))) expect_true(failed((literal("A") %thenx% literal(character(0)))("A"))) expect_true((failed((literal(character(0)) %thenx% literal(character(0)))(character(0))))) expect_true(failed(match_s(get_numbers)(character(0)))) }) test_that("Parsers can consume the input up to the end", { expect_equal((literal("A") %then% literal("B"))(c("A", "B")), list(L = c(list("A"), list("B")), R = character(0))) expect_equal((literal("A") %then% (literal("b") %or% literal("B")))(c("A", "b")), list(L = c(list("A"), list("b")), R = character(0))) expect_equal((literal("A") %xthen% literal("B"))(c("A", "B")), list(L = list("A"), R = character(0))) expect_equal((literal("A") %thenx% literal("B"))(c("A", "B")), list(L = list("B"), R = character(0))) expect_equal((literal("A") %xthen% (literal("b") %or% literal("B")))(c("A", "b")), list(L = list("A"), R = character(0))) expect_equal((zero_or_more(literal("A")))(rep("A", 5)), list(L = c(list("A"), list("A"), list("A"), list("A"), list("A")), R = character(0))) expect_equal((zero_or_more(literal("A")))(c("")), list(L = list(), R = "")) expect_equal((zero_or_more(literal("A")))(character(0)), list(L = list(), R = character(0))) expect_equal((one_or_more(literal("A")))(rep("A", 5)), list(L = c(list("A"), list("A"), list("A"), list("A"), list("A")), R = character(0))) expect_equal((one_or_more(literal("A")))(c("A")), list(L = list("A"), R = character(0))) expect_equal((exactly(2, literal("A")))(c("A", "A")), list(L = c(list("A"), list("A")), R = character(0))) expect_equal((match_n(2, literal("A")))(c("A", "A")), list(L = c(list("A"), list("A")), R = character(0))) expect_equal(Spacer()(c(" ", " ")), list(L = list(), R = character(0))) }) test_that("repeater parsers work when nested", { nested1 <- function() { exactly(2, literal("A")) %then% literal("B") } nested2 <- function() { one_or_more(nested1()) } expect_equal(nested1()(c("A", "A", "B", "A", "A", "B")), list(L = c(list("A"), list("A"), list("B")), R = c("A", "A", "B"))) expect_equal(one_or_more(nested1())(c("A", "A", "B", "A", "A", "B")), list(L = c(list("A"), list("A"), list("B"), list("A"), list("A"), list("B")), R = character(0))) expect_equal(zero_or_one(nested1())(c("A", "A", "B")), list(L = c(list("A"), list("A"), list("B")), R = character(0))) expect_equal(zero_or_one(nested1())(c("B", "A", "B")), list(L = list(), R = c("B", "A", "B"))) expect_equal(exactly(2, nested1())(c("A", "A", "B", "A", "A", "B")), list(L = c(list("A"), list("A"), list("B"), list("A"), list("A"), list("B")), R = character(0))) nested3 <- function() { literal("A") %then% literal("A") %then% literal("B") } nested4 <- function() { one_or_more(nested3()) } expect_equal(nested3()(c("A", "A", "B", "A", "A", "B")), list(L = c(list("A"), list("A"), list("B")), R = c("A", "A", "B"))) expect_equal(one_or_more(nested4())(c("A", "A", "B", "A", "A", "B")), list(L = c(list("A"), list("A"), list("B"), list("A"), list("A"), list("B")), R = character(0))) }) p1 <- function(x) { (literal("A") %then% literal("B"))(x) } p2 <- function(x) { (literal("A") %then% literal("C") %then% literal("D"))(x) } p7 <- function(x) { (literal("A") %then% literal("C") %then% literal("E") %then% literal("D"))(x) } p3 <- function(x) { (EmptyLine() %then% EmptyLine() %then% literal("A"))(x) } p4 <- function(x) { (exactly(2, EmptyLine()) %then% literal("A"))(x) } p5 <- function(x) { (one_or_more(EmptyLine()) %then% literal("A"))(x) } p6 <- function(x) { (MaybeEmpty() %then% literal("A"))(x) } test_that("'reporter' works in successful parses", { expect_equal(reporter((p1 %or% p2) %then% eof())(c("A", "C", "D")), c(list("A"), list("C"), list("D"))) }) test_that("'reporter()' yields error messages", { expect_error(reporter(literal("A"))("B"), regexp = "line 1") expect_error(reporter(p1)(c("A", "C")), regexp = "line 2") expect_error(reporter(p2)(c("A", "C", "E")), regexp = "line 3") expect_error(reporter(literal("A") %or% literal("B"))("C"), regexp = "line 1") expect_error(reporter(p2 %or% p1)(c("A", "C", "E")), regexp = "line 3") expect_error(reporter(p1 %or% p2)(c("A", "C", "E")), regexp = "line 3") expect_error(reporter(p3)(c("", "", "C")), regexp = "line 3") expect_error(reporter(p4)(c("", "", "C")), regexp = "line 3") expect_error(reporter(p5)(c("", "", "C")), regexp = "line 3") expect_error(reporter(p6)(c("", "", "C")), regexp = "line 3") }) test_that("'reporter()' yields warning when not completely consuming input", { expect_warning(reporter(p2)(c("A", "C", "D")), "did not completely consume") expect_warning(reporter((literal("B") %or% literal("A")))(c("A", "B")), "did not completely consume") expect_warning(reporter(p1)(c("A", "B")), "did not completely consume") expect_warning(reporter(match_n(1, literal("A")))(c("A", "B", "A")), "did not completely consume") }) test_that("All combinations of 3 alternative, exclusive parsers yield same error", { expect_error(reporter((p1 %or% p2 %or% p7))(c("A", "C", "E", "F")), regexp = "line 4") expect_error(reporter((p1 %or% p7 %or% p2))(c("A", "C", "E", "F")), regexp = "line 4") expect_error(reporter((p7 %or% p1 %or% p2))(c("A", "C", "E", "F")), regexp = "line 4") expect_error(reporter((p7 %or% p2 %or% p1))(c("A", "C", "E", "F")), regexp = "line 4") expect_error(reporter((p2 %or% p1 %or% p7))(c("A", "C", "E", "F")), regexp = "line 4") expect_error(reporter((p2 %or% p7 %or% p1))(c("A", "C", "E", "F")), regexp = "line 4") }) test_that("'match_n' yields correct error", { expect_error(reporter(match_n(2, literal("A")))(c("A", "B", "A")), regexp = "line 2") expect_error(reporter(match_n(5, literal("A")))(c("A", "A", "A", "A", "B")), regexp = "line 5") }) test_that("'exactly' reports correct line number when failing with insufficient matches", { # Test case from issue: exactly(10, literal("A")) with only 5 A's followed by B # Should report failure at line 6 (where B is), not line 5 (last successful A) TenAs <- function() { exactly(10, literal("A")) %then% eof() } input <- c(rep("A", 5), "B", rep("A", 3)) expect_error(reporter(TenAs())(input), regexp = "line 6") # Additional test: failure at line 1 when first element doesn't match expect_error(reporter(exactly(3, literal("A")) %then% eof())(c("B", "A", "A")), regexp = "line 1") # Test: failure at line 3 when we need 5 but only get 2 expect_error(reporter(exactly(5, literal("A")) %then% eof())(c("A", "A", "C")), regexp = "line 3") })