From 274183fa66afc470add9981a3a400523db568a89 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 7 Apr 2024 14:52:16 +0900 Subject: [PATCH] dsl_util: extract helper that parses string literal with \-escapes The top-level assertion is removed since it's now obvious that the pair represents a Rule::string_literal. --- cli/src/template_parser.rs | 31 +++++------------------ lib/src/dsl_util.rs | 52 ++++++++++++++++++++++++++++++++++++++ lib/src/lib.rs | 1 + 3 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 lib/src/dsl_util.rs diff --git a/cli/src/template_parser.rs b/cli/src/template_parser.rs index a24042f0d..6982ae46f 100644 --- a/cli/src/template_parser.rs +++ b/cli/src/template_parser.rs @@ -16,6 +16,7 @@ use std::collections::HashMap; use std::{error, fmt, mem}; use itertools::Itertools as _; +use jj_lib::dsl_util::StringLiteralParser; use once_cell::sync::Lazy; use pest::iterators::{Pair, Pairs}; use pest::pratt_parser::{Assoc, Op, PrattParser}; @@ -27,6 +28,11 @@ use thiserror::Error; #[grammar = "template.pest"] struct TemplateParser; +const STRING_LITERAL_PARSER: StringLiteralParser = StringLiteralParser { + content_rule: Rule::string_content, + escape_rule: Rule::string_escape, +}; + impl Rule { fn to_symbol(self) -> Option<&'static str> { match self { @@ -314,29 +320,6 @@ fn parse_identifier_name(pair: Pair) -> TemplateParseResult<&str> { } } -fn parse_string_literal(pair: Pair) -> String { - assert_eq!(pair.as_rule(), Rule::string_literal); - let mut result = String::new(); - for part in pair.into_inner() { - match part.as_rule() { - Rule::string_content => { - result.push_str(part.as_str()); - } - Rule::string_escape => match &part.as_str()[1..] { - "\"" => result.push('"'), - "\\" => result.push('\\'), - "t" => result.push('\t'), - "r" => result.push('\r'), - "n" => result.push('\n'), - "0" => result.push('\0'), - char => panic!("invalid escape: \\{char:?}"), - }, - _ => panic!("unexpected part of string: {part:?}"), - } - } - result -} - fn parse_formal_parameters(params_pair: Pair) -> TemplateParseResult> { assert_eq!(params_pair.as_rule(), Rule::formal_parameters); let params_span = params_pair.as_span(); @@ -397,7 +380,7 @@ fn parse_term_node(pair: Pair) -> TemplateParseResult { let span = expr.as_span(); let primary = match expr.as_rule() { Rule::string_literal => { - let text = parse_string_literal(expr); + let text = STRING_LITERAL_PARSER.parse(expr.into_inner()); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::integer_literal => { diff --git a/lib/src/dsl_util.rs b/lib/src/dsl_util.rs new file mode 100644 index 000000000..9d97d5c36 --- /dev/null +++ b/lib/src/dsl_util.rs @@ -0,0 +1,52 @@ +// Copyright 2020-2024 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Domain-specific language helpers. + +use pest::iterators::Pairs; +use pest::RuleType; + +/// Helper to parse string literal. +#[derive(Debug)] +pub struct StringLiteralParser { + /// String content part. + pub content_rule: R, + /// Escape sequence part including backslash character. + pub escape_rule: R, +} + +impl StringLiteralParser { + /// Parses the given string literal `pairs` into string. + pub fn parse(&self, pairs: Pairs) -> String { + let mut result = String::new(); + for part in pairs { + if part.as_rule() == self.content_rule { + result.push_str(part.as_str()); + } else if part.as_rule() == self.escape_rule { + match &part.as_str()[1..] { + "\"" => result.push('"'), + "\\" => result.push('\\'), + "t" => result.push('\t'), + "r" => result.push('\r'), + "n" => result.push('\n'), + "0" => result.push('\0'), + char => panic!("invalid escape: \\{char:?}"), + } + } else { + panic!("unexpected part of string: {part:?}"); + } + } + result + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index ab2693e59..a9d2396cf 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -36,6 +36,7 @@ pub mod dag_walk; pub mod default_index; pub mod default_submodule_store; pub mod diff; +pub mod dsl_util; pub mod extensions_map; pub mod file_util; pub mod files;