ZBScript C++脚本编译器(仅共享文档,勿索取源码)

ZBScript C++脚本编译器

程序版本:1

文档版本:1

日期:201085

作者:张博


 

目录

ZBScript C++脚本编译器... 1

程序版本:1. 1

文档版本:1. 1

日期:201085... 1

作者:张博... 1

部门:计费产品... 1

目录... 2

1     介绍... 4

2     功能特点... 4

2.1      简单移植... 4

2.2      标准C++语法(脚本语言)... 4

2.3      强数据类型... 4

2.4      C++运行库支持(内部函数)... 5

2.5      支持plugin(外部函数)... 5

2.6      编译执行... 5

3     脚本语法详解(脚本编写者)... 5

3.1      C++标准的支持... 5

3.2      语言扩展... 5

3.3      语法说明... 6

3.4      内部函数清单... 7

4     编程指南(C++程序员)... 8

4.1      引入包含文件... 8

4.2      使用变量Variable. 9

4.3      使用CZBScript 10

4.3.1       声明... 10

4.3.2       编译... 10

4.3.3       添加外部函数... 10

4.3.4       执行... 10

4.3.5       杂项... 11

4.4      编写plugin(外部函数)... 11

4.4.1       继承CFunction接口... 11

4.4.2       构造CFunction接口... 11

4.4.3       显示函数说明... 11

4.4.4       编译时检查... 11

4.4.5       执行函数功能... 12

5     脚本示例(脚本编写者)... 12

6     编程示例(C++程序员)... 13

6.1      简单调用... 13

6.2      编写外部函数... 14

6.3      为外部plugin实现CFunction接口... 15

6.3.1       已经存在的外部plugin接口... 15

6.3.2       扩展外部plugin接口... 15

6.3.3       注册外部函数... 17

6.3.4       编译、执行脚本... 17

 


 

1       介绍

ZBScript是一个脚本编译程序,可以将类似简单C++语言函数的脚本编译执行,可以嵌入任何C++程序。

大型软件系统中为了增加系统的可扩展性,通常都会引入脚本解释器或脚本编译器执行用户定义的脚本。大型脚本解释器如JavaScript的开源产品功能强大但过于庞大,而各种小型脚本解释器则又存在功能局限和语法差异,而且语法多数并非采用类C++语法,也少见支持plugin(外挂函数)的。

基于上述原因,本人开发了ZBScript脚本解释器,支持C++语法和plugin,在实践中证明是一个C++项目的功能扩展的较好解决方案。

2       功能特点

2.1    简单移植

提供一个C++头文件,包含入任何支持STLC++源文件即可,纯粹的标准C++代码,无需任何配置、无需任何特殊编译选项。

任何环境:UNIXWindowsLinux

任何编译器:Sun CCHP CCIBM xlCMS CPPG++

一句话引用:#include “ZBScript.h”

2.2    标准C++语法(脚本语言)

脚本语言采用标准C++语法,目前的支持度为大致相当于C语言的单个函数体。即编写一个脚本和编写一个C语言函数相似。因此对于C++程序员来说基本无需学习,这将极大地方便使用过C++的用户编写脚本。

本脚本编译器除了增加了string类型外不会对C++做其它扩展。随着功能的逐渐完善,本脚本编译器未来也许可以直接解释真正的C++源代码

2.3    强数据类型

与大多数脚本不同,本系统严格遵循C++的规范,不支持数值和字符串的自动转换。

变量也必须提前定义。

2.4    C++运行库支持(内部函数)

大多数已经实现为内部函数,包括各种字符串操作、数值转换操作。

2.5    支持plugin(外部函数)

外部功能通过支持CFunction接口接入脚本编译器。PluginC++源代码中编写的函数,而不是脚本。

通过插件脚本可以调用系统的任何功能——只要编写一个函数即可。

2.6    编译执行

由于脚本先编译后执行,因此可以预先检查脚本语法,并且可以获得比解释执行高得多的执行速度。

3       脚本语法详解(脚本编写者)

3.1    C++标准的支持

脚本语法为C++的子集,以及少量扩展。

关键字遵循C++标准,即使尚未支持也禁止用作标识符。

标识符、常量、转义字符符合C++标准

运算符、优先级、结合性符合C++标准

数据类型的自动转换符合C++标准

Fordowhileifelsebreakcontinuereturn符合C++标准

3.2    语言扩展

增加string,类似STL::string,代替char[]

运算符“+”可用于连接字符串

取消字符类型,字符串常量可以用双引号也可以用单引号

允许不明确使用return,默认返回最后一条语句的值

3.3    语法说明

                            "未支持的关键字、运算符全部保留,不可用做标识符/r/n"

                            "/r/n"

                            "已经支持的语法:/r/n"

                            "语句:if else return {} for do while break continue;/r/n"

                            "转义字符:C++标准/r/n"

                            "注释://到行尾/r/n"

                            "/r/n"

                            "增加的关键字:string/r/n"

                            "/r/n"

                            "强数据类型:/r/n"

                            "整数:int long/r/n"

                            "浮点数:float double/r/n"

                            "字符串:string/r/n"

                            "数值常量:C++标准/r/n"

                            "字符串常量:双引号单引号均可/r/n"

                            "不允许数值和字符串的自动化转换/r/n"

                            "算术运算+ - * / % ++ --/r/n"

                            "逻辑运算! > < >= <= == !=/r/n"

                            "关系运算&& ||/r/n"

                            "赋值运算= += -= *= /= %=/r/n"

                            "逗号运算,/r/n"

                            "+可用作字符串连接/r/n"

                            "++ --前缀每个变量只能用一次,后缀只能独立使用/r/n"

                            "/r/n"

                            "每个声明语句只能声明一个变量,可初始化/r/n"

                            "for语句的初始化子句不能声明变量/r/n"

                            "/r/n"

                            "最后执行的表达式结果作为返回值,包括循环的判断语句/r/n"

                            "空语句返回空/r/n"

                            "if语句若无匹配语句返回空/r/n"

                            "break continue不影响返回值/r/n"

                            "声明语句返回变量值/r/n"

                            "复杂逻辑下应使用return语句/r/n"

3.4    内部函数清单

abs 返回值 DOUBLE

绝对值,1个参数,参数必须是数值

 

ceil 返回值 DOUBLE

向上取整,1个参数,参数必须是数值

 

floor 返回值 DOUBLE

向下取整,1个参数,参数必须是数值

 

max 返回值 DOUBLE

取最大值,1-N个参数,参数必须是数值

 

min 返回值 DOUBLE

取最小值,1-N个参数,参数必须是数值

 

nullfun 返回值 NULLVARIABLE

测试用函数,用户不可调用

 

round 返回值 LONG

四舍五入,1个参数,参数必须是数值,负数向0舍入,-1.2返回-1-1.8返回-2

 

strcat 返回值 STRING

字符串连接,2个参数,参数必须是字符串

 

strcmp 返回值 LONG

字符串比较,2个参数,参数必须是字符串

 

stricmp 返回值 LONG

字符串忽略大小写比较,2个参数,参数必须是字符串

 

strlen 返回值 LONG

字符串长度,1个参数,参数必须是字符串

 

strncmp 返回值 LONG

字符串部分比较,3个参数

参数1 字符串

参数2 字符串

参数3 整数

 

strnicmp 返回值 LONG

字符串忽略大小写部分比较,3个参数

参数1 字符串

参数2 字符串

参数3 整数

 

strstr 返回值 LONG

查找子串,2个参数,参数必须是字符串,返回参数2在参数1中出现的位置,若没找到则返回-1

 

substr 返回值 STRING

获取子串,2-3个参数,参数必须是数值,参数2为开始位置,参数3为字符数,省略参数3则一直取到字符串结束

 

to_double 返回值 DOUBLE

转换为浮点数,一个参数,任意类型

 

to_long 返回值 LONG

转换为整数,一个参数,任意类型

 

to_string 返回值 STRING

转换为字符串,一个参数,任意类型

4       编程指南(C++程序员)

4.1    引入包含文件

在需要使用脚本功能的源代码中加入:

#include “ZBSCript.h”

4.2    使用变量Variable

Variable是内部使用的数据类型,支持所有的基本数据类型的赋值和比较

包含4种类型:空,整数,浮点数,字符串

任何时候只有一种类型,根据type的不同,数据存储在lValuedValuestrValue之中

不应该直接使用lValuedValuestrValue

应该使用赋值来设置变量的值、使用isXXXX函数来确认数据类型、使用GetXXXX函数来获得指定类型的值(不会改变变量的类型)

Clear将变量清为空

Initvalue将变量的三个值清空,但不改变类型

4.3    使用CZBScript

4.3.1   声明

zbstd_script::CZBScript zbs;

4.3.2   编译

bool Compile(char const * _source,CZBVector<pair<string,Variable > > * pVars=NULL)

 

_source为脚本源代码

pVars指向外部变量集合,为“变量名”-“变量值”对的形式,变量名必需符合C++规范,变量值必需提供类型,建议同时提供初值。

外部变量视同脚本语句定义的变量,编译时不需要提供有意义的变量值,仅需要变量名来编译脚本。

如果存在语法错误返回false,可以用GetMessage获得错误信息。

4.3.3   添加外部函数

CFunctionMap实现了全局的外部函数表,使用此对象的全局方法AddFunction即可添加外部函数到全局外部函数表:

static bool AddFunction(char const * name,Variable::types type,CFunction*p);

name为函数名,必须符合C++函数名规范

type为返回值类型

p指向函数对象,外部函数如何编写见后续章节

在编译脚本前必须完成添加所有必须的外部函数,否则编译会失败

4.3.4   执行

bool Execute(Variable & ret,CZBVector<pair<string,Variable > >  * pVars=NULL,void * pe=NULL)

 

ret返回脚本执行的结果。

pVars指向外部变量,变量类型已经在编译时确定,执行时仅仅赋值。

pe参数直接传递给外部函数,一般可以传递一个指向外部特定对象的指针以供外部函数处理

如果存在执行错误返回false,可以用GetMessage获得错误信息。

注意区分脚本返回值和本函数的返回值。本函数返回true的情况下脚本返回值ret才有意义。

4.3.5   杂项

string GetMessage()const

获得出错信息

 

string const & GetSource()const

获得当前脚本

 

bool IsCompiled()const

判断是否成功编译,取决于之前的Compile是否成功

 

string & Report(string & ret)

输出脚本编译后结构,可用于检查脚本的逻辑错误

4.4    编写plugin(外部函数)

4.4.1   继承CFunction接口

Plugin必需实现CFunction接口,该接口实现了插件的统一管理功能。

4.4.2   构造CFunction接口

CFunction(char const * _name,Variable::types _type)

参数为函数名和返回值类型

4.4.3   显示函数说明

virtual string & help(string & ret)

plugin必须实现此接口来提供函数说明,至少包括函数的参数、返回值等信息。默认的实现仅提供了返回值说明

此函数对于外部管理程序在运行时提供交互式的帮助是很有意义的。脚本解释器并不调用此函数。

4.4.4   编译时检查

virtual bool Check(CZBVector<Variable > & params,void * & pc,string & msg)

此函数在脚本编译时调用。

Params由编译函数Compile传入,包含函数的每个参数

pc引用与外部函数在脚本里的一次使用相关的指针,该指针也会作为函数执行的参数。该指针在编译时由编译器创建,与外部函数的一次使用相关联。该参数指向的数据可以由Check函数建立,根据特定的函数参数情况建立信息,然后在函数执行时根据这些信息作处理。一个典型的用法是外部插件已经使用了独立的参数格式,使用pc构造一个符合外部插件需要的参数对象。

如果检查不通过,msg返回错误信息。

4.4.5   执行函数功能

virtual bool Exec(CZBVector<Variable > & params,void * & pc,Variable & ret,string & msg,void * pe)

params由脚本执行程序传入,为函数本次执行的参数。这些参数可能是变量,每次执行都不同。

PcCheck函数的pc参数相同,引用同一个与函数的一次引用相关的指针。如果Check函数将这个指针指向了一个有效数据结构,该结构此时传递给函数使用。

Ret返回函数执行结果。

Pe由脚本执行程序的参数给出,直接传递给函数。通常为函数执行所需的外部数据。

如果执行失败,msg返回出错信息。

注意区分函数返回值和本调用的返回值,仅当本调用返回true的时候ret才有意义。任何一个函数调用失败(本调用返回false)都将导致整个脚本执行失败(得不到脚本返回值)。

5       脚本示例(脚本编写者)

示例1 简单表达式:

    123+4.5*3-max(1,2,3,0)

 

示例2 简单逻辑:

    if(12>5)12+5;else 12*5

 

示例3 复杂逻辑,注意确保每个执行路径都返回有意义的结果:

    int i;//intlong同义

    double d=1.5+max(1,2,3);//floatdouble同义

    string s=to_string(d);

   

    do       //do循环

    {

        ++i;

    }while(i<10);

    while(i>0)      //while循环

    {

        i-=d;

    }

    for(i=0;i<5;++i)   //for循环

    {

        if(strlen(s+'abc')>=3)  //if语句 if()...if()...else...if()...else if()...else...可以有多个else if

        {

            continue;

        }

        else

        {

            break;

        }

    }

return i+d;

6       编程示例(C++程序员)

6.1    简单调用

如果不使用环境变量,仅需要下面代码中红色字体的三行

 

//声明脚本对象和环境变量组,环境变量组不是必需的

zbstd_script::CZBScript * zbscript=new zbstd_script::CZBScript;

vector<pair<string,zbstd_script::Variable > > envs;

 

//添加了一个环境变量dis_total,类型为整数

     pair<string,zbstd_script::Variable > tmppair;

     zbstd_script::Variable tmpvar;

     tmpvar.type=zbstd_script::Variable::LONG;

     tmppair.first="dis_total";

     tmppair.second=tmpvar;

     envs.push_back(tmppair);

 

//执行编译,如果不需要环境变量可省去第二个参数

         if(!zbscript->Compile(str,&envs))

         {

              MessageBox(zbscript->GetMessage().c_str(),"出错",MB_OK|MB_ICONSTOP);

         }

 

//设置环境变量的值

         envs[0].second=10L;

        

//执行脚本并显示结果

         zbstd_script::Variable var;

if(!zbscript->Execute(var,&envs))

         {

              MessageBox(zbscript->GetMessage().c_str(),"出错",MB_OK|MB_ICONSTOP);

         }

         else

         {

              MessageBox(var.GetString().c_str(),"运行结果");

         }

 

6.2    编写外部函数

可参考内部函数,下面是一个内部函数max的代码,这个函数可以这样使用

max(1,3,-1),结果为3,因为3是三个参数中最大的

该函数实现为CMax

Check函数检查是否至少有一个参数并且所有参数都是数值类型

Exec函数把所有参数转换为double类型比较大小,返回最大的

pcpe参数没有使用

     struct CMax : public CFunction

     {

         CMax():CFunction("max",Variable::DOUBLE){}

         virtual string & help(string & ret)

         {

              ret=CFunction::help(ret);

              ret+="取最大值,-N个参数,参数必须是数值/r/n";

              return ret;

         }

         virtual bool Check(CZBVector<Variable > & params,void * & pc,string & msg)

         {

              msg="";

              if(params.size()<1)msg+="参数不足/r/n";

              for(size_t i=0;i<params.size();++i)

              {

                   if(!params[i].isNumber())msg+="参数必须是数值/r/n";

              }

              return 0==msg.size();

         }

         virtual bool Exec(CZBVector<Variable > & params,void * & pc,Variable & ret,string & msg,void * pe)

         {

              size_t _max=0;

              for(size_t i=1;i<params.size();++i)

              {

                   if(params[i].GetDouble()>params[_max].GetDouble())_max=i;

              }

              ret=params[_max];

              return true;

         }

     };

 

6.3    为外部plugin实现CFunction接口

6.3.1   已经存在的外部plugin接口

class BasePlugin

{

public:

 

     virtual STATE init(){return 0;}

 

     virtual STATE execute(const VParam& params,CEventPacket& inputPacket,long& retValue) = 0;

 

     virtual STATE post(){return 0;}

}

这是一个常见的plugin接口,init对插件初始化,execute执行插件,post做插件清理。

Initpost通常由外部系统调用,需要用脚本解释器调用的是execute函数。

 

Execute函数的参数params是一个参数列表,大致相当于vector<string>inputPacket是函数要处理的对象,retValue获得返回值。从这个函数的参数可以看出脚本解释器的pcpe参数的必要性,pc可以实现外部plugin接口的paramspe则用来传递inputPacket

6.3.2   扩展外部plugin接口

将外部插件基类增加一个父类CFounction,然后实现CFunction

class BasePlugin : public zbstd_script::CFunction

{

public:

     virtual STATE init(){return 0;}

     virtual STATE execute(const VParam& params,CEventPacket& inputPacket,long& retValue) = 0;

     virtual STATE post(){return 0;}

public://下面是zbstd_script::CZBScript::CFunction接口插件不要再重载

     //由于外部插件接口不包含自说明接口,help无法实现,仍调用了默认的help函数

     virtual string & help(string & ret)

     {

         ret=zbstd_script::CFunction::help(ret);

         return ret;

     }

     //Check使用pc建立了外部接口的参数组,然后对参数做了检查(代码已省略)

     virtual bool Check(CZBVector<zbstd_script::Variable > & params,void * & pc,string & msg)

     {

          pc=new VParam;

         VParam * pVparam=(VParam *)pc;

         pVparam->reserve(params.size());

         pVparam->resize(params.size());

         。。。。。。检查参数。。。。。。

         return true;

     }

     //Execpc建立的外部参数组(由pluginparams引用)传入了实际的参数值,将pc还原为inputPacket,然后调用execute

     virtual bool Exec(CZBVector<zbstd_script::Variable > & params,void * & pc,zbstd_script::Variable & ret,string & msg,void * pe)

     {

         VParam & pluginparams=*(VParam *)pc;

         //处理参数

         {

              for(size_t i=0;i<params.size();++i)

              {

                   pluginparams[i].value=params[i].GetString();

              }

          }

 

         CEventPacket * pPacket=(CEventPacket *)pe;

         long tmp=0;

         STATE tmpret;

         if(0==(tmpret=execute(pluginparams,*pPacket,tmp)))

         {

              ret=tmp;

              return true;

         }

         else

         {

              thelog<<"插件执行出错插件名"<<getName()<<" 出错码"<<tmpret<<ende;

              ret=0L;

              return false;

         }

     }

};

6.3.3   注册外部函数

在外部插件管理器注册插件时同时注册外部函数到插件编译器:

bool PluginManager::registerPlugin(const string& pluginName,BasePlugin* plugin)

{

     if(_plugins.find(pluginName)!=_plugins.end())

     {

         thelog<<"插件["<<pluginName<<"]已经存在"<<ende;

         return false;

     }

     _plugins[pluginName] = plugin;

 

     //注册到脚本解释器

     if(!zbstd_script::CFunctionMap::AddFunction(pluginName.c_str(),zbstd_script::Variable::LONG,plugin))

     {

         thelog<<"插件["<<pluginName<<"]注册到脚本解释器失败"<<ende;

         return false;

     }

     return true;

}

6.3.4   编译、执行脚本

完成注册外部函数后就可以编译、执行脚本。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值