Ржавчина 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 будут закрываться скобки, я просто хочу знать, возможно ли это. Я начинаю сомневаться, что это так, поскольку однажды никогда не узнаешь, является ли скобка частью текста или концом свойства, не глядя мимо этого свойства на следующую вещь.

Другие вопросы по тегам