use crate::ast::*;
use crate::parser::combinators::*;
use crate::parser::error::*;
use crate::parser::primitives::*;
use crate::parser::reader::Reader;
use crate::parser::{template, ParseResult};
pub fn unquoted_template(reader: &mut Reader) -> ParseResult<Template> {
let start = reader.state;
let mut chars = vec![];
let mut spaces = vec![];
let mut end = start;
loop {
let pos = reader.state.pos;
match any_char(vec!['#'], reader) {
Err(e) => {
if e.recoverable {
break;
} else {
return Err(e);
}
}
Ok((c, s)) => {
if s == "\n" {
break;
}
if s == " " {
spaces.push((c, s, pos));
} else {
if !spaces.is_empty() {
chars.append(&mut spaces);
spaces = vec![];
}
chars.push((c, s, pos));
end = reader.state;
}
}
}
}
reader.state = end;
let encoded_string = template::EncodedString {
source_info: SourceInfo {
start: start.pos,
end: end.pos,
},
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
delimiter: None,
elements,
source_info: SourceInfo {
start: start.pos,
end: end.pos,
},
})
}
pub fn quoted_oneline_string(reader: &mut Reader) -> ParseResult<String> {
literal("\"", reader)?;
let s = reader.read_while(|c| *c != '"' && *c != '\n');
literal("\"", reader)?;
Ok(s)
}
pub fn quoted_template(reader: &mut Reader) -> ParseResult<Template> {
let start = reader.state.pos;
let mut end = start;
try_literal("\"", reader)?;
let mut chars = vec![];
loop {
let pos = reader.state.pos;
let save = reader.state;
match any_char(vec!['"'], reader) {
Err(e) => {
if e.recoverable {
reader.state = save;
break;
} else {
return Err(e);
}
}
Ok((c, s)) => {
chars.push((c, s, pos));
end = reader.state.pos;
}
}
}
literal("\"", reader)?;
let encoded_string = template::EncodedString {
source_info: SourceInfo { start, end },
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
delimiter: Some('"'),
elements,
source_info: SourceInfo {
start,
end: reader.state.pos,
},
})
}
pub fn backtick_template(reader: &mut Reader) -> ParseResult<Template> {
let delimiter = Some('`');
let start = reader.state.pos;
let mut end = start;
try_literal("`", reader)?;
let mut chars = vec![];
loop {
let pos = reader.state.pos;
let save = reader.state;
match any_char(vec!['`', '\n'], reader) {
Err(e) => {
if e.recoverable {
reader.state = save;
break;
} else {
return Err(e);
}
}
Ok((c, s)) => {
chars.push((c, s, pos));
end = reader.state.pos;
}
}
}
literal("`", reader)?;
let encoded_string = template::EncodedString {
source_info: SourceInfo { start, end },
chars,
};
let elements = template::templatize(encoded_string)?;
Ok(Template {
delimiter,
elements,
source_info: SourceInfo {
start,
end: reader.state.pos,
},
})
}
fn any_char(except: Vec<char>, reader: &mut Reader) -> ParseResult<(char, String)> {
let start = reader.state;
match escape_char(reader) {
Ok(c) => Ok((c, reader.peek_back(start.cursor))),
Err(e) => {
if e.recoverable {
reader.state = start;
match reader.read() {
None => {
let inner = ParseError::Expecting {
value: "char".to_string(),
};
Err(Error::new(start.pos, true, inner))
}
Some(c) => {
if except.contains(&c)
|| ['\\', '\x08', '\n', '\x0c', '\r', '\t'].contains(&c)
{
let inner = ParseError::Expecting {
value: "char".to_string(),
};
Err(Error::new(start.pos, true, inner))
} else {
Ok((c, reader.peek_back(start.cursor)))
}
}
}
} else {
Err(e)
}
}
}
}
fn escape_char(reader: &mut Reader) -> ParseResult<char> {
try_literal("\\", reader)?;
let start = reader.state;
match reader.read() {
Some('#') => Ok('#'),
Some('"') => Ok('"'),
Some('`') => Ok('`'),
Some('\\') => Ok('\\'),
Some('/') => Ok('/'),
Some('b') => Ok('\x08'),
Some('n') => Ok('\n'),
Some('f') => Ok('\x0c'),
Some('r') => Ok('\r'),
Some('t') => Ok('\t'),
Some('u') => unicode(reader),
_ => Err(Error::new(start.pos, false, ParseError::EscapeChar)),
}
}
pub(crate) fn unicode(reader: &mut Reader) -> ParseResult<char> {
literal("{", reader)?;
let v = hex_value(reader)?;
let c = match std::char::from_u32(v) {
None => return Err(Error::new(reader.state.pos, false, ParseError::Unicode)),
Some(c) => c,
};
literal("}", reader)?;
Ok(c)
}
fn hex_value(reader: &mut Reader) -> ParseResult<u32> {
let mut digits = one_or_more(hex_digit, reader)?;
let mut v = 0;
let mut weight = 1;
digits.reverse();
for d in digits.iter() {
v += weight * d;
weight *= 16;
}
Ok(v)
}
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use super::*;
#[test]
fn test_unquoted_template_empty() {
let mut reader = Reader::new("");
assert_eq!(
unquoted_template(&mut reader).unwrap(),
Template {
delimiter: None,
elements: vec![],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
}
);
assert_eq!(reader.state.cursor, 0);
}
#[test]
fn test_unquoted_template_with_hash() {
let mut reader = Reader::new("a#");
assert_eq!(
unquoted_template(&mut reader).unwrap(),
Template {
delimiter: None,
elements: vec![TemplateElement::String {
value: "a".to_string(),
encoded: "a".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
}
);
assert_eq!(reader.state.cursor, 1);
}
#[test]
fn test_unquoted_template_with_encoded_hash() {
let mut reader = Reader::new("a\\u{23}");
assert_eq!(
unquoted_template(&mut reader).unwrap(),
Template {
delimiter: None,
elements: vec![TemplateElement::String {
value: "a#".to_string(),
encoded: "a\\u{23}".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
}
);
assert_eq!(reader.state.cursor, 7);
}
#[test]
fn test_unquoted_template_with_quote() {
let mut reader = Reader::new("\"hi\"");
assert_eq!(
unquoted_template(&mut reader).unwrap(),
Template {
delimiter: None,
elements: vec![TemplateElement::String {
value: "\"hi\"".to_string(),
encoded: "\"hi\"".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
}
);
assert_eq!(reader.state.cursor, 4);
}
#[test]
fn test_unquoted_template_hello_world() {
let mut reader = Reader::new("hello\\u{20}{{name}}!");
assert_eq!(
unquoted_template(&mut reader).unwrap(),
Template {
delimiter: None,
elements: vec![
TemplateElement::String {
value: "hello ".to_string(),
encoded: "hello\\u{20}".to_string(),
},
TemplateElement::Expression(Expr {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 14)),
},
variable: Variable {
name: "name".to_string(),
source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 18)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 18)),
},
}),
TemplateElement::String {
value: "!".to_string(),
encoded: "!".to_string(),
},
],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 21)),
}
);
assert_eq!(reader.state.cursor, 20);
}
#[test]
fn test_quoted_template() {
let mut reader = Reader::new("\"\"");
assert_eq!(
quoted_template(&mut reader).unwrap(),
Template {
delimiter: Some('"'),
elements: vec![],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
}
);
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("\"a#\"");
assert_eq!(
quoted_template(&mut reader).unwrap(),
Template {
delimiter: Some('"'),
elements: vec![TemplateElement::String {
value: "a#".to_string(),
encoded: "a#".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
}
);
assert_eq!(reader.state.cursor, 4);
let mut reader = Reader::new("\"{0}\"");
assert_eq!(
quoted_template(&mut reader).unwrap(),
Template {
delimiter: Some('"'),
elements: vec![TemplateElement::String {
value: "{0}".to_string(),
encoded: "{0}".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6)),
}
);
assert_eq!(reader.state.cursor, 5);
}
#[test]
fn test_quoted_template_with_quote() {
let mut reader = Reader::new("\"\\\"hi\\\"\"");
assert_eq!(
quoted_template(&mut reader).unwrap(),
Template {
delimiter: Some('"'),
elements: vec![TemplateElement::String {
value: "\"hi\"".to_string(),
encoded: "\\\"hi\\\"".to_string()
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
}
);
assert_eq!(reader.state.cursor, 8);
}
#[test]
fn test_quoted_template_error_missing_closing_quote() {
let mut reader = Reader::new("\"not found");
let error = quoted_template(&mut reader).err().unwrap();
assert_eq!(
error.pos,
Pos {
line: 1,
column: 11
}
);
assert!(!error.recoverable);
}
#[test]
fn test_quoted_string() {
let mut reader = Reader::new("\"\"");
assert_eq!(quoted_oneline_string(&mut reader).unwrap(), "");
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("\"Hello\"");
assert_eq!(quoted_oneline_string(&mut reader).unwrap(), "Hello");
assert_eq!(reader.state.cursor, 7);
}
#[test]
fn test_backtick_template() {
let mut reader = Reader::new("``");
assert_eq!(
backtick_template(&mut reader).unwrap(),
Template {
delimiter: Some('`'),
elements: vec![],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
}
);
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("`foo#`");
assert_eq!(
backtick_template(&mut reader).unwrap(),
Template {
delimiter: Some('`'),
elements: vec![TemplateElement::String {
value: "foo#".to_string(),
encoded: "foo#".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 7)),
}
);
assert_eq!(reader.state.cursor, 6);
let mut reader = Reader::new("`{0}`");
assert_eq!(
backtick_template(&mut reader).unwrap(),
Template {
delimiter: Some('`'),
elements: vec![TemplateElement::String {
value: "{0}".to_string(),
encoded: "{0}".to_string(),
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6)),
}
);
assert_eq!(reader.state.cursor, 5);
}
#[test]
fn test_backtick_template_with_backtick() {
let mut reader = Reader::new("`\\`hi\\``");
assert_eq!(
backtick_template(&mut reader).unwrap(),
Template {
delimiter: Some('`'),
elements: vec![TemplateElement::String {
value: "`hi`".to_string(),
encoded: "\\`hi\\`".to_string()
}],
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
}
);
assert_eq!(reader.state.cursor, 8);
}
#[test]
fn test_backtick_template_error_missing_closing_backtick() {
let mut reader = Reader::new("`not found");
let error = backtick_template(&mut reader).err().unwrap();
assert_eq!(
error.pos,
Pos {
line: 1,
column: 11
}
);
assert!(!error.recoverable);
}
#[test]
fn test_any_char() {
let mut reader = Reader::new("a");
assert_eq!(
any_char(vec![], &mut reader).unwrap(),
('a', "a".to_string())
);
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new(" ");
assert_eq!(
any_char(vec![], &mut reader).unwrap(),
(' ', " ".to_string())
);
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new("\\t");
assert_eq!(
any_char(vec![], &mut reader).unwrap(),
('\t', "\\t".to_string())
);
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("#");
assert_eq!(
any_char(vec![], &mut reader).unwrap(),
('#', "#".to_string())
);
assert_eq!(reader.state.cursor, 1);
}
#[test]
fn test_any_char_quote() {
let mut reader = Reader::new("\\\"");
assert_eq!(
any_char(vec![], &mut reader).unwrap(),
('"', "\\\"".to_string())
);
assert_eq!(reader.state.cursor, 2);
}
#[test]
fn test_any_char_error() {
let mut reader = Reader::new("");
let error = any_char(vec![], &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("#");
let error = any_char(vec!['#'], &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("\t");
let error = any_char(vec![], &mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
}
#[test]
fn test_escape_char() {
let mut reader = Reader::new("\\n");
assert_eq!(escape_char(&mut reader).unwrap(), '\n');
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("\\u{0a}");
assert_eq!(escape_char(&mut reader).unwrap(), '\n');
assert_eq!(reader.state.cursor, 6);
let mut reader = Reader::new("x");
let error = escape_char(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: "\\".to_string()
}
);
assert!(error.recoverable);
assert_eq!(reader.state.cursor, 0);
}
#[test]
fn test_unicode() {
let mut reader = Reader::new("{000a}");
assert_eq!(unicode(&mut reader).unwrap(), '\n');
assert_eq!(reader.state.cursor, 6);
let mut reader = Reader::new("{E9}");
assert_eq!(unicode(&mut reader).unwrap(), 'é');
assert_eq!(reader.state.cursor, 4);
}
#[test]
fn test_hex_value() {
let mut reader = Reader::new("20x");
assert_eq!(hex_value(&mut reader).unwrap(), 32);
let mut reader = Reader::new("x");
let error = hex_value(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(error.inner, ParseError::HexDigit);
assert!(!error.recoverable);
}
#[test]
fn test_quoted_template_benchmark() {
let mut reader = Reader::new(
format!(
"\"Hello World!\"{}",
(0..10_000_000).map(|_| "X").collect::<String>()
)
.as_str(),
);
let now = SystemTime::now();
assert!(quoted_template(&mut reader).is_ok());
assert_eq!(reader.state.cursor, 14);
eprintln!("duration= {}", now.elapsed().unwrap().as_nanos());
}
}