我正在关注以下示例:https://github.com/boostorg/spirit/blob/develop/example/x3/calc/calc9/expression_def.hpp
我要完成的工作是编写一个规则,该规则可以像min {x} {y}那样进行解析和生成。通常,代码使用x + y之类的表达式语法,但是现在我想将两个操作数放置并解析为运算符的rhs。
我在expression_def.hpp文件中添加了以下代码:
...
x3::symbols<ast::optoken> additive_op;
x3::symbols<ast::optoken> multiplicative_op;
x3::symbols<ast::optoken> binarypost_op;
x3::symbols<ast::optoken> unary_op;
x3::symbols<> keywords;
...
binarypost_op.add
("min", ast::op_divide) // Dummy operation usage for now
;
...
struct binarypost_expr_class;
struct unary_expr_class;
...
typedef x3::rule<binarypost_expr_class, ast::expression>
binarypost_expr_type;
...
binarypost_expr_type const binarypost_expr = "binarypost_expr";
...
auto const multiplicative_expr_def =
binarypost_expr
>> *(multiplicative_op > binarypost_expr)
;
auto const binarypost_expr_def = // See the chaining operation
('{' > unary_expr > '}')
>> *(binarypost_op > ('{' > unary_expr > '}'))
;
auto const unary_expr_def =
primary_expr
| (unary_op > primary_expr)
;
这很好。但是它只能解析类似{x} min {y}的内容。我希望能够解析最小{x} {y}。我尝试了许多组合,例如:
binarypost_op >>('{'> unary_expr>'}')>('{'> unary_expr>'}')等。但是我似乎无法弄清楚写这个的正确方法是什么?有什么建议/意见吗?
最佳答案
好的,这是更改。困难的部分实际上是代码生成内置函数。
解析中
步骤1:扩展AST
始终从AST开始。我们想要可以是函数调用的操作数:
在ast.hpp中:
struct function_call; // ADDED LINE
// ...
struct operand :
x3::variant<
nil
, unsigned int
, variable
, x3::forward_ast<unary>
, x3::forward_ast<expression>
, x3::forward_ast<function_call> // ADDED LINE
>
{
using base_type::base_type;
using base_type::operator=;
};
// ...
enum funtoken
{
fun_min,
fun_max,
};
// ...
struct function_call : x3::position_tagged
{
funtoken fun;
std::list<operand> args;
};
在ast_adapted.hpp中:
BOOST_FUSION_ADAPT_STRUCT(client::ast::function_call,
fun, args
)
步骤2:扩展语法
(全部在
expression_def.hpp
中)让我们通用一些,因此使用符号表来解析函数名称标记:
x3::symbols<ast::funtoken> functions;
我们必须在
add_keywords
中初始化它:functions.add
("min", ast::fun_min)
("max", ast::fun_max)
;
现在声明一个函数调用规则:
struct function_call_class;
typedef x3::rule<function_call_class, ast::function_call> function_call_type;
function_call_type const function_call = "function_call";
都是红色的磁带。 “有趣的事情”是规则定义:
auto const function_call_def =
functions
>> '(' >> expression % ',' >> ')'
;
好。真是令人难以置信。让我们整合到我们的主要表达规则中:
auto const primary_expr_def =
uint_
| bool_
| function_call
| (!keywords >> identifier)
| ('(' > expression > ')')
;
另外,让AST注释对我们的节点起作用:
struct function_call_class : x3::annotate_on_success {};
代码生成
很容易找到在何处添加对新AST节点的支持:
在compile.hpp中:
bool operator()(ast::function_call const& x) const;
现在是困难的部分。
在compile.cpp中:
bool compiler::operator()(ast::function_call const& x) const
{
auto choice = [&](int opcode) {
BOOST_ASSERT(x.args.size() == 2); // TODO FIXME hardcoded binary builtin
auto it = x.args.begin();
auto& a = *it++;
if (!boost::apply_visitor(*this, a))
return false;
auto& b = *it++;
if (!boost::apply_visitor(*this, b))
return false;
program.op(opcode); // the binary fold operation
program.op(op_jump_if, 0);
size_t const branch = program.size()-1;
if (!boost::apply_visitor(*this, a))
return false;
program.op(op_jump, 0);
std::size_t continue_ = program.size()-1;
program[branch] = int(program.size()-branch);
if (!boost::apply_visitor(*this, b))
return false;
program[continue_] = int(program.size()-continue_);
return true;
};
switch (x.fun) {
case ast::fun_min: return choice(op_lt);
case ast::fun_max: return choice(op_gt);
default: BOOST_ASSERT(0); return false;
}
return true;
}
我刚刚从周围的代码中获得了有关如何生成跳转标签的灵感。
尝试一下
var x = min(1,3);
Assembler----------------
local x, @0
start:
op_stk_adj 1
op_int 1
op_int 3
op_lt
op_jump_if 13
op_int 1
op_jump 15
13:
op_int 3
15:
op_store x
end:
-------------------------
Results------------------
x: 1
-------------------------
./test <<< "var a=$(($RANDOM % 100)); var
b = $((($ RANDOM%100)); var contrived = min(max(27,2 * a),100 + b);“
打印例如:
Assembler----------------
local a, @0
local b, @1
local contrived, @2
start:
op_stk_adj 3
op_int 31
op_store a
op_int 71
op_store b
op_int 27
op_int 2
op_load a
op_mul
op_gt
op_jump_if 24
op_int 27
op_jump 29
24:
op_int 2
op_load a
op_mul
29:
op_int 100
op_load b
op_add
op_lt
op_jump_if 58
op_int 27
op_int 2
op_load a
op_mul
op_gt
op_jump_if 51
op_int 27
op_jump 56
51:
op_int 2
op_load a
op_mul
56:
op_jump 63
58:
op_int 100
op_load b
op_add
63:
op_store contrived
end:
-------------------------
Results------------------
a: 31
b: 71
contrived: 62
-------------------------
关于c++ - 如何使用Boost Spirit x3用两个后操作数语法编写二进制运算符?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44194944/