diff --git a/Cargo.lock b/Cargo.lock index 3537f8f..2d3f754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,7 +301,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.16.14" +version = "0.16.15" dependencies = [ "display_source", "logic_lint", @@ -347,7 +347,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.3.14" +version = "0.3.15" dependencies = [ "lalrpop", "lalrpop-util", @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.28" +version = "0.1.29" dependencies = [ "parser", "syntax", @@ -535,7 +535,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.33" +version = "0.2.34" dependencies = [ "either", "tag_code", diff --git a/Cargo.toml b/Cargo.toml index 0060ea0..7fafea2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.16.14" +version = "0.16.15" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/op_expr.mdtlbl b/examples/op_expr.mdtlbl index f4877af..b706c96 100644 --- a/examples/op_expr.mdtlbl +++ b/examples/op_expr.mdtlbl @@ -47,7 +47,8 @@ * 对于三元表达式, 也就是`if c?a:b`, * 它是低优先级的, 所以参与高优先级运算时需要括号括起. * 就算是`1+if x != y ? x : y`这样的, 也需要写成`1+(if x != y ? x : y)` -* 想要取消这个设计的方案有几个但都极其复杂, 所以就如现在这样 +* 想要取消这个设计的方案有几个但都极其复杂, 所以就如现在这样. +* 注意, 结果带值而非运算的三元表达式最好写在顶层, 不然可能出现不必要的赋值 * * 在0.14.21版本中, 添加了一个语法糖, 可以用 `(?x: a+b)` 来表示 `(x: $=a+b;)` *# @@ -166,3 +167,19 @@ a, b, c = 1 + 2 * 3; * 在0.14.17版本, 可以对常见的运算使用自运算格式, 这会简化一些代码 *# x += n*2; + + +#** +* 在0.16.15版本, 这些自运算可以使用顶层赋值可用的操作了, +* 例如 +*# +a, b += c + d; +x, y += w, h; + +#* >>> +op add __0 c d +op add a a __0 +op add b b __0 +op add x x w +op add y y h +*# diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index 55642a3..ea50dd0 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.3.14" +version = "0.3.15" 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 544cf04..e625d0b 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -1,5 +1,6 @@ // this is a lalrpop file +#![allow(unused_braces)] #![allow(clippy::just_underscores_and_digits)] #![allow(clippy::needless_lifetimes)] #![allow(clippy::deprecated_cfg_attr)] @@ -22,7 +23,9 @@ use ::syntax::{ ClosuredValueMethod, op_expr_build_op, op_expr_build_results, + op_expr_tools::top_assign_oper, OpExprInfo, + OpExprAOperFun, JumpCmp, CmpTree, Goto, @@ -547,58 +550,64 @@ Args2: Vec = { OpExpr: LogicLine = { LEnd => { - let (mut results, value) = <>; + let (mut results, value, oper) = <>; results.reverse(); - op_expr_build_results(meta, results, vec![value]) + op_expr_build_results(meta, results, vec![value], oper) }, LEnd => { - let (mut results, values) = <>; + let (mut results, values, oper) = <>; results.reverse(); - op_expr_build_results(meta, results, values) + op_expr_build_results(meta, results, values, oper) }, - LEnd => <>.into(), } #[inline] OpExprBodySetR: Expand = OpExprBody => { - let line = op_expr_build_results(meta, vec![ResultHandle], vec![<>]); + let line = op_expr_build_results( + meta, + vec![ResultHandle], vec![<>], + |meta, res, v| v.into_logic_line(meta, res), + ); vec![line].into() }; OpExprDExp: DExp = METuple>; -IOpExpr: Expand = { - "+=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Add (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "-=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Sub (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "*=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Mul (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "/=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Div (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "//=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Idiv(tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "%=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Mod (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "**=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Pow (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "&&=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Land(tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "<<=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Shl (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - ">>=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Shr (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "|=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Or (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "&=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::And (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, - "^=" => { let tmp = meta.get_tmp_var(); vec![Take(tmp.clone().into(), v).into(), Op::Xor (tmp.clone().into(), tmp.into(), r.into_value(meta)).into()].into() }, +OpExprAssignOper: OpExprAOperFun = { + "+=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Add (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "-=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Sub (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "*=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Mul (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "/=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Div (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "//=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Idiv(tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "%=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Mod (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "**=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Pow (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "&&=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Land(tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "<<=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Shl (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + ">>=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Shr (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "|=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Or (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "&=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::And (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "^=" => { |meta, res, v| { let tres = meta.get_tmp_var(); Expand(vec![Take(tres.clone().into(), res).into(), Op::Xor (tres.clone().into(), tres.into(), v.into_value(meta)).into()]).into() } }, + "=" => { top_assign_oper }, } // 多个接收者的OpExpr, 例如`a, b, c = 1;` -OpExprMultipleResult: (Vec, OpExprInfo) = { - "=" => { - (vec![result], value) +OpExprMultipleResult: (Vec, OpExprInfo, OpExprAOperFun) = { + => { + (vec![result], value, oper) }, - "," => { - let (mut results, value) = body; - results.push(result); - (results, value) + "," => { + body.0.push(result); + body }, } // 例如`a, b = 1, 2`返回`([b, a], [1, 2])` -OpExprDoMultiple: (Vec, Vec) = { - "," "=" "," - => (vec![res2, res1], vec![val1, val2]), +OpExprDoMultiple: (Vec, Vec, OpExprAOperFun) = { + "," + + "," + => (vec![res2, res1], vec![val1, val2], oper), + "," "," => { - let (results, values) = &mut mid; + let (results, values, _) = &mut mid; results.push(result); values.push(value); mid @@ -1296,3 +1305,4 @@ ControlWithOptionalEnd: LogicLine = { Expand(res).into() }, } +// vim:nowrap diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index 5444d40..7b32385 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.28" +version = "0.1.29" 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 cc4a30d..5236eb9 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -2496,6 +2496,31 @@ fn op_expr_test() { take Foo = (m: $ = a+b;); "#).unwrap(), ); + + assert_eq!( + parse!(parser, r#" + a, b += 2; + "#).unwrap(), + parse!(parser, r#" + { + take ___0 = 2; + {take ___1 = a; ___1 = ___1 + ___0;} + {take ___2 = b; ___2 = ___2 + ___0;} + } + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + a, b += 2, 3; + "#).unwrap(), + parse!(parser, r#" + { + a += 2; + b += 3; + } + "#).unwrap(), + ); } #[test] diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 4a31c6f..b9fae1b 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.33" +version = "0.2.34" 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 3ea7e83..09725fb 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -4635,9 +4635,12 @@ impl OpExprInfo { } impl_enum_froms!(impl From for OpExprInfo { Value => Value; + Value => Var; Op => Op; }); +pub type OpExprAOperFun = fn(&mut Meta, Value, OpExprInfo) -> LogicLine; + /// 构建一个op运算 pub fn op_expr_build_op(f: F) -> OpExprInfo where F: FnOnce() -> Op @@ -4648,6 +4651,7 @@ pub fn op_expr_build_results( meta: &mut Meta, mut results: Vec, mut values: Vec, + f: OpExprAOperFun, ) -> LogicLine { match (results.len(), values.len()) { e @ ((0, _) | (_, 0)) => unreachable!("len by zero, {e:?}"), @@ -4656,9 +4660,9 @@ pub fn op_expr_build_results( results.pop().unwrap(), values.pop().unwrap(), ); - value.into_logic_line(meta, result) + f(meta, result, value) }, - (len, 1) => { + (len, 1) if f == op_expr_tools::top_assign_oper => { let mut lines = Vec::with_capacity(len + 1); let value = values.pop().unwrap(); let mut results = results.into_iter(); @@ -4678,6 +4682,18 @@ pub fn op_expr_build_results( assert_eq!(lines.len(), len + 1); Expand(lines).into() }, + (len, 1) => { + let mut lines = Vec::with_capacity(len + 1); + let value = values.pop().unwrap() + .into_value(meta); + let tmp = meta.get_tmp_var(); + lines.push(Take(tmp.clone().into(), value).into()); + for result in results { + let line = f(meta, result, tmp.clone().into()); + lines.push(line); + } + Expand(lines).into() + }, (res_len, val_len) => { assert_eq!(res_len, val_len); @@ -4686,10 +4702,22 @@ pub fn op_expr_build_results( = results.into_iter().zip(values); for (result, value) in ziped { - let line = value.into_logic_line(meta, result); + let line = f(meta, result, value); lines.push(line) } Expand(lines).into() }, } } + +pub mod op_expr_tools { + use super::{Meta, Value, OpExprInfo, LogicLine}; + + pub fn top_assign_oper( + meta: &mut Meta, + res: Value, + value: OpExprInfo, + ) -> LogicLine { + value.into_logic_line(meta, res) + } +}