Unquote strings and braces to simplify command-Lua interop.

This commit is contained in:
Condorra 2024-09-21 22:11:05 +10:00
parent ba49876f80
commit 855ecfc13a
2 changed files with 107 additions and 45 deletions

View File

@ -64,7 +64,7 @@ impl LuaState {
pub fn execute_command(
&mut self,
command: &str,
arguments: &ParsedCommand<'_>,
arguments: &ParsedCommand,
) -> Result<(), String> {
self.interp
.try_enter(|ctx| {

View File

@ -10,8 +10,8 @@ use nom::{
};
#[derive(PartialEq, Eq, Debug)]
pub struct ParseResult<'l> {
pub commands: Vec<ParsedCommand<'l>>,
pub struct ParseResult {
pub commands: Vec<ParsedCommand>,
}
#[derive(PartialEq, Eq, Debug, Clone)]
@ -21,12 +21,13 @@ pub enum ArgumentGuard {
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ParsedArgument<'l> {
pub struct ParsedArgument {
pub guard: Option<ArgumentGuard>,
pub text: &'l str,
pub text: String,
pub quoted_text: String,
}
impl<'l> ParsedArgument<'l> {
impl ParsedArgument {
pub fn forget_guard(&self) -> Self {
Self {
guard: None,
@ -35,7 +36,7 @@ impl<'l> ParsedArgument<'l> {
}
}
impl<'a> Display for ParsedArgument<'a> {
impl Display for ParsedArgument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.guard {
None => {}
@ -46,7 +47,7 @@ impl<'a> Display for ParsedArgument<'a> {
f.write_str("\"")?;
}
}
f.write_str(self.text)?;
f.write_str(&self.quoted_text)?;
match self.guard {
None => {}
Some(ArgumentGuard::Paren) => {
@ -61,11 +62,11 @@ impl<'a> Display for ParsedArgument<'a> {
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct ParsedCommand<'l> {
pub arguments: VecDeque<ParsedArgument<'l>>,
pub struct ParsedCommand {
pub arguments: VecDeque<ParsedArgument>,
}
impl<'l> ParsedCommand<'l> {
impl ParsedCommand {
pub fn forget_guards(&self) -> Self {
Self {
arguments: self.arguments.iter().map(|v| v.forget_guard()).collect(),
@ -74,7 +75,7 @@ impl<'l> ParsedCommand<'l> {
}
}
impl<'a> Display for ParsedCommand<'a> {
impl Display for ParsedCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut it = self.arguments.iter();
match it.next() {
@ -92,8 +93,8 @@ impl<'a> Display for ParsedCommand<'a> {
}
}
impl ParsedCommand<'_> {
pub fn split_out_command(&self) -> Option<(&str, Self)> {
impl ParsedCommand {
pub fn split_out_command(&self) -> Option<(String, Self)> {
let mut tmp_arguments = self.arguments.clone();
loop {
match tmp_arguments.pop_front() {
@ -102,7 +103,7 @@ impl ParsedCommand<'_> {
let trimmed_cmd = head.text.trim();
if !trimmed_cmd.is_empty() {
return Some((
trimmed_cmd,
trimmed_cmd.to_owned(),
Self {
arguments: tmp_arguments,
},
@ -114,6 +115,31 @@ impl ParsedCommand<'_> {
}
}
fn unquote_string(input: &str) -> String {
let mut buf: String = String::new();
let mut iter = input.chars();
loop {
match iter.next() {
None => return buf,
Some('\\') => match iter.next() {
None => {
buf.push('\\');
return buf;
}
Some('\\') => buf.push('\\'),
Some('{') => buf.push('{'),
Some('}') => buf.push('}'),
Some('"') => buf.push('"'),
Some(c) => {
buf.push('\\');
buf.push(c);
}
},
Some(c) => buf.push(c),
}
}
}
fn parse_string(input: &str) -> IResult<&str, ()> {
value(
(),
@ -155,14 +181,16 @@ fn parse_argument(input: &str) -> IResult<&str, ParsedArgument> {
map(
delimited(char('{'), recognize(parse_parenthetical), char('}')),
|v| ParsedArgument {
text: v,
text: unquote_string(v),
quoted_text: v.to_owned(),
guard: Some(ArgumentGuard::Paren),
},
),
map(
delimited(char('"'), recognize(parse_string), char('"')),
|v| ParsedArgument {
text: v,
text: unquote_string(v),
quoted_text: v.to_owned(),
guard: Some(ArgumentGuard::DoubleQuote),
},
),
@ -191,7 +219,8 @@ fn parse_argument(input: &str) -> IResult<&str, ParsedArgument> {
))),
)),
|v| ParsedArgument {
text: v,
text: unquote_string(v),
quoted_text: v.to_owned(),
guard: None,
},
),
@ -223,7 +252,8 @@ mod tests {
ParseResult {
commands: vec![ParsedCommand {
arguments: vec![ParsedArgument {
text: "",
text: "".to_owned(),
quoted_text: "".to_owned(),
guard: None
}]
.into()
@ -235,7 +265,8 @@ mod tests {
ParseResult {
commands: vec![ParsedCommand {
arguments: vec![ParsedArgument {
text: "north",
text: "north".to_owned(),
quoted_text: "north".to_owned(),
guard: None
}]
.into()
@ -249,11 +280,13 @@ mod tests {
// This is deliberate, ensures we can reconstruct the input.
arguments: vec![
ParsedArgument {
text: "north",
text: "north".to_owned(),
quoted_text: "north".to_owned(),
guard: None
},
ParsedArgument {
text: "",
text: "".to_owned(),
quoted_text: "".to_owned(),
guard: None
}
]
@ -268,11 +301,13 @@ mod tests {
ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "x = 1 + 2; y = 3",
text: "x = 1 + 2; y = 3".to_owned(),
quoted_text: "x = 1 + 2; y = 3".to_owned(),
guard: Some(ArgumentGuard::Paren)
}
]
@ -280,7 +315,8 @@ mod tests {
},
ParsedCommand {
arguments: vec![ParsedArgument {
text: "#home",
text: "#home".to_owned(),
quoted_text: "#home".to_owned(),
guard: None
}]
.into()
@ -294,11 +330,13 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "{x = 1 + 2",
text: "{x = 1 + 2".to_owned(),
quoted_text: "{x = 1 + 2".to_owned(),
guard: None
}
]
@ -312,15 +350,18 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "x = 1",
text: "x = 1".to_owned(),
quoted_text: "x = 1".to_owned(),
guard: Some(ArgumentGuard::Paren)
},
ParsedArgument {
text: "y = 1",
text: "y = 1".to_owned(),
quoted_text: "y = 1".to_owned(),
guard: Some(ArgumentGuard::Paren)
}
]
@ -334,15 +375,18 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "hello",
text: "hello".to_owned(),
quoted_text: "hello".to_owned(),
guard: Some(ArgumentGuard::DoubleQuote)
},
ParsedArgument {
text: "world",
text: "world".to_owned(),
quoted_text: "world".to_owned(),
guard: Some(ArgumentGuard::DoubleQuote)
},
]
@ -356,15 +400,18 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "x = \"}\"",
text: "x = \"}\"".to_owned(),
quoted_text: "x = \"}\"".to_owned(),
guard: Some(ArgumentGuard::Paren)
},
ParsedArgument {
text: "y = 1",
text: "y = 1".to_owned(),
quoted_text: "y = 1".to_owned(),
guard: Some(ArgumentGuard::Paren)
}
]
@ -379,15 +426,18 @@ mod tests {
ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "x = \"}\"; a = \"{\"; y = {}; z = 1;",
text: "x = \"}\"; a = \"{\"; y = {}; z = 1;".to_owned(),
quoted_text: "x = \"}\"; a = \"{\"; y = {}; z = 1;".to_owned(),
guard: Some(ArgumentGuard::Paren)
},
ParsedArgument {
text: " q = 5 ",
text: " q = 5 ".to_owned(),
quoted_text: " q = 5 ".to_owned(),
guard: Some(ArgumentGuard::Paren)
}
]
@ -395,7 +445,8 @@ mod tests {
},
ParsedCommand {
arguments: vec![ParsedArgument {
text: "",
text: "".to_owned(),
quoted_text: "".to_owned(),
guard: None
}]
.into()
@ -409,15 +460,18 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "\\}\\}\\}",
text: "}}}".to_owned(),
quoted_text: "\\}\\}\\}".to_owned(),
guard: Some(ArgumentGuard::Paren)
},
ParsedArgument {
text: "y = 1",
text: "y = 1".to_owned(),
quoted_text: "y = 1".to_owned(),
guard: Some(ArgumentGuard::Paren)
}
]
@ -431,11 +485,13 @@ mod tests {
commands: vec![ParsedCommand {
arguments: vec![
ParsedArgument {
text: "#blah",
text: "#blah".to_owned(),
quoted_text: "#blah".to_owned(),
guard: None
},
ParsedArgument {
text: "This is a \\\"test\\\"",
text: "This is a \"test\"".to_owned(),
quoted_text: "This is a \\\"test\\\"".to_owned(),
guard: Some(ArgumentGuard::DoubleQuote)
}
]
@ -452,5 +508,11 @@ mod tests {
parse_command(input).map(|c| c.1.to_string()),
Ok(input.to_owned())
);
let input = "{{{\"Foo \\ Bar\"}}}";
assert_eq!(
parse_command(input).map(|c| c.1.to_string()),
Ok(input.to_owned())
);
}
}