在pess.rs,pest-ast crate 中,我如何获得枚举字段?

问题描述

我有一个 pest 语法示例:

WHITESPACE = _{ " " }
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
int_literal = { DECIMAL_NUMBER+ }

assignment_op = { ":=" }
formula = { (identifier ~ assignment_op ~ int_literal) | int_literal }

file = { formula ~ EOI }

一个 pest-ast 派生:

extern crate pest_derive;
extern crate from_pest;
extern crate pest_ast;
extern crate pest;


mod parser {
    #[derive(Parser)]
    #[grammar = "talk/formula.pest"]
    pub struct Parser;
}


mod ast {
    use super::parser::Rule;
    use pest::Span;

    fn span_into_str(span: Span) -> &str {
        span.as_str()
    }

    #[derive(Debug,Frompest)]
    #[pest_ast(rule(Rule::int_literal))]
    pub struct IntLiteral {
        #[pest_ast(outer(with(span_into_str),with(str::parse::<i64>),with(Result::unwrap)))]
        pub value: i64
    }

    #[derive(Debug,Frompest)]
    #[pest_ast(rule(Rule::identifier))]
    pub struct Identifier {
        #[pest_ast(inner(with(span_into_str),with(String::from)))]
        pub value: String
    }

    #[derive(Debug,Frompest)]
    #[pest_ast(rule(Rule::assignment_op))]
    pub struct AssignmentOp {
    }

    #[derive(Debug,Frompest)]
    #[pest_ast(rule(Rule::formula))]
    pub enum Formula {
        Assignment {
            lvalue: Identifier,a: AssignmentOp,// can I skip this?
            rvalue: IntLiteral,},IntLiteral {
            rvalue: IntLiteral,}
    }

#[cfg(test)]
mod tests {
    use super::*;
    use super::ast::*;
    use pest::Parser;
    use from_pest::Frompest;

    #[test]
    fn test_formula0() {
        let source = "a := 12";
        let mut parse_tree = parser::Parser::parse(parser::Rule::formula,source).unwrap();
        println!("parse tree = {:#?}",parse_tree);
        let Syntax_tree: Formula = Formula::from_pest(&mut parse_tree).expect("infallible");
        println!("Syntax tree = {:#?}",Syntax_tree);
    }
}

运行测试时,我感到 infallible: NoMatch 恐慌。

  • pest-ast 是否支持使用字段派生枚举变体?
  • 我可以将枚举变体与带括号的 () 终端组匹配吗?
  • 我可以跳过一些终端吗?如果我最终得到 :=,我并不完全需要知道使用了 AssignmentExpression { lvalue,rvalue }

解决方法

我在 pest-ast issue #8 中找到了一个示例。语法规则:

seq = { a ~ b ~ c }
choice = { a | b | c }
compund_seq = { a ~ (b | c) }
compound_choice = { (a ~ b) | (b ~ c) }
assign = { (a|b|c) ~ "=" ~ number }
assigns = { (assign ~ ",")* ~ assign ~ ","? }

对应代码:

enum choice<'pest>{
  struct _1(a<'pest>),struct _2(b<'pest>),struct _3(c<'pest>),}
struct compound_seq<'pest>(
  #[pest_ast(outer)] Span<'pest>,a<'pest>,enum _2 {
    struct _1(b<'pest>),struct _2(c<'pest>),},);
enum compound_choice<'pest>{
  struct _1(
    #[pest_ast(outer)] Span<'pest>,b<'pest>,),struct _2(
    #[pest_ast(outer)] Span<'pest>,c<'pest>,}
struct assign<'pest>(
  #[pest_ast(outer)] Span<'pest>,enum _1 {
    struct _1(a<'pest>),number<'pest>,);
struct assigns<'pest>(
  #[pest_ast(outer)] Span<'pest>,Vec<struct _1(assign<'pest>)>,assign<'pest>,);

一旦我知道自己走在正确的轨道上,我就发现了代码中的错误,这与所提出的问题完全无关。 Identifier 规则应该使用 outer span 而不是 inner

#[derive(Debug,FromPest)]
#[pest_ast(rule(Rule::identifier))]
pub struct Identifier {
    #[pest_ast(outer(with(span_into_str),with(String::from)))]
    pub value: String
}

最有用的调试工具是打印 Identifier 规则生成的原始语法树:

#[test]
fn test_identifier() {
    let source = "foobar";
    let mut parse_tree = parser::Parser::parse(parser::Rule::identifier,source).unwrap();
    println!("parse tree = {:#?}",parse_tree);
    let syntax_tree: Identifier = Identifier::from_pest(&mut parse_tree).expect("infallible");
    println!("syntax tree = {:#?}",syntax_tree);
    assert_eq!(syntax_tree.value,"foobar".to_string());
}

我还必须删除 struct 中的 enum 才能编译 Formula

#[derive(Debug,FromPest)]
#[pest_ast(rule(Rule::formula))]
pub enum Formula {
    Assignment {
        lvalue: Identifier,// a: AssignmentOp,rvalue: IntLiteral,OrTest {
        or_test: IntLiteral,}
}

问题的答案:

pest-ast 是否支持使用字段派生枚举变体?

是的,上面的例子。

我可以将 enum 变体与带括号的 () 终端组匹配吗?

还没有答案。这对我不起作用。

我可以跳过一些终端吗?如果我最终得到一个 AssignmentExpression { lvalue,rvalue },我并不完全需要知道 := 被使用了。

pest-ast 与害虫产生的树一起工作。为了跳过某些内容,请在源语法中将其设为无声规则。