Сопоставление выражений шаблонных фильтров с nom

Я работаю над механизмом создания шаблонов, синтаксис которого может быть таким:

{{ somevar|filter }}

Вместо somevar может быть произвольное «выражение», то есть либо имя переменной, например somevar, либо вложенное выражение фильтра (например, {{ somevar|filter|anotherfilter }}). Я пытаюсь разобрать это, используя библиотеку комбинатора синтаксического анализатора Rust nom, но пока не могу заставить ее работать.

Вот синтаксический анализатор, который я придумал:

#[macro_use]
extern crate nom;

use std::str;

#[derive(Debug)]
pub enum Expr<'a> {
    Var(&'a [u8]),
    Filter(&'a str, Box<Expr<'a>>),
}

#[derive(Debug)]
pub enum Node<'a> {
    Lit(&'a [u8]),
    Expr(Expr<'a>),
}

named!(expr_var<Expr>, dbg_dmp!(map!(nom::alphanumeric, Expr::Var)));

named!(expr_filter<Expr>,
    dbg_dmp!(do_parse!(
         val: any_expr >>
         tag_s!("|") >>
         name: map_res!(nom::alphanumeric, str::from_utf8) >>
         (Expr::Filter(name, Box::new(val)))
    ))
);

named!(any_expr<Expr>, dbg_dmp!(ws!(
    alt_complete!(
        expr_filter |
        expr_var  
    ))));

named!(expr_node<Node>, dbg_dmp!(map!(
    delimited!(tag_s!("{{"), any_expr, tag_s!("}}")),
    Node::Expr)));

named!(parse_template< Vec<Node> >, many1!(expr_node));

С игровой площадкой. Текущая версия вызывает панику из-за переполнения стека. Я могу исправить это, изменив порядок expr_var | expr_filter в any_expr, но затем я снова вернусь к той же ошибке, что и раньше.


person djc    schedule 03.01.2017    source источник
comment
Я настоятельно рекомендую написать фактическую грамматику, прежде чем пытаться писать код. Сильной стороной инструментов синтаксического анализа является то, что они помогают сопоставить грамматику с кодом.   -  person Shepmaster    schedule 03.01.2017
comment
Я бы также порекомендовал 1) Выполнение правильного stackoverflow.com/help/mcve (возможно, с помощью play.rust-lang.org). См. Также sscce.org. 2) Предоставление своего рода модульного теста в качестве примера того, как следует анализировать грамматику.   -  person ArtemGr    schedule 04.01.2017


Ответы (2)


Я не могу сказать, что копаю ваш вопрос: нет примера текста, который следует анализировать, и вы также не описываете проблему, с которой вы столкнулись при создании синтаксического анализатора.

Тем не менее, возможно, следующий пример будет вам полезен. Рабочий рекурсивный парсер:

#[macro_use]
extern crate nom;

use nom::alphanumeric;

type Variable = String;
type Filter = String;

named! (plain_expression (&str) -> (Variable, Filter), do_parse! (
    tag_s! ("{{") >>
    variable: alphanumeric >>
    tag_s! ("|") >>
    filter: alphanumeric >>
    tag_s! ("}}") >>
    ((variable.into(), filter.into()))));

#[derive(Debug)]
enum Expression {
    Plain(Variable, Filter),
    Recursive(Box<Expression>, Filter),
}

named! (recursive_expression (&str) -> Expression,
  alt_complete! (
    map! (plain_expression, |(v, f)| Expression::Plain (v, f)) |
    do_parse! (
      tag_s! ("{{") >>
      sub: recursive_expression >>
      tag_s! ("|") >>
      filter: alphanumeric >>
      tag_s! ("}}") >>
      (Expression::Recursive (Box::new (sub), filter.into())))));

fn main() {
    let plain = "{{var|fil}}";
    let recursive = "{{{{{{var1|fil1}}|fil2}}|fil3}}";
    // Prints: Done("", ("var", "fil")).
    println!("{:?}", plain_expression(plain));
    // Prints: Done("", Recursive(Recursive(Plain("var1", "fil1"), "fil2"), "fil3")).
    println!("{:?}", recursive_expression(recursive));
}

(площадка).

person ArtemGr    schedule 04.01.2017
comment
В любом случае, спасибо, что попробовали. Извините, я не был уверен, что поместил полный код парсера в свой вопрос, чувствуя, что это может быть непосильно. В свою защиту я связался с репозиторием GitHub, который должен был упростить просмотр как полного парсера, так и ввода примера, а также кое-что, что упростило бы это тестирование (хотя, возможно, не так просто, как ваше игровая площадка). Во всяком случае, сейчас я попытался немного прояснить свой вопрос. Я немного углублюсь в ваш пример, чтобы посмотреть, есть ли что-нибудь, что может помочь. - person djc; 04.01.2017
comment
@djc нам не нужен весь парсер, нам нужен минимальный воспроизводимый пример , акцент на M. - person Shepmaster; 04.01.2017
comment
@djc Спасибо за обновление. Что касается ошибки Many1, вам следует скомпилировать nom с включенной функцией подробных ошибок, чтобы она сообщила вам точное место сбоя синтаксического анализа. - person ArtemGr; 05.01.2017

Я исправил это, написав собственную функцию парсера:

named!(expr_var<Expr>, map!(nom::alphanumeric, Expr::Var));

fn expr_filtered(i: &[u8]) -> IResult<&[u8], Expr> {
    let (mut left, mut expr) = match expr_var(i) {
        IResult::Error(err) => { return IResult::Error(err); },
        IResult::Incomplete(needed) => { return IResult::Incomplete(needed); },
        IResult::Done(left, res) => (left, res),
    };
    while left[0] == b'|' {
        match nom::alphanumeric(&left[1..]) {
            IResult::Error(err) => {
                return IResult::Error(err);
            },
            IResult::Incomplete(needed) => {
                return IResult::Incomplete(needed);
            },
            IResult::Done(new_left, res) => {
                left = new_left;
                expr = Expr::Filter(str::from_utf8(res).unwrap(), Box::new(expr));
            },
        };
    }
    return IResult::Done(left, expr);
}

named!(expr_node<Node>, map!(
    delimited!(tag_s!("{{"), ws!(expr_filtered), tag_s!("}}")),
Node::Expr));

Вероятно, есть более приятный способ сделать то же самое с макросами nom, но, по крайней мере, у меня что-то работает.

person djc    schedule 06.01.2017