diff --git a/README.md b/README.md index 7dbaa5b..eee0835 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,9 @@ MT管理器语法文件强化语法, 主要作用就是可以自动展开regexp 拥有几个语法糖, 使用`(`来表示`/(/`, 使用`(?:`来表示`/(?:/`, 使用`)`来表示`/)/`, 使用`){1, 2}`来表示`/){1,2}/`, 使用`|`来表示`/|/` +使用`($color`表示一个幽灵颜色组, +它的颜色定义来源于组声明处写的`color`而非后续本地定义如`$1: color` + # 如何使用 在拥有rust编译环境的情况下, 在项目目录下输入 diff --git a/src/lib.rs b/src/lib.rs index 7192511..215f9b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,8 @@ pub enum Expr<'a> { /// regexp or string literal, and group count. /// e.g /ab/ "abi" Literal(Cow<'a, str>, u32), + /// like Literal, but not consume local color and output is capture group open + ColorGroup(Cow<'a, str>), /// e.g keywordsToRegex("ab cd", "ef gh") KwdsToRegex(Vec<&'a str>), /// rule reference, e.g @foo @@ -72,6 +74,7 @@ impl<'a> fmt::Display for Expr<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Expr::Literal(lit, _) => f.write_str(lit), + Expr::ColorGroup(_) => f.write_str("/(/"), Expr::KwdsToRegex(kwds) => { write!(f, "keywordsToRegex(")?; if let Some(first) = kwds.first() { @@ -93,6 +96,7 @@ impl Expr<'_> { where F: FnOnce(&str) -> Option, { Ok(match self { + | &Expr::ColorGroup(_) => 1, | &Expr::Include(_, c) | &Expr::Literal(_, c) => c, | &Expr::KwdsToRegex(_) => 0, @@ -121,6 +125,12 @@ impl Expr<'_> { })?; rule.build_colors(octx, ctx)? }, + | &Expr::ColorGroup(ref color) => { + let id = ctx.current_color.get(); + octx.newline()?; + octx.output(fa!("{id}: {color}"))?; + ctx.current_color.set(id + 1); + }, | &Expr::Literal(_, count) | &Expr::Include(_, count) => { let cur_color = ctx.current_color.get(); @@ -143,7 +153,7 @@ impl Expr<'_> { #[derive(Debug)] pub struct RuleData<'a> { exprs: Vec>, - colors: Vec>, + colors: Vec>>, group_count: Option, regexp: bool, attrs: Vec<(&'a str, &'a str)>, @@ -193,17 +203,15 @@ impl<'a> RuleData<'a> { where F: FnMut(std::fmt::Arguments<'_>) -> io::Result<()>, { if self.regexp { - for expr in self.exprs.iter().take(self.exprs.len() - 1) { - octx.output(fa!("{expr} + "))?; - } - octx.output(fa!("{}", self.exprs.last().unwrap()))?; + out_exprs(&self.exprs, |args| { + octx.output(args) + })?; } else { octx.with_block(['{', '}'], |octx| { octx.output(fa!("match: "))?; - for expr in self.exprs.iter().take(self.exprs.len() - 1) { - octx.output(fa!("{expr} + "))?; - } - octx.output(fa!("{}", self.exprs.last().unwrap()))?; + out_exprs(&self.exprs, |args| { + octx.output(args) + })?; for &(attr, val) in &self.attrs { octx.newline()?; @@ -389,6 +397,34 @@ impl Default for OutputContext<'static> { } } +/// 融合相邻项并输出结果 +fn out_exprs<'a, F, I>(exprs: I, mut f: F) -> io::Result<()> +where F: FnMut(std::fmt::Arguments<'_>) -> io::Result<()>, + I: IntoIterator>, +{ + let exprs = exprs.into_iter() + .map(ToString::to_string) + .collect::>(); + + let mut iter = exprs.into_iter(); + let first = iter.next().unwrap(); + let end = iter.try_fold(first, |mut a, b| { + io::Result::Ok(if let Some('/' | '"') = a.chars() + .next_back() + .zip(b.chars().next()) + .and_then(|(a, b)| (a == b).then_some(a)) + { + a.pop().unwrap(); + a.push_str(&b[1..]); + a + } else { + f(fa!("{a} + "))?; + b + }) + })?; + f(fa!("{}", end)) +} + pub fn build<'a, I, F>( rules: I, octx: &mut OutputContext<'_, F>, diff --git a/src/parser.rs b/src/parser.rs index 18dcb5f..396bcaa 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,7 +7,7 @@ fn new_rule_data<'a>( regexp: bool, mut exprs: Vec>, attrs: Vec<(&'a str, &'a str)>, - mut colors: Vec>, + mut colors: Vec>>, ) -> RuleData<'a> { if let Some(first) = colors.first_mut() .and_then(|color| color.take()) @@ -102,11 +102,11 @@ peg::parser!(grammar parser() for str { } / expected!("number(0..100000)") - rule color() -> (u32, &'input str) - = "$" n:unum() _ ":" _ name:string() + rule color() -> (u32, Cow<'input, str>) + = "$" n:unum() _ ":" _ name:eident() { (n, name) } - pub rule colors() -> Vec> + pub rule colors() -> Vec>> = colors:color() ** _ { let mut res = Vec::new(); @@ -158,6 +158,7 @@ peg::parser!(grammar parser() for str { rule expr_sugar() -> Expr<'input> = "|" { Expr::Literal("/|/".into(), 0) } / "(?:" { Expr::Literal("/(?:/".into(), 0) } + / "($" color:eident() { Expr::ColorGroup(color) } / "(" { Expr::Literal("/(/".into(), 1) } / ")" x:( "{" a:unum() _ "," _ b:unum() "}" @@ -256,6 +257,8 @@ mod tests { bar := @foo + /;|\// + @foo // ... sugar = (&a) | (?: &b ) | (&c){2} | (&c){2 , 3} | (&d){, 3} | (&d){3 , } + x := ($red /a/) /(b)/ + $1: blue "#; println!("{src}");