Skip to content

Commit

Permalink
Simplify escape parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
jpikl committed Jul 9, 2023
1 parent a498ed9 commit 3a2c0b4
Showing 1 changed file with 27 additions and 28 deletions.
55 changes: 27 additions & 28 deletions src/commands/x/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ use thiserror::Error;
const EXPR_START: char = '{';
const EXPR_END: char = '}';
const PIPE: char = '|';
const DEFAULT_ESCAPE: char = '\\';

const SINGLE_QUOTE: char = '\'';
const DOUBLE_QUOTE: char = '"';

const DEFAULT_ESCAPE: char = '\\';
const ESCAPED_LF: char = 'n';
const ESCAPED_CR: char = 'c';
const ESCAPED_TAB: char = 't';
Expand Down Expand Up @@ -178,7 +180,7 @@ impl Parser<'_> {
self.consume(quote);
return Ok(arg);
} else if char == DEFAULT_ESCAPE {
arg.push(self.parse_escape_sequence(&[quote])?);
arg.push(self.parse_escape_sequence(|char| char == quote));
} else {
arg.push(self.consume(char));
}
Expand All @@ -193,15 +195,15 @@ impl Parser<'_> {
while let Some(char) = self.peek() {
match char {
EXPR_START | PIPE | EXPR_END | ' ' => break,
DEFAULT_ESCAPE => arg.push(self.parse_escape_sequence(&[
' ',
DEFAULT_ESCAPE,
SINGLE_QUOTE,
DOUBLE_QUOTE,
EXPR_START,
PIPE,
EXPR_END,
])?),
DEFAULT_ESCAPE => {
let is_escapable = |char| match char {
DEFAULT_ESCAPE | SINGLE_QUOTE | DOUBLE_QUOTE | EXPR_START | PIPE
| EXPR_END => true,
char if char.is_whitespace() => true,
_ => false,
};
arg.push(self.parse_escape_sequence(is_escapable))
}
quote @ (SINGLE_QUOTE | DOUBLE_QUOTE) => {
arg.push_str(&self.parse_quote_arg(quote)?)
}
Expand All @@ -218,7 +220,8 @@ impl Parser<'_> {
while let Some(char) = self.peek() {
match char {
DEFAULT_ESCAPE => {
constant.push(self.parse_escape_sequence(&[EXPR_START, EXPR_END])?)
let is_escapable = |char| matches!(char, EXPR_START | EXPR_END);
constant.push(self.parse_escape_sequence(is_escapable))
}
EXPR_START => break,
EXPR_END => {
Expand All @@ -231,24 +234,16 @@ impl Parser<'_> {
Ok(constant)
}

fn parse_escape_sequence(&mut self, escapable_chars: &[char]) -> Result<char> {
fn parse_escape_sequence(&mut self, is_escapable: impl Fn(char) -> bool) -> char {
self.consume(DEFAULT_ESCAPE);

match self.peek() {
Some(char @ (ESCAPED_LF | ESCAPED_CR | ESCAPED_TAB | DEFAULT_ESCAPE)) => {
self.parse_escaped_char(char)
}
Some(char) if escapable_chars.contains(&char) => self.parse_escaped_char(char),
_ => Ok(DEFAULT_ESCAPE),
}
}

fn parse_escaped_char(&mut self, char: char) -> Result<char> {
match self.consume(char) {
ESCAPED_LF => Ok('\n'),
ESCAPED_CR => Ok('\r'),
ESCAPED_TAB => Ok('\t'),
other => Ok(other),
Some(DEFAULT_ESCAPE) => self.consume(DEFAULT_ESCAPE),
Some(ESCAPED_LF) => self.consume_as(ESCAPED_LF, '\n'),
Some(ESCAPED_CR) => self.consume_as(ESCAPED_CR, '\r'),
Some(ESCAPED_TAB) => self.consume_as(ESCAPED_TAB, '\t'),
Some(char) if is_escapable(char) => self.consume(char),
_ => DEFAULT_ESCAPE, // If this is not a valid escape sequence, keep the escape character
}
}

Expand All @@ -274,8 +269,12 @@ impl Parser<'_> {
}

fn consume(&mut self, expected: char) -> char {
self.consume_as(expected, expected)
}

fn consume_as(&mut self, expected: char, result: char) -> char {
match self.next() {
Some(char) if char == expected => char,
Some(char) if char == expected => result,
Some(char) => unreachable!("parser expected {:?} but got {:?}", expected, char),
None => unreachable!("parser expected {:?} but got EOF", expected),
}
Expand Down

0 comments on commit 3a2c0b4

Please sign in to comment.