From c2b98238c82df5b6c6e6e1afc21bd376c433f543 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 16 Jun 2024 00:12:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0include=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E5=99=A8=E5=92=8C=E5=8C=B9=E9=85=8D=E5=99=A8=E7=BB=84=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 43 ++++++++++++++--- example.mtsx | 17 ++++++- src/lib.rs | 129 ++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 7 ++- src/parser.rs | 102 ++++++++++++++++++++++++--------------- 7 files changed, 232 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c59ed3b..6a90f44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "mtsyntax-plus" -version = "0.1.0" +version = "0.2.0" dependencies = [ "peg", ] diff --git a/Cargo.toml b/Cargo.toml index e67d5aa..c4f2b17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mtsyntax-plus" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 83ce43d..c6f8144 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,28 @@ MT管理器语法文件强化语法, 主要作用就是可以自动展开regexp $1: "error" string = /"(?:[^"\\]|/ @string-escape /)*"/ - $0: "string" string-def := @string $recordAllGroups: true + $0: "string" + + r-string-def := /r/ @string + $recordAllGroups: true + $0: "keyword2" + + num = /\b\d+\b/ + $0: "number" + + value-def := { + ::string-def + ::r-string-def + : @num + } //!includeEnd ] contains: [ - {include: "string-def"} + {include: "value-def"} ] } ``` @@ -37,26 +50,42 @@ MT管理器语法文件强化语法, 主要作用就是可以自动展开regexp defines: [ // Generated by mtsyntax-plus begin "string-escape": /(\\(?:[nrt'"\\]|(.)))/ - "string": /("(?:[^"\\]|/ + include("string-escape") + /)*")/ + "string": /"(?:[^"\\]|/ + include("string-escape") + /)*"/ "string-def": { - match: include("string") + match: /(/ + include("string") + /)/ recordAllGroups: true 1: "string" 2: "strEscape" 3: "error" } + "r-string-def": { + match: /(r/ + include("string") + /)/ + recordAllGroups: true + 1: "keyword2" + 2: "strEscape" + 3: "error" + } + "num": /(\b\d+\b)/ + "value-def": [ + {include: "string-def"} + {include: "r-string-def"} + { + match: include("num") + 1: "number" + } + ] // Generated by mtsyntax-plus end ] contains: [ - {include: "string-def"} + {include: "value-def"} ] } ``` # 语法 -使用`=`来定义正则, 使用`:=`来定义匹配器 +使用`=`来定义正则, 使用`:=`来定义匹配器, 使用`:= { ... }`来定义匹配器组 使用`@name`来引用定义的正则, 使用`&name`来引用外部定义的正则, 并且可以使用`&name(count)`标注外部的正则拥有多少个高亮组, 以避免组匹配歪掉 @@ -66,6 +95,8 @@ MT管理器语法文件强化语法, 主要作用就是可以自动展开regexp 在定义了匹配后, 可以跟上属性和颜色组, 属性例如`$name: "value"`, 颜色例如`$2: "xxx"`, 属性定义在颜色前面 +匹配器组里面使用`:`来定义match匹配器, 使用`::`来定义include匹配器 + # 如何使用 在拥有rust编译环境的情况下, 在项目目录下输入 diff --git a/example.mtsx b/example.mtsx index 65f89b4..fc7252e 100644 --- a/example.mtsx +++ b/example.mtsx @@ -9,14 +9,27 @@ $1: "error" string = /"(?:[^"\\]|/ @string-escape /)*"/ - $0: "string" string-def := @string $recordAllGroups: true + $0: "string" + + r-string-def := /r/ @string + $recordAllGroups: true + $0: "keyword2" + + num = /\b\d+\b/ + $0: "number" + + value-def := { + ::string-def + ::r-string-def + : @num + } //!includeEnd ] contains: [ - {include: "string-def"} + {include: "value-def"} ] } diff --git a/src/lib.rs b/src/lib.rs index be27409..8ce9df3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ macro_rules! assert_match { pub enum Error { UndefineRef(String), RepeatDefineName(String), + RefNotARegexp(String), IOError(io::Error), } impl From for Error { @@ -113,7 +114,11 @@ impl Expr<'_> { | &Expr::KwdsToRegex(_) => (), | &Expr::Ref(name) => { let rule = ctx.rule_map.get(name) - .ok_or_else(|| Error::UndefineRef(name.into()))?; + .ok_or_else(|| Error::UndefineRef(name.into())) + .and_then(|data| { + data.regexp.then_some(data) + .ok_or_else(|| Error::RefNotARegexp(name.into())) + })?; rule.build_colors(octx, ctx)? }, | &Expr::Literal(_, count) @@ -135,15 +140,15 @@ impl Expr<'_> { } #[derive(Debug)] -pub struct Rule<'a> { - name: &'a str, +pub struct RuleData<'a> { exprs: Vec>, colors: Vec>, group_count: Option, - hidden: bool, + regexp: bool, attrs: Vec<(&'a str, &'a str)>, } -impl<'a> Rule<'a> { + +impl<'a> RuleData<'a> { pub fn update_group_count(&mut self, mut f: F) -> Result<()> where F: FnMut(&str) -> Option { @@ -186,14 +191,13 @@ impl<'a> Rule<'a> { ) -> Result<()> where F: FnMut(std::fmt::Arguments<'_>) -> io::Result<()>, { - octx.output(fa!("\"{}\": ", self.name))?; - if self.hidden { + if self.regexp { for expr in self.exprs.iter().take(self.exprs.len() - 1) { octx.output(fa!("{expr} + "))?; } - octx.outputln(fa!("{}", self.exprs.last().unwrap()))?; + octx.output(fa!("{}", self.exprs.last().unwrap()))?; } else { - octx.with_block(|octx| { + octx.with_block(['{', '}'], |octx| { octx.output(fa!("match: "))?; for expr in self.exprs.iter().take(self.exprs.len() - 1) { octx.output(fa!("{expr} + "))?; @@ -215,10 +219,89 @@ impl<'a> Rule<'a> { } } +#[derive(Debug)] +pub enum Pattern<'a> { + Normal(RuleData<'a>), + IncludePattern(Cow<'a, str>), +} +impl<'a> From> for Pattern<'a> { + fn from(value: RuleData<'a>) -> Self { + Self::Normal(value) + } +} +impl Pattern<'_> { + pub fn update_group_count(&mut self, f: F) -> Result<()> + where F: FnMut(&str) -> Option + { + match self { + Pattern::Normal(data) => data.update_group_count(f), + Pattern::IncludePattern(_) => Ok(()), + } + } + + pub fn build( + &self, + ctx: &BuildContext<'_>, + octx: &mut OutputContext<'_, F>, + ) -> Result<()> + where F: FnMut(std::fmt::Arguments<'_>) -> io::Result<()>, + { + Ok(match self { + Pattern::Normal(data) => data.build(ctx, octx)?, + Pattern::IncludePattern(name) => { + octx.output(fa!("{{include: {name}}}"))?; + }, + }) + } +} + +#[derive(Debug)] +pub struct Rule<'a> { + name: &'a str, + pats: Vec>, +} +impl<'a> Rule<'a> { + pub fn update_group_count(&mut self, mut f: F) -> Result<()> + where F: FnMut(&str) -> Option + { + self.pats.iter_mut() + .try_for_each(|pat| { + pat.update_group_count(&mut f) + }) + } + + pub fn build( + &self, + ctx: &BuildContext<'_>, + octx: &mut OutputContext<'_, F>, + ) -> Result<()> + where F: FnMut(std::fmt::Arguments<'_>) -> io::Result<()>, + { + octx.output(fa!("\"{}\": ", self.name))?; + match &self.pats[..] { + [one] => { + one.build(ctx, octx) + }, + pats => { + octx.with_block(['[', ']'], |octx| { + if let Some(pat) = pats.first() { + pat.build(ctx, octx)?; + } + for pat in pats.iter().skip(1) { + octx.newline()?; + pat.build(ctx, octx)?; + } + Ok(()) + })? + }, + } + } +} + #[derive(Debug)] pub struct BuildContext<'a> { current_color: Cell, - rule_map: HashMap>, + rule_map: HashMap>, } impl Default for BuildContext<'static> { fn default() -> Self { @@ -279,19 +362,18 @@ where F: FnMut(fmt::Arguments<'_>) -> io::Result<()>, res } - pub fn with_block(&mut self, f: F1) -> io::Result + pub fn with_block(&mut self, ch: [char; 2], f: F1) -> io::Result where F1: FnOnce(&mut Self) -> R, { - self.indent_level += 1; - (self.output)(fa!("{{"))?; - self.newline()?; + let res = self.with_indent(|this| { + (this.output)(fa!("{}", ch[0]))?; + this.newline()?; - let res = f(self); + io::Result::Ok(f(this)) + })?; - self.indent_level -= 1; - self.newline()?; - (self.output)(fa!("}}"))?; self.newline()?; + (self.output)(fa!("{}", ch[1]))?; Ok(res) } @@ -320,11 +402,16 @@ where I: IntoIterator>, })?; rule.build(ctx, octx)?; + octx.newline()?; - if let Some(rule) = ctx.rule_map - .insert((*rule.name).into(), rule) + if let Some(Pattern::Normal(data)) + = rule.pats.into_iter().next() { - return Err(Error::RepeatDefineName((*rule.name).into())); + if let Some(_) = ctx.rule_map + .insert((*rule.name).into(), data) + { + return Err(Error::RepeatDefineName((*rule.name).into())); + } } } Ok(()) diff --git a/src/main.rs b/src/main.rs index 9da93d0..fa6dc9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,10 +55,13 @@ fn main() -> io::Result<()> { println!(); match e { Error::UndefineRef(name) => { - eprintln!("BuildError: undefine reference {name}"); + eprintln!("BuildError: undefine reference `{name}`"); }, Error::RepeatDefineName(name) => { - eprintln!("BuildError: repeat define name {name}"); + eprintln!("BuildError: repeat define name `{name}`"); + }, + Error::RefNotARegexp(name) => { + eprintln!("BuildError: reference `{name}` is not a regexp define"); }, Error::IOError(e) => Err(e)?, } diff --git a/src/parser.rs b/src/parser.rs index e66d9d6..3c6f96e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,43 @@ pub use parser::*; -use crate::{Rule, Expr}; +use crate::{Rule, Expr, RuleData, Pattern}; +use std::borrow::Cow; + +fn new_rule_data<'a>( + regexp: bool, + mut exprs: Vec>, + attrs: Vec<(&'a str, &'a str)>, + mut colors: Vec>, +) -> RuleData<'a> { + if let Some(first) = colors.first_mut() + .map(|color| color.take()) + .flatten() + { + colors.insert(1, first.into()); + match &mut exprs[..] { + [Expr::Literal(s, n), ..] => { + s.to_mut().insert(1, '('); + *n += 1; + }, + _ => exprs.insert(0, Expr::Literal("/(/".into(), 1)), + } + match &mut exprs[..] { + [.., Expr::Literal(s, _)] => { + let tail = s.to_mut().pop().unwrap(); + s.to_mut().push(')'); + s.to_mut().push(tail); + }, + _ => exprs.push(Expr::Literal("/)/".into(), 0)), + } + } + RuleData { + exprs, + colors, + group_count: None, + regexp, + attrs, + } +} peg::parser!(grammar parser() for str { rule newline() @@ -24,6 +61,10 @@ peg::parser!(grammar parser() for str { )} / expected!("ident") + rule eident() -> Cow<'input, str> + = i:ident() { format!("\"{i}\"").into() } + / s:string() { s.into() } + pub rule string() -> &'input str = quiet!{ $("\"" ( @@ -108,56 +149,43 @@ peg::parser!(grammar parser() for str { Expr::KwdsToRegex(s) } / "@" i:ident() { Expr::Ref(i) } - / "&" - n:( i:ident() { format!("\"{i}\"").into() } - / s:string() { s.into() }) - c:("(" n:unum() ")" { n })? { Expr::Include(n, c.unwrap_or(0)) } + / "&" n:eident() + c:("(" n:unum() ")" { n })? + { Expr::Include(n, c.unwrap_or(0)) } pub rule mt_rule() -> Rule<'input> = name:ident() - _ vars:( + _ pats:( ":=" _ exprs:expr() ++ (_ ("+" _)?) _ attrs:attrs() _ colors:colors() - { (false, exprs, attrs, colors) } + { vec![new_rule_data(false, exprs, attrs, colors).into()] } + / ":=" _ "{" + pats:( + _ + p:( ":" + _ exprs:expr() ++ (_ ("+" _)?) + _ attrs:attrs() + _ colors:colors() + { new_rule_data(false, exprs, attrs, colors).into() } + / "::" + _ name:eident() + { Pattern::IncludePattern(name) } + ) + { p } + )+ + _ "}" + { pats } / "=" _ exprs:expr() ++ (_ ("+" _)?) _ colors:colors() - { (true, exprs, vec![], colors) } + { vec![new_rule_data(true, exprs, vec![], colors).into()] } ) { - let (hidden, mut exprs, attrs, mut colors) = vars; - - if let Some(first) = colors.first_mut() - .map(|color| color.take()) - .flatten() - { - colors.insert(1, first.into()); - let [b, e] = ["/(/", "/)/"]; - match &mut exprs[..] { - [Expr::Literal(s, n), ..] => { - s.to_mut().insert(1, '('); - *n += 1; - }, - _ => exprs.insert(0, Expr::Literal("/(/".into(), 1)), - } - match &mut exprs[..] { - [.., Expr::Literal(s, _)] => { - let tail = s.to_mut().pop().unwrap(); - s.to_mut().push(')'); - s.to_mut().push(tail); - }, - _ => exprs.push(Expr::Literal("/)/".into(), 0)), - } - } Rule { name, - exprs, - colors, - group_count: None, - hidden, - attrs, + pats, } }