Ржавчина lalrpop lexing двусмысленность: не жадное соответствие внутри скобок
Я пытаюсь разобрать формат SGF, который имеет этот BNF:
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)
Я не пытаюсь разобрать ValueType
в различные типы, используя lalrpop. Я просто хочу сырое текстовое содержание свойств.
У меня проблема с моим 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"\]";
1 ответ
Вот один ответ, который я придумал. Я уронил несколько отдельных терминалов и объединил их в один большой терминал, используя [^\]]
трюк.
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).
Редактировать: несмотря на то, что Стефан указывает, что действительным файлам SGF 4 будут закрываться скобки, я просто хочу знать, возможно ли это. Я начинаю сомневаться, что это так, поскольку однажды никогда не узнаешь, является ли скобка частью текста или концом свойства, не глядя мимо этого свойства на следующую вещь.