C++ 将用户定义的脚本系统解析为结构数据类型
C++ Parse user-defined script system into structure data types
我开发了一个简单的脚本系统来完成我正在制作的游戏的简单任务。
基本上一个简单的脚本如下所示:
QuestValue(100)>=10 -> ShowText("You pass the test!")
我正在使用分词器来读取这样的脚本并将它们传递到结构中。
一个简单的解释是这样的:
Token token = script.tokenNext();
if (token == IDENT)
{
string ident = script.readIdent();
if (ident == "questvalue")
{
script.readSymbol("(");
Condition* condition = new Condition();
condition->type = CONDITION_QUESTVALUE;
condition->intParams[0] = script.readInt();
script.readSymbol(")");
condition->operatorType = script.readOperator();
condition->intParams[1] = script.readInt();
ScriptData.push_back(condition);
}
}
这可以很好地传递像 QuestValue(int)< 这样简单的东西运算符>int
我可以很好地使用它并根据需要评估条件,但我开始意识到我可能需要进一步的复杂性,例如:
QuestValue(100)>=QuestValue(101) -> ShowText("You pass the test!")
我该如何解释这样的事情?
为了简单起见,我为第一个例子做了:
enum ConditionType
{
CONDITION_QUESTVALUE;
};
struct Condition
{
public:
OperatorType operatorType;
int intParams[5];
ConditionType type;
};
它只是允许我支持整数作为参数,但是如果值参数不是 int,而是来自不同任务值的第二个值的另一个条件怎么办? QuestValue(<int>)<Operator>QuestValue (<int>)
老实说,我的大脑似乎无法找到解决这个问题的方法。
有什么建议、想法吗?
我误读了这个问题,因为我认为那是一个 dsl(它几乎是有效的 C++ 代码,除非对运算符优先级进行轻微更改),但也许您仍然可以使用类似这样的东西
#include <iostream>
/*
grammer:
statement -> boolean_expr "->" action
boolean_expr -> primitve binary_op primitve
binary_op -> ">="
primiate -> QuestValue(integer) | integer
action -> ShowText(string)
*/
struct visitor{
virtual ~visitor(){} // not neccasary
virtual void binary_op(const std::string& s)const=0;
virtual void int_(int value)const=0;
virtual void quest_value(int value)const=0;
virtual void show_text(const std::string& text)const=0;
};
struct quest_value_prim{
explicit quest_value_prim(int value):value_(value){}
void visit(visitor& v)const{
v.quest_value(value_);
}
private:
int value_;
};
struct int_prim{
explicit int_prim(int value):value_(value){}
void visit(visitor& v)const{
v.int_(value_);
}
private:
int value_;
};
struct show_text_action{
explicit show_text_action(const std::string& text):text_(text){}
void visit(visitor* v)const{
v->show_text(text_);
}
private:
std::string text_;
};
#include <type_traits>
#define MAKE_TRAIT(NAME) \
template<typename T> \
struct NAME{ \
static const bool value = false; \
};
#define TRAIT_ADD(TRAIT, NAME) \
template<> \
struct TRAIT<NAME>{ \
static const bool value = true; \
};
#define TRAIT_ADD_UNARY( TRAIT, NAME ) \
template<typename T> \
struct TRAIT<NAME<T> >{ \
static const bool value = true; \
};
#define TRAIT_ADD_BINARY( TRAIT, NAME ) \
template<typename LP, typename RP> \
struct TRAIT<NAME<LP,RP> >{ \
static const bool value = true; \
};
MAKE_TRAIT(is_primitive)
MAKE_TRAIT(is_boolean_expr)
MAKE_TRAIT(is_action)
TRAIT_ADD(is_primitive,quest_value_prim)
TRAIT_ADD(is_primitive,int_prim)
TRAIT_ADD(is_primitive,int)
TRAIT_ADD(is_action,show_text_action)
template<typename T>
struct make_primitive_type{
typedef T type;
};
template<>
struct make_primitive_type<int>{
typedef int_prim type;
};
#include <algorithm>
#include <functional>
#include <memory>
#include <list>
#include <string>
template<typename L_Param, typename R_Param>
struct binary_op_ge{
binary_op_ge(const L_Param& l_param, const R_Param& r_param):
l_param_(l_param),
r_param_(r_param),
p_(this)
{}
template<typename Visitor>
void visit(Visitor& v)const{
// v expects 2 more calls (prefix notation)
v.binary_op(">=");
l_param_.visit(v);
r_param_.visit(v);
using std::placeholders::_1;
std::for_each(actions_.begin(),actions_.end(),
std::bind(std::mem_fn(&show_text_action::visit),_1,&v));
}
struct proxy{
explicit proxy(binary_op_ge* parent):parent_(parent){}
binary_op_ge& ShowText(const std::string& s){
parent_->actions_.emplace_back(s);
return *parent_;
}
private:
binary_op_ge* parent_;
};
proxy* operator->(){
return &p_;
}
private:
L_Param l_param_;
R_Param r_param_;
proxy p_;
std::list<show_text_action> actions_;
};
TRAIT_ADD_BINARY(is_boolean_expr,binary_op_ge)
template<typename L_Param, typename R_Param>
typename std::enable_if<
is_primitive<L_Param>::value && is_primitive<R_Param>::value,
binary_op_ge<
typename make_primitive_type<L_Param>::type,
typename make_primitive_type<R_Param>::type >
>::type
operator >= (const L_Param& l_param, const R_Param& r_param){
typedef typename make_primitive_type<L_Param>::type lp_type;
typedef typename make_primitive_type<R_Param>::type rp_type;
return binary_op_ge<lp_type,rp_type>(
lp_type(l_param),
rp_type(r_param));
}
static_assert( is_primitive<quest_value_prim>::value, "");
static_assert( is_primitive<int>::value, "");
static_assert( is_boolean_expr<binary_op_ge<void,void> >::value,"");
int main(){
typedef quest_value_prim QuestValue;
struct cout_visitor : public visitor{
void binary_op(const std::string& s)const{
std::cout << "binary_op(" << s << ")\n";
}
void quest_value(int value)const{
std::cout << "quest_value(" << value << ")\n";
}
void int_(int value)const{
std::cout << "int_(" << value << ")\n";
}
void show_text(const std::string& text)const{
std::cout << "show_text(" << text << ")\n";
}
};
cout_visitor v;
( ( QuestValue(100)>=10 )-> ShowText("You pass the test!") ).visit(v);
( ( QuestValue(100)>=QuestValue(101) )-> ShowText("You pass the test!") ).visit(v);
}
输出
binary_op(>=)
quest_value(100)
int_(10)
show_text(You pass the test!)
binary_op(>=)
quest_value(100)
quest_value(101)
show_text(You pass the test!)
我开发了一个简单的脚本系统来完成我正在制作的游戏的简单任务。 基本上一个简单的脚本如下所示:
QuestValue(100)>=10 -> ShowText("You pass the test!")
我正在使用分词器来读取这样的脚本并将它们传递到结构中。
一个简单的解释是这样的:
Token token = script.tokenNext();
if (token == IDENT)
{
string ident = script.readIdent();
if (ident == "questvalue")
{
script.readSymbol("(");
Condition* condition = new Condition();
condition->type = CONDITION_QUESTVALUE;
condition->intParams[0] = script.readInt();
script.readSymbol(")");
condition->operatorType = script.readOperator();
condition->intParams[1] = script.readInt();
ScriptData.push_back(condition);
}
}
这可以很好地传递像 QuestValue(int)< 这样简单的东西运算符>int
我可以很好地使用它并根据需要评估条件,但我开始意识到我可能需要进一步的复杂性,例如:
QuestValue(100)>=QuestValue(101) -> ShowText("You pass the test!")
我该如何解释这样的事情? 为了简单起见,我为第一个例子做了:
enum ConditionType
{
CONDITION_QUESTVALUE;
};
struct Condition
{
public:
OperatorType operatorType;
int intParams[5];
ConditionType type;
};
它只是允许我支持整数作为参数,但是如果值参数不是 int,而是来自不同任务值的第二个值的另一个条件怎么办? QuestValue(<int>)<Operator>QuestValue (<int>)
老实说,我的大脑似乎无法找到解决这个问题的方法。 有什么建议、想法吗?
我误读了这个问题,因为我认为那是一个 dsl(它几乎是有效的 C++ 代码,除非对运算符优先级进行轻微更改),但也许您仍然可以使用类似这样的东西
#include <iostream>
/*
grammer:
statement -> boolean_expr "->" action
boolean_expr -> primitve binary_op primitve
binary_op -> ">="
primiate -> QuestValue(integer) | integer
action -> ShowText(string)
*/
struct visitor{
virtual ~visitor(){} // not neccasary
virtual void binary_op(const std::string& s)const=0;
virtual void int_(int value)const=0;
virtual void quest_value(int value)const=0;
virtual void show_text(const std::string& text)const=0;
};
struct quest_value_prim{
explicit quest_value_prim(int value):value_(value){}
void visit(visitor& v)const{
v.quest_value(value_);
}
private:
int value_;
};
struct int_prim{
explicit int_prim(int value):value_(value){}
void visit(visitor& v)const{
v.int_(value_);
}
private:
int value_;
};
struct show_text_action{
explicit show_text_action(const std::string& text):text_(text){}
void visit(visitor* v)const{
v->show_text(text_);
}
private:
std::string text_;
};
#include <type_traits>
#define MAKE_TRAIT(NAME) \
template<typename T> \
struct NAME{ \
static const bool value = false; \
};
#define TRAIT_ADD(TRAIT, NAME) \
template<> \
struct TRAIT<NAME>{ \
static const bool value = true; \
};
#define TRAIT_ADD_UNARY( TRAIT, NAME ) \
template<typename T> \
struct TRAIT<NAME<T> >{ \
static const bool value = true; \
};
#define TRAIT_ADD_BINARY( TRAIT, NAME ) \
template<typename LP, typename RP> \
struct TRAIT<NAME<LP,RP> >{ \
static const bool value = true; \
};
MAKE_TRAIT(is_primitive)
MAKE_TRAIT(is_boolean_expr)
MAKE_TRAIT(is_action)
TRAIT_ADD(is_primitive,quest_value_prim)
TRAIT_ADD(is_primitive,int_prim)
TRAIT_ADD(is_primitive,int)
TRAIT_ADD(is_action,show_text_action)
template<typename T>
struct make_primitive_type{
typedef T type;
};
template<>
struct make_primitive_type<int>{
typedef int_prim type;
};
#include <algorithm>
#include <functional>
#include <memory>
#include <list>
#include <string>
template<typename L_Param, typename R_Param>
struct binary_op_ge{
binary_op_ge(const L_Param& l_param, const R_Param& r_param):
l_param_(l_param),
r_param_(r_param),
p_(this)
{}
template<typename Visitor>
void visit(Visitor& v)const{
// v expects 2 more calls (prefix notation)
v.binary_op(">=");
l_param_.visit(v);
r_param_.visit(v);
using std::placeholders::_1;
std::for_each(actions_.begin(),actions_.end(),
std::bind(std::mem_fn(&show_text_action::visit),_1,&v));
}
struct proxy{
explicit proxy(binary_op_ge* parent):parent_(parent){}
binary_op_ge& ShowText(const std::string& s){
parent_->actions_.emplace_back(s);
return *parent_;
}
private:
binary_op_ge* parent_;
};
proxy* operator->(){
return &p_;
}
private:
L_Param l_param_;
R_Param r_param_;
proxy p_;
std::list<show_text_action> actions_;
};
TRAIT_ADD_BINARY(is_boolean_expr,binary_op_ge)
template<typename L_Param, typename R_Param>
typename std::enable_if<
is_primitive<L_Param>::value && is_primitive<R_Param>::value,
binary_op_ge<
typename make_primitive_type<L_Param>::type,
typename make_primitive_type<R_Param>::type >
>::type
operator >= (const L_Param& l_param, const R_Param& r_param){
typedef typename make_primitive_type<L_Param>::type lp_type;
typedef typename make_primitive_type<R_Param>::type rp_type;
return binary_op_ge<lp_type,rp_type>(
lp_type(l_param),
rp_type(r_param));
}
static_assert( is_primitive<quest_value_prim>::value, "");
static_assert( is_primitive<int>::value, "");
static_assert( is_boolean_expr<binary_op_ge<void,void> >::value,"");
int main(){
typedef quest_value_prim QuestValue;
struct cout_visitor : public visitor{
void binary_op(const std::string& s)const{
std::cout << "binary_op(" << s << ")\n";
}
void quest_value(int value)const{
std::cout << "quest_value(" << value << ")\n";
}
void int_(int value)const{
std::cout << "int_(" << value << ")\n";
}
void show_text(const std::string& text)const{
std::cout << "show_text(" << text << ")\n";
}
};
cout_visitor v;
( ( QuestValue(100)>=10 )-> ShowText("You pass the test!") ).visit(v);
( ( QuestValue(100)>=QuestValue(101) )-> ShowText("You pass the test!") ).visit(v);
}
输出
binary_op(>=)
quest_value(100)
int_(10)
show_text(You pass the test!)
binary_op(>=)
quest_value(100)
quest_value(101)
show_text(You pass the test!)