mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-21 18:10:42 -04:00
DEV: Fix escaping in both strings and format strings.
This commit is contained in:
parent
dffa535f1b
commit
949d468f55
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
};
|
17
integration_tests/simple_values_test.ucg
Normal file
17
integration_tests/simple_values_test.ucg
Normal 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",
|
||||
};
|
@ -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);
|
||||
|
@ -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)?)));
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user