DEV: Fix escaping in both strings and format strings.

This commit is contained in:
Jeremy Wall 2019-08-12 18:10:48 -05:00
parent dffa535f1b
commit 949d468f55
7 changed files with 81 additions and 5 deletions

View File

@ -320,6 +320,12 @@ let tpl = {
"foo.bar.1 == @{item.foo.bar.1}" % tpl;
```
The `@` symbol can be escaped with a double slash.
```
"https://username:password\\@@:@/" % (host, port)
```
If the `%` operator is followed by a parenthesized expression it will be treated
as the first form with one item.

View File

@ -47,6 +47,12 @@ Strings are any double quoted text. You can use the `\` to esacpe characters in
"This is an escaped \"string\"";
```
*Character Escapes*
* '\n' is a new line
* '\r' is a carriage return
* '\t' is a tab
### NULL or the Empty type
NULL is the empty type. It represents the absence of a value. It is represented by the

View File

@ -48,4 +48,14 @@ assert t.equal{
assert t.equal{
left = "@{item.op()} is just great" % {op=func() => "BOB!"},
right = "BOB! is just great",
};
assert t.equal{
left = "\\@@" % (NULL),
right = "@NULL",
};
assert t.equal{
left = "\\@{foo}@{item.op()} is just great" % {op=func() => "BOB!"},
right = "@{foo}BOB! is just great",
};

View File

@ -0,0 +1,17 @@
let t = import "std/testing.ucg";
assert t.equal{
left = "foo\n",
right = "foo
",
};
assert t.equal{
left = "foo\t",
right = "foo ",
};
assert t.not_equal{
left = "foo\\t",
right = "foo\\" + "\t",
};

View File

@ -67,16 +67,23 @@ impl<V: Into<String> + Clone> FormatRenderer for SimpleFormatter<V> {
let strval = arg.into();
buf.push_str(&strval);
count += 1;
should_escape = false;
} else if c == '\\' && !should_escape {
eprintln!("found an escape char {}", self.tmpl);
should_escape = true;
} else {
buf.push(c);
should_escape = false;
}
}
if self.args.len() != count {
return Err(error::BuildError::with_pos(
"Too many arguments to string \
formatter.",
format!(
"Too many arguments to string \
formatter. Expected {} got {}",
count,
self.args.len()
),
error::ErrorType::FormatError,
pos.clone(),
)
@ -201,10 +208,12 @@ where
let val = self.consume_expr(&mut self.builder.borrow_mut(), &mut iter, pos)?;
let strval: String = val.into();
buf.push_str(&strval);
should_escape = false;
} else if c == '\\' && !should_escape {
should_escape = true;
} else {
buf.push(c);
should_escape = false;
}
}
return Ok(buf);

View File

@ -71,7 +71,11 @@ impl YamlConverter {
Ok(yaml_val)
}
fn merge_mapping_keys(&self, mut fs: &mut Vec<(String, Rc<Val>)>, m: &serde_yaml::Mapping) -> Result<(), Box<dyn Error>> {
fn merge_mapping_keys(
&self,
mut fs: &mut Vec<(String, Rc<Val>)>,
m: &serde_yaml::Mapping,
) -> Result<(), Box<dyn Error>> {
for (key, value) in m {
// This is a little gross but since yaml allows maps to be keyed
// by more than just a string it's necessary.
@ -90,7 +94,6 @@ impl YamlConverter {
if let serde_yaml::Value::Mapping(merge_map) = value {
self.merge_mapping_keys(&mut fs, merge_map)?;
}
} else {
fs.push((key, Rc::new(self.convert_yaml_val(&value)?)));
}

View File

@ -49,7 +49,7 @@ fn is_symbol_char<'a>(i: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, u8> {
fn escapequoted<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, String> {
// loop until we find a " that is not preceded by \.
// Collapse all \<char> to just char for escaping.
// Collapse all \<char> to just char for escaping exept for \n \r \t and \@.
let mut frag = String::new();
let mut escape = false;
let mut _input = input.clone();
@ -58,6 +58,31 @@ fn escapequoted<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Strin
Some(c) => *c,
None => break,
};
if escape {
match c as char {
'n' => {
eprintln!("Pushing new line onto string");
frag.push('\n');
escape = false;
continue;
}
'r' => {
eprintln!("Pushing carriage return onto string");
frag.push('\r');
escape = false;
continue;
}
't' => {
eprintln!("Pushing tab onto string");
frag.push('\t');
escape = false;
continue;
}
_ => {
//noop
}
}
}
if c == '\\' as u8 && !escape {
// eat this slash and set our escaping sentinel
escape = true;