使用 nom 匹配模板过滤器表达式
Matching template filter expressions with nom
我正在开发一个模板引擎,其中的一些语法可能是这样的:
{{ somevar|filter }}
代替 somevar
可以是任意的 "expression",也就是说,可以是像 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));
同一个playground。当前版本因堆栈溢出而恐慌。我可以通过反转 any_expr
中的 expr_var | expr_filter
顺序来解决这个问题,但我又回到了与以前基本相同的错误。
我不能说我理解你的问题:没有应该解析的文本示例,你也没有描述你在构建解析器时遇到的问题。
不过,也许下面的例子会有帮助。一个有效的递归解析器:
#[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));
}
(playground).
我通过编写自己的解析器函数修复了它:
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 宏做同样的事情,但至少我得到了一些工作。
我正在开发一个模板引擎,其中的一些语法可能是这样的:
{{ somevar|filter }}
代替 somevar
可以是任意的 "expression",也就是说,可以是像 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));
同一个playground。当前版本因堆栈溢出而恐慌。我可以通过反转 any_expr
中的 expr_var | expr_filter
顺序来解决这个问题,但我又回到了与以前基本相同的错误。
我不能说我理解你的问题:没有应该解析的文本示例,你也没有描述你在构建解析器时遇到的问题。
不过,也许下面的例子会有帮助。一个有效的递归解析器:
#[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));
}
(playground).
我通过编写自己的解析器函数修复了它:
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 宏做同样的事情,但至少我得到了一些工作。