beet_rsx_combinator 0.0.8

JSX-like parser combinator for Rust
Documentation
use combine::ParseResult;
use combine::Parser;
use combine::Stream;
use combine::char::digit;
use combine::char::spaces;
use combine::choice;
use combine::combinator::many1;
use combine::combinator::one_of;
use combine::combinator::optional;
use combine::combinator::parser;
use combine::combinator::token;
use combine::combinator::value;

pub fn open_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	token('<').with(value(())).parse_stream(input)
}

pub fn close_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	token('>').with(value(())).parse_stream(input)
}

pub fn closing_element_open_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	(token('<').skip(spaces()), token('/'))
		.with(value(()))
		.parse_stream(input)
}

pub fn self_closing_element_close_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	(token('/').skip(spaces()), token('>'))
		.with(value(()))
		.parse_stream(input)
}

pub fn fragment_open_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	(token('<'), token('>')).with(value(())).parse_stream(input)
}

pub fn fragment_close_tag<I>(input: I) -> ParseResult<(), I>
where
	I: Stream<Item = char>,
{
	(token('<'), token('/'), token('>'))
		.with(value(()))
		.parse_stream(input)
}

pub fn sign<I>(input: I) -> ParseResult<char, I>
where
	I: Stream<Item = char>,
{
	choice!(token('-'), token('+')).parse_stream(input)
}

pub fn frac<I>(input: I) -> ParseResult<i64, I>
where
	I: Stream<Item = char>,
{
	token('.')
		.with(many1(digit()))
		.map(|frac: String| frac.parse::<i64>().unwrap())
		.parse_stream(input)
}

pub fn exp<I>(input: I) -> ParseResult<i64, I>
where
	I: Stream<Item = char>,
{
	choice!(token('e'), token('E'))
		.with(parser(integer))
		.parse_stream(input)
}

pub fn integer<I>(input: I) -> ParseResult<i64, I>
where
	I: Stream<Item = char>,
{
	(optional(parser(sign)), many1(digit()))
		.map(|(sign, digits): (Option<_>, String)| {
			format!("{}{}", sign.unwrap_or('+'), digits)
				.parse::<i64>()
				.unwrap()
		})
		.parse_stream(input)
}

pub fn float_simple<I>(input: I) -> ParseResult<f64, I>
where
	I: Stream<Item = char>,
{
	(parser(integer), parser(frac))
		.map(|(int, frac)| format!("{}.{}", int, frac).parse::<f64>().unwrap())
		.parse_stream(input)
}

pub fn float_exp<I>(input: I) -> ParseResult<f64, I>
where
	I: Stream<Item = char>,
{
	(parser(integer), optional(parser(frac)), parser(exp))
		.map(|(int, frac, exp)| {
			format!("{}.{}e{}", int, frac.unwrap_or(0), exp)
				.parse::<f64>()
				.unwrap()
		})
		.parse_stream(input)
}

pub fn identifier_non_alpha_numeric<I>(input: I) -> ParseResult<char, I>
where
	I: Stream<Item = char>,
{
	choice!(token('_'), token('$')).parse_stream(input)
}

pub fn escaped_character<I>(input: I) -> ParseResult<char, I>
where
	I: Stream<Item = char>,
{
	token('\\')
		.with(one_of(r#"'"\nrtbfv0"#.chars()))
		.map(|c| match c {
			'\'' => '\'',
			'"' => '"',
			'\\' => '\\',
			'n' => '\n',
			'r' => '\r',
			't' => '\t',
			'b' => '\u{0008}',
			'f' => '\u{000c}',
			'v' => '\u{2b7f}',
			'0' => '\u{0000}',
			_ => panic!("Unhandled escape character"),
		})
		.parse_stream(input)
}

#[cfg(test)]
mod test {
	use super::*;

	#[test]
	pub fn test_open_tag() {
		assert_eq!(parser(open_tag).parse("").is_err(), true);
		assert_eq!(parser(open_tag).parse("foo").is_err(), true);
		assert_eq!(parser(open_tag).parse("<").unwrap(), ((), ""));
		assert_eq!(parser(open_tag).parse("foo<").is_err(), true);
	}

	#[test]
	pub fn test_close_tag() {
		assert_eq!(parser(close_tag).parse("").is_err(), true);
		assert_eq!(parser(close_tag).parse("foo").is_err(), true);
		assert_eq!(parser(close_tag).parse(">").unwrap(), ((), ""));
		assert_eq!(parser(close_tag).parse("foo>").is_err(), true);
	}

	#[test]
	pub fn test_closing_element_open_tag() {
		assert_eq!(parser(closing_element_open_tag).parse("").is_err(), true);
		assert_eq!(
			parser(closing_element_open_tag).parse("foo").is_err(),
			true
		);
		assert_eq!(
			parser(closing_element_open_tag).parse("</").unwrap(),
			((), "")
		);
		assert_eq!(
			parser(closing_element_open_tag).parse("foo</").is_err(),
			true
		);
	}

	#[test]
	pub fn test_self_closing_element_close_tag() {
		assert_eq!(
			parser(self_closing_element_close_tag).parse("").is_err(),
			true
		);
		assert_eq!(
			parser(self_closing_element_close_tag).parse("foo").is_err(),
			true
		);
		assert_eq!(
			parser(self_closing_element_close_tag).parse("/>").unwrap(),
			((), "")
		);
		assert_eq!(
			parser(self_closing_element_close_tag)
				.parse("foo/>")
				.is_err(),
			true
		);
	}

	#[test]
	pub fn test_fragment_open_tag() {
		assert_eq!(parser(fragment_open_tag).parse("").is_err(), true);
		assert_eq!(parser(fragment_open_tag).parse("foo").is_err(), true);
		assert_eq!(parser(fragment_open_tag).parse("<>").unwrap(), ((), ""));
		assert_eq!(parser(fragment_open_tag).parse("foo<>").is_err(), true);
		assert_eq!(parser(fragment_open_tag).parse("<").is_err(), true);
		assert_eq!(parser(fragment_open_tag).parse(">").is_err(), true);
		assert_eq!(parser(fragment_open_tag).parse("<>").unwrap(), ((), ""));
		assert_eq!(parser(fragment_open_tag).parse("foo<>").is_err(), true);
	}

	#[test]
	pub fn test_fragment_close_tag() {
		assert_eq!(parser(fragment_close_tag).parse("").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("foo").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("</>").unwrap(), ((), ""));
		assert_eq!(parser(fragment_close_tag).parse("foo</>").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("<").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("</").is_err(), true);
		assert_eq!(parser(fragment_close_tag).parse("</>").unwrap(), ((), ""));
		assert_eq!(parser(fragment_close_tag).parse("foo</>").is_err(), true);
	}

	#[test]
	pub fn test_sign() {
		assert_eq!(parser(sign).parse("").is_err(), true);
		assert_eq!(parser(sign).parse(" ").is_err(), true);
		assert_eq!(parser(sign).parse("+").unwrap(), ('+', ""));
		assert_eq!(parser(sign).parse("-").unwrap(), ('-', ""));
		assert_eq!(parser(sign).parse("a").is_err(), true);
	}

	#[test]
	pub fn test_frac() {
		assert_eq!(parser(frac).parse("").is_err(), true);
		assert_eq!(parser(frac).parse(" ").is_err(), true);
		assert_eq!(parser(frac).parse("1234").is_err(), true);
		assert_eq!(parser(frac).parse(".1234").unwrap(), (1234, ""));
		assert_eq!(parser(frac).parse("a").is_err(), true);
	}

	#[test]
	pub fn test_exp() {
		assert_eq!(parser(exp).parse("").is_err(), true);
		assert_eq!(parser(exp).parse(" ").is_err(), true);
		assert_eq!(parser(exp).parse("1234").is_err(), true);
		assert_eq!(parser(exp).parse("e1234").unwrap(), (1234, ""));
		assert_eq!(parser(exp).parse("e+1234").unwrap(), (1234, ""));
		assert_eq!(parser(exp).parse("e-1234").unwrap(), (-1234, ""));
		assert_eq!(parser(exp).parse("a").is_err(), true);
	}

	#[test]
	pub fn test_integer() {
		assert_eq!(parser(integer).parse("").is_err(), true);
		assert_eq!(parser(integer).parse(" ").is_err(), true);
		assert_eq!(parser(integer).parse("1").unwrap(), (1i64, ""));
		assert_eq!(parser(integer).parse("1.2").unwrap(), (1i64, ".2"));
		assert_eq!(parser(integer).parse("1e3").unwrap(), (1i64, "e3"));
		assert_eq!(parser(integer).parse("1.2e3").unwrap(), (1i64, ".2e3"));
		assert_eq!(parser(integer).parse("+1").unwrap(), (1i64, ""));
		assert_eq!(parser(integer).parse("-1").unwrap(), (-1i64, ""));
	}

	#[test]
	pub fn test_float_simple() {
		assert_eq!(parser(float_simple).parse("").is_err(), true);
		assert_eq!(parser(float_simple).parse(" ").is_err(), true);
		assert_eq!(parser(float_simple).parse("1").is_err(), true);
		assert_eq!(parser(float_simple).parse("1.2").unwrap(), (1.2f64, ""));
		assert_eq!(parser(float_simple).parse("1e3").is_err(), true);
		assert_eq!(
			parser(float_simple).parse("1.2e3").unwrap(),
			(1.2f64, "e3")
		);
		assert_eq!(parser(float_simple).parse("+1.2").unwrap(), (1.2f64, ""));
		assert_eq!(parser(float_simple).parse("-1.2").unwrap(), (-1.2f64, ""));
	}

	#[test]
	pub fn test_float_exp() {
		assert_eq!(parser(float_exp).parse("").is_err(), true);
		assert_eq!(parser(float_exp).parse(" ").is_err(), true);
		assert_eq!(parser(float_exp).parse("1").is_err(), true);
		assert_eq!(parser(float_exp).parse("1.2").is_err(), true);
		assert_eq!(parser(float_exp).parse("1e3").unwrap(), (1e3f64, ""));
		assert_eq!(parser(float_exp).parse("1.2e3").unwrap(), (1.2e3f64, ""));
		assert_eq!(parser(float_exp).parse("+1.2e3").unwrap(), (1.2e3f64, ""));
		assert_eq!(parser(float_exp).parse("-1.2e3").unwrap(), (-1.2e3f64, ""));
	}

	#[test]
	pub fn test_identifier_non_alpha_numeric() {
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse("").is_err(),
			true
		);
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse(" ").is_err(),
			true
		);
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse("_").unwrap(),
			('_', "")
		);
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse("$").unwrap(),
			('$', "")
		);
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse("a").is_err(),
			true
		);
		assert_eq!(
			parser(identifier_non_alpha_numeric).parse("0").is_err(),
			true
		);
	}

	#[test]
	pub fn test_escaped_character() {
		assert_eq!(parser(escaped_character).parse("").is_err(), true);
		assert_eq!(parser(escaped_character).parse(" ").is_err(), true);
		assert_eq!(
			parser(escaped_character).parse(r#"\""#).unwrap(),
			('"', "")
		);
		assert_eq!(
			parser(escaped_character).parse(r#"\'"#).unwrap(),
			('\'', "")
		);
		assert_eq!(parser(escaped_character).parse(r#"""#).is_err(), true);
		assert_eq!(parser(escaped_character).parse(r#"'"#).is_err(), true);
	}
}