From 5ced7ce1b50a819c3c30df2cb283cf412788e1c0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 15 May 2024 12:46:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E9=97=AD=E5=8C=85=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=8D=95=E8=8E=B7=E6=A0=87=E7=AD=BE=E7=9A=84=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E5=B0=8F=E4=BF=AE=E6=95=B4=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 14 +-- Cargo.toml | 2 +- examples/closured_value.mdtlbl | 42 ++++++++ src/main.rs | 4 +- syntax/vim/mdtlbl.vim | 7 +- tools/display_source/Cargo.toml | 2 +- tools/display_source/src/impls.rs | 49 ++++++++- tools/display_source/src/lib.rs | 12 ++- tools/logic_lint/Cargo.toml | 2 +- tools/logic_lint/src/lints.rs | 2 +- tools/parser/Cargo.toml | 2 +- tools/parser/src/parser.lalrpop | 11 +- tools/parser/tests/Cargo.toml | 2 +- tools/parser/tests/src/lib.rs | 171 ++++++++++++++++++++++++++++++ tools/syntax/Cargo.toml | 2 +- tools/syntax/src/lib.rs | 91 +++++++++++----- tools/tag_code/Cargo.toml | 2 +- tools/tag_code/src/lib.rs | 2 +- 18 files changed, 362 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc33967..4628313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ dependencies = [ [[package]] name = "display_source" -version = "0.3.17" +version = "0.3.18" dependencies = [ "parser", "syntax", @@ -286,7 +286,7 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "logic_lint" -version = "0.1.6" +version = "0.1.7" dependencies = [ "lazy-regex", "tag_code", @@ -301,7 +301,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.16.9" +version = "0.16.10" dependencies = [ "display_source", "logic_lint", @@ -347,7 +347,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.3.12" +version = "0.3.13" dependencies = [ "lalrpop", "lalrpop-util", @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.25" +version = "0.1.26" dependencies = [ "parser", "syntax", @@ -535,7 +535,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.28" +version = "0.2.29" dependencies = [ "either", "tag_code", @@ -545,7 +545,7 @@ dependencies = [ [[package]] name = "tag_code" -version = "0.1.6" +version = "0.1.7" [[package]] name = "term" diff --git a/Cargo.toml b/Cargo.toml index d302646..2934ebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.16.9" +version = "0.16.10" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/closured_value.mdtlbl b/examples/closured_value.mdtlbl index 0dde627..15a7d01 100644 --- a/examples/closured_value.mdtlbl +++ b/examples/closured_value.mdtlbl @@ -10,6 +10,10 @@ * 有省略语法糖, 可以使用`A`表示`A:A`, 使用`&B`表示`&B:B` * * 语法起始处使用圆括号接方括号, 方括号内是捕获表, 方括号结束后接着一个值 +* +* 在0.16.10添加了对label的捕获, 终于不用怕内层label和外层的同名了 +* 使用方法在捕获的参数那里添加一个竖线, 然后在右边编写捕获的label +* 比如`([A B | :x](goto :x;))` *# const A = (a: print "makeA";); @@ -54,3 +58,41 @@ const Clos.V = ( print "inited"; print "do"; take Clos.V; + + +# 使用闭包捕获标记, 不会被内层跳转影响 +const F = ( + const Run = (const match @ { + F { + :x + print "unexpected"; + take F[]; + } + }); + + :x + print "expected"; + + take Run[([| :x]( + goto :x; + ))]; +); +take F F; # 这里证明它不会被展开标签重命名破坏 +#* >>> +print "expected" +print "unexpected" +jump 0 always 0 0 +print "expected" +print "unexpected" +jump 3 always 0 0 +*# +# 可以看到, 并没有跳到Run里面的:x, 而是闭包捕获到的 +# 如果我们将闭包去掉的话, 会得到我们不希望看到的, 如下 +#* >>> +print "expected" +print "unexpected" +jump 1 always 0 0 +print "expected" +print "unexpected" +jump 4 always 0 0 +*# diff --git a/src/main.rs b/src/main.rs index fe565d3..c940d4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,9 +40,9 @@ macro_rules! err { ( $($args:tt)* ) => {{ let str = format!($($args)*); let mut iter = str.lines(); - eprintln!("\x1b[1;31mMainError: {}\x1b[0m", iter.next().unwrap()); + eprintln!("\x1b[1;31mMainError: {}\x1b[22;39m", iter.next().unwrap()); for line in iter { - eprintln!(" \x1b[1;31m{}\x1b[0m", line); + eprintln!(" \x1b[1;31m{}\x1b[22;39m", line); } }}; } diff --git a/syntax/vim/mdtlbl.vim b/syntax/vim/mdtlbl.vim index d0dce5d..f81650d 100644 --- a/syntax/vim/mdtlbl.vim +++ b/syntax/vim/mdtlbl.vim @@ -73,11 +73,8 @@ syn match mdtlblDefineResultHandle /\%(([%?]\=\%(\s\|#\*.*\*#\|\%(#[^*].*\|#\)\= syn match mdtlblQuickDExpTakeIdent /\I\i*\%(\[\)\@=/ syn match mdtlblQuickDExpTakeIdent /'[^' \t]\+'\%(\[\)\@=/ syn match mdtlblQuickDExpTakeIdent /->/ -syn match mdtlblIdentLabel /:\I\i*/ -syn match mdtlblIdentLabel /:@\I\i*\%(-\i*\)*/ -syn match mdtlblIdentLabel /:'[^' \t]\+'/ -syn match mdtlblIdentLabel /:\v0%(x-=%(_)@![0-9a-fA-F_]+|x-=%(_)@![01_]+)/ -syn match mdtlblIdentLabel /:\v-=%(_)@![0-9_]+/ +syn match mdtlblIdentLabel /\v%(\w@1 { @@ -60,6 +61,17 @@ impl DisplaySource for ClosuredValue { }, catch_values, ); + if !catch_labels.is_empty() { + if !catch_values.is_empty() { + meta.add_space(); + } + meta.push("|"); + for label in catch_labels { + meta.add_space(); + meta.push(":"); + label.display_source(meta); + } + } meta.push("]"); value.display_source(meta); inline_labs(labels, meta); @@ -67,6 +79,7 @@ impl DisplaySource for ClosuredValue { }, ClosuredValue::Inited { bind_handle, + rename_labels, vars, } => { struct CatchedVar<'a>(&'a Var); @@ -80,15 +93,27 @@ impl DisplaySource for ClosuredValue { } } let catcheds = vars.iter() - .map(|var| CatchedVar(var)) - .collect::>(); + .map(|var| CatchedVar(var)); meta.push("(["); meta.display_source_iter_by_splitter( |meta| { meta.add_space(); }, - &catcheds, + catcheds, ); + if !rename_labels.is_empty() { + if !vars.is_empty() { + meta.add_space(); + } + meta.push("|"); + for (src, dst) in rename_labels { + meta.add_space(); + meta.push(":"); + src.display_source(meta); + meta.push("->:"); + dst.display_source(meta); + } + } meta.push("]"); bind_handle.display_source(meta); meta.push(")"); @@ -1060,4 +1085,22 @@ fn display_source_test() { }\ " ); + + assert_eq!( + parse!(line_parser, r#" + const X = ([| :c :d]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([| :c :d]2);" + ); + + assert_eq!( + parse!(line_parser, r#" + const X = ([A B | :c :d]2); + "#) + .unwrap() + .display_source_and_get(&mut meta), + "const X = ([A:A B:B | :c :d]2);" + ); } diff --git a/tools/display_source/src/lib.rs b/tools/display_source/src/lib.rs index f1f25f2..3970bbb 100644 --- a/tools/display_source/src/lib.rs +++ b/tools/display_source/src/lib.rs @@ -168,7 +168,7 @@ impl DisplaySourceMeta { pub fn display_source_iter_by_splitter<'a, T: DisplaySource + 'a>( &mut self, mut split: impl FnMut(&mut Self), - iter: impl IntoIterator + iter: impl IntoIterator ) { let mut iter = iter.into_iter(); if let Some(s) = iter.next() { @@ -202,6 +202,16 @@ where T: DisplaySource T::display_source_and_get(&self, meta) } } +impl DisplaySource for &'_ mut 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/logic_lint/Cargo.toml b/tools/logic_lint/Cargo.toml index 240a1d9..4fc8611 100644 --- a/tools/logic_lint/Cargo.toml +++ b/tools/logic_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logic_lint" -version = "0.1.6" +version = "0.1.7" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/logic_lint/src/lints.rs b/tools/logic_lint/src/lints.rs index 126b36a..508d75d 100644 --- a/tools/logic_lint/src/lints.rs +++ b/tools/logic_lint/src/lints.rs @@ -14,7 +14,7 @@ macro_rules! color_str { $(";", stringify!($num), )* "m", $str, - "\x1b[0m", + "\x1b[22;39m", ) }; } diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index 12e47ae..9a26712 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.3.12" +version = "0.3.13" 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 e9f3869..c5bd7dd 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -860,10 +860,17 @@ ClosuredValueCatch: ClosuredValueMethod = { => Const(name.into(), val, labels).into(), } ClosuredValue: ClosuredValue = { - MLTuple )> => { - let (catchs, (value, labels)) = <>; + MLTuple< + ( + ClosuredValueCatch* + ("|" )? + ), + (ConstStart ), + > => { + let ((catchs, catch_labels), (value, labels)) = <>; ClosuredValue::Uninit { catch_values: catchs, + catch_labels: catch_labels.unwrap_or_default(), value: value.into(), labels, } diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index a434b6e..9021466 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.25" +version = "0.1.26" 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 2e4a8c4..1e1f583 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -6492,3 +6492,174 @@ fn gswitch_test() { ], ); } + +#[test] +fn closure_catch_label_test() { + let parser = TopLevelParser::new(); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Run = ( + :x + print unexpected; + take _0; + ); + :x + print expected; + take Run[( + goto :x; + )]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print expected"#, + r#"print unexpected"#, + r#"jump 1 always 0 0"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Run = ( + :x + print unexpected; + take _0; + ); + :x + print expected; + take Run[([| :x]( + goto :x; + ))]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print expected"#, + r#"print unexpected"#, + r#"jump 0 always 0 0"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const F = ( + const Run = ( + :x + print unexpected; + take _0; + ); + :x + print expected; + take Run[([| :x]( + goto :x; + ))]; + ); + take F; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print expected"#, + r#"print unexpected"#, + r#"jump 0 always 0 0"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const F = ( + const Run = ( + :x + print unexpected; + take _0; + ); + :x + print expected; + take Run[([| :x]( + goto :x; + ))]; + ); + take F F; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print expected"#, + r#"print unexpected"#, + r#"jump 0 always 0 0"#, + r#"print expected"#, + r#"print unexpected"#, + r#"jump 3 always 0 0"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const F = ( + const Run = ( + :x + print unexpected; + take _0 _0; + ); + :x + print expected; + take Run[([| :x]( + goto :x; + ))]; + ); + take F F; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print expected"#, + r#"print unexpected"#, + r#"jump 0 always 0 0"#, + r#"jump 0 always 0 0"#, + r#"print expected"#, + r#"print unexpected"#, + r#"jump 4 always 0 0"#, + r#"jump 4 always 0 0"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const F = ( + const Run = ( + :x + print unexpected; + take _0 _0; + ); + take Run[([| :x]( + goto :x; + ))]; + :x + print expected; + ); + take F F; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print unexpected"#, + r#"jump 3 always 0 0"#, + r#"jump 3 always 0 0"#, + r#"print expected"#, + r#"print unexpected"#, + r#"jump 7 always 0 0"#, + r#"jump 7 always 0 0"#, + r#"print expected"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Run = ( + :x + print unexpected; + take _0 _0; + ); + take Run[([| :x]( + goto :x; + ))]; + :x + print expected; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print unexpected"#, + r#"jump 3 always 0 0"#, + r#"jump 3 always 0 0"#, + r#"print expected"#, + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 28bc295..995f05b 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.28" +version = "0.2.29" 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 f5af39a..6aa3e9e 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -153,7 +153,7 @@ pub enum Errors { /// 带有错误前缀, 并且文本为红色的eprintln macro_rules! err { ( $fmtter:expr $(, $args:expr)* $(,)? ) => { - eprintln!(concat!("\x1b[1;31m", "CompileError:\n", $fmtter, "\x1b[0m"), $($args),*); + eprintln!(concat!("\x1b[1;31m", "CompileError:\n", $fmtter, "\x1b[22;39m"), $($args),*); }; } @@ -790,11 +790,13 @@ impl_enum_froms!(impl From for ClosuredValueMethod { pub enum ClosuredValue { Uninit { catch_values: Vec, + catch_labels: Vec, value: Box, labels: Vec, }, Inited { bind_handle: Var, + rename_labels: HashMap, vars: Vec, }, /// 操作过程中使用, 过程外不能使用 @@ -809,7 +811,7 @@ impl ClosuredValue { key } - pub fn catch_vars(&mut self, meta: &mut CompileMeta) { + pub fn catch_env(&mut self, meta: &mut CompileMeta) { let this = match self { Self::Uninit { .. } => mem::replace(self, Self::Empty), Self::Inited { .. } => return, @@ -817,6 +819,7 @@ impl ClosuredValue { }; let Self::Uninit { catch_values, + catch_labels, value, labels, } = this else { panic!() }; @@ -836,14 +839,30 @@ impl ClosuredValue { let r#const = Const(key, *value, labels); r#const.compile(meta); - *self = Self::Inited { bind_handle: bindh, vars }; + let mut rename_labels = catch_labels.into_iter() + .map(|name| ( + name.clone(), + meta.get_in_const_label(name), + )) + .collect::>(); + rename_labels.shrink_to_fit(); + + *self = Self::Inited { + bind_handle: bindh, + rename_labels, + vars, + }; } } impl TakeHandle for ClosuredValue { fn take_handle(mut self, meta: &mut CompileMeta) -> Var { - self.catch_vars(meta); - let Self::Inited { bind_handle, vars } = self else { - panic!("self uninit, {:?}", self) + self.catch_env(meta); + let Self::Inited { + bind_handle, + rename_labels, + vars, + } = self else { + panic!("closured value uninit, {:?}", self) }; let mut result = None; meta.with_block(|meta| { @@ -855,9 +874,14 @@ impl TakeHandle for ClosuredValue { Const(ConstKey::Var(var), handle.into(), Vec::new()) .compile(meta); } - result = Self::make_valkey(bind_handle) - .take_handle(meta) - .into(); + meta.with_const_expand_tag_name_map_scope( + rename_labels, + |meta| { + result = Self::make_valkey(bind_handle) + .take_handle(meta) + .into(); + }, + ); }); result.unwrap() } @@ -2540,7 +2564,7 @@ impl Const { return self.extend_data(data.clone()); } }, - Value::ClosuredValue(closure) => closure.catch_vars(meta), + Value::ClosuredValue(closure) => closure.catch_env(meta), val @ Value::ValueBindRef(_) => { let Value::ValueBindRef(bindref) = replace(val, Value::ResultHandle) @@ -3782,6 +3806,7 @@ pub struct CompileMeta { /// 一个标签从尾部上寻, 寻到就返回找到的, 没找到就返回原本的 /// 所以它支持在宏A内部展开的宏B跳转到宏A内部的标记 const_expand_tag_name_map: Vec>, + /// 每层展开的句柄记录, 用于栈回溯 const_expand_names: Vec, const_expand_max_depth: usize, value_binds: HashMap<(Var, Var), Var>, @@ -4169,20 +4194,26 @@ impl CompileMeta { .unwrap_or(name) } + pub fn with_const_expand_tag_name_map_scope( + &mut self, + map: HashMap, + f: impl FnOnce(&mut Self) -> R, + ) -> (R, HashMap) { + self.const_expand_tag_name_map.push(map); + let result = f(self); + ( + result, + self.const_expand_tag_name_map.pop().unwrap(), + ) + } + /// 进入一层宏展开环境, 并且返回其值 /// 这个函数会直接调用获取函数将标记映射完毕, 然后返回其值 /// 如果不是一个宏则直接返回None, 也不会进入无需清理 pub fn const_expand_enter(&mut self, name: &Var) -> Option { let label_count = self.get_const_value(name)?.labels().len(); if self.const_expand_names.len() >= self.const_expand_max_depth { - self.log_err(format!( - "Stack Expand:\n{}", - self.debug_expand_stack() - .flat_map(|var| [ - var.to_string().into(), - Cow::Borrowed("\n"), - ]).into_iter_fmtter(), - )); + self.log_expand_stack::(); err!( "Maximum recursion depth exceeded ({})", self.const_expand_max_depth, @@ -4197,14 +4228,15 @@ impl CompileMeta { = self.get_const_value(name).unwrap(); let mut labels_map = HashMap::with_capacity(labels.len()); for (tmp_tag, label) in zip(tmp_tags, labels.iter().cloned()) { - labels_map.entry(label).or_insert_with_key(|label| { - format!( - "{}_const_{}_{}", - tmp_tag, - &name, - &label - ).into() - }); + labels_map.entry(label) + .or_insert_with_key(|label| { + format!( + "{}_const_{}_{}", + tmp_tag, + &name, + &label + ).into() + }); } let res = value.clone(); self.dexp_expand_binders.push(binder.clone()); @@ -4213,8 +4245,11 @@ impl CompileMeta { res.into() } - pub fn const_expand_exit(&mut self) - -> (Var, HashMap, Option) { + pub fn const_expand_exit(&mut self) -> ( + Var, + HashMap, + Option, + ) { ( self.const_expand_names.pop().unwrap(), self.const_expand_tag_name_map.pop().unwrap(), diff --git a/tools/tag_code/Cargo.toml b/tools/tag_code/Cargo.toml index c2f55a9..f38ac43 100644 --- a/tools/tag_code/Cargo.toml +++ b/tools/tag_code/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tag_code" -version = "0.1.6" +version = "0.1.7" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/tag_code/src/lib.rs b/tools/tag_code/src/lib.rs index f4598bb..88ba4c1 100644 --- a/tools/tag_code/src/lib.rs +++ b/tools/tag_code/src/lib.rs @@ -22,7 +22,7 @@ pub const UNINIT_TAG_TARGET: usize = usize::MAX; /// 带有错误前缀, 并且文本为红色的eprintln macro_rules! err { ( $fmtter:expr $(, $args:expr)* $(,)? ) => { - eprintln!(concat!("\x1b[1;31m", "TagCodeError: ", $fmtter, "\x1b[0m"), $($args),*); + eprintln!(concat!("\x1b[1;31m", "TagCodeError: ", $fmtter, "\x1b[22;39m"), $($args),*); }; }