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 {
|
pub fn debrace(inp: &str) -> &str {
|
||||||
let v = inp.trim();
|
let v = inp.trim();
|
||||||
if v.starts_with("{") && v.ends_with("}") {
|
if v.starts_with('{') && v.ends_with('}') {
|
||||||
&v[1..(v.len() - 1)]
|
&v[1..(v.len() - 1)]
|
||||||
} else {
|
} else {
|
||||||
v
|
v
|
||||||
@ -26,7 +26,7 @@ fn reentrant_command_handler(
|
|||||||
Some((cmd, rest)) => {
|
Some((cmd, rest)) => {
|
||||||
if let ("#", command_rest) = cmd.split_at(1) {
|
if let ("#", command_rest) = cmd.split_at(1) {
|
||||||
if cmd == "##" {
|
if cmd == "##" {
|
||||||
match lua_state.execute(debrace(&join(rest.arguments.iter(), " "))) {
|
match lua_state.execute(&rest.forget_guards().to_string()) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
||||||
@ -43,7 +43,7 @@ fn reentrant_command_handler(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match lua_state.execute_command(command_rest, &rest.arguments) {
|
match lua_state.execute_command(command_rest, &rest) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
||||||
|
@ -7,8 +7,9 @@ use piccolo::{
|
|||||||
};
|
};
|
||||||
use yew::UseStateSetter;
|
use yew::UseStateSetter;
|
||||||
|
|
||||||
use crate::{id_intern::intern_id, GlobalLayoutCell, GlobalMemoCell, TermFrame};
|
use crate::{
|
||||||
use std::collections::VecDeque;
|
id_intern::intern_id, parsing::ParsedCommand, GlobalLayoutCell, GlobalMemoCell, TermFrame,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct LuaState {
|
pub struct LuaState {
|
||||||
pub interp: Lua,
|
pub interp: Lua,
|
||||||
@ -63,7 +64,7 @@ impl LuaState {
|
|||||||
pub fn execute_command(
|
pub fn execute_command(
|
||||||
&mut self,
|
&mut self,
|
||||||
command: &str,
|
command: &str,
|
||||||
arguments: &VecDeque<&str>,
|
arguments: &ParsedCommand<'_>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
self.interp
|
self.interp
|
||||||
.try_enter(|ctx| {
|
.try_enter(|ctx| {
|
||||||
@ -78,8 +79,9 @@ impl LuaState {
|
|||||||
command_fn,
|
command_fn,
|
||||||
Variadic(
|
Variadic(
|
||||||
arguments
|
arguments
|
||||||
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| ctx.intern(s.as_bytes()).into())
|
.map(|s| ctx.intern(s.text.as_bytes()).into())
|
||||||
.collect::<Vec<Value>>(),
|
.collect::<Vec<Value>>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -47,7 +47,7 @@ pub fn echo_frame<'gc>(
|
|||||||
let all_parts: Vec<String> = stack
|
let all_parts: Vec<String> = stack
|
||||||
.consume::<Variadic<Vec<Value>>>(ctx)?
|
.consume::<Variadic<Vec<Value>>>(ctx)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| format!("{:?}", v))
|
.map(|v| format!("{}", v.display()))
|
||||||
.collect();
|
.collect();
|
||||||
stack.push_front(frame_no);
|
stack.push_front(frame_no);
|
||||||
let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes());
|
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::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
character::complete::{anychar, char, none_of},
|
character::complete::{anychar, char, none_of},
|
||||||
combinator::{eof, map, recognize, value},
|
combinator::{eof, map, recognize, value},
|
||||||
multi::{many0_count, separated_list0},
|
multi::{many0_count, separated_list0},
|
||||||
sequence::{preceded, separated_pair},
|
sequence::{delimited, preceded, separated_pair},
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,9 +14,82 @@ pub struct ParseResult<'l> {
|
|||||||
pub commands: Vec<ParsedCommand<'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 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<'_> {
|
impl ParsedCommand<'_> {
|
||||||
@ -26,7 +99,7 @@ impl ParsedCommand<'_> {
|
|||||||
match tmp_arguments.pop_front() {
|
match tmp_arguments.pop_front() {
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(head) => {
|
Some(head) => {
|
||||||
let trimmed_cmd = head.trim();
|
let trimmed_cmd = head.text.trim();
|
||||||
if !trimmed_cmd.is_empty() {
|
if !trimmed_cmd.is_empty() {
|
||||||
return Some((
|
return Some((
|
||||||
trimmed_cmd,
|
trimmed_cmd,
|
||||||
@ -77,39 +150,60 @@ fn parse_parenthetical(input: &str) -> IResult<&str, ()> {
|
|||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_argument(input: &str) -> IResult<&str, ()> {
|
fn parse_argument(input: &str) -> IResult<&str, ParsedArgument> {
|
||||||
value(
|
alt((
|
||||||
(),
|
map(
|
||||||
many0_count(alt((
|
delimited(char('{'), recognize(parse_parenthetical), char('}')),
|
||||||
value((), preceded(char('\\'), anychar)),
|
|v| ParsedArgument {
|
||||||
value(
|
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(
|
many0_count(alt((
|
||||||
char('{'),
|
value((), preceded(char('\\'), anychar)),
|
||||||
parse_parenthetical,
|
value(
|
||||||
alt((value((), eof), value((), char('}')))),
|
(),
|
||||||
),
|
separated_pair(
|
||||||
),
|
char('{'),
|
||||||
value(
|
parse_parenthetical,
|
||||||
(),
|
alt((value((), eof), value((), char('}')))),
|
||||||
separated_pair(
|
),
|
||||||
char('"'),
|
),
|
||||||
parse_string,
|
value(
|
||||||
alt((value((), eof), value((), char('"')))),
|
(),
|
||||||
),
|
separated_pair(
|
||||||
),
|
char('"'),
|
||||||
value((), none_of(" ;")),
|
parse_string,
|
||||||
))),
|
alt((value((), eof), value((), char('"')))),
|
||||||
)(input)
|
),
|
||||||
|
),
|
||||||
|
value((), none_of(" ;")),
|
||||||
|
))),
|
||||||
|
)),
|
||||||
|
|v| ParsedArgument {
|
||||||
|
text: v,
|
||||||
|
guard: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command(input: &str) -> IResult<&str, ParsedCommand> {
|
fn parse_command(input: &str) -> IResult<&str, ParsedCommand> {
|
||||||
map(
|
map(separated_list0(char(' '), parse_argument), |arguments| {
|
||||||
separated_list0(char(' '), recognize(parse_argument)),
|
ParsedCommand {
|
||||||
|arguments| ParsedCommand {
|
|
||||||
arguments: arguments.into(),
|
arguments: arguments.into(),
|
||||||
},
|
}
|
||||||
)(input)
|
})(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_commands(input: &str) -> ParseResult {
|
pub fn parse_commands(input: &str) -> ParseResult {
|
||||||
@ -128,7 +222,11 @@ mod tests {
|
|||||||
parse_commands(""),
|
parse_commands(""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec![""].into()
|
arguments: vec![ParsedArgument {
|
||||||
|
text: "",
|
||||||
|
guard: None
|
||||||
|
}]
|
||||||
|
.into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -136,7 +234,11 @@ mod tests {
|
|||||||
parse_commands("north"),
|
parse_commands("north"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
arguments: vec!["north"].into()
|
arguments: vec![ParsedArgument {
|
||||||
|
text: "north",
|
||||||
|
guard: None
|
||||||
|
}]
|
||||||
|
.into()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -145,7 +247,17 @@ mod tests {
|
|||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
commands: vec![ParsedCommand {
|
||||||
// This is deliberate, ensures we can reconstruct the input.
|
// 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 {
|
ParseResult {
|
||||||
commands: vec![
|
commands: vec![
|
||||||
ParsedCommand {
|
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 {
|
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"),
|
parse_commands("#blah {x = 1 + 2"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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}"),
|
parse_commands("#blah {x = 1} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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\""),
|
parse_commands("#blah \"hello\" \"world\""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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}"),
|
parse_commands("#blah {x = \"}\"} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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![
|
commands: vec![
|
||||||
ParsedCommand {
|
ParsedCommand {
|
||||||
arguments: vec![
|
arguments: vec![
|
||||||
"#blah",
|
ParsedArgument {
|
||||||
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
|
text: "#blah",
|
||||||
"{ q = 5 }"
|
guard: None
|
||||||
|
},
|
||||||
|
ParsedArgument {
|
||||||
|
text: "x = \"}\"; a = \"{\"; y = {}; z = 1;",
|
||||||
|
guard: Some(ArgumentGuard::Paren)
|
||||||
|
},
|
||||||
|
ParsedArgument {
|
||||||
|
text: " q = 5 ",
|
||||||
|
guard: Some(ArgumentGuard::Paren)
|
||||||
|
}
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
ParsedCommand {
|
ParsedCommand {
|
||||||
arguments: vec![""].into()
|
arguments: vec![ParsedArgument {
|
||||||
|
text: "",
|
||||||
|
guard: None
|
||||||
|
}]
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -216,7 +407,21 @@ mod tests {
|
|||||||
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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\\\"\""),
|
parse_commands("#blah \"This is a \\\"test\\\"\""),
|
||||||
ParseResult {
|
ParseResult {
|
||||||
commands: vec![ParsedCommand {
|
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