From 3452ecf168b77083325d633a7054a5ed184e67fb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 12 Oct 2023 14:20:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0match=E5=92=8C=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E5=9D=97,=20=E5=B9=B6=E4=B8=94=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 10 +- Cargo.toml | 2 +- examples/README.md | 1 + examples/match.mdtlbl | 261 ++++++++++++++++ syntax/vim/mdtlbl.snippets | 5 + syntax/vim/mdtlbl.vim | 4 +- tools/display_source/Cargo.toml | 2 +- tools/display_source/src/impls.rs | 271 ++++++++++++++++- tools/display_source/src/lib.rs | 27 ++ tools/parser/Cargo.toml | 2 +- tools/parser/src/parser.lalrpop | 131 ++++++-- tools/parser/src/tests.rs | 481 +++++++++++++++++++++++++++--- tools/syntax/Cargo.toml | 2 +- tools/syntax/src/lib.rs | 372 ++++++++++++++++++++--- tools/var_utils/Cargo.toml | 2 +- tools/var_utils/src/lib.rs | 2 +- 16 files changed, 1451 insertions(+), 124 deletions(-) create mode 100644 examples/match.mdtlbl diff --git a/Cargo.lock b/Cargo.lock index b5a9465..bb5e3bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ dependencies = [ [[package]] name = "display_source" -version = "0.3.2" +version = "0.3.3" dependencies = [ "parser", "syntax", @@ -292,7 +292,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.13.9" +version = "0.14.0" dependencies = [ "display_source", "parser", @@ -337,7 +337,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.1.0" +version = "0.1.1" dependencies = [ "lalrpop", "lalrpop-util", @@ -516,7 +516,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.1.1" +version = "0.1.2" dependencies = [ "tag_code", "utils", @@ -585,7 +585,7 @@ version = "0.1.2" [[package]] name = "var_utils" -version = "0.4.0" +version = "0.5.0" dependencies = [ "lazy-regex", ] diff --git a/Cargo.toml b/Cargo.toml index 4fe620c..f718cb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.13.9" +version = "0.14.0" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/README.md b/examples/README.md index d813d47..74e3abf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -32,6 +32,7 @@ > [`quick_dexp_take.mdtlbl`](./quick_dexp_take.mdtlbl)
> [`value_bind.mdtlbl`](./value_bind.mdtlbl)
> [`caller.mdtlbl`](./caller.mdtlbl)
+> [`match.mdtlbl`](./match.mdtlbl)
如果没有列出那请在看完上述后自行观看, 顺序可以参考文件创建顺序. diff --git a/examples/match.mdtlbl b/examples/match.mdtlbl new file mode 100644 index 0000000..5b832d8 --- /dev/null +++ b/examples/match.mdtlbl @@ -0,0 +1,261 @@ +#** +* 这是0.14.0增加的新功能, 此功能及其强大, 将编译期能力进行了极大的扩展, +* 借此你甚至可以基于值编写一门LL(k)文法语言的parser与一定的代码生成 +* +* 简单概述为凭借对句柄的模式匹配完成分支条件编译 +* +* 同时还增加了重复块, 可以对参数进行重复 +* +* 修改了参数方式, 由构建期展开为编号参数改为编译期展开特定语句 +* +* --- +* +* 内部语句SetArgs, 它将设置参数, 其作用范围为Expand和重复块 +* +* 扩展Other, Match和Print的参数, +* 增加展开符`@`, 展开符将会展开为全部参数, 参数来源于SetArgs +* +* 可以使用重复块, 它将在一个内联块中重复给定块, 并且每次重复迭代指定个参数, +* 此数字未指定则为1, 它不能为0, 它是构建期被指定的 +* +* match语句 +* === +* 改语句由参数输入, 并在之后的块中编写多个分支. +* 每个分支编写零至多个句柄模式, 和一个分支代码体 +* +* 句柄模式由两部分组成: 绑定值 和 模式 +* +* 绑定值和模式都是可选的, 模式是由方括号括起的多个值, 顺序匹配其中任意一值 +* +* 执行到match语句时, 会先将输入参数take, +* 然后使用得到的多个句柄, 向下对每个分支进行匹配. +* +* 开始匹配一个分支时, 会将该分支中所有模式中的值进行take, +* 然后使用其句柄集合与参数进行完全匹配. +* +* 如果在其中使用`@`符号, 那么将会匹配零个以上参数, 并将匹配到重新设定到参数 +* +* 成功完全匹配后, 终止向下探查分支, 并将匹配成功分支的代码体直接编译 +* +* 如果没有匹配到任何句柄模式, 那么将不会编译任何代码体, +* 但是参数和每个分支的模式仍被take, 所以需要注意这件事 +*# + +match a b c d e { @ {} } # 利用match没有作用域来人为的设置参数 +print @; +match { @ {} } +#* >>> +print a +print b +print c +print d +print e +*# + + +# 块作用域 +{ + match a b c d e { @ {} } + print @; +} +print "xxx"; +print @; +#* >>> +print a +print b +print c +print d +print e +print "xxx" +*# + + +# 重复块的作用域 +{ + match a b c d e { @ {} } + inline@{ + print @; + } + print "xxx"; + print @; +} +#* >>> +print a +print b +print c +print d +print e +print "xxx" +print a +print b +print c +print d +print e +*# +# 可以看到, 重复块中最后一次设置的参数并没有影响重复块外的参数 + + +# 重复块迭代数 +{ + match a b c d e { @ {} } + inline 2@{ + print @; + print "---"; + } +} +#* >>> +print a +print b +print "---" +print c +print d +print "---" +print e +print "---" +*# + + +# 利用match完成递归展开 +const Foo = ( + match @ { + [end] {} + [endl] { print "\n"; } + X @ { + print X; + take Foo[@]; + } + } +); +take Foo["Hello" "World!" endl]; +take Foo["hi" end]; +#* >>> +print "Hello" +print "World!" +print "\n" +print "hi" +*# + + +# 利用match比较句柄相等 +const Eq = ( + match @ { + A B { match B { + [A] { print "equal" A B; } + __ { print "notEqual" A B; } + }} + } +); +take Eq[a a]; +take Eq[a b]; +#* >>> +print "equal" +print a +print a +print "notEqual" +print a +print b +*# + + +# 利用编译时运算完成计数 +const PrintId = ( + take N = 0; + inline@{ + print N":"@"\n"; + take N = ($ = N+1;); + } +); +take PrintId[a b c d]; +printflush message1; +#* >>> +print 0 +print ":" +print a +print "\n" +print 1 +print ":" +print b +print "\n" +print 2 +print ":" +print c +print "\n" +print 3 +print ":" +print d +print "\n" +printflush message1 +*# +# 这里利用了循环块没有作用域来完成 + + +const PrintId = ( + match @ { + [begin] @ { + take PrintId[0 @]; + } + N Arg @ { + print N":"Arg"\n"; + take PrintId[($ = N+1;) @]; + } + } +); +take PrintId[begin a b c d]; +printflush message1; +#* >>> +print 0 +print ":" +print a +print "\n" +print 1 +print ":" +print b +print "\n" +print 2 +print ":" +print c +print "\n" +print 3 +print ":" +print d +print "\n" +printflush message1 +*# +# 这里利用了递归展开和嵌套的子作用域来完成 + + +# 句柄匹配的或运算 +const GetNum = ( + match @ { + [one] { + setres 1; + } + [two] { + setres 2; + } + [three four] { + setres 3.5; + } + } +); +print GetNum[one]; +print GetNum[two]; +print GetNum[three]; +print GetNum[four]; +#* >>> +print 1 +print 2 +print 3.5 +print 3.5 +*# +# 写在同一个匹配里的多个值即为或运算 + + +# 记住, 它们被take +const YES = yes; +match yes { + [YES] { print "ok"; } +} +#* >>> +print "ok" +*# diff --git a/syntax/vim/mdtlbl.snippets b/syntax/vim/mdtlbl.snippets index 097c252..f3d4ef4 100644 --- a/syntax/vim/mdtlbl.snippets +++ b/syntax/vim/mdtlbl.snippets @@ -147,3 +147,8 @@ endsnippet snippet sensor "sensor" w sensor ${1:result} ${2:block1} ${3:@${4:copper}};$0 endsnippet +snippet match "match args... { (pattern body)... }" w +match $1 { + $0 +} +endsnippet diff --git a/syntax/vim/mdtlbl.vim b/syntax/vim/mdtlbl.vim index 16d81ed..25af9d6 100644 --- a/syntax/vim/mdtlbl.vim +++ b/syntax/vim/mdtlbl.vim @@ -26,7 +26,7 @@ syn case match " 一些关键字 {{{1 syn keyword mdtlblKeyword \ while gwhile do skip goto if elif else switch case break continue - \ const take setres select + \ const take setres select match \ inline \ op set noop print @@ -39,6 +39,7 @@ syn keyword mdtlblOpFunKeyword \ asin acos atan lnot syn match mdtlblCmpTreeOper /&&\|||\|!/ +syn match mdtlblArgsExpand /@/ " 注释 {{{1 syn region mdtlblComment start=/#[^*]\=/ end=/$/ oneline @@ -141,4 +142,5 @@ hi def link mdtlblDefineResultHandle Identifier hi def link mdtlblIdentLabel Label hi def link mdtlblArgsBracket Macro hi def link mdtlblQuickDExpTakeIdent Macro +hi def link mdtlblArgsExpand Structure " }}}1 diff --git a/tools/display_source/Cargo.toml b/tools/display_source/Cargo.toml index 43860dd..3aeb007 100644 --- a/tools/display_source/Cargo.toml +++ b/tools/display_source/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "display_source" -version = "0.3.2" +version = "0.3.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/display_source/src/impls.rs b/tools/display_source/src/impls.rs index b987507..c29efb9 100644 --- a/tools/display_source/src/impls.rs +++ b/tools/display_source/src/impls.rs @@ -1,12 +1,25 @@ use syntax::*; use crate::{DisplaySource, DisplaySourceMeta}; +impl DisplaySource for str { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + meta.push(&Value::replace_ident(self)) + } +} +impl DisplaySource for Var { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + self.as_str().display_source(meta) + } +} impl DisplaySource for Value { fn display_source(&self, meta: &mut DisplaySourceMeta) { - let replace_ident = Self::replace_ident; match self { - Self::Var(s) => meta.push(&replace_ident(s)), - Self::ReprVar(s) => meta.push(&format!("`{}`", replace_ident(s))), + Self::Var(s) => s.display_source(meta), + Self::ReprVar(s) => { + meta.push("`"); + s.display_source(meta); + meta.push("`"); + }, Self::ResultHandle => meta.push("$"), Self::DExp(dexp) => dexp.display_source(meta), Self::ValueBind(value_attr) => value_attr.display_source(meta), @@ -24,7 +37,7 @@ impl DisplaySource for DExp { meta.push("("); let has_named_res = !self.result().is_empty(); if has_named_res { - meta.push(&Value::replace_ident(self.result())); + self.result().display_source(meta); meta.push(":"); } match self.lines().len() { @@ -49,7 +62,7 @@ impl DisplaySource for ValueBind { fn display_source(&self, meta: &mut DisplaySourceMeta) { self.0.display_source(meta); meta.push("."); - meta.push(&Value::replace_ident(&self.1)); + self.1.display_source(meta); } } impl DisplaySource for JumpCmp { @@ -182,7 +195,7 @@ impl DisplaySource for Goto { meta.push("goto"); meta.add_space(); meta.push(":"); - meta.push(&Value::replace_ident(lab)); + lab.display_source(meta); meta.add_space(); cmp.display_source(meta); meta.push(";"); @@ -229,7 +242,7 @@ impl DisplaySource for Const { meta.push("const"); meta.add_space(); - meta.push(&Value::replace_ident(&self.0)); + self.0.display_source(meta); meta.add_space(); meta.push("="); @@ -240,6 +253,12 @@ impl DisplaySource for Const { meta.push(";"); meta.add_space(); + meta.push("# labels: ["); + meta.display_source_iter_by_splitter( + |meta| meta.push(", "), + &self.2, + ); + meta.push("]"); let labs = self.2 .iter() .map(|s| Value::replace_ident(&**s)) @@ -294,6 +313,13 @@ impl DisplaySource for LogicLine { meta.push("}"); }, Self::Ignore => meta.push("{} # ignore line"), + Self::SetArgs(args) => { + meta.push("# setArgs"); + meta.add_space(); + + args.display_source(meta); + meta.push(";"); + }, Self::NoOp => meta.push("noop;"), Self::Label(lab) => { meta.push(":"); @@ -316,19 +342,126 @@ impl DisplaySource for LogicLine { val.display_source(meta); meta.push(";"); }, + Self::ArgsRepeat(args_repeat) => args_repeat.display_source(meta), + Self::Match(r#match) => r#match.display_source(meta), Self::Other(args) => { - assert_ne!(args.len(), 0); - let mut iter = args.iter(); - iter.next().unwrap().display_source(meta); - iter.for_each(|arg| { + if let Some(args) = args.as_normal() { + assert_ne!(args.len(), 0); + } + args.display_source(meta); + meta.push(";"); + }, + } + } +} +impl DisplaySource for Args { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + match self { + Args::Normal(args) => { + meta.display_source_iter_by_splitter( + DisplaySourceMeta::add_space, args); + }, + Args::Expanded(prefix, suffix) => { + prefix.iter().for_each(|arg| { + arg.display_source(meta); + meta.add_space(); + }); + + meta.push("@"); + + suffix.iter().for_each(|arg| { meta.add_space(); arg.display_source(meta); }); - meta.push(";"); }, } } } +impl DisplaySource for ArgsRepeat { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + meta.push("inline"); + meta.add_space(); + meta.push(&self.count().to_string()); + meta.push("@"); + meta.push("{"); + if !self.block().is_empty() { + meta.add_lf(); + meta.do_block(|meta| { + self.block().display_source(meta); + }); + } + meta.push("}"); + } +} +impl DisplaySource for MatchPatAtom { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + let show_name = !self.name().is_empty(); + let show_list = !self.pattern().is_empty(); + if show_name { + meta.push(self.name()); + if show_list { meta.push(":") } + } + if show_list { + meta.push("["); + meta.display_source_iter_by_splitter( + DisplaySourceMeta::add_space, + self.pattern() + ); + meta.push("]"); + } + } +} +impl DisplaySource for MatchPat { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + match self { + MatchPat::Normal(args) => { + meta.display_source_iter_by_splitter( + DisplaySourceMeta::add_space, + args, + ) + }, + MatchPat::Expanded(prefix, suffix) => { + for s in prefix { + s.display_source(meta); + meta.add_space(); + } + meta.push("@"); + for s in suffix { + meta.add_space(); + s.display_source(meta); + } + }, + } + } +} +impl DisplaySource for Match { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + meta.push("match"); + meta.add_space(); + let slen = meta.len(); + self.args().display_source(meta); + if meta.len() != slen { meta.add_space(); } + meta.push("{"); + if !self.cases().is_empty() { + meta.add_lf(); + meta.do_block(|meta| { + self.cases().iter().for_each(|(pat, block)| { + let slen = meta.len(); + pat.display_source(meta); + if meta.len() != slen { meta.add_space(); } + meta.push("{"); + if !block.is_empty() { + meta.add_lf(); + meta.do_block(|meta| block.display_source(meta)); + } + meta.push("}"); + meta.add_lf(); + }); + }); + } + meta.push("}"); + } +} #[cfg(test)] #[test] @@ -449,4 +582,118 @@ fn display_source_test() { .display_source_and_get(&mut meta), r#"`'set'` a "\n\\[[hi]\\n";"# ); + assert_eq!( + parse!(line_parser, r#"foo bar baz;"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"foo bar baz;"# + ); + assert_eq!( + parse!(line_parser, r#"foo @ bar baz;"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"foo @ bar baz;"# + ); + assert_eq!( + parse!(line_parser, r#"@ bar baz;"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"@ bar baz;"# + ); + assert_eq!( + parse!(line_parser, r#"foo @;"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"foo @;"# + ); + assert_eq!( + parse!(line_parser, r#"@;"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"@;"# + ); + assert_eq!( + parse!(line_parser, r#"inline @{}"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"inline 1@{}"# + ); + assert_eq!( + parse!(line_parser, r#"inline 23@{}"#) + .unwrap() + .display_source_and_get(&mut meta), + r#"inline 23@{}"# + ); + assert_eq!( + parse!(line_parser, r#"print @;"#) + .unwrap() + .display_source_and_get(&mut meta), + "inline 1@{\n `'print'` @;\n}" + ); + assert_eq!( + parse!(line_parser, r#"print a b @ c d;"#) + .unwrap() + .display_source_and_get(&mut meta), + "\ + inline {\n\ + \x20 `'print'` a;\n\ + \x20 `'print'` b;\n\ + \x20 inline 1@{\n\ + \x20 `'print'` @;\n\ + \x20 }\n\ + \x20 `'print'` c;\n\ + \x20 `'print'` d;\n\ + }" + ); + assert_eq!( + parse!(line_parser, r#" + match a b c @ d e f { + x y:[m n] [a b] { + foo; + } + x @ { + bar; + } + {} + @ {} + } + "#) + .unwrap() + .display_source_and_get(&mut meta), + "\ + match a b c @ d e f {\n\ + \x20 x y:[m n] [a b] {\n\ + \x20 foo;\n\ + \x20 }\n\ + \x20 x @ {\n\ + \x20 bar;\n\ + \x20 }\n\ + \x20 {}\n\ + \x20 @ {}\n\ + }" + ); + assert_eq!( + parse!(line_parser, r#" + match a b c @ d e f {} + "#) + .unwrap() + .display_source_and_get(&mut meta), + "match a b c @ d e f {}" + ); + assert_eq!( + parse!(line_parser, r#" + match {} + "#) + .unwrap() + .display_source_and_get(&mut meta), + "match {}" + ); + assert_eq!( + parse!(line_parser, r#" + foo 'match'; + "#) + .unwrap() + .display_source_and_get(&mut meta), + "foo 'match';" + ); } diff --git a/tools/display_source/src/lib.rs b/tools/display_source/src/lib.rs index a013b72..232a7b9 100644 --- a/tools/display_source/src/lib.rs +++ b/tools/display_source/src/lib.rs @@ -140,10 +140,27 @@ impl DisplaySourceMeta { pub fn space_str(&self) -> &str { self.space_str.as_ref() } + + /// 从可迭代对象中生成, 并且在每两次生成之间调用分割函数 + pub fn display_source_iter_by_splitter<'a, T: DisplaySource + 'a>( + &mut self, + mut split: impl FnMut(&mut Self), + iter: impl IntoIterator + ) { + let mut iter = iter.into_iter(); + if let Some(s) = iter.next() { + s.display_source(self) + } + iter.for_each(|s| { + split(self); + s.display_source(self) + }) + } } pub trait DisplaySource { fn display_source(&self, meta: &mut DisplaySourceMeta); + /// 构建元数据的同时返回已构建的引用 /// 注意, 返回的是这次构建的, 不包括在此之前构建的 fn display_source_and_get<'a>(&self, meta: &'a mut DisplaySourceMeta) -> &'a str { @@ -152,6 +169,16 @@ pub trait DisplaySource { &meta.buffer()[start..] } } +impl DisplaySource for &'_ T +where T: DisplaySource +{ + fn display_source(&self, meta: &mut DisplaySourceMeta) { + T::display_source(self, meta) + } + fn display_source_and_get<'a>(&self, meta: &'a mut DisplaySourceMeta) -> &'a str { + T::display_source_and_get(&self, meta) + } +} #[cfg(test)] mod tests { diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index abac0a2..c448704 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.1.0" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/parser/src/parser.lalrpop b/tools/parser/src/parser.lalrpop index ecb7a8f..5152356 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -29,6 +29,11 @@ use ::syntax::{ Const, Take, LogicLine, + Args, + ArgsRepeat, + Match, + MatchPat, + MatchPatAtom, Meta, ZERO_VAR, FALSE_VAR, @@ -117,13 +122,12 @@ pub Value: Value = { DExp => <>.into(), "`" "`" => ReprVar(<>), // 原始值 "$" => ResultHandle, - > => { + > => { // QuickDExpTake - let len = args.len() + 1; - let mut expand = Vec::with_capacity(len); - Take::build_arg_consts_to_expand(args, &mut expand); - expand.push(LogicLine::SetResultHandle(name.into())); - DExp::new("__".into(), expand.into()).into() + DExp::new("__".into(), vec![ + LogicLine::SetArgs(args.unwrap_or_default()), + LogicLine::SetResultHandle(name.into()), + ].into()).into() }, "." => ValueBind(value.into(), attr).into(), // consted-dexp @@ -280,31 +284,102 @@ pub LogicLine: LogicLine = { =>? Meta::build_sets([l, r], vars, values).map_err(|e| e.into()), OpExpr, Print, - LEnd => { - LogicLine::Other(args) + //ArgsRepeatBlock => <>.into(), + LEnd => LogicLine::Other(<>), + "inline" => <>.into(), + Match => <>.into(), +} + +Match: Match = "match" <("@" )?> > +)*>> => { + Match::new(args.unwrap_or_default(), cases.into_iter() + .map(|(prep, sufp, body)| { + (if let Some(sufp) = sufp { + MatchPat::Expanded(prep, sufp) + } else { + MatchPat::Normal(prep) + }, body.into()) + }) + .collect() + ) +}; + +MatchPat: MatchPatAtom = { + MList => MatchPatAtom::new_unnamed(<>), + Var => MatchPatAtom::new(<>, vec![]), + ":" > => MatchPatAtom::new(name, pat), +} + +Args: Args = { + "@" => { + Args::Expanded(prefix, suffix) + }, + => { + args.into() }, } +ArgsRepeatBlock: ArgsRepeat += "@" > =>? { + if chunk == Some(0) { + return Err(Error::from((l, Errors::ArgsRepeatChunkByZero, r)).into()) + } + Ok(ArgsRepeat::new(chunk.unwrap_or(1), block.into())) +}; -Print: LogicLine = "print" LEnd => { - if <>.is_empty() { - // 无参数, 啥也不做 - return LogicLine::Ignore - } - if <>.len() == 1 { +Print: LogicLine = "print" LEnd => { + let Some(args) = <> else { + // 无参数, 啥也不做 + return LogicLine::Ignore + }; + fn one(arg: Value) -> LogicLine { + LogicLine::Other(vec![ + ReprVar("print".into()), + arg, + ].into()) + } + match args { + Args::Normal(mut args) if args.len() == 1 => { // 单个参数, 老式处理 - return LogicLine::Other(vec![ - ReprVar("print".into()), - <>.into_iter().next().unwrap() - ]) - } - Expand( - <>.into_iter() - .map(|arg| LogicLine::Other(vec![ + return one(args.pop().unwrap()) + }, + Args::Normal(args) => { + Expand( + args.into_iter() + .map(|arg| one(arg)) + .collect() + ).into() + }, + Args::Expanded(p, s) if p.len() == 0 && s.len() == 0 => { + ArgsRepeat::new(1, vec![ + LogicLine::Other(Args::Expanded( + vec![ReprVar("print".into())], + vec![], + )), + ].into()).into() + }, + Args::Expanded(prefix, suffix) => { + let len = prefix.len() + 1 + suffix.len(); + let mut res = Vec::with_capacity(len); + + for arg in prefix { + res.push(one(arg)) + } + + res.push(ArgsRepeat::new(1, vec![ + LogicLine::Other(Args::Expanded(vec![ ReprVar("print".into()), - arg - ])) - .collect() - ).into() + ], vec![])) + ].into()).into()); + + for arg in suffix { + res.push(one(arg)) + } + + assert_eq!(res.len(), len); + InlineBlock(res).into() + }, + } }; // 0.. @@ -595,11 +670,11 @@ pub BuiltinCommand: LogicLine = { // 如果后方是一个Var则直接将常量映射到后方的值 // 如果后方是一个DExp则将其计算然后将常量映射到计算出的句柄 // 此处默认句柄使用Value - "take" ?> "=")?> LEnd => { + "take" ?> "=")?> LEnd => { let do_leak_res = var.is_some(); Take::new( - args.unwrap_or_default(), + args.flatten().unwrap_or_default(), var.unwrap_or_else(|| String::from("__")), do_leak_res, value diff --git a/tools/parser/src/tests.rs b/tools/parser/src/tests.rs index b9398f3..d30cadf 100644 --- a/tools/parser/src/tests.rs +++ b/tools/parser/src/tests.rs @@ -122,7 +122,7 @@ fn control_test() { parse!(parser, r#"skip 1 < 2 print "hello";"#).unwrap(), Expand(vec![ Goto("___0".into(), JumpCmp::LessThan("1".into(), "2".into()).into()).into(), - LogicLine::Other(vec![Value::ReprVar("print".into()), r#""hello""#.into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), r#""hello""#.into()].into()), LogicLine::Label("___0".into()), ]).into() ); @@ -402,20 +402,20 @@ fn switch_test() { "2".into(), Expand(vec![ LogicLine::Ignore, - Expand(vec![LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()])]).into(), + Expand(vec![LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()].into())]).into(), Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "2".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "4".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "2".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "4".into()].into()), ]).into(), LogicLine::Ignore, Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "2".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "4".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "2".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "4".into()].into()), ]).into(), Expand(vec![ LogicLine::Label("a".into()), LogicLine::Label("b".into()), - LogicLine::Other(vec![Value::ReprVar("print".into()), "5".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "5".into()].into()), ]).into(), ]) ).into() @@ -455,12 +455,12 @@ fn switch_test() { "1".into(), Expand(vec![ Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "0".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "0".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()].into()), ]).into(), Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()].into()), ]).into(), ]) ).into() @@ -479,11 +479,11 @@ fn switch_test() { "1".into(), Expand(vec![ Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()].into()), ]).into(), Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "1".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()].into()), ]).into(), ]) ).into() @@ -501,10 +501,10 @@ fn switch_test() { "1".into(), Expand(vec![ Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "0".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "0".into()].into()), Expand(vec![ - LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()]), - LogicLine::Other(vec![Value::ReprVar("print".into()), "end1".into()]), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end".into()].into()), + LogicLine::Other(vec![Value::ReprVar("print".into()), "end1".into()].into()), ]).into(), ]).into(), ]) @@ -793,7 +793,7 @@ fn in_const_label_test() { DExp::new_nores( vec![ LogicLine::Label("in_const".into()), - LogicLine::Other(vec![Value::ReprVar("print".into()), "\"hi\"".into()]) + LogicLine::Other(vec![Value::ReprVar("print".into()), "\"hi\"".into()].into()) ].into() ).into(), vec!["in_const".into()] @@ -934,11 +934,11 @@ fn take_default_result_test() { fn const_value_leak_test() { let ast: Expand = vec![ Expand(vec![ - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), Const("N".into(), "2".into(), Vec::new()).into(), - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), ]).into(), - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), ].into(); let meta = CompileMeta::new(); let mut tag_codes = meta.compile(ast); @@ -951,12 +951,12 @@ fn const_value_leak_test() { let ast: Expand = vec![ Expand(vec![ - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), Const("N".into(), "2".into(), Vec::new()).into(), - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), LogicLine::ConstLeak("N".into()), ]).into(), - LogicLine::Other(vec!["print".into(), "N".into()]), + LogicLine::Other(vec!["print".into(), "N".into()].into()), ].into(); let meta = CompileMeta::new(); let mut tag_codes = meta.compile(ast); @@ -986,16 +986,14 @@ fn take_test2() { let ast = parse!(parser, "take[1 2] R = X;").unwrap(); assert_eq!(ast, Expand(vec![ - Const::new("_0".into(), "1".into()).into(), - Const::new("_1".into(), "2".into()).into(), + LogicLine::SetArgs(vec!["1".into(), "2".into()].into()), Take("R".into(), "X".into()).into(), LogicLine::ConstLeak("R".into()), ]).into()); let ast = parse!(parser, "take[1 2] X;").unwrap(); assert_eq!(ast, Expand(vec![ - Const::new("_0".into(), "1".into()).into(), - Const::new("_1".into(), "2".into()).into(), + LogicLine::SetArgs(vec!["1".into(), "2".into()].into()), Take("__".into(), "X".into()).into(), ]).into()); } @@ -1963,13 +1961,13 @@ fn quick_dexp_take_test() { parse!(parser, r#" print Foo[1 2]; "#).unwrap(), - parse!(parser, r#" - print (__: - const _0 = 1; - const _1 = 2; - setres Foo; - ); - "#).unwrap(), + vec![LogicLine::Other(vec![ + Value::ReprVar("print".into()), + DExp::new("__".into(), vec![ + LogicLine::SetArgs(vec!["1".into(), "2".into()].into()), + LogicLine::SetResultHandle("Foo".into()), + ].into()).into(), + ].into())].into(), ); @@ -2213,7 +2211,7 @@ fn inline_block_test() { "#).unwrap(), Expand(vec![ InlineBlock(vec![ - LogicLine::Other(vec!["foo".into()]) + LogicLine::Other(vec!["foo".into()].into()) ]).into() ]).into() ); @@ -2251,14 +2249,14 @@ fn consted_dexp() { "___0".into(), DExp::new_nores(vec![ LogicLine::Label("x".into()), - LogicLine::Other(vec!["bar".into()]) + LogicLine::Other(vec!["bar".into()].into()) ].into()).into(), vec!["x".into()], ).into(), LogicLine::SetResultHandle("___0".into()), ].into() ).into() - ]), + ].into()), ]).into() ); @@ -3829,3 +3827,410 @@ fn string_escape_test() { assert!(res.is_err(), "fail: {:?} -> {:?}", res, src) } } + +#[test] +fn match_test() { + let parser = TopLevelParser::new(); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + inline@{ + print @; + } + ); + take Foo[a b c]; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + print @; + ); + take Foo[a b c]; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + take Foo[a b c]; + print @; + "#).unwrap()).compile().unwrap(), + Vec::<&str>::new(), + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c {} + print @; + "#).unwrap()).compile().unwrap(), + Vec::<&str>::new(), + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c { @{} } + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( // 作用域测试 + CompileMeta::new().compile(parse!(parser, r#" + { + match a b c { @{} } + } + print @; + "#).unwrap()).compile().unwrap(), + Vec::<&str>::new(), + ); + + assert_eq!( // 作用域测试 + CompileMeta::new().compile(parse!(parser, r#" + match a b c { @{} } + inline@{ + print @; + } + print end; + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + "print end", + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( // 作用域测试 + CompileMeta::new().compile(parse!(parser, r#" + match a b c { @{} } + inline 2@{ + foo @; + } + print end; + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "foo a b", + "foo c", + "print end", + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c { __ @{} } + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c { + X Y {} + @{} + } + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b { + X Y {} + @{} + } + print @; + "#).unwrap()).compile().unwrap(), + Vec::<&str>::new(), + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b { + X Y {} + @{} + } + print X Y; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + match @ { + Fst @ { + print Fst; + take Foo[@]; + } + { + print end; + } + } + ); + take Foo[a b c]; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print b", + "print c", + "print end", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + match @ { + @ Lst { + print Lst; + take Foo[@]; + } + { + print end; + } + } + ); + take Foo[a b c]; + "#).unwrap()).compile().unwrap(), + vec![ + "print c", + "print b", + "print a", + "print end", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + match @ { + Fst @ Lst { + print Fst; + print Lst; + take Foo[@]; + } + Mid { + print Mid; + } + { + print end; + } + } + ); + take Foo[a b c d e]; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print e", + "print b", + "print d", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( + match @ { + Fst @ Lst { + print Fst; + print Lst; + take Foo[@]; + } + Mid { + print Mid; + } + { + print end; + } + } + ); + take Foo[a b c d e f]; + "#).unwrap()).compile().unwrap(), + vec![ + "print a", + "print f", + "print b", + "print e", + "print c", + "print d", + "print end", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( # 循环展开版本 + inline@{ + match @ { + [1] { + print one; + } + [2] { + print two; + } + N:[3 4] { + print three_or_four N; + } + N { + print other N; + } + } + } + ); + take Foo[1 2 3 4 5 6]; + "#).unwrap()).compile().unwrap(), + vec![ + "print one", + "print two", + "print three_or_four", + "print 3", + "print three_or_four", + "print 4", + "print other", + "print 5", + "print other", + "print 6", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( # 右递归版本 + match @ { + [1] @ { + print one; + take Foo[@]; + } + [2] @ { + print two; + take Foo[@]; + } + N:[3 4] @ { + print three_or_four N; + take Foo[@]; + } + N @ { + print other N; + take Foo[@]; + } + } + ); + take Foo[1 2 3 4 5 6]; + "#).unwrap()).compile().unwrap(), + vec![ + "print one", + "print two", + "print three_or_four", + "print 3", + "print three_or_four", + "print 4", + "print other", + "print 5", + "print other", + "print 6", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Foo = ( # 左递归版本 + match @ { + @ [1] { + take Foo[@]; + print one; + } + @ [2] { + take Foo[@]; + print two; + } + @ N:[3 4] { + take Foo[@]; + print three_or_four N; + } + @ N { + take Foo[@]; + print other N; + } + } + ); + take Foo[1 2 3 4 5 6]; + "#).unwrap()).compile().unwrap(), + vec![ + "print one", + "print two", + "print three_or_four", + "print 3", + "print three_or_four", + "print 4", + "print other", + "print 5", + "print other", + "print 6", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Eq = ( # 引用前部匹配 + match @ { + A B { + match B { + [A] { + print 'equal' A; + } + __ { + print not_equal A B; + } + } + } + } + ); + take Eq[a a]; + take Eq[a b]; + "#).unwrap()).compile().unwrap(), + vec![ + "print equal", + "print a", + "print not_equal", + "print a", + "print b", + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index d62c047..7b09f0f 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.1.1" +version = "0.1.2" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/syntax/src/lib.rs b/tools/syntax/src/lib.rs index 10f7cee..eb08029 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -9,7 +9,7 @@ use std::{ process::exit, mem::{self, replace}, fmt::{Display, Debug}, - convert::identity, + convert::identity, borrow::Borrow, hash::Hash, }; use tag_code::{ Jump, @@ -106,6 +106,7 @@ impl From<((Location, Location), Errors)> for Error { pub enum Errors { NotALiteralUInteger(String, ParseIntError), SetVarNoPatternValue(usize, usize), + ArgsRepeatChunkByZero, } /// 带有错误前缀, 并且文本为红色的eprintln @@ -351,6 +352,7 @@ impl Value { .map(|x| (x, true)) }, LogicLine::Other(args) => { + let args = args.as_normal()?; let Value::ReprVar(cmd) = &args[0] else { return None; }; @@ -664,11 +666,11 @@ impl Meta { /// 单纯的构建一个set语句 pub fn build_set(var: Value, value: Value) -> LogicLine { - LogicLine::Other(vec![ + LogicLine::Other(Args::Normal(vec![ Value::ReprVar("set".into()), var, value, - ]) + ])) } } @@ -1641,9 +1643,11 @@ pub struct Expand(pub Vec); impl Compile for Expand { fn compile(self, meta: &mut CompileMeta) { meta.with_block(|this| { - for line in self.0 { - line.compile(this) - } + this.with_env_args_block(|this| { + for line in self.0 { + line.compile(this) + } + }); }); } } @@ -1920,25 +1924,6 @@ impl Compile for Const { #[derive(Debug, PartialEq, Clone)] pub struct Take(pub Var, pub Value); impl Take { - /// 根据一系列构建一系列常量传参 - pub fn build_arg_consts(values: Vec, mut f: impl FnMut(Const)) { - for (i, value) in values.into_iter().enumerate() { - let name = format!("_{}", i); - f(Const(name, value, Vec::with_capacity(0))) - } - } - - /// 将常量传参的行构建到Expand末尾 - pub fn build_arg_consts_to_expand( - values: Vec, - expand: &mut Vec, - ) { - Self::build_arg_consts( - values, - |r#const| expand.push(r#const.into()) - ) - } - /// 构建一个Take语句单元 /// 可以带有参数与返回值 /// @@ -1949,18 +1934,18 @@ impl Take { /// - do_leak_res: 是否泄露绑定量 /// - value: 被求的值 pub fn new( - args: Vec, + args: Args, var: Var, do_leak_res: bool, value: Value, ) -> LogicLine { - if args.is_empty() { + if matches!(args, Args::Normal(ref args) if args.is_empty()) { Take(var, value).into() } else { - let mut len = args.len() + 1; + let mut len = 2; if do_leak_res { len += 1 } let mut expand = Vec::with_capacity(len); - Self::build_arg_consts_to_expand(args, &mut expand); + expand.push(LogicLine::SetArgs(args)); if do_leak_res { expand.push(Take(var.clone(), value).into()); expand.push(LogicLine::ConstLeak(var)); @@ -1978,6 +1963,265 @@ impl Compile for Take { } } +/// 可能含有一个展开的Args +#[derive(Debug, PartialEq, Clone)] +pub enum Args { + /// 正常的参数 + Normal(Vec), + /// 夹杂一个展开的参数 + Expanded(Vec, Vec), +} +impl Args { + pub fn args<'a>(&'a self, meta: &'a mut CompileMeta) -> impl Iterator { + static EMPTY: &[Value] = &[]; + match self { + Self::Normal(args) => args.iter().chain(EMPTY).chain(EMPTY), + Self::Expanded(left, right) => { + let expanded_args = meta.get_env_args(); + left.iter() + .chain(expanded_args) + .chain(right) + }, + } + } + + pub fn into_args(self, meta: &mut CompileMeta) -> Vec { + match self { + Args::Normal(args) => args, + Args::Expanded(mut left, right) => { + let expanded_args = meta.get_env_args(); + left.extend(expanded_args.iter().cloned().chain(right)); + left + }, + } + } + + pub fn base_len(&self) -> usize { + match self { + Self::Normal(args) => args.len(), + Self::Expanded(prefix, suffix) => prefix.len() + suffix.len(), + } + } + + /// Returns `true` if the args is [`Normal`]. + /// + /// [`Normal`]: Args::Normal + #[must_use] + pub fn is_normal(&self) -> bool { + matches!(self, Self::Normal(..)) + } + + pub fn as_normal(&self) -> Option<&Vec> { + if let Self::Normal(v) = self { + Some(v) + } else { + None + } + } + + pub fn into_normal(self) -> Result, Self> { + match self { + Self::Normal(args) => Ok(args), + this => Err(this), + } + } + + /// Returns `true` if the args is [`Expanded`]. + /// + /// [`Expanded`]: Args::Expanded + #[must_use] + pub fn is_expanded(&self) -> bool { + matches!(self, Self::Expanded(..)) + } +} +impl Default for Args { + fn default() -> Self { + Self::Normal(vec![]) + } +} +impl_enum_froms!(impl From for Args { + Normal => Vec; +}); + +/// 拿取指定个参数, 并重复块中代码 +#[derive(Debug, PartialEq, Clone)] +pub struct ArgsRepeat { + count: usize, + block: InlineBlock, +} +impl ArgsRepeat { + pub fn new(count: usize, block: InlineBlock) -> Self { + Self { count, block } + } + + pub fn count(&self) -> usize { + self.count + } + + pub fn count_mut(&mut self) -> &mut usize { + &mut self.count + } + + pub fn block(&self) -> &InlineBlock { + &self.block + } + + pub fn block_mut(&mut self) -> &mut InlineBlock { + &mut self.block + } +} +impl Compile for ArgsRepeat { + fn compile(self, meta: &mut CompileMeta) { + let chunks: Vec> = meta.get_env_args().chunks(self.count) + .map(|chunks| chunks.iter().cloned().collect()) + .collect(); + for args in chunks { + let args = Vec::from_iter(args.iter().cloned()); + meta.with_env_args_block(|meta| { + meta.set_env_args(args); + self.block.clone().compile(meta) + }); + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Match { + args: Args, + cases: Vec<(MatchPat, InlineBlock)>, +} +impl Compile for Match { + fn compile(self, meta: &mut CompileMeta) { + let args = self.args.into_args(meta) + .into_iter() + .map(|x| x.take_handle(meta)) + .collect::>(); + let mut iter = self.cases.into_iter(); + loop { + let Some(case) = iter.next() else { break }; + let (pat, block) = case; + if pat.do_pattern(&args, meta) { + block.compile(meta); + break; + } + } + } +} +impl Match { + pub fn new(args: Args, cases: Vec<(MatchPat, InlineBlock)>) -> Self { + Self { args, cases } + } + + pub fn args(&self) -> &Args { + &self.args + } + + pub fn cases(&self) -> &[(MatchPat, InlineBlock)] { + self.cases.as_ref() + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum MatchPat { + Normal(Vec), + Expanded(Vec, Vec), +} +impl MatchPat { + pub fn base_len(&self) -> usize { + match self { + Self::Normal(args) => args.len(), + Self::Expanded(prefix, suffix) => prefix.len() + suffix.len(), + } + } + + /// 进行匹配, 如果成功则直接将量绑定 + pub fn do_pattern(self, args: &[Var], meta: &mut CompileMeta) -> bool { + fn to_vars(args: Vec, meta: &mut CompileMeta) + -> Vec<(Var, Vec)> { + args.into_iter() + .map(|arg| ( + arg.name, + arg.pattern.into_iter() + .map(|pat| pat.take_handle(meta)) + .collect() + )) + .collect() + } + fn cmp(pats: &[(Var, Vec)], args: &[Var]) -> bool { + pats.iter() + .map(|(_, x)| &x[..]) + .zip(args) + .all(|(pat, var)| { + pat.is_empty() + || pat.iter().any(|x| x == var) + }) + } + fn binds(name: Var, value: &Var, meta: &mut CompileMeta) { + if !name.is_empty() { + meta.add_const_value(Const(name, value.clone().into(), vec![])); + } + } + match self { + Self::Normal(iargs) if iargs.len() == args.len() => { + let pats: Vec<(Var, Vec)> = to_vars(iargs, meta); + cmp(&pats, args).then(|| { + for ((name, _), arg) in pats.into_iter().zip(args) { + binds(name, arg, meta) + } + }).is_some() + }, + Self::Expanded(prefix, suffix) + if self.base_len() <= args.len() => { + let (prefix, suffix) + = (to_vars(prefix, meta), to_vars(suffix, meta)); + let tl = args.len()-suffix.len(); + let extracted = &args[prefix.len()..tl]; + (cmp(&prefix, args) && cmp(&suffix, &args[tl..])) + .then(|| { + let (a, b) = ( + prefix.into_iter().zip(args), + suffix.into_iter().zip(&args[tl..]), + ); + for ((name, _), arg) in a.chain(b) { + binds(name, arg, meta) + } + meta.set_env_args(Vec::from_iter( + extracted.iter().cloned().map(Into::into))) + }).is_some() + }, + _ => false, + } + } +} +impl From> for MatchPat { + fn from(value: Vec) -> Self { + Self::Normal(value) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct MatchPatAtom { + name: Var, + pattern: Vec, +} +impl MatchPatAtom { + pub fn new(name: Var, pattern: Vec) -> Self { + Self { name, pattern } + } + + pub fn new_unnamed(pattern: Vec) -> Self { + Self::new("".into(), pattern) + } + + pub fn name(&self) -> &str { + self.name.as_ref() + } + + pub fn pattern(&self) -> &[Value] { + self.pattern.as_ref() + } +} + #[derive(Debug, PartialEq, Clone)] pub enum LogicLine { Op(Op), @@ -1985,7 +2229,7 @@ pub enum LogicLine { /// 否则无法将它注册到可能的`const` Label(Var), Goto(Goto), - Other(Vec), + Other(Args), Expand(Expand), InlineBlock(InlineBlock), Select(Select), @@ -1998,6 +2242,9 @@ pub enum LogicLine { ConstLeak(Var), /// 将返回句柄设置为一个指定值 SetResultHandle(Value), + SetArgs(Args), + ArgsRepeat(ArgsRepeat), + Match(Match), } impl Compile for LogicLine { fn compile(self, meta: &mut CompileMeta) { @@ -2010,7 +2257,7 @@ impl Compile for LogicLine { meta.push(data) }, Self::Other(args) => { - let handles: Vec = args + let handles: Vec = args.into_args(meta) .into_iter() .map(|val| val.take_handle(meta)) .collect(); @@ -2020,6 +2267,15 @@ impl Compile for LogicLine { let new_dexp_handle = value.take_handle(meta); meta.set_dexp_handle((new_dexp_handle, false)); }, + Self::SetArgs(args) => { + let expand_args = args.into_args(meta); + let mut f = |r#const| meta.add_const_value(r#const); + for (i, value) in expand_args.clone().into_iter().enumerate() { + let name = format!("_{}", i); + f(Const(name, value, Vec::with_capacity(0)).into()); + } + meta.set_env_args(expand_args); + }, Self::Select(select) => select.compile(meta), Self::Expand(expand) => expand.compile(meta), Self::InlineBlock(block) => block.compile(meta), @@ -2028,6 +2284,8 @@ impl Compile for LogicLine { Self::Const(r#const) => r#const.compile(meta), Self::Take(take) => take.compile(meta), Self::ConstLeak(r#const) => meta.add_const_value_leak(r#const), + Self::ArgsRepeat(args_repeat) => args_repeat.compile(meta), + Self::Match(r#match) => r#match.compile(meta), Self::Ignore => (), } } @@ -2108,7 +2366,7 @@ impl LogicLine { matches!(self, Self::Other(..)) } - pub fn as_other(&self) -> Option<&Vec> { + pub fn as_other(&self) -> Option<&Args> { if let Self::Other(v) = self { Some(v) } else { @@ -2140,6 +2398,8 @@ impl_enum_froms!(impl From for LogicLine { Select => Select; Const => Const; Take => Take; + ArgsRepeat => ArgsRepeat; + Match => Match; }); impl TryFrom<&TagLine> for LogicLine { type Error = LogicLineFromTagError; @@ -2182,7 +2442,7 @@ impl TryFrom<&TagLine> for LogicLine { _ => { let mut args_value = Vec::with_capacity(args.len()); args_value.extend(args.into_iter().map(Into::into)); - Ok(Self::Other(args_value)) + Ok(Self::Other(Args::Normal(args_value))) }, } }, @@ -2235,13 +2495,18 @@ impl ConstData { /// 每层Expand的环境 #[derive(Debug, PartialEq, Clone)] #[derive(Default)] +#[non_exhaustive] pub struct ExpandEnv { leak_vars: Vec, consts: HashMap, } impl ExpandEnv { pub fn new(leak_vars: Vec, consts: HashMap) -> Self { - Self { leak_vars, consts } + Self { + leak_vars, + consts, + ..Default::default() + } } pub fn leak_vars(&self) -> &[String] { @@ -2268,6 +2533,7 @@ pub struct CompileMeta { tag_codes: TagCodes, tmp_var_count: Counter Var>, expand_env: Vec, + env_args: Vec>>, /// 每层DExp所使用的句柄, 末尾为当前层, 同时有一个是否为自动分配名称的标志 dexp_result_handles: Vec<(Var, bool)>, tmp_tag_count: Counter Var>, @@ -2316,6 +2582,7 @@ impl CompileMeta { tag_codes, tmp_var_count: Counter::new(Self::tmp_var_getter), expand_env: Vec::new(), + env_args: Vec::new(), dexp_result_handles: Vec::new(), tmp_tag_count: Counter::new(Self::tmp_tag_getter), const_expand_tag_name_map: Vec::new(), @@ -2422,6 +2689,7 @@ impl CompileMeta { let ExpandEnv { leak_vars: leaks, consts: mut res, + .. } = this.expand_env.pop().unwrap(); // do leak @@ -2517,6 +2785,18 @@ impl CompileMeta { .insert(var, ConstData::new(value, labels)) } + /// 从当前作用域移除一个常量到值的映射 + pub fn remove_const_value(&mut self, query: Q) -> Option + where Var: Borrow + Eq + Hash, + Q: Hash + Eq, + { + self.expand_env + .last_mut() + .unwrap() + .consts + .remove(&query) + } + /// 新增一层DExp, 并且传入它使用的返回句柄 pub fn push_dexp_handle(&mut self, handle: (Var, bool)) { self.dexp_result_handles.push(handle) @@ -2639,6 +2919,30 @@ impl CompileMeta { pub fn const_var_namespace(&self) -> &[ExpandEnv] { self.expand_env.as_ref() } + + /// 获取最内层args, 如果不存在则返回空切片 + pub fn get_env_args(&self) -> &[Value] { + static EMPTY: &[Value] = &[]; + self.env_args.iter().map(Option::as_ref) + .filter_map(identity) + .map(Vec::as_slice) + .next_back() + .unwrap_or(EMPTY) + } + + /// 设置最内层args, 返回旧值 + pub fn set_env_args(&mut self, expand_args: Vec) -> Option> { + let args = self.env_args.last_mut().unwrap(); + replace(args, expand_args.into()) + } + + pub fn with_env_args_block(&mut self, f: F) -> Option> + where F: FnOnce(&mut Self) + { + self.env_args.push(None); + let _ = f(self); + self.env_args.pop().unwrap() + } } pub fn line_first_add(lines: &mut Vec, insert: &str) { diff --git a/tools/var_utils/Cargo.toml b/tools/var_utils/Cargo.toml index b546726..8e79f12 100644 --- a/tools/var_utils/Cargo.toml +++ b/tools/var_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "var_utils" -version = "0.4.0" +version = "0.5.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/var_utils/src/lib.rs b/tools/var_utils/src/lib.rs index 30a9bb8..e9ac4eb 100644 --- a/tools/var_utils/src/lib.rs +++ b/tools/var_utils/src/lib.rs @@ -18,7 +18,7 @@ pub const VAR_KEYWORDS: &[&str] = {&[ "asin", "atan", "break", "case", "ceil", "const", "continue", "cos", "div", "do", "elif", "else", "equal", "floor", "goto", "greaterThan", "greaterThanEq", "gwhile", "idiv", "if", "inline", - "land", "len", "lessThan", "lessThanEq", "lnot", "log", "max", + "land", "len", "lessThan", "lessThanEq", "lnot", "log", "match", "max", "min", "mod", "mul", "noise", "noop", "not", "notEqual", "op", "or", "pow", "print", "rand", "select", "set", "setres", "shl", "shr", "sin", "skip", "sqrt", "strictEqual",