From 3407690c0aef0c804c10b79e2e34d9ac6b4cabfb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 14 Nov 2024 22:04:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=97=AD=E5=8C=85=E6=8D=95?= =?UTF-8?q?=E8=8E=B7=E7=8E=AF=E5=A2=83=E5=8F=82=E6=95=B0=E7=9A=84=E8=83=BD?= =?UTF-8?q?=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 10 +- Cargo.toml | 2 +- examples/closured_value.mdtlbl | 18 ++++ tools/display_source/Cargo.toml | 2 +- tools/display_source/src/impls.rs | 87 ++++++++++----- tools/display_source/src/lib.rs | 20 +++- tools/parser/Cargo.toml | 2 +- tools/parser/src/parser.lalrpop | 4 +- tools/parser/tests/Cargo.toml | 2 +- tools/parser/tests/src/lib.rs | 174 ++++++++++++++++++++++++++++++ tools/syntax/Cargo.toml | 2 +- tools/syntax/src/lib.rs | 82 ++++++++++---- 12 files changed, 347 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32435aa..31adaf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "display_source" -version = "0.3.24" +version = "0.3.25" dependencies = [ "parser", "syntax", @@ -284,7 +284,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mindustry_logic_bang_lang" -version = "0.17.9" +version = "0.17.10" dependencies = [ "display_source", "logic_lint", @@ -330,7 +330,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.3.22" +version = "0.3.23" dependencies = [ "lalrpop", "lalrpop-util", @@ -341,7 +341,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.38" +version = "0.1.39" dependencies = [ "either", "parser", @@ -530,7 +530,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.43" +version = "0.2.44" dependencies = [ "either", "itermaps", diff --git a/Cargo.toml b/Cargo.toml index a35202a..f4327f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.17.9" +version = "0.17.10" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/closured_value.mdtlbl b/examples/closured_value.mdtlbl index 15a7d01..d3c88cd 100644 --- a/examples/closured_value.mdtlbl +++ b/examples/closured_value.mdtlbl @@ -96,3 +96,21 @@ print "expected" print "unexpected" jump 4 always 0 0 *# + + +# 在0.17.10版本添加了捕获环境参数的功能, 会在展开闭包前设置环境参数 +const Builder = ( + const X = 2; + const $.F = ([X @]( + print X @; + )); +); + +const Clos = Builder[a b c]->F; +take Clos[d e f]; +#* >>> +print 2 +print a +print b +print c +*# diff --git a/tools/display_source/Cargo.toml b/tools/display_source/Cargo.toml index 925e585..44f208c 100644 --- a/tools/display_source/Cargo.toml +++ b/tools/display_source/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "display_source" -version = "0.3.24" +version = "0.3.25" 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 3cdbce8..8d02d8d 100644 --- a/tools/display_source/src/impls.rs +++ b/tools/display_source/src/impls.rs @@ -72,16 +72,18 @@ impl DisplaySource for ClosuredValue { catch_labels, value, labels, + catch_args, } => { meta.push("(["); - meta.display_source_iter_by_splitter( - |meta| { + meta.display_source_iter_by_space(catch_values); + if *catch_args { + if !catch_values.is_empty() { meta.add_space(); - }, - catch_values, - ); + } + meta.push("@"); + } if !catch_labels.is_empty() { - if !catch_values.is_empty() { + if !catch_values.is_empty() || *catch_args { meta.add_space(); } meta.push("|"); @@ -100,6 +102,7 @@ impl DisplaySource for ClosuredValue { bind_handle, rename_labels, vars, + reset_argc: reset_args, } => { struct CatchedVar<'a>(&'a Var); impl DisplaySource for CatchedVar<'_> { @@ -114,14 +117,17 @@ impl DisplaySource for ClosuredValue { let catcheds = vars.iter() .map(|var| CatchedVar(var)); meta.push("(["); - meta.display_source_iter_by_splitter( - |meta| { + meta.display_source_iter_by_space(catcheds); + if let Some(args) = reset_args { + if !vars.is_empty() { meta.add_space(); - }, - catcheds, - ); + } + meta.push("@("); + meta.push_fmt(args); + meta.push(")"); + } if !rename_labels.is_empty() { - if !vars.is_empty() { + if !vars.is_empty() || reset_args.is_some() { meta.add_space(); } meta.push("|"); @@ -525,8 +531,7 @@ 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); + meta.display_source_iter_by_space(args); }, Args::Expanded(prefix, suffix) => { prefix.iter().for_each(|arg| { @@ -570,10 +575,7 @@ impl DisplaySource for MatchPatAtom { } if show_list { meta.push("["); - meta.display_source_iter_by_splitter( - DisplaySourceMeta::add_space, - self.pattern() - ); + meta.display_source_iter_by_space(self.pattern()); meta.push("]"); } } @@ -582,10 +584,7 @@ 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, - ) + meta.display_source_iter_by_space(args) }, MatchPat::Expanded(prefix, suffix) => { for s in prefix { @@ -654,8 +653,7 @@ impl DisplaySource for ConstMatchPatAtom { if self.pattern().is_right() { meta.push("?"); } - meta.display_source_iter_by_splitter( - DisplaySourceMeta::add_space, + meta.display_source_iter_by_space( self.pattern() .as_ref() .map_right(Some) @@ -669,10 +667,7 @@ impl DisplaySource for ConstMatchPat { fn display_source(&self, meta: &mut DisplaySourceMeta) { match self { ConstMatchPat::Normal(args) => { - meta.display_source_iter_by_splitter( - DisplaySourceMeta::add_space, - args, - ) + meta.display_source_iter_by_space(args) }, ConstMatchPat::Expanded(prefix, suffix) => { for s in prefix { @@ -1176,6 +1171,42 @@ fn display_source_test() { "const X = ([A:A B:B | :c :d]2);" ); + assert_eq!( + parse!(line_parser, r#" + const X = ([@]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([@]2);" + ); + + assert_eq!( + parse!(line_parser, r#" + const X = ([A @]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([A:A @]2);" + ); + + assert_eq!( + parse!(line_parser, r#" + const X = ([A @ | :c]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([A:A @ | :c]2);" + ); + + assert_eq!( + parse!(line_parser, r#" + const X = ([@ | :c]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([@ | :c]2);" + ); + assert_eq!( parse!(line_parser, r#" (a:) (`b`:); diff --git a/tools/display_source/src/lib.rs b/tools/display_source/src/lib.rs index 3970bbb..aa638d0 100644 --- a/tools/display_source/src/lib.rs +++ b/tools/display_source/src/lib.rs @@ -1,4 +1,5 @@ -use std::{ops::Deref, mem}; +use core::fmt; +use std::{fmt::Write, mem, ops::Deref}; pub mod impls; pub const LF: char = '\n'; @@ -85,6 +86,12 @@ impl DisplaySourceMeta { self.buffer.push_str(s) } + /// 添加字符串到缓冲区 + pub fn push_fmt(&mut self, f: impl fmt::Display) { + self.check_indent(); + self.buffer.write_fmt(format_args!("{f}")).unwrap() + } + /// 如果缩进标志位打开, 则向缓冲区加入缩进 pub fn check_indent(&mut self) { if self.do_indent_flag { @@ -179,6 +186,17 @@ impl DisplaySourceMeta { s.display_source(self) }) } + + /// 从可迭代对象中生成, 以空格分割 + pub fn display_source_iter_by_space<'a, T: DisplaySource + 'a>( + &mut self, + iter: impl IntoIterator + ) { + self.display_source_iter_by_splitter( + |meta| meta.add_space(), + iter, + ) + } } pub trait DisplaySource { diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index d7a0f93..ab37314 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.3.22" +version = "0.3.23" 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 c52ed7a..4cb6b0d 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -1028,16 +1028,18 @@ ClosuredValue: ClosuredValue = { MLTuple< ( ClosuredValueCatch* + Opt<"@"> ("|" )? ), (ConstStart ), > => { - let ((catchs, catch_labels), (value, labels)) = <>; + let ((catchs, catch_args, catch_labels), (value, labels)) = <>; ClosuredValue::Uninit { catch_values: catchs, catch_labels: catch_labels.unwrap_or_default(), value: value.into(), labels, + catch_args, } }, } diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index f4a1a55..b7f473e 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.38" +version = "0.1.39" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/parser/tests/src/lib.rs b/tools/parser/tests/src/lib.rs index e60f269..f8d8b48 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -7333,3 +7333,177 @@ fn to_label_code_test() { ], ); } + +#[test] +fn closure_catch_args_test() { + let parser = TopLevelParser::new(); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c => @ {} + const Clos = ([@]( + print @; + )); + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c => @ {} + const Clos = ([@]( + print @; + )); + match d e f => @ {} + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c => @ {} + const Clos = ([@]( + print @; + )); + match d e f => @ {} + take Clos[]; + print @; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + r#"print d"#, + r#"print e"#, + r#"print f"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c => @ {} + const Clos = ([@]( + print @; + )); + const a = 2; + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + match a b c => @ {} + const Clos = ([@]( + print @; + )); + take Clos[1 2]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( // moved value owned test + CompileMeta::new().compile(parse!(parser, r#" + const Builder = ( + const $.F = ([@]( + print @; + )); + ); + match 1 2 3 => @ {} + const Clos = Builder[a b c]->F; + match 4 5 6 => @ {} + take Clos; + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Builder = ( + const $.F = ([@]( + print @; + )); + ); + const Clos = Builder[a b c]->F; + const a = 1; + take Clos; + const b = 2; + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print a"#, + r#"print b"#, + r#"print c"#, + r#"print a"#, + r#"print b"#, + r#"print c"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Builder = ( + const $.F = ([@]( + print @; + )); + ); + const Clos = Builder[(x: + print run; + )]->F; + print split; + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print split"#, + r#"print run"#, + r#"print x"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Builder = ( + const $.F = ([P:(print pre;) @]( + print @; + )); + ); + const Clos = Builder[(x: + print run; + )]->F; + print split; + take Clos[]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print pre"#, + r#"print split"#, + r#"print run"#, + r#"print x"#, + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index d44e15d..a00482e 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.43" +version = "0.2.44" 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 135c9d2..0c91e26 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -723,11 +723,13 @@ pub enum ClosuredValue { catch_labels: Vec, value: Box, labels: Vec, + catch_args: bool, }, Inited { bind_handle: Var, rename_labels: HashMap, vars: Vec, + reset_argc: Option, }, /// 操作过程中使用, 过程外不能使用 Empty, @@ -752,6 +754,7 @@ impl ClosuredValue { catch_labels, value, labels, + catch_args, } = this else { panic!() }; let vars = catch_values.iter() @@ -769,18 +772,31 @@ impl ClosuredValue { let r#const = Const(key, *value, labels); r#const.compile(meta); - let mut rename_labels = catch_labels.into_iter() + let rename_labels = catch_labels.into_iter() .map(|name| ( name.clone(), meta.get_in_const_label(name), )) .collect::>(); - rename_labels.shrink_to_fit(); + + let reset_argc = catch_args.then(|| { + let args = meta.get_env_args().to_vec(); + let len = args.len(); + for (i, arg) in args.into_iter().enumerate() { + let key = ValueBind( + Value::Var(bindh.clone()).into(), + format!("_{i}").into(), + ); + Const(key.into(), arg.into(), vec![]).compile(meta); + } + len + }); *self = Self::Inited { bind_handle: bindh, rename_labels, vars, + reset_argc, }; } @@ -792,11 +808,12 @@ impl ClosuredValue { bind_handle, rename_labels, vars, + reset_argc, } = self else { panic!("closured value uninit, {:?}", self) }; let mut result = None; - meta.with_block(|meta| { + let f = |meta: &mut CompileMeta| { for var in vars { let handle = meta.get_value_binded( bind_handle.clone(), @@ -809,13 +826,27 @@ impl ClosuredValue { rename_labels, |meta| { let binded = meta.get_value_binded( - bind_handle, + bind_handle.clone(), Self::BINDED_VALUE_NAME.into(), ); result = f(binded, meta).into(); }, ); - }); + }; + if let Some(argc) = reset_argc { + meta.with_block_and_env_args(|meta| { + meta.set_env_args((0..argc).map(|i| { + let name = format!("_{i}").into(); + ValueBindRef::new( + Box::new(bind_handle.clone().into()), + ValueBindRefTarget::NameBind(name), + ) + })); + f(meta); + }); + } else { + meta.with_block(f); + } result.unwrap() } @@ -2795,7 +2826,7 @@ impl Compile for ArgsRepeat { .collect(); for args in chunks { let args = Vec::from_iter(args.iter().cloned()); - meta.with_env_args_block(|meta| { + meta.with_env_args_scope(|meta| { meta.set_env_args(args); self.block.clone().compile(meta) }); @@ -4052,11 +4083,11 @@ impl CompileMeta { /// like `with_block(|meta| meta.with_env_args_block(f))` pub fn with_block_and_env_args(&mut self, f: F) -> (HashMap, Option>) - where F: FnOnce(&mut Self) + where F: FnOnce(&mut Self), { let mut inner_res = None; let block_res = self.with_block(|meta| { - inner_res = Some(meta.with_env_args_block(f)) + inner_res = Some(meta.with_env_args_scope(f)) }); (block_res, inner_res.unwrap()) @@ -4140,11 +4171,7 @@ impl CompileMeta { if let Some(extra_binder) = extra_binder { data = data.set_binder(extra_binder) } - self.expand_env - .last_mut() - .unwrap() - .consts - .insert(var, data) + self.add_local_const_data(var, data) }, ConstKey::ValueBind(ValueBind(binder, name)) => { let binder_handle = binder.take_handle(self); @@ -4153,14 +4180,33 @@ impl CompileMeta { .unwrap_or_else(|| binder_handle.clone())); let binded = self.get_value_binded( binder_handle, name); - self.expand_env.first_mut() // global - .unwrap() - .consts_mut() - .insert(binded, data) + self.add_global_const_data(binded, data) }, } } + pub fn add_global_const_data( + &mut self, + name: Var, + data: ConstData, + ) -> Option { + self.expand_env.first_mut() // global + .unwrap() + .consts_mut() + .insert(name, data) + } + + pub fn add_local_const_data( + &mut self, + name: Var, + data: ConstData, + ) -> Option { + self.expand_env.last_mut() // global + .unwrap() + .consts_mut() + .insert(name, data) + } + /// 从当前作用域移除一个常量到值的映射 pub fn remove_const_value(&mut self, query: Q) -> Option where Var: Borrow + Eq + Hash, @@ -4395,7 +4441,7 @@ impl CompileMeta { self.set_env_second_args(args); } - pub fn with_env_args_block(&mut self, f: F) -> Option> + pub fn with_env_args_scope(&mut self, f: F) -> Option> where F: FnOnce(&mut Self) { self.env_args.push(None);