From 088e6748f64919dd9273aacc75f639f2379f8227 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 27 Jan 2025 23:39:43 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=99=E9=87=8D=E5=A4=8D=E5=9D=97=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=AC=A1=E6=95=B0=E6=B7=BB=E5=8A=A0=E5=8F=82?= =?UTF-8?q?=E4=B8=8E=E5=B8=B8=E9=87=8F=E7=B3=BB=E7=BB=9F=E7=9A=84=E8=83=BD?= =?UTF-8?q?=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 13 ++++---- Cargo.toml | 2 +- examples/learn.md | 21 +++++++++++++ examples/match.mdtlbl | 14 +++++++++ syntax/vim/syntax/mdtlbl.vim | 8 ++--- tools/display_source/Cargo.toml | 1 + tools/display_source/src/impls.rs | 13 ++++++-- tools/parser/Cargo.toml | 2 +- tools/parser/src/parser.lalrpop | 3 ++ tools/parser/tests/Cargo.toml | 2 +- tools/parser/tests/src/lib.rs | 41 ++++++++++++++++++++++++ tools/syntax/Cargo.toml | 4 +-- tools/syntax/src/lib.rs | 52 ++++++++++++++++++++++++++----- 13 files changed, 151 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9dc0a11..2e95222 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ dependencies = [ name = "display_source" version = "0.3.26" dependencies = [ + "either", "parser", "syntax", ] @@ -167,9 +168,9 @@ dependencies = [ [[package]] name = "itermaps" -version = "0.2.10" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5cbb695aa2a11f8e32a97fc3955767c1b49f7c22bbe4fe26301f0097a9735f" +checksum = "db210bc7b7cfa8972281c5b647fe6663efe37bc8d60c7bb8c647716fdbc5aa5e" [[package]] name = "itertools" @@ -284,7 +285,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mindustry_logic_bang_lang" -version = "0.17.19" +version = "0.17.20" dependencies = [ "display_source", "logic_lint", @@ -330,7 +331,7 @@ dependencies = [ [[package]] name = "parser" -version = "0.3.26" +version = "0.3.27" dependencies = [ "lalrpop", "lalrpop-util", @@ -341,7 +342,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.45" +version = "0.1.46" dependencies = [ "either", "parser", @@ -530,7 +531,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.49" +version = "0.2.50" dependencies = [ "either", "itermaps", diff --git a/Cargo.toml b/Cargo.toml index 45689ac..f260a0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.17.19" +version = "0.17.20" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/learn.md b/examples/learn.md index 175004c..9b22ce7 100644 --- a/examples/learn.md +++ b/examples/learn.md @@ -1264,6 +1264,27 @@ print 1 通常重复块也配合着 match 一起使用, 来取出参数内容, 或者仅关心参数足够时的情况 +> [!TIP] +> 重复块的重复次数类似 gswitch, 也可以参与常量系统, 但是需要额外标注才能使用 +``` +const C = 2; +match 1 2 3 4 5 6 7 { @ {} } +inline*C@{ + foo @; +} +``` +编译为 +``` +foo 1 2 +foo 3 4 +foo 5 6 +foo 7 +``` + +> [!NOTE] +> 重复块从常量系统中获取的重复次数是使用常量评估而不是求值进行的, +> 所以对于一些值你可能需要先求值再给重复块使用 + 常用语法糖 =============================================================================== diff --git a/examples/match.mdtlbl b/examples/match.mdtlbl index c14101f..c8faef5 100644 --- a/examples/match.mdtlbl +++ b/examples/match.mdtlbl @@ -397,3 +397,17 @@ match @ { } } # 在只有一个分支时可以减少一层括号, 用于快速接收参数等情况非常舒适 + + +# 在0.17.20版本为重复块添加了从常量系统中获取重复次数的能力 +# 需要注意的是, 它获取的方式是使用编译期计算完成的, +# 所以有时你需要先take再给重复块使用 +match a b c { @ {} } +const C = 2; +inline*C@ { + foo @; +} +#* >>> +foo a b +foo c +*# diff --git a/syntax/vim/syntax/mdtlbl.vim b/syntax/vim/syntax/mdtlbl.vim index f8f9816..fdea9d7 100644 --- a/syntax/vim/syntax/mdtlbl.vim +++ b/syntax/vim/syntax/mdtlbl.vim @@ -22,11 +22,11 @@ syn case match syn keyword mdtlblKeyword \ while gwhile do skip if elif else switch gswitch break continue \ const setres select match - \ inline \ op noop print -syn keyword mdtlblKeyword goto nextgroup=mdtlblIdentLabelRest -syn keyword mdtlblKeyword case nextgroup=mdtlblStar skipwhite -syn keyword mdtlblKeyword take nextgroup=mdtlblStar skipwhite +syn keyword mdtlblKeyword goto nextgroup=mdtlblIdentLabelRest +syn keyword mdtlblKeyword case nextgroup=mdtlblStar skipwhite +syn keyword mdtlblKeyword take nextgroup=mdtlblStar skipwhite +syn keyword mdtlblKeyword inline nextgroup=mdtlblStar skipwhite syn match mdtlblStar /\*/ contained syn keyword mdtlblOpFunKeyword diff --git a/tools/display_source/Cargo.toml b/tools/display_source/Cargo.toml index 7c84c0b..a2a5c1f 100644 --- a/tools/display_source/Cargo.toml +++ b/tools/display_source/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +either = "1.10.0" syntax = { path = "../syntax", version = "*" } [dev-dependencies] diff --git a/tools/display_source/src/impls.rs b/tools/display_source/src/impls.rs index ef66690..d186633 100644 --- a/tools/display_source/src/impls.rs +++ b/tools/display_source/src/impls.rs @@ -1,5 +1,6 @@ use syntax::*; use crate::{DisplaySource, DisplaySourceMeta}; +use either::{self, Left, Right}; fn inline_labs_and_binder( labs: &[T], @@ -552,8 +553,16 @@ impl DisplaySource for Args { impl DisplaySource for ArgsRepeat { fn display_source(&self, meta: &mut DisplaySourceMeta) { meta.push("inline"); - meta.add_space(); - meta.push(&self.count().to_string()); + match self.count() { + Left(n) => { + meta.add_space(); + meta.push_fmt(n); + }, + Right(value) => { + meta.push("*"); + value.display_source(meta); + }, + } meta.push("@"); meta.push("{"); if !self.block().is_empty() { diff --git a/tools/parser/Cargo.toml b/tools/parser/Cargo.toml index 126f465..cddc5c9 100644 --- a/tools/parser/Cargo.toml +++ b/tools/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "0.3.26" +version = "0.3.27" 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 307c375..c3daaaa 100644 --- a/tools/parser/src/parser.lalrpop +++ b/tools/parser/src/parser.lalrpop @@ -526,6 +526,9 @@ ArgsRepeatBlock: ArgsRepeat = { } Ok(ArgsRepeat::new(chunk.unwrap_or(1), block)) }, + "*" > "@" => { + ArgsRepeat::new_valued(value, block) + }, > => { let glob = l.new_value(Args::GLOB_ONLY); ArgsRepeat::new(pats.len(), vec![ diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index 69e822e..edefea2 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.45" +version = "0.1.46" 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 ce540a7..eb14805 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -4786,6 +4786,47 @@ fn match_test() { ], ); + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const C = 1; + match a b c { @{} } + inline*C@{ + foo @; + } + print end; + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "foo a", + "foo b", + "foo c", + "print end", + "print a", + "print b", + "print c", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const C = 2; + match a b c { @{} } + inline*C@{ + foo @; + } + print end; + print @; + "#).unwrap()).compile().unwrap(), + vec![ + "foo a b", + "foo c", + "print end", + "print a", + "print b", + "print c", + ], + ); + assert_eq!( CompileMeta::new().compile(parse!(parser, r#" match a b c { __ @{} } diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 98f3e4f..7f5e4c7 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.49" +version = "0.2.50" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,4 +10,4 @@ tag_code = { path = "../tag_code", version = "*" } var_utils = { path = "../var_utils", version = "*"} utils = { path = "../utils", version = "*" } either = "1.10.0" -itermaps = "0.2.8" +itermaps = "0.3.1" diff --git a/tools/syntax/src/lib.rs b/tools/syntax/src/lib.rs index c348570..7c935ec 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -19,7 +19,7 @@ use std::{ use builtins::{build_builtins, BuiltinFunc}; use either::Either; -use itermaps::{fields, MapExt, Unpack}; +use itermaps::{fields, short_funcs::copy, MapExt, Unpack}; use tag_code::{ args, logic_parser::{Args as LArgs, IdxBox, ParseLine, ParseLines}, @@ -2811,20 +2811,21 @@ impl_enum_froms!(impl From for Args { /// 拿取指定个参数, 并重复块中代码 #[derive(Debug, PartialEq, Clone)] pub struct ArgsRepeat { - count: usize, + count: Either>, block: InlineBlock, } impl ArgsRepeat { pub fn new(count: usize, block: InlineBlock) -> Self { - Self { count, block } + assert_ne!(count, 0); + Self { count: Either::Left(count), block } } - pub fn count(&self) -> usize { - self.count + pub fn new_valued(count: IdxBox, block: InlineBlock) -> Self { + Self { count: Either::Right(count), block } } - pub fn count_mut(&mut self) -> &mut usize { - &mut self.count + pub fn count(&self) -> Either> { + self.count.as_ref().map_left(copy) } pub fn block(&self) -> &InlineBlock { @@ -2837,8 +2838,43 @@ impl ArgsRepeat { } impl Compile for ArgsRepeat { fn compile(self, meta: &mut CompileMeta) { + let count = match self.count { + Either::Left(count) => count, + Either::Right(value) => { + let Some((n, _)) = value.try_eval_const_num(meta) else { + let (line, col) = value.location(&meta.source); + err!( + "{}\n重复块次数不是数字, 位于: {}:{}\n{}", + meta.err_info().join("\n"), + line, col, + value.display_src(meta), + ); + exit(6) + }; + let n = n.round(); + if n <= 0.0 || !n.is_finite() { + let (line, col) = value.location(&meta.source); + err!( + "{}\n重复块次数必须大于0 ({}), 位于: {}:{}", + meta.err_info().join("\n"), + n, line, col, + ); + exit(6) + } + if n > 512.0 { + let (line, col) = value.location(&meta.source); + err!( + "{}\n重复块次数过大 ({}), 位于: {}:{}", + meta.err_info().join("\n"), + n, line, col, + ); + exit(6) + } + n as usize + }, + }; let chunks: Vec> = meta.get_env_args() - .chunks(self.count) + .chunks(count) .map(|chunks| chunks.iter() .cloned() .map(Into::into)