diff --git a/Cargo.lock b/Cargo.lock index 2d3f754..9f7451a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,7 +301,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.16.15" +version = "0.16.16" dependencies = [ "display_source", "logic_lint", @@ -347,7 +347,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.3.15" +version = "0.3.16" dependencies = [ "lalrpop", "lalrpop-util", @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.29" +version = "0.1.30" dependencies = [ "parser", "syntax", @@ -535,7 +535,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.34" +version = "0.2.35" dependencies = [ "either", "tag_code", diff --git a/Cargo.toml b/Cargo.toml index 7fafea2..bc1b476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.16.15" +version = "0.16.16" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/op_expr.mdtlbl b/examples/op_expr.mdtlbl index b706c96..7f644a4 100644 --- a/examples/op_expr.mdtlbl +++ b/examples/op_expr.mdtlbl @@ -7,32 +7,36 @@ * 以下为各种运算符的优先级与结合性: * | 符号 | 实际运算(op) | 优先级 | 结合性 | * | ---------- | ------------ | ------ | ------ | -* | `a ** b` | `a ** b` | -1 | RL | -* | `! x` | `x != false` | -2 | R | -* | `- x` | `0 - x` | -2 | R | -* | `~ x` | `~ x` | -2 | R | -* | `a * b` | `a * b` | -3 | LR | -* | `a / b` | `a / b` | -3 | LR | -* | `a % b` | `a % b` | -3 | LR | -* | `a // b` | `a // b` | -3 | LR | -* | `a + b` | `a + b` | -4 | LR | -* | `a - b` | `a - b` | -4 | LR | -* | `a << b` | `a << b` | -5 | LR | -* | `a >> b` | `a >> b` | -5 | LR | -* | `a & b` | `a & b` | -6 | LR | -* | `a ^ b` | `a ^ b` | -7 | LR | -* | `a | b` | `a | b` | -8 | LR | -* | `a < b` | `a < b` | -9 | - | -* | `a > b` | `a > b` | -9 | - | -* | `a <= b` | `a <= b` | -9 | - | -* | `a >= b` | `a >= b` | -9 | - | -* | `a == b` | `a == b` | -10 | - | -* | `a != b` | `a != b` | -10 | - | -* | `a === b` | `a === b` | -10 | - | -* | `a !== b` | `a !== b` | -10 | - | -* | `a && b` | `a && b` | -11 | LR | -* | `a || b` | `a + b` | -12 | LR | -* | `if c?a:b` | ... | -13 | LR | +* | `++ a` | `++ a` | -1 | - | +* | `-- a` | `-- a` | -1 | - | +* | `a ++` | `a ++` | -1 | - | +* | `a --` | `a --` | -1 | - | +* | `a ** b` | `a ** b` | -2 | RL | +* | `! x` | `x != false` | -3 | R | +* | `- x` | `0 - x` | -3 | R | +* | `~ x` | `~ x` | -3 | R | +* | `a * b` | `a * b` | -4 | LR | +* | `a / b` | `a / b` | -4 | LR | +* | `a % b` | `a % b` | -4 | LR | +* | `a // b` | `a // b` | -4 | LR | +* | `a + b` | `a + b` | -5 | LR | +* | `a - b` | `a - b` | -5 | LR | +* | `a << b` | `a << b` | -6 | LR | +* | `a >> b` | `a >> b` | -6 | LR | +* | `a & b` | `a & b` | -7 | LR | +* | `a ^ b` | `a ^ b` | -8 | LR | +* | `a | b` | `a | b` | -9 | LR | +* | `a < b` | `a < b` | -10 | - | +* | `a > b` | `a > b` | -10 | - | +* | `a <= b` | `a <= b` | -10 | - | +* | `a >= b` | `a >= b` | -10 | - | +* | `a == b` | `a == b` | -11 | - | +* | `a != b` | `a != b` | -11 | - | +* | `a === b` | `a === b` | -11 | - | +* | `a !== b` | `a !== b` | -11 | - | +* | `a && b` | `a && b` | -12 | LR | +* | `a || b` | `a + b` | -13 | LR | +* | `if c?a:b` | ... | -14 | LR | * * 结合性的`LR`指左结合 比如`a+b+c`结合为`(a+b)+c` * 而结合性为`-`指不发生结合, 将需要添加括号 @@ -170,7 +174,7 @@ x += n*2; #** -* 在0.16.15版本, 这些自运算可以使用顶层赋值可用的操作了, +* 在0.16.15版本, 这些顶层自运算可以使用顶层赋值可用的操作了, * 例如 *# a, b += c + d; @@ -183,3 +187,49 @@ op add b b __0 op add x x w op add y y h *# + + +#** +* 在0.16.16版本添加了单元自运算, 熟悉的++ --终于登场了 +*# + +j = i++; +k = ++i; +#* >>> +set j i +op add i i 1 +op add i i 1 +set k i +*# + +# 同时还有一个扩展用法, 因为如`x+i++`这种情况`i++`被展开为值而非行, +# 则可能产生不希望的set, 所以可以使用扩展语法将外层操作挪到内层去 +a = x + i++; +#* >>> +set __0 i +op add i i 1 +op add a x __0 +*# +b = i++(x + _); +#* >>> +op add b x i +op add i i 1 +*# +# 可以看到, 使用这种方式使`i++`操作又回到了顶层, 从而被展开成行, 保证了性能 +# 这个问题在if else也有类似例子. +# `_`是占位表达式, 它会构建时被替换为外层的`i++`中i被take的句柄 +# `i++(...)`中的`(...)`接受正常的op-expr, 然后会将其展开成行, +# 返回句柄视外层的`i++`而定, 如果外层不是展开成行, 那么它会直接赋给`$` + +# 还有一个单独的简单用法, 比如 +i++; j--; +#* A >>> +{ + take ___0 = i; + op ___0 ___0 + `1`; +} +{ + take ___1 = j; + op ___1 ___1 - `1`; +} +*# diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index ea50dd0..c76f3c1 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.3.15" +version = "0.3.16" 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 e625d0b..277683c 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -100,6 +100,8 @@ MakeDExpBody: DExp = { "`" "`" ":" => DExp::new_notake(var, value.into()), } +#[inline] +Boxed: Box = T => Box::new(<>); CtrlBreakStart: () = () => meta.add_control_break_level(None); CtrlContinueStart: () = () => meta.add_control_continue_level(None); @@ -160,7 +162,7 @@ NonConstRangeValue: Value = { "$" => ResultHandle, > => { // QuickDExpTake - DExp::new("__".into(), vec![ + DExp::new(meta.unnamed_var(), vec![ LogicLine::SetArgs(args.unwrap_or_default()), LogicLine::SetResultHandle(value), ].into()).into() @@ -183,7 +185,7 @@ NonConstRangeValue: Value = { }, "->" > => { // Refed QuickDExpTake - let value = DExp::new("__".into(), vec![ + let value = DExp::new(meta.unnamed_var(), vec![ LogicLine::SetArgs(args.unwrap_or_default()), LogicLine::SetResultHandle(value), ].into()).into(); @@ -202,7 +204,7 @@ pub Value: Value = { "const" ConstStart )>> => { let tmp_name = meta.get_tmp_var(); let dexp_const = Const(tmp_name.clone().into(), value.into(), labels); - DExp::new("__".into(), vec![ + DExp::new(meta.unnamed_var(), vec![ dexp_const.into(), LogicLine::SetResultHandle(tmp_name.into()), ].into()).into() @@ -416,7 +418,7 @@ ConstMatchPatAtom: ConstMatchPatAtom = { ConstMatchPatAtom::new_guard( dotake, name.unwrap_or_default(), - DExp::new("__".into(), vec![ + DExp::new(meta.unnamed_var(), vec![ LogicLine::from(Match::new( Args::GLOB_ONLY, vec![ @@ -559,6 +561,7 @@ OpExpr: LogicLine = { results.reverse(); op_expr_build_results(meta, results, values, oper) }, + LEnd, } #[inline] OpExprBodySetR: Expand = OpExprBody => { @@ -813,8 +816,57 @@ OpExprCallOp: OpExprInfo = { OpExprAtom: OpExprInfo = { Value => OpExprInfo::Value(<>), + OpExprValueSelfOps, MTuple, OpExprCallOp, + "_" => OpExprInfo::Ref(meta.op_expr_ref()), +} +OpExprTopSelfOps: LogicLine = { + "++" => { + let tmp = meta.get_tmp_var(); + Expand(vec![ + Take(tmp.clone().into(), <>).into(), + Op::Add(tmp.clone().into(), tmp.into(), ReprVar("1".into())).into(), + ]).into() + }, + "--" => { + let tmp = meta.get_tmp_var(); + Expand(vec![ + Take(tmp.clone().into(), <>).into(), + Op::Sub(tmp.clone().into(), tmp.into(), ReprVar("1".into())).into(), + ]).into() + }, +} +OpExprSelfOpsSuf: () = O => { + OpExprInfo::add_suf_selfop_handle_to_meta(meta); +}; +OpExprValueSelfOps: OpExprInfo = { + "++" => { + OpExprInfo::Value(DExp::new(meta.unnamed_var(), vec![ + LogicLine::SetResultHandle(<>), + Op::Add(ResultHandle, ResultHandle, ReprVar("1".into())).into(), + ].into()).into()) + }, + "--" => { + OpExprInfo::Value(DExp::new(meta.unnamed_var(), vec![ + LogicLine::SetResultHandle(<>), + Op::Sub(ResultHandle, ResultHandle, ReprVar("1".into())).into(), + ].into()).into()) + }, + OpExprSelfOpsSuf<"++"> >?> => { + OpExprInfo::new_suf_selfop( + meta, + Op::Add(ResultHandle, ResultHandle, ReprVar("1".into())), + <>, + ) + }, + OpExprSelfOpsSuf<"--"> >?> => { + OpExprInfo::new_suf_selfop( + meta, + Op::Sub(ResultHandle, ResultHandle, ReprVar("1".into())), + <>, + ) + }, } // 开始一个const, 开启了必须负责清理 @@ -842,7 +894,7 @@ pub BuiltinCommand: LogicLine = { Take::new( args.unwrap_or_default(), - var.unwrap_or_else(|| "__".into()), + var.unwrap_or_else(|| meta.unnamed_var()), do_leak_res, value ) @@ -853,12 +905,12 @@ pub BuiltinCommand: LogicLine = { LEnd => { if takes.len() == 1 { let (res, value) = takes.pop().unwrap(); - let res = res.unwrap_or_else(|| "__".into()); + let res = res.unwrap_or_else(|| meta.unnamed_var().into()); return Take(res, value).into(); } let mut lines = Vec::with_capacity(takes.len()); for (res, value) in takes.into_iter() { - let res = res.unwrap_or_else(|| "__".into()); + let res = res.unwrap_or_else(|| meta.unnamed_var().into()); let take = Take(res, value); lines.push(take.into()) } diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index 7b32385..f0484f6 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.29" +version = "0.1.30" 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 5236eb9..20c0c1d 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -2470,15 +2470,6 @@ fn op_expr_test() { "#).unwrap(), ); - assert_eq!( - parse!(parser, r#" - x = y--2; - "#).unwrap(), - parse!(parser, r#" - x = y - -2; - "#).unwrap(), - ); - assert_eq!( parse!(parser, r#" take Foo = (?a+b); @@ -2521,6 +2512,123 @@ fn op_expr_test() { } "#).unwrap(), ); + + assert_eq!( + parse!(parser, r#" + x = ++i; + "#).unwrap(), + parse!(parser, r#" + x = (__: + setres i; + $ = $ + `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = --i; + "#).unwrap(), + parse!(parser, r#" + x = (__: + setres i; + $ = $ - `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = i++; + "#).unwrap(), + parse!(parser, r#" + { + take ___0 = i; + x = ___0; + ___0 = ___0 + `1`; + } + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = 2 + ++i; + "#).unwrap(), + parse!(parser, r#" + x = 2 + (__: + setres i; + $ = $ + `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = 2 + --i; + "#).unwrap(), + parse!(parser, r#" + x = 2 + (__: + setres i; + $ = $ - `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = 2 + i++; + "#).unwrap(), + parse!(parser, r#" + x = 2 + ( + take ___0 = i; + $ = ___0; + ___0 = ___0 + `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = i++(2+_); + "#).unwrap(), + parse!(parser, r#" + { + take ___0 = i; + x = 2 + ___0; + ___0 = ___0 + `1`; + } + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = 8+i++(2+_); + "#).unwrap(), + parse!(parser, r#" + x = 8 + ( + take ___0 = i; + $ = 2 + ___0; + ___0 = ___0 + `1`; + ); + "#).unwrap(), + ); + + assert_eq!( + parse!(parser, r#" + x = 8+i++(j++(_) + _); + "#).unwrap(), + parse!(parser, r#" + x = 8 + ( + take ___0 = i; + $ = ( + take ___1 = j; + $ = ___1; + ___1 = ___1 + `1`; + ) + ___0; + ___0 = ___0 + `1`; + ); + "#).unwrap(), + ); } #[test] diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index b9fae1b..155c3c1 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.34" +version = "0.2.35" 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 09725fb..05524d0 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -274,6 +274,7 @@ pub const COUNTER: &str = "@counter"; pub const FALSE_VAR: &str = "false"; pub const ZERO_VAR: &str = "0"; pub const UNUSED_VAR: &str = "0"; +pub const UNNAMED_VAR: &str = "__"; pub trait TakeHandle: Sized { /// 编译依赖并返回句柄 @@ -1036,15 +1037,21 @@ pub struct Meta { defined_labels: Vec>, break_labels: Vec>, continue_labels: Vec>, + /// 用于op-expr的引用栈, 可以使用占位表达式引用最顶层的句柄 + op_expr_refs: Vec, + unnamed_var: Var, } impl Default for Meta { fn default() -> Self { + let unnamed_var = Var::from(UNNAMED_VAR); Self { tmp_var_count: 0, tag_number: 0, defined_labels: vec![HashSet::new()], break_labels: Vec::new(), continue_labels: Vec::new(), + op_expr_refs: vec![unnamed_var.clone()], + unnamed_var, } } } @@ -1214,6 +1221,17 @@ impl Meta { value, ])) } + + pub fn unnamed_var(&self) -> Var { + self.unnamed_var.clone() + } + + pub fn op_expr_ref(&self) -> Var { + self.op_expr_refs + .last() + .cloned() + .unwrap() + } } pub trait FromMdtArgs @@ -4546,6 +4564,7 @@ pub fn line_first_add(lines: &mut Vec, insert: &str) { } } +#[derive(Debug)] pub enum OpExprInfo { Value(Value), Op(Op), @@ -4555,6 +4574,13 @@ pub enum OpExprInfo { true_line: LogicLine, false_line: LogicLine, }, + SufSelfOp { + op: Op, + value: Value, + ext: Option>, + handle: Var, + }, + Ref(Var), } impl OpExprInfo { pub fn new_if_else( @@ -4572,6 +4598,26 @@ impl OpExprInfo { } } + pub fn new_suf_selfop( + meta: &mut Meta, + op: Op, + value: Value, + ext: Option>, + ) -> Self { + Self::SufSelfOp { + op, + value, + ext, + handle: meta.op_expr_refs.pop().unwrap(), + } + } + + pub fn add_suf_selfop_handle_to_meta(meta: &mut Meta) -> Var { + let tmp = meta.get_tmp_var(); + meta.op_expr_refs.push(tmp.clone()); + tmp + } + pub fn into_value(self, meta: &mut Meta) -> Value { match self { Self::Op(op) => { @@ -4599,6 +4645,28 @@ impl OpExprInfo { LogicLine::Label(skip_lab), ].into()).into() }, + Self::SufSelfOp { + mut op, + value, + ext, + handle, + } => { + let op_info = op.get_info_mut(); + *op_info.result = handle.clone().into(); + *op_info.arg1 = handle.clone().into(); + + let set_line = ext + .map(|ext| *ext) + .unwrap_or_else(|| Self::Value(handle.clone().into())) + .into_logic_line(meta, Value::ResultHandle); + + DExp::new_nores(vec![ + Take(handle.into(), value).into(), + set_line, + op.into(), + ].into()).into() + }, + Self::Ref(var) => var.into(), } } @@ -4630,6 +4698,31 @@ impl OpExprInfo { LogicLine::Label(skip_lab), ]).into() }, + Self::SufSelfOp { + mut op, + value, + ext, + handle, + } => { + let op_info = op.get_info_mut(); + *op_info.result = handle.clone().into(); + *op_info.arg1 = handle.clone().into(); + + let set_line = ext + .map(|ext| *ext) + .unwrap_or_else(|| Self::Value(handle.clone().into())) + .into_logic_line(meta, result); + + Expand(vec![ + Take(handle.into(), value).into(), + set_line, + op.into(), + ]).into() + }, + Self::Ref(var) => { + Self::Value(var.into()) + .into_logic_line(meta, result) + }, } } }