Skip to content

Commit

Permalink
强化select语句, 使其编译时会在简单模式和跳转表式中择优选用
Browse files Browse the repository at this point in the history
- 同时修改其填充语句为跳转, 使其更高效及可以追踪其它跳转语句
  • Loading branch information
A4-Tacks committed Dec 1, 2023
1 parent 269b533 commit bc569c6
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mindustry_logic_bang_lang"
version = "0.13.2"
version = "0.13.3"
edition = "2021"

authors = ["A4-Tacks <[email protected]>"]
Expand Down
4 changes: 2 additions & 2 deletions examples/control.mdtlbl
Original file line number Diff line number Diff line change
Expand Up @@ -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, 直接跳转到指定行
Expand Down
22 changes: 10 additions & 12 deletions examples/switch.mdtlbl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
114 changes: 107 additions & 7 deletions src/syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,27 +1768,115 @@ 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::<usize>(),
};

#[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<usize>,
cases: Vec<Vec<TagLine>>,
) {
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<Var>
= 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<usize>,
mut cases: Vec<Vec<TagLine>>,
) {
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
_ => {
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(
Expand All @@ -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();
Expand Down Expand Up @@ -2281,6 +2380,7 @@ pub trait Compile {
///
/// 使用时需要考虑其副作用, 例如`compile`并不止做了`push`至尾部,
/// 它还可能做了其他事
#[must_use]
fn compile_take(self, meta: &mut CompileMeta) -> Vec<TagLine>
where Self: Sized
{
Expand Down
61 changes: 40 additions & 21 deletions src/syntax/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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#"
Expand Down Expand Up @@ -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#"
Expand All @@ -1575,24 +1575,43 @@ 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#"
select x {}
"#).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]
Expand Down Expand Up @@ -1905,10 +1924,10 @@ fn switch_catch_test() {
stop;
}
select tmp {
goto :mis _;
noop;
goto :mis _;
noop;
goto :mis;
{}
goto :mis;
{}
}
"#).unwrap()).compile().unwrap());
}
Expand Down

0 comments on commit bc569c6

Please sign in to comment.