Skip to content

Commit

Permalink
添加const-match语法, 可以很好的进行非take的匹配, 也方便做模式参数
Browse files Browse the repository at this point in the history
  • Loading branch information
A4-Tacks committed May 3, 2024
1 parent eb0049a commit 82e344e
Show file tree
Hide file tree
Showing 14 changed files with 876 additions and 26 deletions.
10 changes: 5 additions & 5 deletions 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.15.11"
version = "0.16.0"
edition = "2021"

authors = ["A4-Tacks <[email protected]>"]
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
> [`closured_value.mdtlbl`](./closured_value.mdtlbl)<br/>
> [`caller.mdtlbl`](./caller.mdtlbl)<br/>
> [`match.mdtlbl`](./match.mdtlbl)<br/>
> [`const_match.mdtlbl`](./const_match.mdtlbl)<br/>
> [`builtin_functions.mdtlbl`](./builtin_functions.mdtlbl)<br/>
> [`value_bind_ref.mdtlbl`](./value_bind_ref.mdtlbl)<br/>
Expand Down
52 changes: 52 additions & 0 deletions examples/const_match.mdtlbl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#**
* 这是0.16.0添加的语法, 很像match, 但是在前面添加const
* 与match不同的是, 它不会先把参数take, 而是尽量针对参数本身匹配
* 它会把每个参数进行const, 然后以其const后的依据去对指向的值进行匹配
*
* 它的var-pat和match很像, 但是它本身也是惰性的, 也就是说例如
* `[A B C]` 如果A匹配上了, 那么B和C将不会被take.
* 其作用是匹配目标值为Var且其值与pat相等
*
* 它还有其它几个模式:
* 下划线通配模式: 例如 `_`, 这会允许任何值
*
* 守卫模式: 例如 `[?foo]`, 这里里面是一个 op-expr, 当然一般也用不上.
* 在这里面将会有一个独立的参数环境, 拥有一个参数指向要匹配的值
* 在匹配时会将守卫take, 得到的句柄非`0`则判定通过
*
* 值模式: 它会直接将对应这个位置的被匹配值take, 然后直接对句柄匹配
* 需要注意的是, 它可能在多个分支被take多次, 只是守卫模式的一个语法糖
* 其具体原理可以使用`A`编译选项查看
*
* 同时, 它还有一个take标志, 在pat前面加上`*`时,
* 将会在整个分支通过时采用take而非const绑定,
* 例如: `*A`
*#


# 在以前, 我们经常使用手动的_0 _1来接受参数, 哪怕有了match也依旧如此
# 因为match会将每个参数take, 如果某个参数不需要立即take时,
# 就基本整个都用不了match来方便的接受参数了
# 而const-match可以很好的解决这个问题
# 比如一个简单的For
const For = (const match @ {
Start Stop F {
take For[Start Stop 1 F];
}
*Start *Stop *Step F {
take I = ();
I = Start;
while I < Stop {
take F[I];
I += Step;
}
}
});

take For[0 10 (
print "a: "_0;
)];

take For[0 10 2 (
print "b: "_0;
)];
5 changes: 5 additions & 0 deletions syntax/vim/mdtlbl.snippets
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,8 @@ match $1 {
$0
}
endsnippet
snippet cmatch "const match args... { (pattern body)... }" w
const match $1 {
$0
}
endsnippet
3 changes: 2 additions & 1 deletion syntax/vim/mdtlbl.vim
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ syn match mdtlblResultHandle /\$/
" Label And ResultH {{{1
syn match mdtlblDefineResultHandle /\%(([%?]\=\%(\s\|#\*.*\*#\|\%(#[^*].*\|#\)\=\n\)*\)\@<=\I\i*:/

syn match mdtlblQuickDExpTakeIdent /\I\i*\%(\%(\s\|#\*.*\*#\|\%(#[^*].*\|#\)\=\n\)*\[\)\@=/
syn match mdtlblQuickDExpTakeIdent /\I\i*\%(\[\)\@=/
syn match mdtlblQuickDExpTakeIdent /'[^' \t]\+'\%(\[\)\@=/
syn match mdtlblQuickDExpTakeIdent /->/
syn match mdtlblIdentLabel /:\I\i*/

Expand Down
2 changes: 1 addition & 1 deletion tools/display_source/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "display_source"
version = "0.3.11"
version = "0.3.12"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
108 changes: 108 additions & 0 deletions tools/display_source/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ impl DisplaySource for LogicLine {
},
Self::ArgsRepeat(args_repeat) => args_repeat.display_source(meta),
Self::Match(r#match) => r#match.display_source(meta),
Self::ConstMatch(r#match) => r#match.display_source(meta),
Self::Other(args) => {
if let Some(args) = args.as_normal() {
assert_ne!(args.len(), 0);
Expand Down Expand Up @@ -540,6 +541,92 @@ impl DisplaySource for Match {
meta.push("}");
}
}
impl DisplaySource for ConstMatchPatAtom {
fn display_source(&self, meta: &mut DisplaySourceMeta) {
let show_list = self.pattern()
.as_ref()
.left()
.map(|pats| !pats.is_empty())
.unwrap_or_default()
|| self.pattern().is_right();
if self.do_take() {
meta.push("*")
}
if !self.name().is_empty() {
meta.push(self.name());
if show_list { meta.push(":") }
} else if !show_list {
meta.push("_")
}
if show_list {
meta.push("[");
if self.pattern().is_right() {
meta.push("?");
}
meta.display_source_iter_by_splitter(
DisplaySourceMeta::add_space,
self.pattern()
.as_ref()
.map_right(Some)
.into_iter()
);
meta.push("]");
}
}
}
impl DisplaySource for ConstMatchPat {
fn display_source(&self, meta: &mut DisplaySourceMeta) {
match self {
ConstMatchPat::Normal(args) => {
meta.display_source_iter_by_splitter(
DisplaySourceMeta::add_space,
args,
)
},
ConstMatchPat::Expanded(prefix, suffix) => {
for s in prefix {
s.display_source(meta);
meta.add_space();
}
meta.push("@");
for s in suffix {
meta.add_space();
s.display_source(meta);
}
},
}
}
}
impl DisplaySource for ConstMatch {
fn display_source(&self, meta: &mut DisplaySourceMeta) {
meta.push("const");
meta.add_space();
meta.push("match");
meta.add_space();
let slen = meta.len();
self.args().display_source(meta);
if meta.len() != slen { meta.add_space(); }
meta.push("{");
if !self.cases().is_empty() {
meta.add_lf();
meta.do_block(|meta| {
for (pat, block) in self.cases() {
let slen = meta.len();
pat.display_source(meta);
if meta.len() != slen { meta.add_space(); }
meta.push("{");
if !block.is_empty() {
meta.add_lf();
meta.do_block(|meta| block.display_source(meta));
}
meta.push("}");
meta.add_lf();
}
});
}
meta.push("}");
}
}

#[cfg(test)]
#[test]
Expand Down Expand Up @@ -822,4 +909,25 @@ fn display_source_test() {
.display_source_and_get(&mut meta),
"const X = ([A:A &B:B C:2 &D:(:m)#*labels: [m]*#](:z)#*labels: [z]*#);"
);

assert_eq!(
parse!(line_parser, r#"
const match @ {
A *B *_ C:[1] *D:[2 3] E:[?x] [1 2] {}
X @ Y {}
@ Z {}
@ {}
}
"#)
.unwrap()
.display_source_and_get(&mut meta),
"\
const match @ {\n\
\x20 A *B *_ C:[1] *D:[2 3] E:[?x] [1 2] {}\n\
\x20 X @ Y {}\n\
\x20 @ Z {}\n\
\x20 @ {}\n\
}\
"
);
}
2 changes: 1 addition & 1 deletion tools/parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "parser"
version = "0.3.7"
version = "0.3.8"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
75 changes: 73 additions & 2 deletions tools/parser/src/parser.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ use ::syntax::{
Match,
MatchPat,
MatchPatAtom,
ConstMatch,
ConstMatchPat,
ConstMatchPatAtom,
Meta,
ZERO_VAR,
FALSE_VAR,
Expand Down Expand Up @@ -83,6 +86,8 @@ OOCArgs<T, S>: Vec<T> = {
CloseArgs<T, S>,
}
Span<T> = @L T @R;
#[inline]
Opt<T>: bool = T? => <>.is_some();

CtrlBreakStart: () = () => meta.add_control_break_level(None);
CtrlContinueStart: () = () => meta.add_control_continue_level(None);
Expand Down Expand Up @@ -307,13 +312,15 @@ pub Op: Op = {

Label: Var = ":" <Var>;

NoPrefixInlineBlock: InlineBlock = MBlock<LogicLine*> => <>.into();

#[inline]
Expand: Expand = LogicLine+? => Expand(<>.unwrap_or_default());
pub LogicLine: LogicLine = {
Control,
BuiltinCommand,
MBlock<Expand> => <>.into(),
"inline" <MBlock<LogicLine*>> => InlineBlock(<>).into(),
"inline" <NoPrefixInlineBlock> => <>.into(),
Label => LogicLine::new_label(<>, meta),
"op" <Op> LEnd => <>.into(),
"noop" LEnd => LogicLine::NoOp,
Expand All @@ -326,6 +333,7 @@ pub LogicLine: LogicLine = {
<Args> LEnd => LogicLine::Other(<>),
"inline" <ArgsRepeatBlock> => <>.into(),
Match => <>.into(),
ConstMatch => <>.into(),
}

Match: Match = "match" <args:Args?> <cases:MBlock<(
Expand All @@ -344,11 +352,74 @@ Match: Match = "match" <args:Args?> <cases:MBlock<(
};

MatchPat: MatchPatAtom = {
MList<Args1> => MatchPatAtom::new_unnamed(<>),
MList<Args1> => MatchPatAtom::new_unamed(<>),
Var => MatchPatAtom::new(<>, vec![]),
<name:Var> ":" <pat:MList<Args1>> => MatchPatAtom::new(name, pat),
}

ConstMatch: ConstMatch
= "const" "match" <args:Args?>
<cases:MBlock<(
ConstMatchPat NoPrefixInlineBlock
)*>>
=> {
ConstMatch::new(args.unwrap_or_default(), cases)
};

ConstMatchPat: ConstMatchPat = {
ConstMatchPatAtom* => ConstMatchPat::Normal(<>),
<ConstMatchPatAtom*> "@" <ConstMatchPatAtom*>
=> ConstMatchPat::Expanded(<>),
}

ConstMatchPatAtom: ConstMatchPatAtom = {
<dotake:Opt<"*">> <name:(<Var> ":")?> <pat:MList<Args1>> => {
ConstMatchPatAtom::new(dotake, name.unwrap_or_default(), pat)
},
<dotake:Opt<"*">> <name:(<Var> ":")?> <pat:MList<("?" <OpExprBody>)>> => {
ConstMatchPatAtom::new_guard(
dotake,
name.unwrap_or_default(),
pat.into_value(meta),
)
},
<dotake:Opt<"*">> <name:(<Var> ":")?> <pat:MList<("*" <Args0>)>> => {
ConstMatchPatAtom::new_guard(
dotake,
name.unwrap_or_default(),
DExp::new("__".into(), vec![
LogicLine::from(Match::new(
Args::GLOB_ONLY,
vec![
(
vec![MatchPatAtom::new_unamed(pat)].into(),
vec![
LogicLine::SetResultHandle(
Value::ReprVar("1".into())
),
].into(),
),
(
vec![MatchPatAtom::default()].into(),
vec![
LogicLine::SetResultHandle(
Value::ReprVar("0".into())
),
].into(),
),
]
)),
].into()).into()
)
},
<dotake:Opt<"*">> <name:Var> => {
ConstMatchPatAtom::new(dotake, name, vec![])
},
<dotake:Opt<"*">> "_" => {
ConstMatchPatAtom::new(dotake, Default::default(), vec![])
},
}

Args: Args = {
<prefix:Args0> "@" <suffix:Args0> => {
Args::Expanded(prefix, suffix)
Expand Down
Loading

0 comments on commit 82e344e

Please sign in to comment.