- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有类似 json 形式的输入
{
type: "name",
value: <json_value>,
}
类型名称可以是“int1”、“int2a”、“int2b”中的一个(当然,我简化了实际情况以提供最少的相关代码)。
该值始终符合 JSON 语法,但也取决于类型名称。可能的情况是:
type: int1 => expected value: <number>
type: int2a => expected value: [ <number>, <number> ]
type: int2b => expected value: [ <number>, <number> ]
我需要将输入解析为以下数据类型:
struct int_1 { int i1; };
struct int_2a { int i1, i2; };
struct int_2b { int i1, i2; };
using any_value = boost::variant<int_1, int_2a, int_2b>;
struct data { std::string type; any_value value; };
我将关键字解析器与 Nabialek 技巧相结合。我创建符号表并将指向 int_1、int_2a 和 int_2b 解析器的指针存储到其中:
using value_rule_type = qi::rule<It, any_value (), Skipper>;
qi::symbols<char, value_rule_type *> value_selector;
qi::rule<It, int_1 (), Skipper> int1_parser;
qi::rule<It, int_2a (), Skipper> int2a_parser;
qi::rule<It, int_2b (), Skipper> int2b_parser;
value_rule_type int1_rule, int2a_rule, int2b_rule;
int1_parser = int_ ;
int2a_parser = '[' >> int_ >> ',' >> int_ >> ']';
int2b_parser = '[' >> int_ >> ',' >> int_ >> ']';
int1_rule = int1_parser;
int2a_rule = int2a_parser;
int2b_rule = int2b_parser;
value_selector.add
( "\"int1\"", &int1_rule )
("\"int2a\"", &int2a_rule )
("\"int2b\"", &int2b_rule )
;
我使用关键字解析器来解析外部数据结构:
data_
%= eps [ _a = px::val (nullptr) ]
> '{' > (
kwd ( lit ("\"type\"" ) ) [ ':' >> parsed_type_ (_a) >> ',' ]
/ kwd ( lit ("\"value\"") ) [ ':' >> value_ (_a) >> ',' ]
) > '}'
;
此处的 parsed_type_ 规则在符号表中查找类型名称,并将 data 规则的局部变量设置为找到的规则指针。
parsed_type_ %= raw[value_selector [ _r1 = _1 ]];
并且 value_ 规则具有 Nabialek 技巧的常用形式:
value_ = lazy (*_r1);
这个解析器工作得很好(live demo)...除了在类型名称之前传递值的情况:
{
value: <json_value>,
type: "name",
}
由于我们在存储的指向规则的指针中有 NULL,并且“类型”字段的解析器尚未运行,程序在解析“值”字段期间崩溃。
我想解决这个问题。如果值字段跟在类型名称字段之后,我希望应用当前的解析器逻辑(如现场演示)。
如果值在类型键之前,我想用 json 解析器预先解析值字段(只是为了找到该字段的结束边界)并以迭代器范围的形式将该字段存储到某个本地多变的。在解析器获得“类型”字段后,我想在存储范围内启动特定的解析器 - int1_rule、int2a_rule 或 int2b_rule。
所以,表达式
value_ = lazy (*_r1);
可能应该更改为:
if (_r1 == NULL) {
<parse json value>
<store raw range into local variable>
} else {
lazy (*_r1);
}
和表达
parsed_type_ %= raw[value_selector [ _r1 = _1 ]];
应该扩展为:
if (has stored range) parse it with lazy (*_r1);
不幸的是,我不知道如何实现它,或者根本不可能。
为了方便起见,我将在 stackoverflow 上找到的简化 JSON 解析器包含到我的现场演示中。
问题: spirit 是否可能?如果是,怎么办?
附言。完整的演示源:
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/repository/include/qi_kwd.hpp>
#include <boost/spirit/repository/include/qi_keywords.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
namespace spirit = ::boost::spirit;
namespace qi = spirit::qi;
namespace px = ::boost::phoenix;
namespace json {
struct null {
constexpr bool operator== (null) const { return true; }
};
template<typename Ch, typename Tr>
std::basic_ostream<Ch, Tr>&
operator<< (std::basic_ostream<Ch, Tr>& os, null) { return os << "null"; }
using text = std::string;
using value = boost::make_recursive_variant<
null
, text // string
, double // number
, std::map<text, boost::recursive_variant_> // object
, std::vector<boost::recursive_variant_> // array
, bool
>::type;
using member = std::pair<text, value>;
using object = std::map<text, value>;
using array = std::vector<value>;
static auto const null_ = "null" >> qi::attr (null {});
static auto const bool_ =
"true" >> qi::attr (true) | "false" >> qi::attr (false);
#if 0
static auto const text_ =
'"' >> qi::raw [*('\\' >> qi::char_ | ~qi::char_('"'))] >> '"';
#endif
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, value (), Skipper>
{
grammar () : grammar::base_type (value_)
{
using namespace qi;
text_ = '"' >> qi::raw [*('\\' >> qi::char_ | ~qi::char_('"'))] >> '"';
value_ = null_ | bool_ | text_ | double_ | object_ | array_;
member_ = text_ >> ':' >> value_;
object_ = '{' >> -(member_ % ',') >> '}';
array_ = '[' >> -(value_ % ',') >> ']';
BOOST_SPIRIT_DEBUG_NODES((value_)(member_)(object_)(array_))
}
private:
qi::rule<It, std::string ()> text_;
qi::rule<It, json:: value (), Skipper> value_;
qi::rule<It, json::member (), Skipper> member_;
qi::rule<It, json::object (), Skipper> object_;
qi::rule<It, json:: array (), Skipper> array_;
};
template <typename Range,
typename It = typename boost::range_iterator<Range const>::type>
value parse(Range const& input)
{
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
value parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" +
std::string(first, last) + "'");
}
} // namespace json
namespace mine {
struct int_1 { int_1 (int i) : i1 (i) {} int_1 () : i1 () {} int i1; };
struct int_2a { int i1, i2; };
struct int_2b { int i1, i2; };
using any_value = boost::variant<int_1, int_2a, int_2b>;
struct data { std::string type; any_value value; };
template <class C, class T> std::basic_ostream<C,T>&
operator<< (std::basic_ostream<C,T>& os, int_1 const& i)
{
return os << "{int1:" << i.i1 << '}';
}
template <class C, class T> std::basic_ostream<C,T>&
operator<< (std::basic_ostream<C,T>& os, int_2a const& i)
{
return os << "{int2a:" << i.i1 << ',' << i.i2 << '}';
}
template <class C, class T> std::basic_ostream<C,T>&
operator<< (std::basic_ostream<C,T>& os, int_2b const& i)
{
return os << "{int2b:" << i.i1 << ',' << i.i2 << '}';
}
template <class C, class T> std::basic_ostream<C,T>&
operator<< (std::basic_ostream<C,T>& os, data const& d)
{
return os << "{type=" << d.type << ",value=" << d.value << '}';
}
}
BOOST_FUSION_ADAPT_STRUCT(mine::int_1, (int, i1) )
BOOST_FUSION_ADAPT_STRUCT(mine::int_2a, (int, i1) (int, i2) )
BOOST_FUSION_ADAPT_STRUCT(mine::int_2b, (int, i1) (int, i2) )
BOOST_FUSION_ADAPT_STRUCT(mine::data,(std::string,type)(mine::any_value,value))
namespace mine {
template <typename It, typename Skipper = qi::space_type>
struct grammar: qi::grammar<It, data (), Skipper>
{
grammar () : grammar::base_type (start)
{
using namespace qi;
using spirit::repository::qi::kwd;
int1_parser = int_ ;
int2a_parser = '[' >> int_ >> ',' >> int_ >> ']';
int2b_parser = '[' >> int_ >> ',' >> int_ >> ']';
int1_rule = int1_parser;
int2a_rule = int2a_parser;
int2b_rule = int2b_parser;
value_selector.add
( "\"int1\"", &int1_rule )
("\"int2a\"", &int2a_rule )
("\"int2b\"", &int2b_rule )
;
start = data_.alias ();
parsed_type_ %= raw[value_selector [ _r1 = _1 ]];
value_ = lazy (*_r1);
data_
%= eps [ _a = px::val (nullptr) ]
> '{' > (
kwd ( lit ("\"type\"" ) ) [ ':' >> parsed_type_ (_a) >> ',' ]
/ kwd ( lit ("\"value\"") ) [ ':' >> value_ (_a) >> ',' ]
) > '}'
;
on_error<fail>(start,
px::ref(std::cout)
<< "Error! Expecting "
<< qi::_4
<< " here: '"
<< px::construct<std::string>(qi::_3, qi::_2)
<< "'\n"
);
}
private:
using value_rule_type = qi::rule<It, any_value (), Skipper>;
qi::rule<It, data (), Skipper> start;
qi::rule<It, data (), qi::locals<value_rule_type *>, Skipper> data_;
qi::symbols<char, value_rule_type *> value_selector;
qi::rule<It, int_1 (), Skipper> int1_parser;
qi::rule<It, int_2a (), Skipper> int2a_parser;
qi::rule<It, int_2b (), Skipper> int2b_parser;
value_rule_type int1_rule, int2a_rule, int2b_rule;
qi::rule<It, std::string (value_rule_type *&) > parsed_type_;
qi::rule<It, any_value (value_rule_type *&), Skipper> value_;
};
template <typename Range,
typename It = typename boost::range_iterator<Range const>::type>
data parse(Range const& input)
{
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
data parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" +
std::string(first, last) + "'");
}
}
static std::string const sample1 = R"(
{
"type": "int1",
"value": 111,
})";
static std::string const sample2 = R"(
{
"type": "int2a",
"value": [ 111, 222 ],
})";
static std::string const sample3 = R"(
{
"type": "int2b",
"value": [ 111, 333 ],
})";
static std::string const sample4 = R"(
{
"value": 111,
"type": "int1",
})";
int main()
{
auto mine = mine::parse(sample1); std::cout << mine << '\n';
mine = mine::parse(sample2); std::cout << mine << '\n';
mine = mine::parse(sample3); std::cout << mine << '\n';
// mine = mine::parse(sample4); std::cout << mine << '\n';
return 0;
}
最佳答案
spirit 无法展望 future 。
Nabialek 技巧服务于与变体替代解析器不同的目标。
因此,从概念上讲,您所描述的是无法完成的:您不能根据 future 的类型切换解析器。
Nabialek 把戏根本不适合这种情况。您需要的是一个通用的 JSON 数据类型和一个后处理函数,以便在所有必要数据已知后创建实际的 AST nose。
我过去发布过完整的 JSON 语法,并在我自己的项目中亲自使用它们。
您描述的解决方法可以解释为与此方法类似,但我的印象是您正在努力坚持当前的解析器结构。我会说您需要重构解析器(概念上),只有然后您可能会发现您仍然可以重用旧解析器的一些部分。
如果您关心并且有耐心,当我靠近一台可以上网的电脑时,我可以尝试一下它。
关于c++ - boost spirit : combining preparsing with keyword parser and with Nabialek trick,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31932432/
我想使用 Haskell 的 parsec 库来实现这个语法规则: ((a | b | c)* (a | b))? 这是一个接受可选(即可能为空)字符串的解析器规则。如果它接受的字符串不为空,则可以通
Python 的 itertools.combinations() 创建的结果是数字的组合。例如: a = [7, 5, 5, 4] b = list(itertools.combinations(a
I found a good script for the permutation of lists, combining and not combining list position on
我正在使用 Beam 管道计算流式数据的电话号码频率。我使用的滑动窗口每 5 分钟重复一次,总周期为 15 分钟,因此正如预期的那样,对于某些输入,当输入落在多个窗口中时,我会得到多个输出。 计算出现
这个问题已经有答案了: Pandas Merging 101 (8 个回答) 已关闭 3 年前。 我有两个数据帧,我想对其执行外连接。两个数据框共享一个公共(public)索引名称以及多个也共享相同名
我在谷歌上搜索了很多天这个问题,但一无所获。我需要做一个 SELECT,DUPLICATE 和 DUPLICATE 和 DUPLICATE 取决于用户。之后,我需要将每个选项的值组合到我选择的一个选择
这个问题在这里已经有了答案: Java 8 Streams: multiple filters vs. complex condition (4 个答案) 关闭 4 年前。 需要过滤所有适合其领域某
运行 cv2.getRectSubPix(img, (5,5), (0,0)) 抛出错误: OpenCV Error: Unsupported format or combination of for
没有重复的组合看起来像这样,当可供选择的元素数 (n) 为 5 且选择的元素数 (r) 为 3 时: 0 1 2 0 1 3 0 1 4 0 2 3 0 2 4 0 3 4 1 2 3 1 2 4 1
我在学校的数学一直不太好,我意识到我实际上需要与 pow(base, exponent) 函数相反的函数,该函数对某个数字进行乘方运算,例如 2 ^ 4 = 16 搜索答案我发现对数 log() 应该
我确信这很简单,但我很难找到一种方法来做到这一点。基本上,如果我有一个包含 P 列和 V^P 行的数组,我如何填写所有组合,即基本上所有可能的数字以 P 数字的 V 为基数。例如,对于 P=3 和 V
我想知道一种可能的算法来计算所有可能的组合,没有重复,从 length=1 开始,直到 length=N 的 N 个元素。 例子: 元素:1、2、3。 输出: 1 2 3 12 13 23 123 最
使用三种不同颜色的颜料可以用多少种不同的方式来绘制立方体? 最佳答案 如果您以唯一可能的有趣方式解释它,那么这是一个比 3^6 更难的问题:有多少种不同的(即对称的)方法来为立方体着色。这是一篇论文:
我正在尝试解决优化问题,但首先我必须找到 n 个元素的所有可能组合的数量,但要考虑一些冲突。一个可能的例子是: 元素:{1,2,3,4}冲突:{1,2},{3,4} 术语“冲突”是指属于同一冲突集合的
Cleave 是一个非常有用的组合器,可以最大限度地减少代码重复。假设我要分类 Abundant, Perfect, Deficient numbers : USING: arrays assocs
有没有办法让 @Published 变量只在新值与旧值不同时才发布其值? 现在如果我们有 @Published var test: Bool = false 我们做到了 test = false te
有一个数组 [1, 2, ..., m] ,并且有一个整数 n . 如 m=2和 n=3 ,我想获得 [1, 1, 1] [1, 1, 2] [1, 2, 1] [1, 2, 2] [2, 1, 1]
我在我的应用程序中使用了一个用于日志记录页面的表单,并且在页脚上有一个绑定(bind)来显示任何错误,如下所示: 内容 View .Swift : Form { Section(footer: Tex
HTML first second third SCSS $statistics: ("first", "second", "third"); :root { --first: r
我有一个 HTTP 请求发布者,当返回 401 错误时,我想停止执行并显示我的登录屏幕。 这是我的代码的一部分: cancellable = fetcher.hello(helloRequest: H
我是一名优秀的程序员,十分优秀!