不应用Boost Spirit解析规则

2023-12-11

我在这里看不到我的错误..这个规则可以解析一些东西,但最后两个样本不能。有人可以给我一个提示吗..

目标是一个能够识别成员属性访问和成员函数调用的解析器。也以某种方式被束缚

 a()
 a(para)
 x.a()
 x.a(para)
 x.a(para).g(para).j()
 x.y
 x.y.z
 x.y.z()    <---fail
 y.z.z(para) <--- fail
  lvalue =
         iter_pos >> name[_val = _1]
          >> *(lit('(') > paralistopt  > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_parameter, construct<std::vector<common_node> >(_1))]        
       >> *(lit('.') >> name_pure >> lit('(') > paralistopt > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _3), key_this, construct<common_node>(_val), key_callname, construct<std::wstring>(_1), key_parameter, construct<std::vector<common_node> >(_2))]
       >> *(lit('.') >> name_pure >> iter_pos)[_val = construct<common_node>(type_cmd_dot_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_propname, construct<std::wstring>(_1))]
    ;

谢谢 马库斯


您提供的信息很少。让我用我进入这个猜谜游戏的方式来逗逗你:

假设您想要解析一种简单的“语言”,它只允许成员表达式和函数调用,但是是链接的。

现在,你的语法没有提到参数(尽管很明显参数列表可以为空),所以让我进一步假设你想要接受相同类型的表达式(所以foo(a)没关系,但也bar(foo(a)) or bar(b.foo(a))).

由于您接受函数调用的链接,因此函数似乎是第一类对象(并且函数可以返回函数),因此foo(a)(b, c, d)也应该被接受。

您没有提到它,但参数通常包含文字(sqrt(9)我想到了,或者println("hello world")).

其他项目:

  • 你没有说,但你可能想忽略某些地方的空白
  • 来自iter_pos(ab)use 看来您有兴趣跟踪生成的 AST 中的原始源位置。

1. 定义 AST

我们应该一如既往地保持简单:

namespace Ast {
    using Identifier = boost::iterator_range<It>;

    struct MemberExpression;
    struct FunctionCall;

    using Expression = boost::variant<
                double,       // some literal types
                std::string,
                // non-literals
                Identifier,
                boost::recursive_wrapper<MemberExpression>,
                boost::recursive_wrapper<FunctionCall>
            >;

    struct MemberExpression {
        Expression object; // antecedent
        Identifier member; // function or field
    };

    using Parameter  = Expression;
    using Parameters = std::vector<Parameter>;

    struct FunctionCall {
        Expression function; // could be a member function
        Parameters parameters;
    };
}

NOTE我们不会专注于显示源位置,但已经做出了一项规定,将标识符存储为迭代器范围。

NOTE融合适应 Spirit 不直接支持的唯一类型:

BOOST_FUSION_ADAPT_STRUCT(Ast::MemberExpression, object, member)
BOOST_FUSION_ADAPT_STRUCT(Ast::FunctionCall, function, parameters)

我们会发现我们不使用这些,因为语义动作在这里更方便。

2. 匹配语法

Grammar() : Grammar::base_type(start) {
    using namespace qi;
    start = skip(space) [expression];

    identifier = raw [ (alpha|'_') >> *(alnum|'_') ];
    parameters = -(expression % ',');

    expression 
        = literal 
        | identifier  >> *(
                    ('.' >> identifier)        
                  | ('(' >> parameters >> ')') 
                );

    literal = double_ | string_;
    string_ = '"' >> *('\\' >> char_ | ~char_('"')) >> '"';

    BOOST_SPIRIT_DEBUG_NODES(
            (identifier)(start)(parameters)(expression)(literal)(string_)
        );
}

在这个框架中,大多数规则受益于自动属性传播。没有的一个是expression:

qi::rule<It, Expression()> start;

using Skipper = qi::space_type;
qi::rule<It, Expression(), Skipper> expression, literal;
qi::rule<It, Parameters(), Skipper> parameters;
// lexemes
qi::rule<It, Identifier()> identifier;
qi::rule<It, std::string()> string_;

因此,让我们为语义操作创建一些助手。

NOTE这里的一个重要要点是创建自己的更高级别的构建块,而不是费力地构建boost::phoenix::construct<> etc.

定义两个简单的构造函数:

struct mme_f { MemberExpression operator()(Expression lhs, Identifier rhs) const { return { lhs, rhs }; } };
struct mfc_f { FunctionCall operator()(Expression f, Parameters params) const { return { f, params }; } };
phx::function<mme_f> make_member_expression;
phx::function<mfc_f> make_function_call;

然后使用它们:

expression 
    = literal [_val=_1]
    | identifier [_val=_1] >> *(
                ('.' >> identifier)        [ _val = make_member_expression(_val, _1)]
              | ('(' >> parameters >> ')') [ _val = make_function_call(_val, _1) ]
            );

就这样。我们准备好了!

3. DEMO

Live On Coliru

我创建了一个如下所示的测试床:

int main() {
    using It = std::string::const_iterator;
    Parser::Grammar<It> const g;

    for (std::string const input : {
             "a()", "a(para)", "x.a()", "x.a(para)", "x.a(para).g(para).j()", "x.y", "x.y.z",
             "x.y.z()",
             "y.z.z(para)",
             // now let's add some funkyness that you didn't mention
             "bar(foo(a))",
             "bar(b.foo(a))",
             "foo(a)(b, c, d)", // first class functions
             "sqrt(9)",
             "println(\"hello world\")",
             "allocate(strlen(\"aaaaa\"))",
             "3.14",
             "object.rotate(180)",
             "object.rotate(event.getAngle(), \"torque\")",
             "app.mainwindow().find_child(\"InputBox\").font().size(12)",
             "app.mainwindow().find_child(\"InputBox\").font(config().preferences.baseFont(style.PROPORTIONAL))"
         }) {
        std::cout << " =========== '" << input << "' ========================\n";
        It f(input.begin()), l(input.end());

        Ast::Expression parsed;
        bool ok = parse(f, l, g, parsed);
        if (ok) {
            std::cout << "Parsed: " << parsed << "\n";
        }
        else
            std::cout << "Parse failed\n";

        if (f != l)
            std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
    }
}

令人难以置信的是,这已经解析了所有测试用例并打印:

 =========== 'a()' ========================
Parsed: a()
 =========== 'a(para)' ========================
Parsed: a(para)
 =========== 'x.a()' ========================
Parsed: x.a()
 =========== 'x.a(para)' ========================
Parsed: x.a(para)
 =========== 'x.a(para).g(para).j()' ========================
Parsed: x.a(para).g(para).j()
 =========== 'x.y' ========================
Parsed: x.y
 =========== 'x.y.z' ========================
Parsed: x.y.z
 =========== 'x.y.z()' ========================
Parsed: x.y.z()
 =========== 'y.z.z(para)' ========================
Parsed: y.z.z(para)
 =========== 'bar(foo(a))' ========================
Parsed: bar(foo(a))
 =========== 'bar(b.foo(a))' ========================
Parsed: bar(b.foo(a))
 =========== 'foo(a)(b, c, d)' ========================
Parsed: foo(a)(b, c, d)
 =========== 'sqrt(9)' ========================
Parsed: sqrt(9)
 =========== 'println("hello world")' ========================
Parsed: println(hello world)
 =========== 'allocate(strlen("aaaaa"))' ========================
Parsed: allocate(strlen(aaaaa))
 =========== '3.14' ========================
Parsed: 3.14
 =========== 'object.rotate(180)' ========================
Parsed: object.rotate(180)
 =========== 'object.rotate(event.getAngle(), "torque")' ========================
Parsed: object.rotate(event.getAngle(), torque)
 =========== 'app.mainwindow().find_child("InputBox").font().size(12)' ========================
Parsed: app.mainwindow().find_child(InputBox).font().size(12)
 =========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))

4. 好得令人难以置信?

你说得对。我作弊了。我没有向您展示调试打印已解析 AST 所需的代码:

namespace Ast {
    static inline std::ostream& operator<<(std::ostream& os, MemberExpression const& me) {
        return os << me.object << "." << me.member;
    }

    static inline std::ostream& operator<<(std::ostream& os, FunctionCall const& fc) {
        os << fc.function << "(";
        bool first = true;
        for (auto& p : fc.parameters) { if (!first) os << ", "; first = false; os << p; }
        return os << ")";
    }
}

这只是调试打印,因为字符串文字没有正确往返。但它只有 10 行代码,这是一个额外的好处。

5. Full Monty:来源地点

您对此感兴趣,所以让我们展示一下它的工作原理。让我们添加一个简单的循环来打印标识符的所有位置:

using IOManip::showpos;

for (auto& id : all_identifiers(parsed)) {
    std::cout << " - " << id << " at " << showpos(id, input) << "\n";
}

当然,这就引出了一个问题,什么是showpos and all_identifiers?

namespace IOManip {
    struct showpos_t {
        boost::iterator_range<It> fragment;
        std::string const& source;

        friend std::ostream& operator<<(std::ostream& os, showpos_t const& manip) {
            auto ofs = [&](It it) { return it - manip.source.begin(); };
            return os << "[" << ofs(manip.fragment.begin()) << ".." << ofs(manip.fragment.end()) << ")";
        }
    };

    showpos_t showpos(boost::iterator_range<It> fragment, std::string const& source) {
        return {fragment, source};
    }
}

至于标识符提取:

std::vector<Identifier> all_identifiers(Expression const& expr) {
    std::vector<Identifier> result;
    struct Harvest {
        using result_type = void;
        std::back_insert_iterator<std::vector<Identifier> > out;
        void operator()(Identifier const& id)       { *out++ = id; }
        void operator()(MemberExpression const& me) { apply_visitor(*this, me.object); *out++ = me.member; }
        void operator()(FunctionCall const& fc)     {
            apply_visitor(*this, fc.function); 
            for (auto& p : fc.parameters) apply_visitor(*this, p);
        }
        // non-identifier expressions
        void operator()(std::string const&) { }
        void operator()(double) { }
    } harvest { back_inserter(result) };
    boost::apply_visitor(harvest, expr);

    return result;
}

这是一个树访问者,它递归地收集所有标识符,并将它们插入到容器的后面。

Live On Coliru

输出看起来像(摘录):

 =========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))
 - app at [0..3)
 - mainwindow at [4..14)
 - find_child at [17..27)
 - font at [40..44)
 - config at [45..51)
 - preferences at [54..65)
 - baseFont at [66..74)
 - style at [75..80)
 - PROPORTIONAL at [81..93)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

不应用Boost Spirit解析规则 的相关文章

  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 根据属性的类型使用文本框或复选框

    如果我有这样的结构 public class Parent public string Name get set public List
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • XSD 嵌套元素

  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • AWK:递归下降 CSV 解析器

    响应一个BASH 中的递归下降 CSV 解析器 https codereview stackexchange com questions 11727 need some advice or help with translation and
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐

  • MySQL 尝试截断表时出错

    我在截断 MySQL Server 5 5 上的表时遇到问题 我尝试截断的表有一列充当另一个表中的外键 The CREATE TABLE涉及的两个表如下 CREATE TABLE tbluser id int 11 NOT NULL AUT
  • 默认值、值和零初始化混乱

    我对值初始化 默认初始化和零初始化感到非常困惑 尤其是当他们针对不同的标准时C 03 and C 11 and C 14 我正在引用并试图扩展一个非常好的答案值 默认 零 初始化C 98 and C 03在这里使其更加通用 因为如果有人可以
  • Matlab互相关与相关系数问题

    我正在用 C 编写一个程序 但使用来自 matlab 的涉及互相关的数据 据我所知 当我对两组数据进行相关时 它会给出一个相关系数数字 表明它们是否相关 但我想使用互相关数据系列 当我在 Matlab 上运行互相关时 它给了我很多数据 绘制
  • 如何从Oracle中的正则表达式中提取组?

    我收到这个查询并想要提取括号之间的值 select de desc regexp substr de desc 1 from DATABASE where col name like 然而 它给了我带有括号的值 例如 TEST 我只想 测试
  • JavaScript 中的可变变量

    据我所知 这个功能在PHP中已经存在 让我们看一下下面的 php 代码 color red color dark 功能描述 有时拥有可变的变量名会很方便 即 可以动态设置和使用的变量名 变量变量采用变量的值并将其视为变量的名称 在上面的例子
  • Matplotlib 表面颜色不纯色

    在 Matplotlib 中绘制平面时 我没有得到纯色 我得到以下带有多种红色阴影的结果 我在 Python 3 5 2 中使用 Matplotlib 版本 1 5 1 我正在运行的代码如下 import numpy as np from
  • 无法使用 PHP $_POST[] 变量检索 jQuery 序列化表单数据

    使用 Ajax jQuery 函数 post 我正在发送 jQueryserialized 将数据表单数据发送到 PHP 函数 该函数成功接收它 POST 但又个性 POST form field 变量为空 在我的 PHP 函数中 prin
  • 使用queue.PriorityQueue,不关心比较

    我正在尝试使用queue PriorityQueue在 Python 3 6 中 我想存储具有给定优先级的对象 但如果两个对象具有相同的优先级 我不介意PriorityQueue get返回任一 换句话说 我的对象不能在整数上进行比较 允许
  • 有没有办法连续将 Console.Write 的结果镜像到集合(数组、列表等)?

    我有一个控制台程序 我想不断地将 Console Write 的结果镜像到一个集合中 我可以实时查看该集合的尾部 该集合可以是数组 列表等 我想我必须使用某种事件处理程序 我不介意被指向第三方库的方向 例如NLog Update 我需要在内
  • 在表视图中不显示数据 - 当 api 没有返回数据时

    我有大约 15 个集合视图单元格 当用户单击每个单元格时 相应的单元格数据将显示在下一个屏幕表格视图中 但是 某些单元格没有任何数据 在这种情况下 我需要在表格视图中显示 没有数据 如何表明 这是我的代码 这些是我的表视图中的委托方法 ar
  • node-gyp 构建错误 Windows x64:“致命错误 LNK1181:无法打开输入文件 'kernel32.lib'”

    以下是我迄今为止在 x64 操作系统上所做的操作 安装Python v2 7 特别是2 7 6 并将其添加到系统路径 C Python27 安装了 MS VS C 2010 Express 版本 我已经有 VS 2012 但没有 C 组件
  • Zend 中的分页

    朋友们 我想在 Zend Framework 中创建分页 我是采埃孚的新手 下面给出了index phtml table tr th Name th th Quantity th th nbsp th tr tr td td td td t
  • Windows 中 main() 的返回值

    在Linux中 main函数的返回值存储在 中 可以使用echo 显示 Windows 中有类似的东西吗 echo ERRORLEVEL
  • 缓存在 Identity Server 4 中似乎不起作用

    我正在尝试使用他们的 IS4 实现添加缓存Caching方法 但是 我的实现似乎对登录速度或每次登录访问数据库的查询数量没有任何影响 我希望缓存能够减少这两者 我为实现缓存所做的更改如下 在 Startup cs 配置服务中添加了以下内容
  • 如何在Weblogic中启用JMX通过WLST设置任何属性

    我收到以下错误 java lang RuntimeException java lang RuntimeException 请求的 属性未通过 JMX 公开 setEnabled 我该如何解决这个问题 这是我在 WLST Weblogic
  • .NET 中的 WebClient 和 HTTPWebRequest 类有什么区别?

    两者之间有什么区别WebClient和HttpWebRequest NET 中的类 他们都做非常相似的事情 事实上 为什么它们没有合并到一个类中 太多的方法 变量等可能是一个原因 但 NET 中还有其他类打破了这一规则 Thanks Web
  • 浮点数集

    所以我想创建一组浮点数 其中两个数字在给定一定容差的情况下被视为相等 例如 如果我有一组 a set 1 2 3 如果我添加元素 1 00001 且容差为 1e 4 则结果集应该是 1 2 3 and not 1 1 00001 2 3 我
  • ld:找不到架构 x86_64 的符号[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 我正在使用 C 学习原理和实践 并且在链接到正确的架构
  • 记录 setLevel 被忽略

    下面的代码是从文档中复制的 我应该能够看到所有信息日志 但我不这么认为 即使我已将 setLevel 设置为 INFO 我也只能看到警告及以上内容 为什么会发生这种情况 foo py import logging logger loggin
  • 不应用Boost Spirit解析规则

    我在这里看不到我的错误 这个规则可以解析一些东西 但最后两个样本不能 有人可以给我一个提示吗 目标是一个能够识别成员属性访问和成员函数调用的解析器 也以某种方式被束缚 a a para x a x a para x a para g par