nbnf 0.0.2

A parser generator based on nom, with syntax inspired by EBNF and regex
Documentation
use std::collections::HashMap;
use std::str::FromStr;

use nbnf::nbnf;
use nbnf::nom::IResult;
use nbnf::nom::bytes::complete::take_while;
use nbnf::nom::bytes::tag;
use nbnf::nom::combinator::value;
use nbnf::nom::multi::separated_list0;

fn main() {
	let input = r#"[1, 2.3, "four", {"five": false, "six": 7}, null, "abc \"def\" ghi\n"]"#;
	_ = dbg!(json.parse(input));
}

#[derive(Clone, Copy, Debug)]
pub enum Number {
	Int(i128),
	Float(f64),
}

#[derive(Clone, Debug)]
pub enum Json {
	Null,
	Bool(bool),
	Number(Number),
	String(String),
	Array(Vec<Json>),
	Object(HashMap<String, Json>),
}

#[rustfmt::skip]
nbnf!(r#"
	json<Json> = -ws json_inner -ws;
	json_inner<Json> =
		null /
		boolean /
		number /
		string|<Json::String> /
		array /
		object /
		nom::combinator::eof@<Json::Null>;

	null<Json> = "null"@<Json::Null>;
	boolean<Json> =
		"true"@<Json::Bool(true)> /
		"false"@<Json::Bool(false)>;

	number<Json> =
		(number_float / number_int / number_hex)
		|<Json::Number>;
	number_float<Number> =
		~([0-9]+ '.' [0-9]*)
		|!<|str| f64::from_str(str).map(Number::Float)>;
	number_int<Number> =
		~([0-9]+)
		|!<|str| i128::from_str(str).map(Number::Int)>;
	number_hex<Number> =
		(-('0' [xX]) ~([0-9a-fA-F]+))
		|!<|str| i128::from_str_radix(str, 16).map(Number::Int)>;

	string<String> =
		(-'"' string_inner* -'"')
		|<String::from_iter>;
	string_inner<char> =
		"\\\""@<'"'> /
		"\\n"@<'\n'> /
		"\\r"@<'\r'> /
		"\\t"@<'\t'> /
		"\\0"@<'\0'> /
		"\\"@<'\\'> /
		[^"];

	array<Json> =
		(-'[' array_inner -']')
		|<Json::Array>;

	object<Json> =
		(-'{' object_inner -'}')
		|<HashMap::from_iter>
		|<Json::Object>;
	object_pair<(String, Json)> = -ws string -ws -':' -ws json -ws;
"#);

fn array_inner(input: &str) -> IResult<&str, Vec<Json>> {
	separated_list0(tag(","), json).parse(input)
}

fn object_inner(input: &str) -> IResult<&str, Vec<(String, Json)>> {
	separated_list0(tag(","), object_pair).parse(input)
}

fn ws(input: &str) -> IResult<&str, ()> {
	value((), take_while(char::is_whitespace)).parse(input)
}