From a498ed9dadb4466a9c0efd2a969a2800a33b8301 Mon Sep 17 00:00:00 2001 From: Jan Pikl Date: Sun, 9 Jul 2023 15:03:35 +0200 Subject: [PATCH] Use constants for tokens --- src/commands/x/parser.rs | 88 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/src/commands/x/parser.rs b/src/commands/x/parser.rs index 7018bd64..f88182ad 100644 --- a/src/commands/x/parser.rs +++ b/src/commands/x/parser.rs @@ -7,6 +7,16 @@ use std::iter::Peekable; use std::str::Chars; 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 ESCAPED_LF: char = 'n'; +const ESCAPED_CR: char = 'c'; +const ESCAPED_TAB: char = 't'; + #[derive(Debug, Clone)] pub enum Item { Constant(String), @@ -29,7 +39,7 @@ pub struct Error { impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let padding = " ".repeat(self.position); - writeln!(f, "syntax error at position '{}'", self.position.yellow())?; + writeln!(f, "syntax error at position {}", self.position.yellow())?; writeln!(f)?; writeln!(f, "{}", self.input)?; writeln!(f, "{}{}", padding, "^".red().bold())?; @@ -39,17 +49,17 @@ impl Display for Error { #[derive(Debug, Error)] pub enum ErrorKind { - #[error("the previous '{{' was not closed")] + #[error("the previous {} was not closed", EXPR_START)] UnexpectedExpressionStart, - #[error("missing command before '|'")] + #[error("missing command before {}", PIPE)] MissingCommandBefore, - #[error("missing command after '|'")] + #[error("missing command after {}", PIPE)] MissingCommandAfter, #[error("missing closing {}", .0)] MissingClosingQuote(char), - #[error("missing opening '{{'")] + #[error("missing opening {}", EXPR_START)] MissingExpressionStart, - #[error("missing closing '}}'")] + #[error("missing closing {}", EXPR_END)] MissingExpressionEnd, } @@ -74,7 +84,7 @@ impl Parser<'_> { let mut items = Vec::new(); while let Some(char) = self.peek() { - let item = if char == '{' { + let item = if char == EXPR_START { Item::Expression(self.parse_expression()?) } else { Item::Constant(self.parse_constant()?) @@ -88,7 +98,7 @@ impl Parser<'_> { fn parse_expression(&mut self) -> Result> { let start_position = self.position; - self.consume('{'); + self.consume(EXPR_START); self.consume_whitespaces(); let mut calls = Vec::new(); @@ -96,9 +106,11 @@ impl Parser<'_> { while let Some(char) = self.peek() { match char { - '{' => return Err(self.error(ErrorKind::UnexpectedExpressionStart, self.position)), - '|' => return Err(self.error(ErrorKind::MissingCommandBefore, self.position)), - '}' => { + EXPR_START => { + return Err(self.error(ErrorKind::UnexpectedExpressionStart, self.position)) + } + PIPE => return Err(self.error(ErrorKind::MissingCommandBefore, self.position)), + EXPR_END => { if command_expected { return Err(self.error(ErrorKind::MissingCommandAfter, self.position)); } else { @@ -108,7 +120,7 @@ impl Parser<'_> { _ => { calls.push(self.parse_call()?); - if self.try_consume('|') { + if self.try_consume(PIPE) { self.consume_whitespaces(); command_expected = true; } else { @@ -120,7 +132,7 @@ impl Parser<'_> { if command_expected { Err(self.error(ErrorKind::MissingCommandAfter, self.position)) - } else if self.try_consume('}') { + } else if self.try_consume(EXPR_END) { Ok(calls) } else { Err(self.error(ErrorKind::MissingExpressionEnd, start_position)) @@ -134,7 +146,7 @@ impl Parser<'_> { let mut args = Vec::new(); while let Some(char) = self.peek() { - if char == '|' || char == '}' { + if char == PIPE || char == EXPR_END { break; } args.push(self.parse_arg()?); @@ -149,7 +161,7 @@ impl Parser<'_> { fn parse_arg(&mut self) -> Result { match self.peek() { - Some(quote @ ('\'' | '"')) => self.parse_quote_arg(quote), + Some(quote @ (SINGLE_QUOTE | DOUBLE_QUOTE)) => self.parse_quote_arg(quote), Some(_) => self.parse_unquote_arg(), None => unreachable!("unexpected EOF when parsing arg"), } @@ -165,7 +177,7 @@ impl Parser<'_> { if char == quote { self.consume(quote); return Ok(arg); - } else if char == '\\' { + } else if char == DEFAULT_ESCAPE { arg.push(self.parse_escape_sequence(&[quote])?); } else { arg.push(self.consume(char)); @@ -180,9 +192,19 @@ impl Parser<'_> { while let Some(char) = self.peek() { match char { - '{' | '|' | '}' | ' ' => break, - '\\' => arg.push(self.parse_escape_sequence(&[' ', '\'', '"', '{', '|', '}'])?), - quote @ ('\'' | '"') => arg.push_str(&self.parse_quote_arg(quote)?), + 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, + ])?), + quote @ (SINGLE_QUOTE | DOUBLE_QUOTE) => { + arg.push_str(&self.parse_quote_arg(quote)?) + } char => arg.push(self.consume(char)), }; } @@ -195,9 +217,13 @@ impl Parser<'_> { while let Some(char) = self.peek() { match char { - '\\' => constant.push(self.parse_escape_sequence(&['{', '}'])?), - '{' => break, - '}' => return Err(self.error(ErrorKind::MissingExpressionStart, self.position)), + DEFAULT_ESCAPE => { + constant.push(self.parse_escape_sequence(&[EXPR_START, EXPR_END])?) + } + EXPR_START => break, + EXPR_END => { + return Err(self.error(ErrorKind::MissingExpressionStart, self.position)) + } char => constant.push(self.consume(char)), } } @@ -206,20 +232,22 @@ impl Parser<'_> { } fn parse_escape_sequence(&mut self, escapable_chars: &[char]) -> Result { - self.consume('\\'); + self.consume(DEFAULT_ESCAPE); match self.peek() { - Some(char @ ('n' | 'r' | 't' | '\\')) => self.parse_escaped_char(char), + 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('\\'), + _ => Ok(DEFAULT_ESCAPE), } } fn parse_escaped_char(&mut self, char: char) -> Result { match self.consume(char) { - 'n' => Ok('\n'), - 'r' => Ok('\r'), - 't' => Ok('\t'), + ESCAPED_LF => Ok('\n'), + ESCAPED_CR => Ok('\r'), + ESCAPED_TAB => Ok('\t'), other => Ok(other), } } @@ -248,8 +276,8 @@ impl Parser<'_> { fn consume(&mut self, expected: char) -> char { match self.next() { Some(char) if char == expected => char, - Some(char) => unreachable!("parser expected '{}' but got '{}'", expected, char), - None => unreachable!("parser expected '{}' but got EOF", expected), + Some(char) => unreachable!("parser expected {:?} but got {:?}", expected, char), + None => unreachable!("parser expected {:?} but got EOF", expected), } }