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]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Macro strings
|
||||||
|
- Function parameters/arguments
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -299,17 +299,7 @@ impl MacroStringLiteral {
|
||||||
for part in &self.parts {
|
for part in &self.parts {
|
||||||
match part {
|
match part {
|
||||||
MacroStringLiteralPart::Text(span) => {
|
MacroStringLiteralPart::Text(span) => {
|
||||||
let string = span.str();
|
content += &crate::util::unescape_macro_string(span.str());
|
||||||
if string.contains('\\') {
|
|
||||||
content += &string
|
|
||||||
.replace("\\n", "\n")
|
|
||||||
.replace("\\r", "\r")
|
|
||||||
.replace("\\t", "\t")
|
|
||||||
.replace("\\\"", "\"")
|
|
||||||
.replace("\\\\", "\\");
|
|
||||||
} else {
|
|
||||||
content += string;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||||
write!(
|
write!(
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub mod lexical;
|
||||||
pub mod semantic;
|
pub mod semantic;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
pub mod transpile;
|
pub mod transpile;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
|
@ -66,13 +66,12 @@ impl From<&MacroStringLiteral> for MacroString {
|
||||||
.parts()
|
.parts()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|part| match part {
|
.map(|part| match part {
|
||||||
MacroStringLiteralPart::Text(span) => {
|
MacroStringLiteralPart::Text(span) => MacroStringPart::String(
|
||||||
MacroStringPart::String(span.str().to_string())
|
crate::util::unescape_macro_string(span.str()).to_string(),
|
||||||
}
|
),
|
||||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||||
MacroStringPart::MacroUsage(
|
MacroStringPart::MacroUsage(
|
||||||
crate::transpile::util::identifier_to_macro(identifier.span.str())
|
super::util::identifier_to_macro(identifier.span.str()).to_string(),
|
||||||
.to_string(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -548,7 +548,7 @@ impl Transpiler {
|
||||||
format!(
|
format!(
|
||||||
r#"{macro_name}:"{escaped}""#,
|
r#"{macro_name}:"{escaped}""#,
|
||||||
macro_name = super::util::identifier_to_macro(ident),
|
macro_name = super::util::identifier_to_macro(ident),
|
||||||
escaped = super::util::escape_str(v)
|
escaped = crate::util::escape_str(v)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|
|
@ -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_`.
|
/// Transforms an identifier to a macro name that only contains `a-zA-Z0-9_`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn identifier_to_macro(ident: &str) -> Cow<str> {
|
pub fn identifier_to_macro(ident: &str) -> Cow<str> {
|
||||||
if ident
|
if ident.contains("__")
|
||||||
|
|| ident
|
||||||
.chars()
|
.chars()
|
||||||
.any(|c| !(c == '_' && c.is_ascii_alphanumeric()))
|
.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();
|
let chksum = md5::hash(ident).to_hex_lowercase();
|
||||||
|
|
||||||
Cow::Owned(new_ident + "_" + &chksum[..8])
|
Cow::Owned(new_ident + "__" + &chksum[..8])
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed(ident)
|
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