Add escaping using a backslash for string tokens.

This commit is contained in:
Jeremy Wall 2018-02-05 19:35:17 -06:00
parent 6b27ff1d25
commit 1e063fd129
3 changed files with 47 additions and 7 deletions

View File

@ -17,5 +17,4 @@ organiztion for a given configuration structure. Some options here could be
# Minor Fixes and Polish # Minor Fixes and Polish
* JSON export
* YAML export * YAML export

View File

@ -67,7 +67,8 @@
//! .1 // the leading 0 is also optional. //! .1 // the leading 0 is also optional.
//! ``` //! ```
//! //!
//! * String is any quoted text. //! * String is any quoted text. backslashes within a string escape the next preceding
//! character.
//! //!
//! ``` ucg //! ``` ucg
//! "foo"; // a smiple string //! "foo"; // a smiple string

View File

@ -14,7 +14,7 @@
use nom_locate::LocatedSpan; use nom_locate::LocatedSpan;
use nom; use nom;
use nom::{alpha, is_alphanumeric, digit, multispace}; use nom::{alpha, is_alphanumeric, digit, multispace};
use nom::{InputLength, Slice}; use nom::{InputLength, InputIter, Slice};
use ast::*; use ast::*;
use std; use std;
use std::result::Result; use std::result::Result;
@ -34,16 +34,37 @@ fn is_symbol_char(c: char) -> bool {
is_alphanumeric(c as u8) || c == '-' as char || c == '_' as char is_alphanumeric(c as u8) || c == '-' as char || c == '_' as char
} }
fn escapequoted(input: Span) -> nom::IResult<Span, String> {
// loop until we find a " that is not preceded by \.
// Collapse all \<char> to just char for escaping.
let mut frag = String::new();
let mut escape = false;
for (i, c) in input.iter_indices() {
if c == '\\' && ! escape { // eat this slash and set our escaping sentinel
escape = true;
} else if c == '"' && !escape { // Bail if this is an unescaped "
// we exit here.
return nom::IResult::Done(input.slice(i..), frag);
} else {
// we accumulate this character.
frag.push(c);
escape = false; // reset our escaping sentinel
}
}
return nom::IResult::Incomplete(nom::Needed::Unknown);
}
// TODO(jwall): Handle escapes
named!(strtok( Span ) -> Token, named!(strtok( Span ) -> Token,
do_parse!( do_parse!(
span: position!() >> span: position!() >>
tag!("\"") >> tag!("\"") >>
frag: take_until!("\"") >> frag: escapequoted >>
tag!("\"") >> tag!("\"") >>
(Token{ (Token{
typ: TokenType::QUOTED, typ: TokenType::QUOTED,
pos: Position::from(span), pos: Position::from(span),
fragment: frag.fragment.to_string(), fragment: frag,
}) })
) )
); );
@ -476,7 +497,7 @@ impl<'a> std::ops::Index<usize> for TokenIter<'a> {
} }
} }
impl<'a> nom::InputIter for TokenIter<'a> { impl<'a> InputIter for TokenIter<'a> {
type Item = &'a Token; type Item = &'a Token;
type RawItem = Token; type RawItem = Token;
@ -523,6 +544,25 @@ mod tokenizer_test {
use nom; use nom;
use nom_locate::LocatedSpan; use nom_locate::LocatedSpan;
#[test]
fn test_escape_quoted() {
let result = escapequoted(LocatedSpan::new("foo \\\"bar\""));
assert!(result.is_done(), format!("result {:?} is not ok", result));
if let nom::IResult::Done(rest, frag) = result {
assert_eq!(frag, "foo \"bar");
assert_eq!(rest.fragment, "\"");
}
}
#[test]
fn test_string_with_escaping() {
let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\""));
assert!(result.is_done(), format!("result {:?} is not ok", result));
if let nom::IResult::Done(_, tok) = result {
assert_eq!(tok.fragment, "foo \\ \"bar".to_string());
}
}
#[test] #[test]
fn test_tokenize_one_of_each() { fn test_tokenize_one_of_each() {
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 // 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
@ -574,7 +614,7 @@ mod tokenizer_test {
fragment: " comment".to_string(), fragment: " comment".to_string(),
pos: Position{column: 1, line: 1}, pos: Position{column: 1, line: 1},
})); }));
// TODO(jwall): assert!(comment(LocatedSpan::new("// comment")).is_done()); assert!(comment(LocatedSpan::new("// comment")).is_done());
} }
#[test] #[test]