diff --git a/Cargo.lock b/Cargo.lock index 9990071..4761a1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ dependencies = [ [[package]] name = "display_source" -version = "0.3.4" +version = "0.3.5" dependencies = [ "parser", "syntax", @@ -292,7 +292,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.14.2" +version = "0.14.3" dependencies = [ "display_source", "parser", @@ -337,7 +337,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.1.1" +version = "0.2.0" dependencies = [ "lalrpop", "lalrpop-util", @@ -348,7 +348,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.0" +version = "0.1.1" dependencies = [ "parser", "syntax", @@ -525,7 +525,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.1.2" +version = "0.2.0" dependencies = [ "tag_code", "utils", diff --git a/Cargo.toml b/Cargo.toml index 82ccef8..467a82b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.14.2" +version = "0.14.3" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/value_bind.mdtlbl b/examples/value_bind.mdtlbl index 6896311..b200197 100644 --- a/examples/value_bind.mdtlbl +++ b/examples/value_bind.mdtlbl @@ -2,8 +2,19 @@ * 这是在0.10.0版本加入的语法 * 可以有效的把数据组织起来 * 作用是将一个Var按照特定格式绑定到一个Value的句柄 -* 语法为 `Value "." NoStringVar`, 例如`$.data` `foo.bar` +* 语法为 `NonDExpValue "." NoStringVar`, 例如`$.data` `foo.bar` * 这里需要注意的是, 其左值的句柄不能为字符串, 否则会报错 +* +* 在0.14.3版本将其扩展, 使其可以出现在take和const目标中, 并且会添加至全局作用域 +* 当局部作用域都没有命中时, 将会尝试命中全局作用域 +* +* 需要注意的是, 最佳实践中最好被绑定的量仅为匿名量, 否则可能会造成一定的混乱. +* 因为那时候或许难以区分哪些是展开为同一个量的值, 而值绑定是绑定在其句柄, +* 也就是展开的量上的 +* +* 这个功能扩展主要是配合match使用的, +* 对于主要关注其量为变量的情况, 这很可能导致生命周期的模糊边界, +* 可能为项目增加额外的复杂度, 谨慎使用 *# const Human_new = ( @@ -56,3 +67,30 @@ print "," print __5 *# # 可以看到, 这可以使我们方便的组织数据 + + +const Foo = ( + take $.X = _0; +); +print Foo[2].X; +#* >>> +print 2 +*# +# 全局作用域并不受局部作用域限制 + + +const Foo = ( + const $.F = ( + print "foo"; + ); + print "bar"; +); +take R = Foo; +print "---"; +take R.F; +#* >>> +print "bar" +print "---" +print "foo" +*# +# 使用const直接将值传出而不take diff --git a/tools/display_source/Cargo.toml b/tools/display_source/Cargo.toml index ed1964a..6a165d9 100644 --- a/tools/display_source/Cargo.toml +++ b/tools/display_source/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "display_source" -version = "0.3.4" +version = "0.3.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/display_source/src/impls.rs b/tools/display_source/src/impls.rs index de6fe80..b9b6f34 100644 --- a/tools/display_source/src/impls.rs +++ b/tools/display_source/src/impls.rs @@ -266,7 +266,7 @@ impl DisplaySource for Take { meta.push("take"); meta.add_space(); - meta.push(&Value::replace_ident(&self.0)); + self.0.display_source(meta); meta.add_space(); meta.push("="); @@ -276,6 +276,14 @@ impl DisplaySource for Take { meta.push(";"); } } +impl DisplaySource for ConstKey { + fn display_source(&self, meta: &mut DisplaySourceMeta) { + match self { + Self::Var(var) => var.display_source(meta), + Self::ValueBind(vbind) => vbind.display_source(meta), + } + } +} impl DisplaySource for LogicLine { fn display_source(&self, meta: &mut DisplaySourceMeta) { match self { diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index c448704..f7278d6 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.1.1" +version = "0.2.0" 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 5152356..07b0f05 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -28,6 +28,7 @@ use ::syntax::{ Select, Const, Take, + ConstKey, LogicLine, Args, ArgsRepeat, @@ -117,9 +118,12 @@ pub Var: Var = { NoStringVar, } -pub Value: Value = { - Var => <>.into(), - DExp => <>.into(), +ValueBind: ValueBind = "." => { + ValueBind(value.into(), attr) +}; + +NonDExpValue: Value = { + => v.into(), "`" "`" => ReprVar(<>), // 原始值 "$" => ResultHandle, > => { @@ -129,17 +133,22 @@ pub Value: Value = { LogicLine::SetResultHandle(name.into()), ].into()).into() }, - "." => ValueBind(value.into(), attr).into(), + ValueBind => <>.into(), + "goto" > => Value::Cmper(<>.into()), +} + +pub Value: Value = { + NonDExpValue, + DExp => <>.into(), // consted-dexp "const" ConstStart => { let tmp_name = meta.get_tmp_var(); - let dexp_const = Const(tmp_name.clone(), dexp.into(), labels); + let dexp_const = Const(tmp_name.clone().into(), dexp.into(), labels); DExp::new("__".into(), vec![ dexp_const.into(), LogicLine::SetResultHandle(tmp_name.into()), ].into()).into() }, - "goto" > => Value::Cmper(<>.into()), } pub LiteralUInt: usize = =>? { @@ -647,12 +656,17 @@ OpExprAtom: OpExprInfo = { OpExprCallOp, } +ConstKey: ConstKey = { + ValueBind => <>.into(), + Var => <>.into(), +} + // 开始一个const, 开启了必须负责清理 ConstStart: () = () => meta.add_label_scope(); ConstStop: Vec = () => Vec::from_iter(meta.pop_label_scope()); pub BuiltinCommand: LogicLine = { - "const" "=" ConstStart )+> LEnd + "const" "=" ConstStart )+> LEnd => { if values.len() == 1 { let (var, value, labels) = values.pop().unwrap(); @@ -667,28 +681,27 @@ pub BuiltinCommand: LogicLine = { } }, - // 如果后方是一个Var则直接将常量映射到后方的值 - // 如果后方是一个DExp则将其计算然后将常量映射到计算出的句柄 - // 此处默认句柄使用Value - "take" ?> "=")?> LEnd => { + "take" > "=")?> LEnd => { let do_leak_res = var.is_some(); Take::new( - args.flatten().unwrap_or_default(), + args.unwrap_or_default(), var.unwrap_or_else(|| String::from("__")), do_leak_res, value ) }, - // 两个以上take "take" - "=")?> )> - "=")?> )+> + "=")?> )+> LEnd => { - let mut lines = Vec::with_capacity(1 + nexts.len()); - let iter = [first].into_iter().chain(nexts); - for (res, value) in iter { + if takes.len() == 1 { + let (res, value) = takes.pop().unwrap(); + let res = res.unwrap_or_else(|| "__".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 take = Take(res, value); lines.push(take.into()) @@ -912,7 +925,7 @@ pub Control: LogicLine = { out_block.insert( 0, Const::new( - name, + name.into(), value_handle.as_str().into() ).into() ) diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index 08776f1..a7ce235 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.0" +version = "0.1.1" 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 6e23bbe..003682c 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -970,7 +970,7 @@ fn const_value_leak_test() { } #[test] -fn take_test2() { +fn take2_test() { let parser = LogicLineParser::new(); let ast = parse!(parser, "take X;").unwrap(); @@ -4235,3 +4235,50 @@ fn match_test() { ], ); } + +#[test] +fn value_bind_of_constkey_test() { + let parser = TopLevelParser::new(); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + take X = (); + take X.Y = 2; + print X.Y; + "#).unwrap()).compile().unwrap(), + vec![ + "print 2", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + take X = (); + { + take X.Y = 2; # to global + } + print X.Y; + "#).unwrap()).compile().unwrap(), + vec![ + "print 2", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + take X = (); + { + const X.Y = ( + :x + goto :x; + ); + } + take X.Y; + take X.Y; + "#).unwrap()).compile().unwrap(), + vec![ + "jump 0 always 0 0", + "jump 1 always 0 0", + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 7b09f0f..edce1a8 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.1.2" +version = "0.2.0" 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 eb08029..92f83b8 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -476,7 +476,6 @@ impl_derefs!(impl for DExp => (self: self.lines): Expand); pub struct ValueBind(pub Box, pub Var); impl TakeHandle for ValueBind { fn take_handle(self, meta: &mut CompileMeta) -> Var { - // 以`__{}__bind__{}`的形式组合 let handle = self.0.take_handle(meta); assert!(! Value::is_string(&self.1)); let binded @@ -1903,11 +1902,72 @@ impl SwitchCatch { } } +#[derive(Debug, PartialEq, Clone)] +pub enum ConstKey { + Var(Var), + ValueBind(ValueBind), +} + +impl ConstKey { + /// Returns `true` if the const key is [`Var`]. + /// + /// [`Var`]: ConstKey::Var + #[must_use] + pub fn is_var(&self) -> bool { + matches!(self, Self::Var(..)) + } + + pub fn as_var(&self) -> Option<&Var> { + if let Self::Var(v) = self { + Some(v) + } else { + None + } + } + + /// Returns `true` if the const key is [`ValueBind`]. + /// + /// [`ValueBind`]: ConstKey::ValueBind + #[must_use] + pub fn is_value_bind(&self) -> bool { + matches!(self, Self::ValueBind(..)) + } + + pub fn as_value_bind(&self) -> Option<&ValueBind> { + if let Self::ValueBind(v) = self { + Some(v) + } else { + None + } + } +} +impl From for Value { + fn from(value: ConstKey) -> Self { + match value { + ConstKey::Var(var) => var.into(), + ConstKey::ValueBind(vb) => vb.into(), + } + } +} +impl TakeHandle for ConstKey { + fn take_handle(self, meta: &mut CompileMeta) -> Var { + match self { + Self::Var(var) => var, + Self::ValueBind(vb) => vb.take_handle(meta), + } + } +} +impl_enum_froms!(impl From for ConstKey { + Var => &str; + Var => Var; + ValueBind => ValueBind; +}); + /// 在块作用域将Var常量为后方值, 之后使用Var时都会被替换为后方值 #[derive(Debug, PartialEq, Clone)] -pub struct Const(pub Var, pub Value, pub Vec); +pub struct Const(pub ConstKey, pub Value, pub Vec); impl Const { - pub fn new(var: Var, value: Value) -> Self { + pub fn new(var: ConstKey, value: Value) -> Self { Self(var, value, Default::default()) } } @@ -1922,8 +1982,10 @@ impl Compile for Const { /// 在此处计算后方的值, 并将句柄赋给前方值 /// 如果后方不是一个DExp, 而是Var, 那么自然等价于一个常量定义 #[derive(Debug, PartialEq, Clone)] -pub struct Take(pub Var, pub Value); +pub struct Take(pub ConstKey, pub Value); impl Take { + /// 过时的API + /// /// 构建一个Take语句单元 /// 可以带有参数与返回值 /// @@ -1940,17 +2002,17 @@ impl Take { value: Value, ) -> LogicLine { if matches!(args, Args::Normal(ref args) if args.is_empty()) { - Take(var, value).into() + Take(var.into(), value).into() } else { let mut len = 2; if do_leak_res { len += 1 } let mut expand = Vec::with_capacity(len); expand.push(LogicLine::SetArgs(args)); if do_leak_res { - expand.push(Take(var.clone(), value).into()); + expand.push(Take(var.clone().into(), value).into()); expand.push(LogicLine::ConstLeak(var)); } else { - expand.push(Take(var, value).into()) + expand.push(Take(var.into(), value).into()) } debug_assert_eq!(expand.len(), len); Expand(expand).into() @@ -2158,7 +2220,7 @@ impl MatchPat { } fn binds(name: Var, value: &Var, meta: &mut CompileMeta) { if !name.is_empty() { - meta.add_const_value(Const(name, value.clone().into(), vec![])); + meta.add_const_value(Const(name.into(), value.clone().into(), vec![])); } } match self { @@ -2272,7 +2334,7 @@ impl Compile for LogicLine { let mut f = |r#const| meta.add_const_value(r#const); for (i, value) in expand_args.clone().into_iter().enumerate() { let name = format!("_{}", i); - f(Const(name, value, Vec::with_capacity(0)).into()); + f(Const(name.into(), value, Vec::with_capacity(0)).into()); } meta.set_env_args(expand_args); }, @@ -2542,6 +2604,8 @@ pub struct CompileMeta { /// 所以它支持在宏A内部展开的宏B跳转到宏A内部的标记 const_expand_tag_name_map: Vec>, value_binds: HashMap<(Var, Var), Var>, + /// 值绑定全局常量表, 只有值绑定在使用它 + value_bind_global_consts: HashMap, } impl Debug for CompileMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -2562,6 +2626,7 @@ impl Debug for CompileMeta { .field("tmp_tag_count", &self.tmp_tag_count.counter()) .field("const_expand_tag_name_map", &self.const_expand_tag_name_map) .field("value_binds", &self.value_binds) + .field("..", &DotDot) .finish() } } @@ -2587,6 +2652,7 @@ impl CompileMeta { tmp_tag_count: Counter::new(Self::tmp_tag_getter), const_expand_tag_name_map: Vec::new(), value_binds: HashMap::new(), + value_bind_global_consts: HashMap::new(), } } @@ -2731,6 +2797,7 @@ impl CompileMeta { env.consts().get(name) }) .map(|x| { assert!(! x.value().is_repr_var()); x }) + .or_else(|| self.value_bind_global_consts.get(name)) } /// 获取一个常量到值的使用次数与映射与其内部标记的可变引用, @@ -2744,6 +2811,7 @@ impl CompileMeta { env.consts_mut().get_mut(name) }) .map(|x| { assert!(! x.value().is_repr_var()); x }) + .or_else(|| self.value_bind_global_consts.get_mut(name)) } /// 新增一个常量到值的映射, 如果当前作用域已有此映射则返回旧的值并插入新值 @@ -2778,11 +2846,22 @@ impl CompileMeta { value => value, }; - self.expand_env - .last_mut() - .unwrap() - .consts - .insert(var, ConstData::new(value, labels)) + match &var { + ConstKey::Var(_) => { + let var = var.take_handle(self); + + self.expand_env + .last_mut() + .unwrap() + .consts + .insert(var, ConstData::new(value, labels)) + }, + ConstKey::ValueBind(_) => { + let var = var.take_handle(self); + self.value_bind_global_consts + .insert(var, ConstData::new(value, labels)) + }, + } } /// 从当前作用域移除一个常量到值的映射 @@ -2996,7 +3075,7 @@ impl OpExprInfo { let (true_lab, skip_lab) = (meta.get_tag(), meta.get_tag()); DExp::new_nores(vec![ - Take(child_result, Value::ResultHandle).into(), + Take(child_result.into(), Value::ResultHandle).into(), Goto(true_lab.clone(), cmp).into(), false_line, Goto(skip_lab.clone(), JumpCmp::Always.into()).into(), @@ -3027,7 +3106,7 @@ impl OpExprInfo { let (true_lab, skip_lab) = (meta.get_tag(), meta.get_tag()); Expand(vec![ - Take(child_result, result).into(), + Take(child_result.into(), result).into(), Goto(true_lab.clone(), cmp).into(), false_line, Goto(skip_lab.clone(), JumpCmp::Always.into()).into(), @@ -3070,7 +3149,7 @@ pub fn op_expr_build_results( let mut results = results.into_iter(); let first_result_handle = meta.get_tmp_var(); lines.push(Take( - first_result_handle.clone(), results.next().unwrap() + first_result_handle.clone().into(), results.next().unwrap() ).into()); lines.push(value.into_logic_line( meta,