rust lalrpop 词法歧义:括号内的非贪婪匹配
rust lalrpop lexing ambiguity: non-greedy matching inside of brackets
我正在尝试解析具有此 BNF 的 SGF 格式:
Collection = GameTree { GameTree }
GameTree = "(" Sequence { GameTree } ")"
Sequence = Node { Node }
Node = ";" { Property }
Property = PropIdent PropValue { PropValue }
PropIdent = UcLetter { UcLetter }
PropValue = "[" CValueType "]"
CValueType = (ValueType | Compose)
ValueType = (None | Number | Real | Double | Color | SimpleText |
Text | Point | Move | Stone)
我不是要使用 lalrpop 将 ValueType
解析为不同的类型。我只想要属性的原始文本内容。
我的 Property
规则有问题。具体来说,我在测试文件中有这一行
;AB[dp];AB[pp]HA[6]
这是两个节点。第一个节点有一个 Property
,第二个有两个。括号中的内容必须是 .*
因为任何东西都可以放在那里。任意自由文本是某些属性的有效值。
使用 lalrpop
PropValue = r"\[" <r".*"> r"\]";
作为规则失败,因为它匹配 pp]HA[6
当然它只需要匹配 pp
.
合理的,(因为我不知道这是如何实现的),
PropValue = r"\[" <r".*?"> r"\]";
也失败了,错误信息非常好:
/mnt/c/Users/mason_000/wsl/dev/rust/seraph/gosgf/src/parse_sgf.lalrpop:18:5: 18:10 error: "non-greedy" repetitions (`*?` or `+?`) are not supported in regular expressions
但现在我陷入了困境,因为我需要在这里进行非贪婪匹配。
我可以做的一件事是匹配不是右括号的所有内容。我不确定这是否是解决这种特殊类型歧义的预期方法(第一次使用 lalr 解析器)。我也不确定 ;HA[My free text ]]]]
是否是一个包含 My free text ]]]
内容的有效文件。但是,如果它 是 一个有效文件,则此解决方法将不起作用。
而且,这似乎没有用:
PropValue = r"\[" <r"[^\]]"> r"\]";
解析失败,我无法准确破译位置。
thread 'core::sgf_replays::game189_has_6_handicap' panicked at 'called `Result::unwrap()` on an `Err` value: UnrecognizedToken { token: Some((7, Token(0, "9"), 8)), expected: ["r#\"\\]\"#"] }', libcore/result.rs:945:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at libstd/sys_common/backtrace.rs:71
at libstd/sys_common/backtrace.rs:59
2: std::panicking::default_hook::{{closure}}
at libstd/panicking.rs:380
3: std::panicking::default_hook
at libstd/panicking.rs:390
4: std::panicking::rust_panic_with_hook
at libstd/panicking.rs:576
5: std::panicking::begin_panic
at libstd/panicking.rs:537
6: std::panicking::begin_panic_fmt
at libstd/panicking.rs:521
7: rust_begin_unwind
at libstd/panicking.rs:497
8: core::panicking::panic_fmt
at libcore/panicking.rs:71
9: core::result::unwrap_failed
at /checkout/src/libcore/macros.rs:23
10: <core::result::Result<T, E>>::unwrap
at /checkout/src/libcore/result.rs:782
11: seraph::core::sgf_replays::game189_has_6_handicap
at src/core/mod.rs:612
12: <F as alloc::boxed::FnBox<A>>::call_box
at libtest/lib.rs:1453
at /checkout/src/libcore/ops/function.rs:223
at /checkout/src/liballoc/boxed.rs:788
13: __rust_maybe_catch_panic
at libpanic_unwind/lib.rs:102
为了完整起见,这里是 .lalrpop
use std::str::FromStr;
use gosgf::*;
use std::collections::HashMap;
grammar;
pub Collection: GoCollection = <GameTree*>;
match {
r"\(",
r"\)",
r";" ,
r"\[",
r"\]",
r"[A-Z]+",
}
else {
r"[^\]]",
}
GameTree: GameTree = {
r"\(" <sequence: Sequence> <children: GameTree*> r"\)" => {
let komi = f64::from_str(sequence[0].properties.get("KM").unwrap_or(&"0.0".to_owned())).unwrap();
let size = usize::from_str(sequence[0].properties.get("SZ").unwrap_or(&"19".to_owned())).unwrap();
let handicap;
{
let mut handistr = String::from("0");
for node in &sequence {
if let Some(ha) = node.properties.get("HA") {
handistr = ha.to_string();
break;
}
}
handicap = usize::from_str(&handistr).unwrap();
}
GameTree {
komi,
size,
handicap,
sequence,
children,
}
}
};
Sequence = <Node+>;
Node: Node = {
r";" <pairs: Property+> => {
let mut properties : HashMap<String, String> = HashMap::new();
for (k, v) in pairs {
properties.insert(k, v);
}
Node { properties }
}
};
Property: (String, String) = {
<k: PropIdent> <v: PropValue> => (k.to_string(), v.to_string())
};
PropIdent = <r"[A-Z]+">;
PropValue = r"\[" <r".*"> r"\]";
这是我想出的一个答案。我放弃了多个单独的终端,并使用 [^\]]
技巧将它们全部组合成一个大终端。
Property: (String, String) = {
<r"[A-Z]+\[[^\]]*\]"> => {
lazy_static! {
static ref RE : regex::Regex = regex::Regex::new(r"([A-Z]+)\[([^\]]*)\]").unwrap();
}
let cap = RE.captures(<>).unwrap();
let k = &cap[1];
let v = &cap[2];
(k.to_string(), v.to_string())
}
};
还不打算接受这个答案,因为我不确定这是给定可用工具的最优雅的解决方案,而且我还想知道是否可以创建一个解析
的规则
`;HA[My Text]]QB[[Nested]]`
作为两个属性
("HA", "My Text]")
和 ("QB", "[Nested]")
作为结果,或者如果这种表达式无法用 lalr(1) 解析器解析。
编辑:尽管 Stefan 指出有效的 SGF 4 文件将转义其右括号,但我只是想知道这是否可能。我开始怀疑它是否是,因为一旦不看 属性 到下一件事,就永远无法知道括号是文本的一部分还是 属性 的结尾。
我正在尝试解析具有此 BNF 的 SGF 格式:
Collection = GameTree { GameTree }
GameTree = "(" Sequence { GameTree } ")"
Sequence = Node { Node }
Node = ";" { Property }
Property = PropIdent PropValue { PropValue }
PropIdent = UcLetter { UcLetter }
PropValue = "[" CValueType "]"
CValueType = (ValueType | Compose)
ValueType = (None | Number | Real | Double | Color | SimpleText |
Text | Point | Move | Stone)
我不是要使用 lalrpop 将 ValueType
解析为不同的类型。我只想要属性的原始文本内容。
我的 Property
规则有问题。具体来说,我在测试文件中有这一行
;AB[dp];AB[pp]HA[6]
这是两个节点。第一个节点有一个 Property
,第二个有两个。括号中的内容必须是 .*
因为任何东西都可以放在那里。任意自由文本是某些属性的有效值。
使用 lalrpop
PropValue = r"\[" <r".*"> r"\]";
作为规则失败,因为它匹配 pp]HA[6
当然它只需要匹配 pp
.
合理的,(因为我不知道这是如何实现的),
PropValue = r"\[" <r".*?"> r"\]";
也失败了,错误信息非常好:
/mnt/c/Users/mason_000/wsl/dev/rust/seraph/gosgf/src/parse_sgf.lalrpop:18:5: 18:10 error: "non-greedy" repetitions (`*?` or `+?`) are not supported in regular expressions
但现在我陷入了困境,因为我需要在这里进行非贪婪匹配。
我可以做的一件事是匹配不是右括号的所有内容。我不确定这是否是解决这种特殊类型歧义的预期方法(第一次使用 lalr 解析器)。我也不确定 ;HA[My free text ]]]]
是否是一个包含 My free text ]]]
内容的有效文件。但是,如果它 是 一个有效文件,则此解决方法将不起作用。
而且,这似乎没有用:
PropValue = r"\[" <r"[^\]]"> r"\]";
解析失败,我无法准确破译位置。
thread 'core::sgf_replays::game189_has_6_handicap' panicked at 'called `Result::unwrap()` on an `Err` value: UnrecognizedToken { token: Some((7, Token(0, "9"), 8)), expected: ["r#\"\\]\"#"] }', libcore/result.rs:945:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at libstd/sys_common/backtrace.rs:71
at libstd/sys_common/backtrace.rs:59
2: std::panicking::default_hook::{{closure}}
at libstd/panicking.rs:380
3: std::panicking::default_hook
at libstd/panicking.rs:390
4: std::panicking::rust_panic_with_hook
at libstd/panicking.rs:576
5: std::panicking::begin_panic
at libstd/panicking.rs:537
6: std::panicking::begin_panic_fmt
at libstd/panicking.rs:521
7: rust_begin_unwind
at libstd/panicking.rs:497
8: core::panicking::panic_fmt
at libcore/panicking.rs:71
9: core::result::unwrap_failed
at /checkout/src/libcore/macros.rs:23
10: <core::result::Result<T, E>>::unwrap
at /checkout/src/libcore/result.rs:782
11: seraph::core::sgf_replays::game189_has_6_handicap
at src/core/mod.rs:612
12: <F as alloc::boxed::FnBox<A>>::call_box
at libtest/lib.rs:1453
at /checkout/src/libcore/ops/function.rs:223
at /checkout/src/liballoc/boxed.rs:788
13: __rust_maybe_catch_panic
at libpanic_unwind/lib.rs:102
为了完整起见,这里是 .lalrpop
use std::str::FromStr;
use gosgf::*;
use std::collections::HashMap;
grammar;
pub Collection: GoCollection = <GameTree*>;
match {
r"\(",
r"\)",
r";" ,
r"\[",
r"\]",
r"[A-Z]+",
}
else {
r"[^\]]",
}
GameTree: GameTree = {
r"\(" <sequence: Sequence> <children: GameTree*> r"\)" => {
let komi = f64::from_str(sequence[0].properties.get("KM").unwrap_or(&"0.0".to_owned())).unwrap();
let size = usize::from_str(sequence[0].properties.get("SZ").unwrap_or(&"19".to_owned())).unwrap();
let handicap;
{
let mut handistr = String::from("0");
for node in &sequence {
if let Some(ha) = node.properties.get("HA") {
handistr = ha.to_string();
break;
}
}
handicap = usize::from_str(&handistr).unwrap();
}
GameTree {
komi,
size,
handicap,
sequence,
children,
}
}
};
Sequence = <Node+>;
Node: Node = {
r";" <pairs: Property+> => {
let mut properties : HashMap<String, String> = HashMap::new();
for (k, v) in pairs {
properties.insert(k, v);
}
Node { properties }
}
};
Property: (String, String) = {
<k: PropIdent> <v: PropValue> => (k.to_string(), v.to_string())
};
PropIdent = <r"[A-Z]+">;
PropValue = r"\[" <r".*"> r"\]";
这是我想出的一个答案。我放弃了多个单独的终端,并使用 [^\]]
技巧将它们全部组合成一个大终端。
Property: (String, String) = {
<r"[A-Z]+\[[^\]]*\]"> => {
lazy_static! {
static ref RE : regex::Regex = regex::Regex::new(r"([A-Z]+)\[([^\]]*)\]").unwrap();
}
let cap = RE.captures(<>).unwrap();
let k = &cap[1];
let v = &cap[2];
(k.to_string(), v.to_string())
}
};
还不打算接受这个答案,因为我不确定这是给定可用工具的最优雅的解决方案,而且我还想知道是否可以创建一个解析
的规则`;HA[My Text]]QB[[Nested]]`
作为两个属性
("HA", "My Text]")
和 ("QB", "[Nested]")
作为结果,或者如果这种表达式无法用 lalr(1) 解析器解析。
编辑:尽管 Stefan 指出有效的 SGF 4 文件将转义其右括号,但我只是想知道这是否可能。我开始怀疑它是否是,因为一旦不看 属性 到下一件事,就永远无法知道括号是文本的一部分还是 属性 的结尾。