使用Boost.Spirit Qi和Lex时的空白队长(Whitespace skipper when using Boost.Spirit Qi and Lex)

我们考虑以下代码:

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;

template<typename Lexer>
class expression_lexer
    : public lex::lexer<Lexer>
{
public:
    typedef lex::token_def<> operator_token_type;
    typedef lex::token_def<> value_token_type;
    typedef lex::token_def<> variable_token_type;
    typedef lex::token_def<lex::omit> parenthesis_token_type;
    typedef std::pair<parenthesis_token_type, parenthesis_token_type> parenthesis_token_pair_type;
    typedef lex::token_def<lex::omit> whitespace_token_type;

    expression_lexer()
        : operator_add('+'),
          operator_sub('-'),
          operator_mul("[x*]"),
          operator_div("[:/]"),
          value("\\d+(\\.\\d+)?"),
          variable("%(\\w+)"),
          parenthesis({
            std::make_pair(parenthesis_token_type('('), parenthesis_token_type(')')),
            std::make_pair(parenthesis_token_type('['), parenthesis_token_type(']'))
          }),
          whitespace("[ \\t]+")
    {
        this->self
            = operator_add
            | operator_sub
            | operator_mul
            | operator_div
            | value
            | variable
            ;

        std::for_each(parenthesis.cbegin(), parenthesis.cend(),
            [&](parenthesis_token_pair_type const& token_pair)
            {
                this->self += token_pair.first | token_pair.second;
            }
        );

        this->self("WS") = whitespace;
    }

    operator_token_type operator_add;
    operator_token_type operator_sub;
    operator_token_type operator_mul;
    operator_token_type operator_div;

    value_token_type value;
    variable_token_type variable;

    std::vector<parenthesis_token_pair_type> parenthesis;

    whitespace_token_type whitespace;
};

template<typename Iterator, typename Skipper>
class expression_grammar
    : public qi::grammar<Iterator, Skipper>
{
public:
    template<typename Tokens>
    explicit expression_grammar(Tokens const& tokens)
        : expression_grammar::base_type(start)
    {
        start                     %= expression >> qi::eoi;

        expression                %= sum_operand >> -(sum_operator >> expression);
        sum_operator              %= tokens.operator_add | tokens.operator_sub;
        sum_operand               %= fac_operand >> -(fac_operator >> sum_operand);
        fac_operator              %= tokens.operator_mul | tokens.operator_div;

        if(!tokens.parenthesis.empty())
            fac_operand           %= parenthesised | terminal;
        else
            fac_operand           %= terminal;

        terminal                  %= tokens.value | tokens.variable;

        if(!tokens.parenthesis.empty())
        {
            parenthesised         %= tokens.parenthesis.front().first >> expression >> tokens.parenthesis.front().second;
            std::for_each(tokens.parenthesis.cbegin() + 1, tokens.parenthesis.cend(),
                [&](typename Tokens::parenthesis_token_pair_type const& token_pair)
                {
                    parenthesised %= parenthesised.copy() | (token_pair.first >> expression >> token_pair.second);
                }
            );
        }
    }

private:
    qi::rule<Iterator, Skipper> start;
    qi::rule<Iterator, Skipper> expression;
    qi::rule<Iterator, Skipper> sum_operand;
    qi::rule<Iterator, Skipper> sum_operator;
    qi::rule<Iterator, Skipper> fac_operand;
    qi::rule<Iterator, Skipper> fac_operator;
    qi::rule<Iterator, Skipper> terminal;
    qi::rule<Iterator, Skipper> parenthesised;
};


int main()
{
    typedef lex::lexertl::token<std::string::const_iterator> token_type;
    typedef expression_lexer<lex::lexertl::lexer<token_type>> expression_lexer_type;
    typedef expression_lexer_type::iterator_type expression_lexer_iterator_type;
    typedef qi::in_state_skipper<expression_lexer_type::lexer_def> skipper_type;
    typedef expression_grammar<expression_lexer_iterator_type, skipper_type> expression_grammar_type;

    expression_lexer_type lexer;
    expression_grammar_type grammar(lexer);

    while(std::cin)
    {
        std::string line;
        std::getline(std::cin, line);

        std::string::const_iterator first = line.begin();
        std::string::const_iterator const last = line.end();

        bool const result = lex::tokenize_and_phrase_parse(first, last, lexer, grammar, qi::in_state("WS")[lexer.self]);
        if(!result)
            std::cout << "Parsing failed! Reminder: >" << std::string(first, last) << "<" << std::endl;
        else
        {
            if(first != last)
                std::cout << "Parsing succeeded! Reminder: >" << std::string(first, last) << "<" << std::endl;
            else
                std::cout << "Parsing succeeded!" << std::endl;
        }
    }
}

它是一个带有值和变量的算术表达式的简单解析器。 它是使用expression_lexer构建来提取标记,然后使用expression_grammar来解析标记。

对于如此小的案例使用词法分析器似乎有点矫枉过正,可能就是一个。 但这是简化示例的成本。 另请注意,使用词法分析器可以轻松定义具有正则表达式的标记,同时允许通过外部代码(特别是用户提供的配置)轻松定义它们。 通过提供的示例,从外部配置文件读取标记的定义并且例如允许用户将变量从%name更改为$name

代码似乎工作正常(在Visual Studio 2013上使用Boost 1.61进行检查)。 除了我已经注意到,如果我提供像5++5这样的字符串,它会正确地失败,但报告为仅提醒5而不是+5 ,这意味着违规+被“无法恢复”消耗。 显然,生成但与语法不匹配的令牌绝不会返回到原始输入。 但那不是我要问的。 我在检查代码时意识到了这一点。

现在的问题是空白跳过。 我非常不喜欢它是如何完成的。 虽然我这样做了,因为它似乎是许多例子提供的,包括StackOverflow上的问题答案。

最糟糕的事情似乎是(没有记录?) qi::in_state_skipper 。 此外,似乎我必须添加像这样的whitespace令牌(使用名称),而不是像所有其他的一样,因为使用lexer.whitespace而不是"WS"似乎不起作用。

最后不得不用Skipper论证“混乱”语法似乎不太好。 我不应该摆脱它吗? 毕竟我想基于令牌而不是直接输入来制作语法,我希望将空格从令牌流中排除 - 它不再需要了!

我还有哪些其他选项可以跳过空格? 这样做有什么好处呢?


Let's consider following code:

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;

template<typename Lexer>
class expression_lexer
    : public lex::lexer<Lexer>
{
public:
    typedef lex::token_def<> operator_token_type;
    typedef lex::token_def<> value_token_type;
    typedef lex::token_def<> variable_token_type;
    typedef lex::token_def<lex::omit> parenthesis_token_type;
    typedef std::pair<parenthesis_token_type, parenthesis_token_type> parenthesis_token_pair_type;
    typedef lex::token_def<lex::omit> whitespace_token_type;

    expression_lexer()
        : operator_add('+'),
          operator_sub('-'),
          operator_mul("[x*]"),
          operator_div("[:/]"),
          value("\\d+(\\.\\d+)?"),
          variable("%(\\w+)"),
          parenthesis({
            std::make_pair(parenthesis_token_type('('), parenthesis_token_type(')')),
            std::make_pair(parenthesis_token_type('['), parenthesis_token_type(']'))
          }),
          whitespace("[ \\t]+")
    {
        this->self
            = operator_add
            | operator_sub
            | operator_mul
            | operator_div
            | value
            | variable
            ;

        std::for_each(parenthesis.cbegin(), parenthesis.cend(),
            [&](parenthesis_token_pair_type const& token_pair)
            {
                this->self += token_pair.first | token_pair.second;
            }
        );

        this->self("WS") = whitespace;
    }

    operator_token_type operator_add;
    operator_token_type operator_sub;
    operator_token_type operator_mul;
    operator_token_type operator_div;

    value_token_type value;
    variable_token_type variable;

    std::vector<parenthesis_token_pair_type> parenthesis;

    whitespace_token_type whitespace;
};

template<typename Iterator, typename Skipper>
class expression_grammar
    : public qi::grammar<Iterator, Skipper>
{
public:
    template<typename Tokens>
    explicit expression_grammar(Tokens const& tokens)
        : expression_grammar::base_type(start)
    {
        start                     %= expression >> qi::eoi;

        expression                %= sum_operand >> -(sum_operator >> expression);
        sum_operator              %= tokens.operator_add | tokens.operator_sub;
        sum_operand               %= fac_operand >> -(fac_operator >> sum_operand);
        fac_operator              %= tokens.operator_mul | tokens.operator_div;

        if(!tokens.parenthesis.empty())
            fac_operand           %= parenthesised | terminal;
        else
            fac_operand           %= terminal;

        terminal                  %= tokens.value | tokens.variable;

        if(!tokens.parenthesis.empty())
        {
            parenthesised         %= tokens.parenthesis.front().first >> expression >> tokens.parenthesis.front().second;
            std::for_each(tokens.parenthesis.cbegin() + 1, tokens.parenthesis.cend(),
                [&](typename Tokens::parenthesis_token_pair_type const& token_pair)
                {
                    parenthesised %= parenthesised.copy() | (token_pair.first >> expression >> token_pair.second);
                }
            );
        }
    }

private:
    qi::rule<Iterator, Skipper> start;
    qi::rule<Iterator, Skipper> expression;
    qi::rule<Iterator, Skipper> sum_operand;
    qi::rule<Iterator, Skipper> sum_operator;
    qi::rule<Iterator, Skipper> fac_operand;
    qi::rule<Iterator, Skipper> fac_operator;
    qi::rule<Iterator, Skipper> terminal;
    qi::rule<Iterator, Skipper> parenthesised;
};


int main()
{
    typedef lex::lexertl::token<std::string::const_iterator> token_type;
    typedef expression_lexer<lex::lexertl::lexer<token_type>> expression_lexer_type;
    typedef expression_lexer_type::iterator_type expression_lexer_iterator_type;
    typedef qi::in_state_skipper<expression_lexer_type::lexer_def> skipper_type;
    typedef expression_grammar<expression_lexer_iterator_type, skipper_type> expression_grammar_type;

    expression_lexer_type lexer;
    expression_grammar_type grammar(lexer);

    while(std::cin)
    {
        std::string line;
        std::getline(std::cin, line);

        std::string::const_iterator first = line.begin();
        std::string::const_iterator const last = line.end();

        bool const result = lex::tokenize_and_phrase_parse(first, last, lexer, grammar, qi::in_state("WS")[lexer.self]);
        if(!result)
            std::cout << "Parsing failed! Reminder: >" << std::string(first, last) << "<" << std::endl;
        else
        {
            if(first != last)
                std::cout << "Parsing succeeded! Reminder: >" << std::string(first, last) << "<" << std::endl;
            else
                std::cout << "Parsing succeeded!" << std::endl;
        }
    }
}

It is a simple parser for arithmetic expressions with values and variables. It is build using expression_lexer for extracting tokens, and then with expression_grammar to parse the tokens.

Use of lexer for such a small case might seem an overkill and probably is one. But that is the cost of simplified example. Also note that use of lexer allows to easily define tokens with regular expression while that allows to easily define them by external code (and user provided configuration in particular). With the example provided it would be no issue at all to read definition of tokens from an external config file and for example allow user to change variables from %name to $name.

The code seems to be working fine (checked on Visual Studio 2013 with Boost 1.61). Except that I have noticed that if I provide string like 5++5 it properly fails but reports as reminder just 5 rather than +5 which means the offending + was "unrecoverably" consumed. Apparently a token that was produced but did not match grammar is in no way returned to the original input. But that is not what I'm asking about. Just a side note I realized when checking the code.

Now the problem is with whitespace skipping. I very much don't like how it is done. While I have done it this way as it seems to be the one provided by many examples including answers to questions here on StackOverflow.

The worst thing seems to be that (nowhere documented?) qi::in_state_skipper. Also it seems that I have to add the whitespace token like that (with a name) rather than like all the other ones as using lexer.whitespace instead of "WS" doesn't seem to work.

And finally having to "clutter" the grammar with the Skipper argument doesn't seem nice. Shouldn't I be free of it? After all I want to make the grammar based on tokens rather than direct input and I want the whitespace to be excluded from tokens stream - it is not needed there anymore!

What other options do I have to skip whitespaces? What are advantages of doing it like it is now?


原文:https://stackoverflow.com/questions/39468278
2024-05-06 21:05

满意答案

您仍然在文件上下文管理器内部执行重新启动( with open("...") as text_file部分)。 您写入的文件尚未关闭,因此尚未刷新。 在发出命令之前执行text_file.flush()或在上下文管理器之外发出重新启动。


You're issuing the reboot while still inside the file context manager (the with open("...") as text_file part). The file you've written to hasn't been closed yet, so it hasn't been flushed. Either do text_file.flush() before issuing your command or issue the reboot outside the context manager.

相关问答

更多

python中os.system出错问题

import os cmd=r'c:\"Program Files"\notepad.exe c:\Program Files\1.txt' os.system(cmd) 注意点:cmd中的命令的路径出现空格需要用引号,后面的文件路径不需要引号。

Python 3 中 os.system调用问题

if not os.path.exists(dst_dir): os.mkdir(dst_dir) print("this DIR %s set up Successful!" % dst_dir) else: print("this DIR %s is exists!" % dst_dir) zip_command = "winrar a %s %s" % (r_name,' '.join(source)) # 这个winrar,你在cmd能运行吗?不能的话就加入绝对路径 print (zip_...

通过os.system强制在Python中触发的程序输入(Forcing an enter on a program fired off in Python by os.system)

查看subprocess模块,特别是subprocess.Popen() 。 子进程运行后,您可以与其进行communicate以及其他有用的功能。 Check out the subprocess module, in particular subprocess.Popen(). Once your subprocess is running, you can communicate with it, along with other useful functions.

Python在os.system(“sleep ...”)时如何阻塞信号?(How is Python blocking signals while os.system(“sleep…”)?)

也许魔术在PY_BEGIN_ALLOW_THREADS? 魔术主要在system本身。 system不能返回EINTR,所以libc实现很难继续wait子进程。 这意味着在使用os.system ,控制从不返回到python,直到底层system完成,因此python信号处理机制不会被及时调用。 然而, subprocess.call基本上是这样做的: # Compare subprocess.py:Popen/_eintr_retry_call(os.waitpid, self.pid, 0) ...

使用os.system()或subprocess()通过Python运行Rscript(Running Rscript via Python using os.system() or subprocess())

在得到这样一个简单问题的精神之后。 我决定将RStatistics重新安装到没有空格或点的路径,例如: C:/R/bin/Rscript.exe 。 现在subprocess.call(["C:/R/bin/Rscript.exe", "D:/otsu_Script.R"] )或os.system("C:/R/bin/Rscript.exe D:/otsu_Script.R")工作正常。 应该在两天前尝试过...... ......但是现在我还是一只快乐的猴子:-) After getting m...

os.system()的问题没有生成输出(issues with os.system() not generating output)

它与使用os.system()而不是os.system()无关。 问题是您在运行程序之前没有关闭outfname文件。 将关闭移动到os.system()调用之前: handle.close() outf.close() gamma = outfname + ".mafft" delta = gamma + ".stock" q = "mafft %s > %s" % (outfname, gamma) os.system(q) mafft ...

使用os.system异步,从python进程中独立?(Using os.system asynchronous, independ from the python process?)

如果你想用默认程序在windows上打开一个文件,那么有一个特殊的函数可以独立于你自己的程序打开它: os.startfile 。 这应该做你想要的。 更一般地说,为了异步运行程序, os.system无法做到。 但是, 子进程模块可以。 子os.system模块几乎完成了os.system所做的一切,还有更多。 但是,与os.system类似,当您使用subprocess.Popen时,程序不会独立于您的进程打开 - 当您的进程终止时,子进程也会死亡。 If you want to open a...

将参数传递给os.system(Passing arguments into os.system)

不要使用os.system() ; subprocess绝对是要走的路。 你的问题虽然是你期望Python能够理解你想要将args.fileread内args.fileread一个字符串中。 就像Python一样,它无法像这样读懂你的思想! 改用字符串格式: os.system("rtl2gds -rtl={args.fileread} -rtl_top={args.module_name} -syn".format(args=args) 如果你想传递一个文件名到另一个命令,你不应该使用File...

os.system(/ sbin / shutdown -r now)在写入之前先执行(os.system(/sbin/shutdown -r now) executes first before writing)

您仍然在文件上下文管理器内部执行重新启动( with open("...") as text_file部分)。 您写入的文件尚未关闭,因此尚未刷新。 在发出命令之前执行text_file.flush()或在上下文管理器之外发出重新启动。 You're issuing the reboot while still inside the file context manager (the with open("...") as text_file part). The file you've writ...

cmd执行但python os.popen和subprocess.call和subprocess.popen和os.system不执行,为什么?(cmd executes but python os.popen and subprocess.call and subprocess.popen and os.system does not execute, why?)

您的命令失败,因为它们包含反斜杠,这在Python字符串文字中具有特殊含义。 特别是,网络路径开始处的“\\”变成单个“\”,使其不再是网络路径(此替换在粘贴文本最后一行的错误消息中可见) 。 您可以将所有反斜杠加起来以避免它们(在这种情况下很快就会变得不可读),或者在字符串前加上“r”使它成为一个“原始字符串”,它不会专门解释反斜杠。 例如: os.popen(r'net use J: \\Purushoth-pc\d\Materials').read() Your commands are f...

相关文章

更多

Solr boost某字段的特殊值

http://www.quora.com/Boosting-documents-in-solr-on- ...

Lucene4:获取中文分词结果,根据文本计算boost

1. 要求 环境: Lucene 4.1版本/IKAnalyzer 2012 FF版本/mmseg4 ...

Lucene4:运用中文分词器创建索引,给指定文本增加boost值

1. 要求 环境: Lucene 4.1版本/IKAnalyzer 2012 FF版本/mmseg4 ...

lucene/solr 修改评分规则方法总结

说明:由于solr底层使用的是lucene,因此修改solr打分机制归根结底还是依赖于lucene的打 ...

拼音分割的思路请教

请教一个题目: 测试题目 拼音:zhongguorenminzhanqilaile 分割目标:z ...

jfreechart如何去掉为null的空白占位

在第二个区组里面,14和12之间5个为null的值,但是他也占了位置,我想不让他们占位置。就是14和1 ...

解决点击没有内容的空白div没有响应click事件的方法

给一个div绑定一个click事件,如果这个div没有内容,在一般浏览器下都没有问题,在IE8下点击的 ...

Solr参数(DisMax Event Facet)

一.DisMax 1.tie:query and init param for tiebreaker ...

solr如何计算score?

solr计算一个query的score分为两个部分: Lucene的算分模型 Boost 其中 ...

Solr DisjunctionMax 注解

Disjunction Max析取最大(并集) 本质多域联合搜索,并且不同域指定不同的权重,命中时取最 ...

最新问答

更多

获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)

我用Google搜索了一个解决方案。 “EnumDisplayModeProvider”是我自己设置网站的各种模式的枚举。 public EnumDisplayModeProvider GetDisplayModeId() { foreach (var mode in DisplayModeProvider.Instance.Modes) if (mode.CanHandleContext(HttpContext)) {

如何通过引用返回对象?(How is returning an object by reference possible?)

这相对简单:在类的构造函数中,您可以分配内存,例如使用new 。 如果你制作一个对象的副本,你不是每次都分配新的内存,而是只复制指向原始内存块的指针,同时递增一个也存储在内存中的引用计数器,使得每个副本都是对象可以访问它。 如果引用计数降至零,则销毁对象将减少引用计数并仅释放分配的内存。 您只需要一个自定义复制构造函数和赋值运算符。 这基本上是共享指针的工作方式。 This is relatively easy: In the class' constructor, you allocate m

矩阵如何存储在内存中?(How are matrices stored in memory?)

正如它在“熵编码”中所说的那样,使用Z字形图案,与RLE一起使用,在许多情况下,RLE已经减小了尺寸。 但是,据我所知,DCT本身并没有给出稀疏矩阵。 但它通常会增强矩阵的熵。 这是compressen变得有损的点:输入矩阵用DCT传输,然后量化量化然后使用霍夫曼编码。 As it says in "Entropy coding" a zig-zag pattern is used, together with RLE which will already reduce size for man

每个请求的Java新会话?(Java New Session For Each Request?)

你是如何进行重定向的? 您是否事先调用了HttpServletResponse.encodeRedirectURL()? 在这里阅读javadoc 您可以使用它像response.sendRedirect(response.encodeRedirectURL(path)); The issue was with the path in the JSESSIONID cookie. I still can't figure out why it was being set to the tomca

css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)

我认为word-break ,如果你想在一个单词中打破行,你可以指定它,这样做可以解决问题: .column { word-break:break-all; } jsFiddle演示。 您可以在此处阅读有关word-break属性的更多信息。 I think word-break, with which you can specify if you want to break line within a word, will do the trick: .column { word-break

无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)

我认为您忘记在分类时间内缩放输入图像,如train_test.prototxt文件的第11行所示。 您可能应该在C ++代码中的某个位置乘以该因子,或者使用Caffe图层来缩放输入(请查看ELTWISE或POWER图层)。 编辑: 在评论中进行了一次对话之后,结果发现在classification.cpp文件中错误地删除了图像均值,而在原始训练/测试管道中没有减去图像均值。 I think you have forgotten to scale the input image during cl

xcode语法颜色编码解释?(xcode syntax color coding explained?)

转到: Xcode => Preferences => Fonts & Colors 您将看到每个语法高亮颜色旁边都有一个简短的解释。 Go to: Xcode => Preferences => Fonts & Colors You'll see that each syntax highlighting colour has a brief explanation next to it.

在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)

你考虑过第三方拼写检查吗? 您可以将在C#中开发的自定义WinForms控件插入访问数据库吗? VB6控件怎么样? 如果你能找到一个使用第三方库进行拼写检查的控件,那可能会有效。 Have you considered a third party spell checker? Can you insert a custom WinForms controls developed in C# into an access database? What about a VB6 control? If

从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)

我有同样的问题,因为我在远程服务器上有两个图像,我需要在每天的预定义时间复制到我的本地服务器,这是我能够提出的代码... try { if(@copy('url/to/source/image.ext', 'local/absolute/path/on/server/' . date("d-m-Y") . ".gif")) { } else { $errors = error_get_last(); throw new Exception($err

从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))

我不确定我完全明白你在说什么。 你能编辑你的帖子并包含你正在做的Subversion命令/操作的特定顺序吗? 最好使用命令行svn客户端,以便容易为其他人重现问题。 如果您只是想获取文件的旧副本(即使该文件不再存在),您可以使用如下命令: svn copy ${repo}/trunk/moduleA/file1@${rev} ${repo}/trunk/moduleB/file1 其中${repo}是您的存储库的URL, ${rev}是您想要的文件的版本。 这将恢复该文件的旧版本,包括最高版本