unescape macro string contents

This commit is contained in:
Moritz Hölting 2024-11-15 10:33:55 +01:00
parent 03973bbac1
commit 5154531083
7 changed files with 96 additions and 51 deletions

View File

@ -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

View File

@ -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!(

View File

@ -19,6 +19,7 @@ pub mod lexical;
pub mod semantic;
pub mod syntax;
pub mod transpile;
pub mod util;
use std::path::Path;

View File

@ -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(),
)
}
})

View File

@ -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<_>>()

View File

@ -40,28 +40,11 @@ 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
if ident.contains("__")
|| ident
.chars()
.any(|c| !(c == '_' && c.is_ascii_alphanumeric()))
{
@ -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\\!");
}
}

82
src/util.rs Normal file
View File

@ -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\!"
);
}
}