From bc569c6ef388fb454ceec06ad147a2f39422ebf7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 2 Dec 2023 05:59:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E5=8C=96select=E8=AF=AD=E5=8F=A5,=20?= =?UTF-8?q?=E4=BD=BF=E5=85=B6=E7=BC=96=E8=AF=91=E6=97=B6=E4=BC=9A=E5=9C=A8?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E6=A8=A1=E5=BC=8F=E5=92=8C=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E8=A1=A8=E5=BC=8F=E4=B8=AD=E6=8B=A9=E4=BC=98=E9=80=89=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 同时修改其填充语句为跳转, 使其更高效及可以追踪其它跳转语句 --- Cargo.lock | 2 +- Cargo.toml | 2 +- examples/control.mdtlbl | 4 +- examples/switch.mdtlbl | 22 ++++---- src/syntax/mod.rs | 114 +++++++++++++++++++++++++++++++++++++--- src/syntax/tests.rs | 61 +++++++++++++-------- 6 files changed, 161 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f36d89..965ac5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,7 +288,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.13.2" +version = "0.13.3" dependencies = [ "display_source", "lalrpop", diff --git a/Cargo.toml b/Cargo.toml index 72a2660..98ab2c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.13.2" +version = "0.13.3" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/control.mdtlbl b/examples/control.mdtlbl index 86ef3a8..12100f8 100644 --- a/examples/control.mdtlbl +++ b/examples/control.mdtlbl @@ -123,11 +123,11 @@ select x { op mul __0 x 2 op add @counter @counter __0 print 0 -noop +jump 4 always 0 0 print 1 print "is one!" print 2 -noop +jump 0 always 0 0 *# # select是原始版本, 没有switch那么多的功能, # 但是相比switch的好处是不用编写case, 直接跳转到指定行 diff --git a/examples/switch.mdtlbl b/examples/switch.mdtlbl index 0060dce..2156fa7 100644 --- a/examples/switch.mdtlbl +++ b/examples/switch.mdtlbl @@ -23,26 +23,24 @@ switch i { printflush message1; #* 以上代码会生成如下结构: set i 4 -op mul __0 i 3 -op add @counter @counter __0 -noop -noop -noop +op add @counter @counter i +jump 8 always 0 0 +jump 8 always 0 0 +jump 11 always 0 0 +jump 14 always 0 0 +jump 16 always 0 0 +jump 18 always 0 0 print "1 or 2\n" print "foo" -jump 21 always 0 0 +jump 19 always 0 0 print "1 or 2\n" print "foo" -jump 21 always 0 0 +jump 19 always 0 0 print "3\n" -jump 21 always 0 0 -noop +jump 19 always 0 0 print "4\n" print "穿透到5\n" -noop print "5\n" -noop -noop printflush message1 *# # 可以看到, 我们在case尾部不跳出, 我们就会进入到下一个case. diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 3adb9be..af1db57 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1768,19 +1768,107 @@ impl Compile for Select { .count() }).collect(); let max_len = lens.iter().copied().max().unwrap_or_default(); + let target = self.0; + if let 0 | 1 = cases.len() { + Take("__".into(), target).compile(meta); + if let Some(case) = cases.pop() { + meta.tag_codes_mut().extend(case) + } + assert!(cases.is_empty(), "{}", cases.len()); + return + } + + let simple_select_len = match max_len { + 0 => 0, + 1 => cases.len() + 1, + n => n * cases.len() + 2, + }; + let goto_table_select_len = match max_len { + 0 => 0, + _ => cases.len() + 1 + cases.iter().map(Vec::len).sum::(), + }; + + #[cfg(debug_assertions)] + let old_tag_codes_len = meta.tag_codes.count_no_tag(); + if simple_select_len <= goto_table_select_len { + Self::build_simple_select(target, max_len, meta, lens, cases); + #[cfg(debug_assertions)] + assert_eq!( + meta.tag_codes.count_no_tag(), + old_tag_codes_len + simple_select_len, + "预期长度公式错误\n{}", + meta.tag_codes, + ); + } else { + Self::build_goto_table_select(target, max_len, meta, lens, cases); + #[cfg(debug_assertions)] + assert_eq!( + meta.tag_codes.count_no_tag(), + old_tag_codes_len + goto_table_select_len, + "预期长度公式错误\n{}", + meta.tag_codes, + ); + } + } +} + +impl Select { + fn build_goto_table_select( + target: Value, + max_len: usize, + meta: &mut CompileMeta, + lens: Vec, + cases: Vec>, + ) { + let counter = Value::ReprVar(COUNTER.into()); + + if max_len == 0 { + return Self::build_simple_select(target, max_len, meta, lens, cases) + } + + Op::Add( + counter.clone(), + counter, + target + ).compile(meta); + + let tmp_tags: Vec + = repeat_with(|| meta.get_tmp_tag()) + .take(cases.len()) + .collect(); + tmp_tags.iter() + .cloned() + .map(|tag| Goto(tag, CmpTree::default())) + .for_each(|goto| goto.compile(meta)); + + let mut tags_iter = tmp_tags.into_iter(); + for case in cases { + let tag = tags_iter.next().unwrap(); + LogicLine::Label(tag).compile(meta); + meta.tag_codes.lines_mut().extend(case); + } + } + + fn build_simple_select( + target: Value, + max_len: usize, + meta: &mut CompileMeta, + lens: Vec, + mut cases: Vec>, + ) { let counter = Value::ReprVar(COUNTER.into()); // build head let head = match max_len { 0 => { // no op - Take("__".into(), self.0).compile_take(meta) + Take("__".into(), target).compile_take(meta) }, 1 => { // no mul Op::Add( counter.clone(), counter, - self.0 + target ).compile_take(meta) }, // normal @@ -1788,7 +1876,7 @@ impl Compile for Select { let tmp_var = meta.get_tmp_var(); let mut head = Op::Mul( tmp_var.clone().into(), - self.0, + target, Value::ReprVar(max_len.to_string()) ).compile_take(meta); let head_1 = Op::Add( @@ -1803,10 +1891,21 @@ impl Compile for Select { // 填补不够长的`case` for (len, case) in zip(lens, &mut cases) { - case.extend( - repeat_with(Default::default) - .take(max_len - len) - ) + match max_len - len { + 0 => continue, + insert_counts => { + let end_tag = meta.get_tmp_tag(); + let end_tag = meta.get_tag(end_tag); + case.push(TagLine::Jump( + tag_code::Jump::new_always(end_tag).into() + )); + case.extend( + repeat_with(Default::default) + .take(insert_counts - 1) + ); + case.push(TagLine::TagDown(end_tag)); + }, + } } let lines = meta.tag_codes.lines_mut(); @@ -2281,6 +2380,7 @@ pub trait Compile { /// /// 使用时需要考虑其副作用, 例如`compile`并不止做了`push`至尾部, /// 它还可能做了其他事 + #[must_use] fn compile_take(self, meta: &mut CompileMeta) -> Vec where Self: Sized { diff --git a/src/syntax/tests.rs b/src/syntax/tests.rs index 24b26de..e9ed7ac 100644 --- a/src/syntax/tests.rs +++ b/src/syntax/tests.rs @@ -428,18 +428,18 @@ fn switch_test() { assert_eq!(lines, [ "op mul __0 2 2", "op add @counter @counter __0", - "noop", + "jump 4 always 0 0", "noop", "print 1", - "noop", + "jump 6 always 0 0", "print 2", "print 4", - "noop", + "jump 10 always 0 0", "noop", "print 2", "print 4", "print 5", - "noop", + "jump 0 always 0 0" ]); let ast = parse!(parser, r#" @@ -1557,14 +1557,14 @@ fn select_test() { } "#).unwrap()).compile().unwrap(); assert_eq!(logic_lines, vec![ - "op mul __0 1 2", - "op add @counter @counter __0", - "print 0", - "noop", - "print 1", - "print \" is one!\"", - "print 2", - "noop", + "op mul __0 1 2", + "op add @counter @counter __0", + "print 0", + "jump 4 always 0 0", + "print 1", + "print \" is one!\"", + "print 2", + "jump 0 always 0 0", ]); let logic_lines = CompileMeta::new().compile(parse!(parser, r#" @@ -1575,17 +1575,17 @@ fn select_test() { } "#).unwrap()).compile().unwrap(); assert_eq!(logic_lines, vec![ - "op add @counter @counter x", - "print 0", - "print 1", - "print 2", + "op add @counter @counter x", + "print 0", + "print 1", + "print 2", ]); let logic_lines = CompileMeta::new().compile(parse!(parser, r#" select (y: op $ x + 2;) {} "#).unwrap()).compile().unwrap(); assert_eq!(logic_lines, vec![ - "op add y x 2", + "op add y x 2", ]); let logic_lines = CompileMeta::new().compile(parse!(parser, r#" @@ -1593,6 +1593,25 @@ fn select_test() { "#).unwrap()).compile().unwrap(); assert_eq!(logic_lines, Vec::<&str>::new()); + let logic_lines = CompileMeta::new().compile(parse!(parser, r#" + select m { + print 0; + print 1 " is one!" ", one!!"; + print 2; + } + "#).unwrap()).compile().unwrap(); + assert_eq!(logic_lines, vec![ // 跳转表式, 因为这样更省行数 + "op add @counter @counter m", + "jump 4 always 0 0", + "jump 5 always 0 0", + "jump 8 always 0 0", + "print 0", + "print 1", + "print \" is one!\"", + "print \", one!!\"", + "print 2", + ]); + } #[test] @@ -1905,10 +1924,10 @@ fn switch_catch_test() { stop; } select tmp { - goto :mis _; - noop; - goto :mis _; - noop; + goto :mis; + {} + goto :mis; + {} } "#).unwrap()).compile().unwrap()); }