use ast::ASTNode;
use nom::{hex_digit, digit};
use std::{str, char};
named!(pub parse_string<ASTNode>,
map!(alt!( parse_string_short_literal), |s| ASTNode::String(s)));
named!(parse_string_short_literal<String>,
delimited!(
alt!(tag!("\"") | tag!("'")),
fold_many0!(alt!(
map!(linebreak, |_| '\n') |
parse_byte |
parse_unicode |
one_of!("\x07\x08\x09\x0A\x0B\x0C\x0D")
), String::new(), |mut acc: String, item| {
acc.push(item);
acc
}),
alt!(tag!("\"") | tag!("'"))));
named!(parse_byte<char>, alt!(parse_byte_x | parse_byte_d));
named!(parse_byte_x<char>, map!(map_res!(map_res!(
preceded!(tag!("\\x"), hex_digit),
str::from_utf8),
|s| u8::from_str_radix(s, 16)), |i: u8| i as char));
named!(linebreak, alt!(tag!("\\\r\n") | tag!("\\\n\r") | tag!("\\\n")));
named!(parse_byte_d<char>, map!(map_res!(
preceded!(tag!("\\"), fold_many_m_n!(1, 3, digit, String::new(), |mut acc: String, item: &[u8]| {
for c in item {
acc.push(*c as char);
}
acc
})),
|s: String| s.parse::<u8>()), |i: u8| i as char));
named!(parse_unicode<char>,
map_opt!(
map_res!(
map_res!(
delimited!(tag!("\\u{"), recognize!(hex_digit), tag!("}")),
str::from_utf8),
|h| u32::from_str_radix(h, 16)),
char::from_u32));
#[cfg(test)]
mod tests {
ast_panic_test!(parse_unicode_1, parse_unicode, r#"\u{}"#);
ast_test!(parse_unicode_2, parse_unicode, r#"\u{A}"#, char::from_u32(0xA).unwrap());
ast_test!(parse_unicode_3, parse_unicode, r#"\u{a2}"#, char::from_u32(0xa2).unwrap());
ast_test!(parse_unicode_4, parse_unicode, r#"\u{AFf9}"#, char::from_u32(0xAFf9).unwrap());
ast_test!(parse_unicode_5, parse_unicode, r#"\u{0000000000000FFFF}"#, char::from_u32(0xFFFF).unwrap());
ast_test!(parse_unicode_6, parse_unicode, r#"\u{10FFFF}"#, char::from_u32(0x10FFFF).unwrap());
ast_panic_test!(parse_unicode_7, parse_unicode, r#"\u{110000}"#);
ast_test!(parse_byte_d_1, parse_byte_d, r#"\0"#, '\0');
ast_test!(parse_byte_d_2, parse_byte_d, r#"\00"#, '\0');
ast_test!(parse_byte_d_3, parse_byte_d, r#"\000"#, '\0');
ast_test!(parse_byte_d_4, parse_byte_d, r#"\0000"#, '\0');
ast_test!(parse_byte_d_5, parse_byte_d, r#"\230"#, '\u{E6}');
ast_panic_test!(parse_byte_d_6, parse_byte_d, r#"\256"#);
ast_test!(parse_byte_x_1, parse_byte_x, r#"\x00"#, '\0');
ast_test!(parse_byte_x_3, parse_byte_x, r#"\x23"#, '\u{23}');
ast_test!(parse_byte_x_4, parse_byte_x, r#"\x000023"#, '\u{23}');
ast_test!(parse_byte_x_5, parse_byte_x, r#"\xFf"#, '\u{FF}');
ast_test!(parse_string_short_literal_1, parse_string_short_literal, r#""""#, "");
ast_test!(parse_string_short_literal_2, parse_string_short_literal, r#"''"#, "");
ast_test!(parse_string_short_literal_3, parse_string_short_literal, r#"'\u{1F62A}'"#, "😪");
ast_test!(parse_string_short_literal_4, parse_string_short_literal, r#"'\097'"#, "a");
ast_test!(parse_string_short_literal_5, parse_string_short_literal, format!("'{}'", "\x07\x08\x09\x0A\x0B\x0C\x0D"), "\x07\x08\x09\x0A\x0B\x0C\x0D");
ast_test!(parse_string_short_literal_6, parse_string_short_literal, "'\\\n\r'", "\n");
ast_test!(parse_string_short_literal_7, parse_string_short_literal, "'\\\r\n'", "\n");
ast_test!(parse_string_short_literal_8, parse_string_short_literal, "'\\\n'", "\n");
}