mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
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.
This commit is contained in:
parent
46b4c68325
commit
274183fa66
3 changed files with 60 additions and 24 deletions
|
@ -16,6 +16,7 @@ use std::collections::HashMap;
|
||||||
use std::{error, fmt, mem};
|
use std::{error, fmt, mem};
|
||||||
|
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
|
use jj_lib::dsl_util::StringLiteralParser;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use pest::iterators::{Pair, Pairs};
|
use pest::iterators::{Pair, Pairs};
|
||||||
use pest::pratt_parser::{Assoc, Op, PrattParser};
|
use pest::pratt_parser::{Assoc, Op, PrattParser};
|
||||||
|
@ -27,6 +28,11 @@ use thiserror::Error;
|
||||||
#[grammar = "template.pest"]
|
#[grammar = "template.pest"]
|
||||||
struct TemplateParser;
|
struct TemplateParser;
|
||||||
|
|
||||||
|
const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser {
|
||||||
|
content_rule: Rule::string_content,
|
||||||
|
escape_rule: Rule::string_escape,
|
||||||
|
};
|
||||||
|
|
||||||
impl Rule {
|
impl Rule {
|
||||||
fn to_symbol(self) -> Option<&'static str> {
|
fn to_symbol(self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -314,29 +320,6 @@ fn parse_identifier_name(pair: Pair<Rule>) -> TemplateParseResult<&str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_string_literal(pair: Pair<Rule>) -> 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<Rule>) -> TemplateParseResult<Vec<&str>> {
|
fn parse_formal_parameters(params_pair: Pair<Rule>) -> TemplateParseResult<Vec<&str>> {
|
||||||
assert_eq!(params_pair.as_rule(), Rule::formal_parameters);
|
assert_eq!(params_pair.as_rule(), Rule::formal_parameters);
|
||||||
let params_span = params_pair.as_span();
|
let params_span = params_pair.as_span();
|
||||||
|
@ -397,7 +380,7 @@ fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> {
|
||||||
let span = expr.as_span();
|
let span = expr.as_span();
|
||||||
let primary = match expr.as_rule() {
|
let primary = match expr.as_rule() {
|
||||||
Rule::string_literal => {
|
Rule::string_literal => {
|
||||||
let text = parse_string_literal(expr);
|
let text = STRING_LITERAL_PARSER.parse(expr.into_inner());
|
||||||
ExpressionNode::new(ExpressionKind::String(text), span)
|
ExpressionNode::new(ExpressionKind::String(text), span)
|
||||||
}
|
}
|
||||||
Rule::integer_literal => {
|
Rule::integer_literal => {
|
||||||
|
|
52
lib/src/dsl_util.rs
Normal file
52
lib/src/dsl_util.rs
Normal file
|
@ -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<R> {
|
||||||
|
/// String content part.
|
||||||
|
pub content_rule: R,
|
||||||
|
/// Escape sequence part including backslash character.
|
||||||
|
pub escape_rule: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: RuleType> StringLiteralParser<R> {
|
||||||
|
/// Parses the given string literal `pairs` into string.
|
||||||
|
pub fn parse(&self, pairs: Pairs<R>) -> 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ pub mod dag_walk;
|
||||||
pub mod default_index;
|
pub mod default_index;
|
||||||
pub mod default_submodule_store;
|
pub mod default_submodule_store;
|
||||||
pub mod diff;
|
pub mod diff;
|
||||||
|
pub mod dsl_util;
|
||||||
pub mod extensions_map;
|
pub mod extensions_map;
|
||||||
pub mod file_util;
|
pub mod file_util;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
|
|
Loading…
Reference in a new issue