use std::fmt::Debug; use aoc_parse::{parser, prelude::*}; #[track_caller] fn assert_parse
(parser: P, s: &str) where P: Parser, { if let Err(err) = parser.parse(s) { panic!("parse failed: {}", err); } } #[track_caller] fn assert_parse_eq
(parser: P, s: &str, expected: E)
where
P: Parser,
P::Output: PartialEq (parser: P, s: &str)
where
P: Parser,
P::Output: Debug,
{
if let Ok(m) = parser.parse(s) {
panic!("expected no match, got: {:?}", m);
}
}
#[track_caller]
fn assert_parse_error (parser: P, s: &str, expected_message: &str)
where
P: Parser,
P::Output: Debug,
{
match parser.parse(s) {
Ok(m) => panic!("expected no match, got: {:?}", m),
Err(err) => {
let actual = err.to_string();
if !actual.contains(expected_message) {
panic!("expected error message containing {expected_message:?}, got {actual:?}");
}
}
}
}
#[test]
fn test_hello_world() {
let p = parser!("hello " "world");
assert_parse_eq(p, "hello world", ());
assert_no_parse(p, "hello ");
}
#[test]
fn test_repeat_exact() {
let p = parser!("hello " "strange "* "world");
assert_parse(p, "hello world");
assert_parse(p, "hello strange world");
assert_parse(p, "hello strange strange strange strange strange world");
}
#[test]
fn test_alt_exact() {
let p = parser!({"one", "two"});
assert_parse(p, "one");
assert_parse(p, "two");
assert_no_parse(p, "");
assert_no_parse(p, "onetwo");
assert_no_parse(p, "twoone");
}
#[test]
fn test_lines_exact() {
let p = parser!(lines("whee!"));
assert_parse(p, "");
assert_parse(p, "whee!\nwhee!\nwhee!\n");
assert_parse(p, "whee!\n");
assert_parse(p, "whee!");
assert_no_parse(p, "\n");
assert_no_parse(p, "whee!\n\n");
}
#[test]
fn test_const_char() {
const SP: char = ' ';
let p = parser!(u32 SP u32);
assert_parse_eq(p, "311 4249", (311, 4249));
}
#[test]
fn test_mappers() {
const SP: char = ' ';
let p = parser!(a:u32 SP b:u32 => (a, b));
assert_parse_eq(p, "31 54", (31, 54));
}
#[test]
fn test_unused_labels() {
let p = parser!(_a:"ok" => "OK");
assert_parse_eq(p, "ok", "OK");
// TODO: this is not great -- the test makes no sense this way and fails to compile if you remove the excess parentheses
let p = parser!((_a:"hello") " " (_b:"world") => "!");
assert_parse_eq(p, "hello world", "!");
assert_no_parse(p, "");
assert_no_parse(p, "hello");
assert_no_parse(p, "hello ");
assert_no_parse(p, "helloworld");
assert_no_parse(p, " world");
assert_no_parse(p, "world");
assert_no_parse(p, "hello world ");
}
#[test]
fn test_repeat() {
let p = parser!("stop"*);
assert_parse_eq(p, "", vec![]);
assert_parse_eq(p, "stop", vec![()]);
assert_parse_eq(p, "stopstop", vec![(), ()]);
}
#[test]
fn test_repeat_sep() {
let p = parser!("lucky numbers: " (u32 ", ")* u32);
assert_parse_error(p, "lucky numbers: ", "expected u32");
}
#[test]
fn test_alt_tuple() {
// Tuples returned by an alternation don't get concatenated with other
// nearby terms.
assert_parse_eq(
parser!({u32 "x" u32, a:u32 "^2" => (a, a)} " -> " alpha),
"3x4 -> J",
((3, 4), 'J'),
);
assert_parse_eq(
parser!(alpha " = " {u32 "x" u32, a:u32 "^2" => (a, a)}),
"J = 5^2",
('J', (5, 5)),
);
assert_parse_eq(
parser!({u32 "," u32, "O" => (0, 0)} " + " alpha " + " alpha),
"3,7 + j + p",
((3, 7), 'j', 'p'),
);
// Try one where neither alternative is mapped with `=>`.
assert_parse_eq(
parser!(u32 ":" {alpha digit, "<" alpha "#" digit ">"}),
"57:j1",
(57, ('j', 1)),
);
}
#[test]
fn test_alt_map() {
let bit = parser!({ "0" => false, "1" => true });
let p = parser!(bit*);
assert_parse_eq(
p,
"0010101",
vec![false, false, true, false, true, false, true],
);
}
mod names_and_scopes {
use super::assert_parse_eq;
// `=>` should work even if `Parser` has not been imported.
#[test]
fn test_map_syntax() {
use aoc_parse::{parser, prelude::u64};
let p = parser!(a:u64 " " b:u64 => 100 * a + b);
assert_parse_eq(p, "31 34", 3134);
}
}
#[test]
fn test_chars() {
assert_parse_eq(parser!('A' 'b' 'c'), "Abc", ());
assert_no_parse(parser!('Q'), "q");
assert_parse_error(parser!('\n'), "q", r#"expected '\n' at"#);
let p = parser!(a:alpha+ => a.into_iter().collect::