Improve parsing to remove need for debrace everywhere.
This commit is contained in:
parent
f619aa4770
commit
f6486498f6
@ -5,7 +5,7 @@ use itertools::{join, Itertools};
|
||||
|
||||
pub fn debrace(inp: &str) -> &str {
|
||||
let v = inp.trim();
|
||||
if v.starts_with("{") && v.ends_with("}") {
|
||||
if v.starts_with('{') && v.ends_with('}') {
|
||||
&v[1..(v.len() - 1)]
|
||||
} else {
|
||||
v
|
||||
@ -26,7 +26,7 @@ fn reentrant_command_handler(
|
||||
Some((cmd, rest)) => {
|
||||
if let ("#", command_rest) = cmd.split_at(1) {
|
||||
if cmd == "##" {
|
||||
match lua_state.execute(debrace(&join(rest.arguments.iter(), " "))) {
|
||||
match lua_state.execute(&rest.forget_guards().to_string()) {
|
||||
Ok(()) => (),
|
||||
Err(msg) => {
|
||||
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
||||
@ -43,7 +43,7 @@ fn reentrant_command_handler(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
match lua_state.execute_command(command_rest, &rest.arguments) {
|
||||
match lua_state.execute_command(command_rest, &rest) {
|
||||
Ok(()) => (),
|
||||
Err(msg) => {
|
||||
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
||||
|
@ -7,8 +7,9 @@ use piccolo::{
|
||||
};
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use crate::{id_intern::intern_id, GlobalLayoutCell, GlobalMemoCell, TermFrame};
|
||||
use std::collections::VecDeque;
|
||||
use crate::{
|
||||
id_intern::intern_id, parsing::ParsedCommand, GlobalLayoutCell, GlobalMemoCell, TermFrame,
|
||||
};
|
||||
|
||||
pub struct LuaState {
|
||||
pub interp: Lua,
|
||||
@ -63,7 +64,7 @@ impl LuaState {
|
||||
pub fn execute_command(
|
||||
&mut self,
|
||||
command: &str,
|
||||
arguments: &VecDeque<&str>,
|
||||
arguments: &ParsedCommand<'_>,
|
||||
) -> Result<(), String> {
|
||||
self.interp
|
||||
.try_enter(|ctx| {
|
||||
@ -78,8 +79,9 @@ impl LuaState {
|
||||
command_fn,
|
||||
Variadic(
|
||||
arguments
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|s| ctx.intern(s.as_bytes()).into())
|
||||
.map(|s| ctx.intern(s.text.as_bytes()).into())
|
||||
.collect::<Vec<Value>>(),
|
||||
),
|
||||
);
|
||||
|
@ -47,7 +47,7 @@ pub fn echo_frame<'gc>(
|
||||
let all_parts: Vec<String> = stack
|
||||
.consume::<Variadic<Vec<Value>>>(ctx)?
|
||||
.into_iter()
|
||||
.map(|v| format!("{:?}", v))
|
||||
.map(|v| format!("{}", v.display()))
|
||||
.collect();
|
||||
stack.push_front(frame_no);
|
||||
let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes());
|
||||
|
320
src/parsing.rs
320
src/parsing.rs
@ -1,11 +1,11 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::{collections::VecDeque, fmt::Display};
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
character::complete::{anychar, char, none_of},
|
||||
combinator::{eof, map, recognize, value},
|
||||
multi::{many0_count, separated_list0},
|
||||
sequence::{preceded, separated_pair},
|
||||
sequence::{delimited, preceded, separated_pair},
|
||||
IResult,
|
||||
};
|
||||
|
||||
@ -14,9 +14,82 @@ pub struct ParseResult<'l> {
|
||||
pub commands: Vec<ParsedCommand<'l>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum ArgumentGuard {
|
||||
Paren, // {}
|
||||
DoubleQuote, // ""
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct ParsedArgument<'l> {
|
||||
pub guard: Option<ArgumentGuard>,
|
||||
pub text: &'l str,
|
||||
}
|
||||
|
||||
impl<'l> ParsedArgument<'l> {
|
||||
pub fn forget_guard(&self) -> Self {
|
||||
Self {
|
||||
guard: None,
|
||||
..self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for ParsedArgument<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.guard {
|
||||
None => {}
|
||||
Some(ArgumentGuard::Paren) => {
|
||||
f.write_str("{")?;
|
||||
}
|
||||
Some(ArgumentGuard::DoubleQuote) => {
|
||||
f.write_str("\"")?;
|
||||
}
|
||||
}
|
||||
f.write_str(self.text)?;
|
||||
match self.guard {
|
||||
None => {}
|
||||
Some(ArgumentGuard::Paren) => {
|
||||
f.write_str("}")?;
|
||||
}
|
||||
Some(ArgumentGuard::DoubleQuote) => {
|
||||
f.write_str("\"")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct ParsedCommand<'l> {
|
||||
pub arguments: VecDeque<&'l str>,
|
||||
pub arguments: VecDeque<ParsedArgument<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> ParsedCommand<'l> {
|
||||
pub fn forget_guards(&self) -> Self {
|
||||
Self {
|
||||
arguments: self.arguments.iter().map(|v| v.forget_guard()).collect(),
|
||||
..self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for ParsedCommand<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut it = self.arguments.iter();
|
||||
match it.next() {
|
||||
None => {}
|
||||
Some(head) => {
|
||||
head.fmt(f)?;
|
||||
|
||||
for v in it {
|
||||
f.write_str(" ")?;
|
||||
v.fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParsedCommand<'_> {
|
||||
@ -26,7 +99,7 @@ impl ParsedCommand<'_> {
|
||||
match tmp_arguments.pop_front() {
|
||||
None => return None,
|
||||
Some(head) => {
|
||||
let trimmed_cmd = head.trim();
|
||||
let trimmed_cmd = head.text.trim();
|
||||
if !trimmed_cmd.is_empty() {
|
||||
return Some((
|
||||
trimmed_cmd,
|
||||
@ -77,39 +150,60 @@ fn parse_parenthetical(input: &str) -> IResult<&str, ()> {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_argument(input: &str) -> IResult<&str, ()> {
|
||||
value(
|
||||
(),
|
||||
many0_count(alt((
|
||||
value((), preceded(char('\\'), anychar)),
|
||||
value(
|
||||
fn parse_argument(input: &str) -> IResult<&str, ParsedArgument> {
|
||||
alt((
|
||||
map(
|
||||
delimited(char('{'), recognize(parse_parenthetical), char('}')),
|
||||
|v| ParsedArgument {
|
||||
text: v,
|
||||
guard: Some(ArgumentGuard::Paren),
|
||||
},
|
||||
),
|
||||
map(
|
||||
delimited(char('"'), recognize(parse_string), char('"')),
|
||||
|v| ParsedArgument {
|
||||
text: v,
|
||||
guard: Some(ArgumentGuard::DoubleQuote),
|
||||
},
|
||||
),
|
||||
map(
|
||||
recognize(value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('{'),
|
||||
parse_parenthetical,
|
||||
alt((value((), eof), value((), char('}')))),
|
||||
),
|
||||
),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('"'),
|
||||
parse_string,
|
||||
alt((value((), eof), value((), char('"')))),
|
||||
),
|
||||
),
|
||||
value((), none_of(" ;")),
|
||||
))),
|
||||
)(input)
|
||||
many0_count(alt((
|
||||
value((), preceded(char('\\'), anychar)),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('{'),
|
||||
parse_parenthetical,
|
||||
alt((value((), eof), value((), char('}')))),
|
||||
),
|
||||
),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('"'),
|
||||
parse_string,
|
||||
alt((value((), eof), value((), char('"')))),
|
||||
),
|
||||
),
|
||||
value((), none_of(" ;")),
|
||||
))),
|
||||
)),
|
||||
|v| ParsedArgument {
|
||||
text: v,
|
||||
guard: None,
|
||||
},
|
||||
),
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn parse_command(input: &str) -> IResult<&str, ParsedCommand> {
|
||||
map(
|
||||
separated_list0(char(' '), recognize(parse_argument)),
|
||||
|arguments| ParsedCommand {
|
||||
map(separated_list0(char(' '), parse_argument), |arguments| {
|
||||
ParsedCommand {
|
||||
arguments: arguments.into(),
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
})(input)
|
||||
}
|
||||
|
||||
pub fn parse_commands(input: &str) -> ParseResult {
|
||||
@ -128,7 +222,11 @@ mod tests {
|
||||
parse_commands(""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec![""].into()
|
||||
arguments: vec![ParsedArgument {
|
||||
text: "",
|
||||
guard: None
|
||||
}]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -136,7 +234,11 @@ mod tests {
|
||||
parse_commands("north"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["north"].into()
|
||||
arguments: vec![ParsedArgument {
|
||||
text: "north",
|
||||
guard: None
|
||||
}]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -145,7 +247,17 @@ mod tests {
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
// This is deliberate, ensures we can reconstruct the input.
|
||||
arguments: vec!["north", ""].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "north",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "",
|
||||
guard: None
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -154,10 +266,24 @@ mod tests {
|
||||
ParseResult {
|
||||
commands: vec![
|
||||
ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1 + 2; y = 3}"].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "x = 1 + 2; y = 3",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
},
|
||||
ParsedCommand {
|
||||
arguments: vec!["#home"].into()
|
||||
arguments: vec![ParsedArgument {
|
||||
text: "#home",
|
||||
guard: None
|
||||
}]
|
||||
.into()
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -166,7 +292,17 @@ mod tests {
|
||||
parse_commands("#blah {x = 1 + 2"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1 + 2"].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "{x = 1 + 2",
|
||||
guard: None
|
||||
}
|
||||
]
|
||||
.into()
|
||||
},]
|
||||
}
|
||||
);
|
||||
@ -174,7 +310,21 @@ mod tests {
|
||||
parse_commands("#blah {x = 1} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1}", "{y = 1}"].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "x = 1",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "y = 1",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -182,7 +332,21 @@ mod tests {
|
||||
parse_commands("#blah \"hello\" \"world\""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "\"hello\"", "\"world\""].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "hello",
|
||||
guard: Some(ArgumentGuard::DoubleQuote)
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "world",
|
||||
guard: Some(ArgumentGuard::DoubleQuote)
|
||||
},
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -190,7 +354,21 @@ mod tests {
|
||||
parse_commands("#blah {x = \"}\"} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = \"}\"}", "{y = 1}"].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "x = \"}\"",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "y = 1",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -200,14 +378,27 @@ mod tests {
|
||||
commands: vec![
|
||||
ParsedCommand {
|
||||
arguments: vec![
|
||||
"#blah",
|
||||
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
|
||||
"{ q = 5 }"
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "x = \"}\"; a = \"{\"; y = {}; z = 1;",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
},
|
||||
ParsedArgument {
|
||||
text: " q = 5 ",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
},
|
||||
ParsedCommand {
|
||||
arguments: vec![""].into()
|
||||
arguments: vec![ParsedArgument {
|
||||
text: "",
|
||||
guard: None
|
||||
}]
|
||||
.into()
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -216,7 +407,21 @@ mod tests {
|
||||
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{\\}\\}\\}}", "{y = 1}"].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "\\}\\}\\}",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "y = 1",
|
||||
guard: Some(ArgumentGuard::Paren)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
@ -224,9 +429,28 @@ mod tests {
|
||||
parse_commands("#blah \"This is a \\\"test\\\"\""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "\"This is a \\\"test\\\"\""].into()
|
||||
arguments: vec![
|
||||
ParsedArgument {
|
||||
text: "#blah",
|
||||
guard: None
|
||||
},
|
||||
ParsedArgument {
|
||||
text: "This is a \\\"test\\\"",
|
||||
guard: Some(ArgumentGuard::DoubleQuote)
|
||||
}
|
||||
]
|
||||
.into()
|
||||
}]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_roundtrip() {
|
||||
let input = "#hello {to the} \"world\" I say! ";
|
||||
assert_eq!(
|
||||
parse_command(input).map(|c| c.1.to_string()),
|
||||
Ok(input.to_owned())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user