Parsing 我可以从skipper解析器收集属性吗?

Parsing 我可以从skipper解析器收集属性吗?,parsing,attributes,boost-spirit,skipper,Parsing,Attributes,Boost Spirit,Skipper,我有一个数据文件格式,其中包括 /*评论*/ /*嵌套的/*注释*/太*/和 //C++风格的单行注释… 通常,这些注释可以出现在输入文件中允许正常空白的任何地方 因此,我制作了一个skipper解析器,它将处理空白和各种注释,而不是通过普遍的注释处理来污染语法本身 到目前为止还不错,我能够解析所有的测试用例 然而,在我的用例中,如果存在一个或多个注释,则任何解析的值(double、string、variable、list等)都必须将其前面的注释作为属性携带。也就是说,我的double的A

我有一个数据文件格式,其中包括

  • /*评论*/
  • /*嵌套的/*注释*/太*/和
  • //C++风格的单行注释…
通常,这些注释可以出现在输入文件中允许正常空白的任何地方

因此,我制作了一个skipper解析器,它将处理空白和各种注释,而不是通过普遍的注释处理来污染语法本身

到目前为止还不错,我能够解析所有的测试用例

然而,在我的用例中,如果存在一个或多个注释,则任何解析的值(double、string、variable、list等)都必须将其前面的注释作为属性携带。也就是说,我的double的AST节点应该是

struct Double {
   double value;
   std::string comment;
};
struct Double {
   double value;
   std::string comment;
};
等等,我在语法中的所有值

因此,我想知道是否有可能以某种方式将收集到的注释“存储”在skipper解析器中,然后将它们用于以普通语法构建AST节点

处理注释的跳过程序:

template<typename Iterator>
struct SkipperRules : qi::grammar<Iterator> {
    SkipperRules() : SkipperRules::base_type(skipper) {
        single_line_comment = lit("//") >> *(char_ - eol) >> (eol | eoi);
        block_comment = ((string("/*") >> *(block_comment | char_ - "*/")) >> string("*/"));
        skipper = space | single_line_comment | block_comment;
    }
    qi::rule<Iterator> skipper;
    qi::rule<Iterator, std::string()> block_comment;
    qi::rule<Iterator, std::string()> single_line_comment;
};
模板
struct SkipperRules:qi::grammar{
SkipperRules():SkipperRules::基本类型(skipper){
单行注释=点亮(“/”>>*(字符-下线)>>(下线下线下线);
块注释=((字符串(“/*”)>>*(块注释(“*/”)>>字符串(“*/”));
skipper=空格|单行|注释|块|注释;
}
qi::规则跳跃者;
qi::规则块注释;
qi::规则单行注释;
};
我可以在skipper规则中使用全局变量和语义操作来存储这些命令,但这似乎是错误的,并且在解析器回溯中可能不会很好地发挥作用。存储注释的好方法是什么,以便以后可以在主语法中检索它们

我可以在skipper规则中使用全局变量和语义操作来存储这些命令,但这似乎是错误的,并且在解析器回溯中可能不会很好地发挥作用

好主意。看见此外,在您的情况下,这将不必要地使源位置与注释的关联复杂化

我可以从skipper解析器收集属性吗

你不能。跳过符是隐式的
qi::omit[]
(顺便说一下,就像Kleene-%列表中的分隔符一样)

然而,在我的用例中,任何解析的值(double、string、, 变量,列表,…)必须将其前面的注释作为 属性,如果存在一个或多个注释。即,我的AST节点 双人间应该是

struct Double {
   double value;
   std::string comment;
};
struct Double {
   double value;
   std::string comment;
};
你有了它:你的评论不是评论。在AST中需要它们,所以在语法中也需要它们

思想 我有几个想法

  • 您不能简单地使用skipper来添加注释,正如您所提到的,这在语法上会很麻烦/嘈杂

  • 您可以临时覆盖skipper,使其仅在需要注释的地方成为
    qi::space
    。差不多

    value_ = qi::skip(qi::space) [ comment_ >> (string_|qi::double_|qi::int_)  ];
    
    或者考虑到你的AST,可能更详细一点

    value_ = qi::skip(qi::space) [ comment_ >> (string_|double_|int_) ];
    string_ = comment_ >> lexeme['"' >> *('\\' >> qi::char_ | ~qi::char_('"')) >> '"'];
    double_ = comment_ >> qi::real_parser<double, qi::strict_real_policies<double> >{};
    int_    = comment_ >> qi::int_;
    

  • 我认为选择2。这是一种你可能没有意识到的“直截了当”的方法。备选案文3。如果您想享受更大的通用性/灵活性,这是一种奇特的方法。你打算用它做什么

      /*obsolete*/ /*deprecated*/ 5.12e7
    
    或者呢

      bla = /*this is*/ 42 /*also relevant*/;
    
    在“幻想”的情况下,这些问题更容易正确处理


    所以,如果你想避免复杂性,我建议选择2。如果您需要灵活性,我建议选择3。

    感谢您的全面回复。我很清楚您给出的奇怪示例,在一个节点之前有多个注释,或者在一个节点之后有多个注释。首先,处理这些问题的方法是实用的——现有的手写解析器只需在遇到注释时将它们推到注释堆栈上,然后将它们合并到一个注释堆栈中,并将它们粘贴到下一个创建的节点上。我认为方法3是模仿这一点的方法,并且具有灵活性,可以在以后对其进行改进。至少我现在有东西可以试一下。这听起来是一个很好的分析。如果您有一些特定的代码来帮助它工作,我愿意提供帮助——在一定程度上。我现在深入到您提到的所有奇怪示例的单元测试中,坦率地说,解析器尝试/回溯“value=double | string | list | struct | table |…”使我的skipper注释收集器多次收集相同的注释。即:当尝试规则替代方案时,片段“a=/*hi*/10”将在注释堆栈上推送/*hi*/6次。我的想法是跟踪解析器的进度,只有在注释稍后出现在输入流中时才推送注释,然后推送到目前为止处理的内容。这就是你所想的吗?我正在考虑对集合进行决定性的选通(想想
    qi::eps[一些动作]
    )。qi::如果需要,请稍候。谢谢您的示例。看起来很好。我同意/*c7*/处理——现有的解析器不是这样做的,但它更符合逻辑,我希望保留这种方式。你肯定不止回答了这个问题,我还有一些事情要做。谢谢。