unescape macro string contents
This commit is contained in:
parent
03973bbac1
commit
5154531083
|
@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Macro strings
|
||||
- Function parameters/arguments
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -299,17 +299,7 @@ impl MacroStringLiteral {
|
|||
for part in &self.parts {
|
||||
match part {
|
||||
MacroStringLiteralPart::Text(span) => {
|
||||
let string = span.str();
|
||||
if string.contains('\\') {
|
||||
content += &string
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\r", "\r")
|
||||
.replace("\\t", "\t")
|
||||
.replace("\\\"", "\"")
|
||||
.replace("\\\\", "\\");
|
||||
} else {
|
||||
content += string;
|
||||
}
|
||||
content += &crate::util::unescape_macro_string(span.str());
|
||||
}
|
||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||
write!(
|
||||
|
|
|
@ -19,6 +19,7 @@ pub mod lexical;
|
|||
pub mod semantic;
|
||||
pub mod syntax;
|
||||
pub mod transpile;
|
||||
pub mod util;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
|
|
|
@ -66,13 +66,12 @@ impl From<&MacroStringLiteral> for MacroString {
|
|||
.parts()
|
||||
.iter()
|
||||
.map(|part| match part {
|
||||
MacroStringLiteralPart::Text(span) => {
|
||||
MacroStringPart::String(span.str().to_string())
|
||||
}
|
||||
MacroStringLiteralPart::Text(span) => MacroStringPart::String(
|
||||
crate::util::unescape_macro_string(span.str()).to_string(),
|
||||
),
|
||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||
MacroStringPart::MacroUsage(
|
||||
crate::transpile::util::identifier_to_macro(identifier.span.str())
|
||||
.to_string(),
|
||||
super::util::identifier_to_macro(identifier.span.str()).to_string(),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -548,7 +548,7 @@ impl Transpiler {
|
|||
format!(
|
||||
r#"{macro_name}:"{escaped}""#,
|
||||
macro_name = super::util::identifier_to_macro(ident),
|
||||
escaped = super::util::escape_str(v)
|
||||
escaped = crate::util::escape_str(v)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
|
|
@ -40,30 +40,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Escapes `"` and `\` in a string.
|
||||
#[must_use]
|
||||
pub fn escape_str(s: &str) -> Cow<str> {
|
||||
if s.contains('"') || s.contains('\\') {
|
||||
let mut escaped = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'"' => escaped.push_str("\\\""),
|
||||
'\\' => escaped.push_str("\\\\"),
|
||||
_ => escaped.push(c),
|
||||
}
|
||||
}
|
||||
Cow::Owned(escaped)
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms an identifier to a macro name that only contains `a-zA-Z0-9_`.
|
||||
#[must_use]
|
||||
pub fn identifier_to_macro(ident: &str) -> Cow<str> {
|
||||
if ident
|
||||
.chars()
|
||||
.any(|c| !(c == '_' && c.is_ascii_alphanumeric()))
|
||||
if ident.contains("__")
|
||||
|| ident
|
||||
.chars()
|
||||
.any(|c| !(c == '_' && c.is_ascii_alphanumeric()))
|
||||
{
|
||||
let new_ident = ident
|
||||
.chars()
|
||||
|
@ -72,20 +55,8 @@ pub fn identifier_to_macro(ident: &str) -> Cow<str> {
|
|||
|
||||
let chksum = md5::hash(ident).to_hex_lowercase();
|
||||
|
||||
Cow::Owned(new_ident + "_" + &chksum[..8])
|
||||
Cow::Owned(new_ident + "__" + &chksum[..8])
|
||||
} else {
|
||||
Cow::Borrowed(ident)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_escape_str() {
|
||||
assert_eq!(escape_str("Hello, world!"), "Hello, world!");
|
||||
assert_eq!(escape_str(r#"Hello, "world"!"#), r#"Hello, \"world\"!"#);
|
||||
assert_eq!(escape_str(r"Hello, \world\!"), r"Hello, \\world\\!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
//! Utility functions for the `Shulkerscript` language.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Escapes `"` and `\` in a string.
|
||||
#[must_use]
|
||||
pub fn escape_str(s: &str) -> Cow<str> {
|
||||
if s.contains('"') || s.contains('\\') {
|
||||
let mut escaped = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'"' => escaped.push_str("\\\""),
|
||||
'\\' => escaped.push_str("\\\\"),
|
||||
_ => escaped.push(c),
|
||||
}
|
||||
}
|
||||
Cow::Owned(escaped)
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unescapes '\`', `\`, `\n`, `\r` and `\t` in a string.
|
||||
#[must_use]
|
||||
pub fn unescape_macro_string(s: &str) -> Cow<str> {
|
||||
if s.contains('\\') || s.contains('`') {
|
||||
Cow::Owned(
|
||||
s.replace("\\n", "\n")
|
||||
.replace("\\r", "\r")
|
||||
.replace("\\t", "\t")
|
||||
.replace("\\`", "`")
|
||||
.replace("\\\\", "\\"),
|
||||
)
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_escape_str() {
|
||||
assert_eq!(escape_str("Hello, world!"), "Hello, world!");
|
||||
assert_eq!(escape_str(r#"Hello, "world"!"#), r#"Hello, \"world\"!"#);
|
||||
assert_eq!(escape_str(r"Hello, \world\!"), r"Hello, \\world\\!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unescape_macro_string() {
|
||||
assert_eq!(unescape_macro_string("Hello, world!"), "Hello, world!");
|
||||
assert_eq!(
|
||||
unescape_macro_string(r#"Hello, "world"!"#),
|
||||
r#"Hello, "world"!"#
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \world\!"),
|
||||
r"Hello, \world\!"
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \nworld\!"),
|
||||
"Hello, \nworld\\!"
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \rworld\!"),
|
||||
"Hello, \rworld\\!"
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \tworld\!"),
|
||||
"Hello, \tworld\\!"
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \`world\!"),
|
||||
r"Hello, `world\!"
|
||||
);
|
||||
assert_eq!(
|
||||
unescape_macro_string(r"Hello, \\world\!"),
|
||||
r"Hello, \world\!"
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue