[译]工具
By robot-v1.0
本文链接 https://www.kyfws.com/applications/tool-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 105 分钟阅读 - 52399 个词 阅读量 0[译]工具
原文地址:https://www.codeproject.com/Articles/11653/TOOL
原文作者:RedZenBird
译文由本站 robot-v1.0 翻译
前言
TOOL (Tiny Object Oriented Language) is an easily-embedded, object-oriented, C++-like-language interpreter. The purpose of this article is to introduce the TOOL interpreter and language from the perspective of a person who has a desire to include a scripting solution as part of his project.
TOOL(微小的面向对象语言)是易于嵌入的,面向对象的,类似于C ++的语言解释器.本文的目的是从一个希望将脚本解决方案作为其项目的一部分的人的角度介绍TOOL解释器和语言.
- 下载源-2.50 MB(Download source - 2.50 MB)
- 下载演示-2.23 MB(Download demo - 2.23 MB)
- 下载TOOLForge-1.38 MB(Download TOOLForge - 1.38 MB)
此版本中的重大新闻(Big News in This Release)
TOOLForge(用于TOOL的IDE)已得到增强,可以直观地创建XML表单.此增强功能将使对将XMLForms与TOOL一起使用感兴趣的任何人免于手动创建表单定义文件的麻烦.对于更高级的XMLForm控件类型,这里还有很多工作要做,但是我认为现有功能集将满足大多数用户的需求.但是,该程序只能以非源代码形式发布.这个项目中的商业图书馆代码太多,无法以其他任何方式使用它.有关如何使用TOOLForge调试TOOL脚本的更多信息,请参见下文.(TOOLForge, the IDE for TOOL, has been enhanced to allow visual creation of XML Forms. This enhancement will save anyone interested in using XMLForms with TOOL the pain of creating form definition files by hand. There is more work to be done here with the more advanced XMLForm control types, but I’m wagering the existing feature set will address most user’s needs. However, this program can only be released in non-source form. There is just too much commercial library code in this project to have it any other way. See below for more information on how to work with TOOLForge to debug TOOL scripts.)
介绍(Introduction)
TOOL(微小的面向对象语言)是易于嵌入的,面向对象的,类似于C ++的语言解释器.该语言确实是TOOL引擎核心的重要部分,它基于BOB项目,该项目最初由David Betz开发,涵盖在Dobb博士的期刊先前发行的期刊中.(TOOL (Tiny Object Oriented Language) is an easily-embedded, object-oriented, C++-like-language interpreter. The language, and indeed a significant part of the core of the TOOL engine, is based on the BOB project, a work that was originally developed by David Betz covered in previously published issues of Dr. Dobb’s Journal.)
原始语言解释器完全以K和R样式C实现.TOOL解释器使用原始BOB解释器的面向对象设计,还包括对原始项目的数十种其他增强功能.通过将原始项目的功能打包到一小组可互操作的C ++类中,可以完成面向对象的重新设计.然后将这些类聚合到一个容器类中,该类充当整个系统的外观.(The original language interpreter was implemented entirely in K and R style C. The TOOL interpreter uses an object-oriented design of the original BOB interpreter, and also includes countless dozens of other enhancements to the original project. The object oriented redesign was accomplished by packaging the functionality of the original project into a small set of inter-operable C++ classes. These classes were then aggregated into a single container class that acts as a facade over the entire system.)
重新设计原始解释器的主要目的之一是使代码达到"更现代的标准".但是,将解释器打包到单个容器类中的主要好处是,任何应用程序都可以简单地通过调用单个类来轻松创建"解释器上下文".应用程序还可以同时在范围内具有多个包装类的实例,从而同时运行多个"独立"解释器会话.(One of the primary purposes for the redesign of the original interpreter was to bring the code up to “more modern standards”. However, the key benefit of packaging the interpreter into a single container class is that any application can easily create an “interpreter context” simply by invoking a single class. Applications can also run multiple “stand-alone” interpreter sessions simultaneously simply by having multiple instances of the wrapper-class in scope at once.)
本文的目的是从一个希望将脚本解决方案作为其项目的一部分的人的角度介绍TOOL解释器和语言.为此,将讨论以下主题:(The purpose of this article is to introduce the TOOL interpreter and language from the perspective of a person who has a desire to include a scripting solution as part of his project. To this end, the following topics will be discussed:)
-
构成TOOL核心的C ++类的设计.(The design of the C++ classes that make up the TOOL core.)
-
如何将TOOL集成到您的应用程序中.(How to integrate TOOL into your application.)
-
如何根据您的自定义要求扩展工具.(How to extend TOOL for your custom requirements.)
-
如何在工具中编程.(How to program in TOOL.)
-
对TOOL类框架的检查,该框架是用TOOL语言编写的一组类,提供了一组经过测试和随时可用的TOOL类,并且还说明了TOOL内部函数的全部集合(TOOL API) .(An examination of the TOOL classes framework, which is a set of classes written in the TOOL language and which provides a set of tested and ready-to-use TOOL classes and that also illustrates the entire set of TOOL intrinsic functions (the TOOL API).) 如今,对口译员的兴趣似乎空前高涨,并且有许多口译员和技术可供选择.在这种情况下,为什么任何人都应该将TOOL引擎视为项目解决方案的一部分?下面是一些注意事项,这促使我将TOOL纳入自己的产品中.(These days, interest in interpreters seems to be at an all time high, and there are many interpreters and technologies to choose from. This being the case, why should anyone consider the TOOL engine as part of a project’s solution? A short list of considerations follows below, which have driven me to include TOOL as part of my own products.)
-
缺乏依赖(Lack of Dependencies).当考虑包括不是操作系统安装的组成部分的脚本系统时,为了使您的产品运行,它取决于第三方产品的正确安装和配置.这种依赖关系是潜在的"断点",因此,我尽可能避免使用这种依赖关系.(. When considering the inclusion of a scripting system that is not an integral component of an Operating System installation, in order for your product to run, it is dependant on the correct installation and configuration of a third party product. This kind of dependency is a potential “break point” and therefore is one that I prefer to avoid if at all possible.)
-
轻松嵌入(Easily Embedded).将第三方脚本引擎真正嵌入到您的解决方案中并不总是一件容易的事.特别是如果您的目标之一是在应用程序上下文和脚本上下文之间交换数据(实际上,将解释器并入到项目不可或缺的程度,而不仅仅是"停留在它的一边").通常,数据必须使用API或语法在两个上下文之间"编组",而这些API或语法并不总是那么容易理解或简单.因此,为了解决这个问题,我使用TOOL的目标之一是使两个上下文之间的数据交换尽可能简单.为此,我将一个应用程序上下文对象包含在TOOL的设计中.此应用程序上下文允许在应用程序环境和脚本上下文之间交换数据的简单,松散耦合但有效的方法.(. Truly embedding a third-party scripting-engine into your solution is not always an easy prospect; especially if part of your goal is to exchange data between your application context and the scripting context (in effect incorporating the interpreter to the degree that it is an integral part of the project, and not just “stuck on the side of it”). Often times, data must be “marshaled between” the two contexts using APIs or syntax which is not always that easily understood or straightforward. So, to address this, one of my goals with TOOL was to make the exchange of data between the two contexts as simple as possible. To this end, I have included an application context object into the design of TOOL. This application context allows for a simple, loosely coupled, yet effective, means of exchanging data between the application environment and the script context.)
-
轻松扩展(Easily Extended).在某些情况下,将脚本系统嵌入项目的部分原因是将应用程序功能的特定部分暴露在脚本上下文中(实际上是使程序可编程).在当今大多数口译员可用的情况下,扩展口译员的前景可能令人生畏.在检查当前可用的一些更常见的口译员的资料时,我发现了数十个(有时是数百个)文件,这些文件的组织性对于偶然的观察者而言并不容易理解.在这种情况下,作为"局外人"很难理解要在何处编辑什么以及扩展这些引擎功能的内容.而且,如果您的项目是"典型的",则很可能会有紧迫的期限,这将使您没有机会花时间了解这些项目的内部组织,从而无法修改其运行时间.而且,即使您成功完成了该任务,现在您也可能是该发行版的定制"一次性"版本的骄傲所有者,该发行版带来了一系列自身的维护挑战,并可能要求您对所有将来版本的扩展进行持续验证所选引擎的.与之形成直接对比的是,TOOL仍然是易于使用的引擎,并且非常易于扩展.本文将详细讨论扩展TOOL的技术.(. There are certain occasions when part of the reason for embedding a scripting system into a project is to expose specific portions of an application’s functions into the scripted context (in effect making your programs programmable). With most interpreters available today, the prospect of extending them can be a daunting one. In examining the source for some of the more common interpreters available at this time, I found dozens and dozens (in some cases hundreds) of files whose organization is not readily apparent to the casual observer. This being the case, it can be extremely difficult as an “outsider” to understand where and what to edit to extend the functionality of these engines. And if your project is “typical”, it most likely has tight deadlines that will not afford you the chance to take the time required to understand the internal organization of these projects to the point of being able to modify their run-times. And, even if you succeed in that task, now you are potentially the proud owner of a customized, “one-off” version of that distribution that brings its own set of maintenance challenges and may require constant validation of your extension with all future versions of the selected engine. In direct contrast, TOOL remains an easily pliable engine and is very easily extended. The techniques to extend TOOL will be thoroughly discussed in this article.)
-
无执照麻烦(No Licensing Hassles).如果将它们作为商业产品的一部分包含在内,那么即使是所谓的"开源"解释器引擎也可能会遭受额外的许可费用和其他费用的困扰.对于"小批量"产品,此类成本可能会令人望而却步.在您,我和饮水机之间,似乎有人觉得有人有权收取"本来应该免费的"钱是不对的,所以我原则上会回避这些情况.(. Even so called “open source” interpreter engines can be encumbered with additional license fees and other costs if they are included as part of a commercial product. For “low volume” products, these kinds of costs can be prohibitive. And between you, me, and the water cooler it seems somehow wrong that anyone feels they have the right to charge monies for something that is “supposed to be free” so I tend to shy away from these situations on principle.)
组成工具的C ++类的设计(The design of the C++ classes that make up the TOOL)
如前所述,TOOL由少量核心类组成.该项目中包含的大部分文件都是对该基本引擎的扩展.由于没有足够的空间来覆盖所有这些类的深度,因此我不得不将扩展的分析留给读者,因此,我的目标是试图加深对TOOL引擎内核心类的理解.我希望给您足够的信息,以使您对项目有所了解.但是,通过将项目摔倒在地上,才能最好地理解.(As was mentioned, TOOL is comprised of a small number of core classes. The bulk of the files included in this project are extensions to that basic engine. Since there is not enough space to cover all these classes in great depth, I’ll have to leave the extended analysis to the reader, and therefore my goal will be to attempt to impart an understanding of the core classes within the TOOL engine. I hope to give you enough information to allow you to get your bearings in the project; but down in the guts understanding is best gained by wrestling the project to the mat.)
**清单1.(Listing 1.)**有关如何驱动TOOL引擎的简单示例.这里显示的是一个名为"(Simple example of how to drive the TOOL engine. What we are shown here is a data exchange variable called “) oArgs
“,将用于在应用程序和脚本引擎之间共享数据. “(” that will be used to share data between the application and the scripting engine. “) oArgs
“,然后传递到的实例(” is then passed into an instance of a) VMFacade
对象,它是整个TOOL脚本引擎的容器.然后,运行脚本所需要做的只是发出一个调用,以在Facade对象上进行编译和执行.该脚本可以通过以下方式将变量返回给应用程序:(object which is the container for the entire TOOL scripting engine. All that is needed to run a script then is issue a call to compile and to execute on the facade object. The script can return variables back to the application through the) oArgs
容器.(container.)
int main(int argc, char* argv[])
{
SCRIPT_ARGUMENTS oArgs;
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestVector" ) ,
new CAppContextVariant( "On" ) ) );
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestStack" ) ,
new CAppContextVariant( "On" ) ) );
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestQueue" ) ,
new CAppContextVariant( "On" ) ) );
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestMap" ) ,
new CAppContextVariant( "On" ) ) );
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestByteArray" ) ,
new CAppContextVariant( "On" ) ) );
oArgs.insert( SCRIPT_ARGUMENTS::value_type( std::string( "TestString" ) ,
new CAppContextVariant( "On" ) ) );
VMFacade* poScript = new VMFacade();
poScript->SetVar( &oArgs );
poScript->CompileFile( "C:\\Projects\\ScriptEngineTester\\CoreClasses.tool" );
poScript->Execute( "TestAll" );
return( 0 );
}
TOOL用户将要遇到的第一类是(The first class that the users of TOOL are going to encounter is the) VMFacade
类(在中声明(class (declared in)VMFacade.h(VMFacade.h)并在(and implemented in)VMFacade.cpp(VMFacade.cpp)).该类是单个包装器类,它汇总了整个TOOL系统的所有组件.您将要在项目中实例化该实例,以获取脚本引擎的实例.从清单1的简短示例中可以看到,该类的使用非常简单.创建此类的实例后,您只需要编译一个文件(如果需要,也可以编译一个文本块),并调用(). This class is the single wrapper class that aggregates all components of the entire TOOL system; and it will be the one that you’ll want to instantiate in your projects to get instances of the scripting engine. You can see from the short example in Listing #1 just how simple this class is to use. After you create an instance of this class, you simply need to compile a file (or a text block if you’d prefer) with a call to) CompileFile(
)
要么(or) CompileText()
.(.)
如果您需要与脚本引擎共享程序中的变量,或者需要从脚本执行中检索结果,则可以通过将参数/变量放置在脚本实例中来轻松实现此目的.(If you need to share variables from your program with the script engine, or if you need to retrieve results from the execution of a script, you can easily accomplish this by placing parameters/variables into an instance of a) SCRIPT_ARGUMENTS
类.此STL派生映射设置为存储可以在应用程序和脚本环境之间交换的所有变量类型.应用程序上下文中的所有变量都存储在字符串键下,并且值类型是对象的变体类型,可以是下列类型之一:字符串,长整数,布尔值,双精度型,DWORD,DateTime,ByteArrray和NULL对象类型.的(class. This STL derived map is set up to store all the variable types that can be exchanged between the application and the scripting environment. All variables in the application context are stored under a string key, and the value type is a variant type of object that can be one of the following types: String, Long, Boolean, Double, DWORD, DateTime, ByteArrray, and a NULL object type. The) CappContextVariant
类在文件中声明:(class is declared in the file:)VMCoreVirtualMachine.h(VMCoreVirtualMachine.h).(.)
该脚本可以在运行时使用对TOOL API函数的调用按名称将值从容器中拉出:(The script can pull values out of the container by name while it is running using a call to the TOOL API function:) GetAppEnvField(
)
.脚本还可以使用TOOL API函数,使用上下文将脚本执行中创建的变量返回给托管应用程序:(. The script can also use the context to return variables created in the script’s execution back to the hosting application using the TOOL API function:) SetAppEnvField(
)
.(.)
为了促进托管过程和脚本环境之间的通信,请:简单声明一个实例.(In order to facilitate communication between the hosting process and the scripting environment then: simply declare an instance of a) SCRIPT_ARGUMENTS
类,将变量放入其中,然后与(class, place variables into it, and then share that context with the) VMFacade
通过致电来上课(class through a call to the) SetVar()
在(on the) VMFacade
目的.脚本完成后,托管应用程序可以检查此映射以查找从脚本引擎发送回应用程序的任何结果.(object. After the script has completed, the hosting application can examine this map for any results sent back to the application from the scripting engine.)
完成所有这些设置后,接下来要做的就是调用脚本引擎并调用(With all this setup being accomplished, the next thing to do is to invoke the scripting engine with a call to the) Execute()
立面上的方法.关于TOOL引擎的一件好事是,您可以简单地通过在对以下代码的调用中声明入口点来指定脚本执行的入口点:(method on the facade. One nice thing about the TOOL engine is that you can specify the entry point for the script execution simply by declaring the entry point in the call to) Execute()
.如果您可能在一个脚本文件中包含多个脚本程序,并且您要基于主应用程序中的逻辑"选择要运行的脚本程序”,则此功能非常有用.可以使用此功能的另一种方法是为所有脚本定义"标准接口”,然后简单地定义所有脚本,使它们具有相同的入口点.(. This capability can be handy in the case where you may have multiple script programs included in a single script file and you want to “select which one to run” based on logic in the main application. Another way this feature can be used, is to define the “standard interface” for all your scripts and then simply define all scripts so that they have the same entry point(s).)
我在项目中使用了两种方法.如果您希望在单个脚本文件中有多个入口点(例如,一种可用于您的项目的宏语言系统),则第一种方法很方便.如果要描述一组独立的脚本文件,则所有方法都将由托管程序以与"脚本对象"相同的方式调用,这是很方便的.就我而言,我已经非常成功地采用了Init/Run语义,其中所有脚本都必须具有(I’ve used both approaches in my projects. The first approach is handy if you would like to have multiple entry points (say for example, a kind of a macro language system available to your project) in a single script file. The second approach is handy if you want to describe a set of standalone script files that all will be invoked by the hosting program in the same fashion as “scripted objects”. In my case, I’ve employed an Init/Run semantic quite successfully wherein all scripts must have) Init(
)
和(and) Run()
它们中的功能完成特定于该脚本的工作.(functions in them that do the work specific to that script.)
在后面偷看(Peeking behind the) VMFacade
课,遇到的下一个主要课是(class, the next major class encountered is the) VMCore
类(在中声明(class (declared in)VMCore.h(VMCore.h)并在(and implemented in)VMCore.cpp(VMCore.cpp)).该类也是系统其余部分的Facade类,但是执行缝合TOOL运行时所需的更多详细操作.因此,此类汇总了TOOL引擎的所有其他主要组件.的(). This class is also a facade class over the rest of the system, but does more of the detailed operations required to stitch the TOOL runtime altogether. As such, this class aggregates all the other major components of the TOOL engine. The) VMCore
类驱动TOOL系统的实际编译和执行操作.它还会启动TOOL中的所有其他主要组件.作为构建TOOL引擎的一部分,此类为所有主要组件提供指向其他主要组件的指针引用.最后,该类还捕获从TOOL引擎引发的所有异常.如果脚本中检测到语法错误,则在编译过程中可能会引发异常.如果由TOOL引擎执行的完整性检查之一失败,则在脚本执行期间也可能引发异常.最后,脚本本身可以通过调用TOOL API函数来启动异常:(class drives the actual compilation and execution operations of the TOOL system. It also initiates all the other major components in TOOL. As part of constructing the TOOL engine, this class provides all the major components with pointer references to the other major components. Finally, this class also catches all exceptions thrown from the TOOL engine. Exceptions can be thrown during compilation if there are syntax errors detected in the script. Exceptions can also be thrown during script execution if one of the integrity checks performed by the TOOL engine fails. Finally, exceptions can be initiated by the script itself through a call to the TOOL API function:) Throw()
.(.)
在主要的TOOL组件之间进行深度"交叉连接"的部分原因是历史悠久的,因为TOOL的原始设计和实现是基于BOB引擎的.即使主要对象是根据广泛的功能区域设计的,但在某些区域中无法完全执行分离(就像在"真实"计算机系统中无法完全将内存与CPU分离一样).即便如此,我还是希望您能发现将BOB项目重新打包到这组特定的类中是明智且具有凝聚力的.(Part of the reason for the deep level of “cross connection” between the major TOOL components is historical, in that the original design and implementation of TOOL was based on the BOB engine. Even though the major objects were designed based on broad functional areas, there were some areas where the separation could not be performed completely (just the same as in one cannot completely separate memory from the CPU in a “real” computer system). Even so, I’m hopeful that you will find my repackaging of the BOB project in to this particular set of classes is sensible and cohesive.)
更仔细地看一下(Looking more closely at what is contained in the) VMCore
,您会看到TOOL系统"底部"是一个(, you can see that “at the bottom” of the TOOL system is a) VMCoreVirtualChip
(在中声明((declared in)VMCoreVirtualChip.h(VMCoreVirtualChip.h)).此类是TOOL系统的” CPU".在此类中,您将找到诸如解释代码的向量,程序计数器,机器的堆栈和帧指针以及主要堆存储区之类的东西.(). This class is the “CPU” for the TOOL system. In this class, you will find such things as a vector for the interpreted code, the program counter, the stack and frame pointers for the machine, as well as the main heap storage areas.)
在下一个层次上,(At the next level out, there is the) VMCoreVirtualMachine
类(在中声明(class (declared in)VMCoreVirtualMachine.h(VMCoreVirtualMachine.h)),它担负着管理TOOL堆的角色,并充当TOOL系统的主要内存管理器类.在这种情况下,该类也可以与() which fills the role of managing the TOOL heap and acts as the main memory manager class for the TOOL system. That being the case, this class will also work with the) VMCoreVirtualChip
定义和管理脚本程序中包含的所有变量和文字的简单字典.源自(to define and manage a simple dictionary of all variables and literals contained within a script program. Derived from the) VMCoreVirtualMachine
类是(class is the) VMCoreVirtualOpSys
类.在此类中定义和实现了所有内部功能(本质上是TOOL引擎的整个API).的(class. This class is where all the intrinsic functions (essentially the entire API to the TOOL engine) are defined and implemented. The) VMCoreVirtualOpSys
只是,这是TOOL引擎提供的"操作系统",也是引擎扩展所在的类别.当您想在自己的TOOL版本中添加新的内在函数时,可以在(is just that, it is the “operating system” that is offered by the TOOL engine and is also the class where extensions to the engine are located. When you would like to add a new intrinsic function to your version of TOOL, you will likely implement it in the) VMCoreVirtualOpSys
类.(class.)
仍然继续我们从TOOL核心出发的旅程,您会发现有一个(Still continuing on our journey out from the TOOL core, you’ll see that there is a) VMCoreCompiler
继承自(class which is inherited from the) VMCoreParserScanner
类. parser-scanner对象是将扫描并标记化脚本文本的对象,而编译器将驱动扫描器并针对扫描器-解析器的输出进行操作,以汇编将在脚本被执行时执行的操作码数组.跑.(class. The parser-scanner object is the one that will scan and tokenize the script text, while the compiler will drive the scanner as well as operate against the scanner-parser output to assemble the array of op-codes that will be executed when the script is run.)
在TOOL的最高级别,是(At the highest level of TOOL, is the) VMCoreInterpreter
负责执行由生成的已编译代码的向量的类(class that is responsible for executing the vector of compiled code that was produced by the) VMCoreCompiler
.解释器充当大型开关扫描器.它扫描由编译器生成的操作码,然后根据检测到的操作码执行一组特定的操作.(. The interpreter acts as a large switching-scanner. It scans the op-codes produced by the compiler, and then based on the op-code detected, will perform a specific set of actions.)
工具引擎如何工作?(How does the TOOL Engine work?)
TOOL被实现为编译器和解释器的混合体(内部编译器?).定义函数后,会将其编译为面向堆栈的字节码机器的指令.调用该函数时,将解释这些字节码指令.与直接解释器相比,此方法的优势在于语法分析在编译时仅进行一次.这大大加快了函数执行的速度,并为构建完全不包含编译器的仅运行时系统提供了可能性.实际上,在我的一些脚本中(它们并不太复杂,但也不是那么简单),我通过系统每秒完成了150多次完整的执行.当然,您的工作量可能会有所不同,但是使用TOOL,虽然远不及编译后的代码那么慢.(TOOL is implemented as a hybrid of a compiler and an interpreter (an inter-piler?). When a function is defined, it is compiled into instructions for a stack-oriented, byte-code machine. When the function is invoked, those byte-code instructions are interpreted. The advantage of this approach over a straight interpreter is that syntax analysis is done only once, at compile time. This speeds up function execution considerably and opens up the possibility of building a run-time-only system that doesn’t include the compiler at all. In fact, in some of my scripts (which are not overly complex, but are not all that simple either), I’ve gotten over 150 complete executions a second through the system. Your mileage may vary of course, but TOOL, while nowhere near as fast as compiled code is no slouch either.)
执行TOOL编译器生成的字节码的虚拟机具有一组寄存器,一个堆栈和一个堆.这些构造包含在(The virtual machine that executes the byte-codes generated by the TOOL compiler has a set of registers, a stack, and a heap. These constructs are contained in the) VMVirtualChip
类.所有指令都从中获取参数并将其结果返回到堆栈.文字存储在代码对象本身中,并通过offset来引用.分支指令测试堆栈顶部的值(不弹出堆栈),并相应地分支.函数参数在堆栈上传递,函数值在堆栈顶部返回.在讨论扩展TOOL引擎的技术时,我们将再次更深入地研究这个概念.(class. All instructions get their arguments from and return their results to the stack. Literals are stored in the code object itself and are referred to by offset. Branch instructions test the value on the top of the stack (without popping the stack) and branch accordingly. Function arguments are passed on the stack, and function values are returned on the top of the stack. We’ll examine this concept again in more depth when the techniques for extending the TOOL engine are discussed.)
在TOOL脚本类中,所有成员函数都是虚拟的.这意味着调用成员函数时,解释器必须确定要调用成员函数的哪个实现.这是由(In TOOL script classes, all member functions are virtual. This means that when a member function is invoked, the interpreter must determine which implementation of the member function to invoke. This is done by the) SEND
操作码,它使用堆栈中的选择器(实际上只是一个包含成员函数名称的字符串)以及与对象类关联的方法字典来确定要使用的成员函数.如果查找失败,则检查来自基类的字典.继续这种做法,遵循基类链,直到找到成员函数或没有基类为止.如果发现成员函数与选择器相对应,则它将替换堆栈上的选择器,并将控制权转移到成员函数,就像常规函数一样.如果未找到成员函数,则会报告错误,并且解释器将中止.我添加到此机制的一个扩展是语法为"(op-code, which uses a selector from the stack (actually, just a string containing the name of the member function) with the method dictionary associated with the object’s class to determine which member function to use. If the lookup fails, the dictionary from the base class is examined. This continues, following the base-class chain until either a member function is found or there is no base class. If a member function is found to correspond to the selector, it replaces the selector on the stack and control is transferred to the member function, just as it would have been for a regular function. If no member function is found, an error is reported and the interpreter aborts. One extension I’ve added to this mechanism is a “cast call” with a syntax of “) *->
“.该调用的作用是允许脚本编写者将该类"投射"到其特定的基类之一(在运算符的左侧命名),然后调用在该名称的右侧命名的选择器.操作员.可以在脚本文件”(”. What this call does is to allow the script-writer to “cast” the class to a specific one of its base classes (named on the left hand side of the operator) and then to invoke the selector named on the right hand side of the operator. Several examples of this operator in action can be found in the script file “)coreclasses.tool(coreclasses.tool)".该运算符有两个主要目的.其一是它使对基类的调用变得显式,这在多个父类共享相同的函数名的情况下特别有用,并且还可以注释脚本代码,以说明作者对执行以下代码的特定意图.该类方法.(”. This operator serves two primary purposes. One is that it makes the call to a base class explicit, which is especially useful if multiple parent classes share the same function name, and also serves to comment to the script code as to what the author’s specific intentions are with regards to the execution of that class method.)
TOOL变量类型已扩展为支持以下基本数据类型:Longs,Doubles,Bytes,Strings,Tokenizers,ByteArrays,Vectors,Classes,DateTimes,Queues,Maps,Stacks,WaitObjects,NT Kernel-handles,DWORDs,NT-File句柄,文件查找句柄,zib文件,SQLite数据库,ODBC连接和nil.在内部,解释器使用四种其他类型:类,编译的字节码函数,内置函数头和变量.在可以存储值的任何地方,标签都指示当前存储在其中的值的类型.这是"(TOOL variable types have been extended to support the following basic data types: Longs, Doubles, Bytes, Strings, Tokenizers, ByteArrays, Vectors, Classes, DateTimes, Queues, Maps, Stacks, WaitObjects, NT Kernel-handles, DWORDs, NT-File handles, File-Find handles, zib-files, SQLite databases, ODBC connections, and nil. Internally, the interpreter uses four more types: classes, compiled bytecode functions, built-in function headers, and variables. Wherever a value can be stored, a tag indicates the type of value presently stored there. This is the “) v_type
“字段上(” field on the) VMVariant
类,在”(class, declared in “)VMCoreGlobal.h(VMCoreGlobal.h)".(”.)
对象,向量和字节码对象都由值结构数组表示.对于字节码对象,向量中的第一个元素是指向该函数的字节码字符串的指针,其余为字节码指令所引用的文字.类对象是向量,其中第一个元素是指向类对象的指针,其余元素是该对象的非静态成员变量的值.内置函数只是指向实现内置函数的C函数的指针.变量是指向该变量的字典条目的指针.有一个用于全局符号的字典,一个用于类的字典.每个类还具有用于数据成员和成员函数的字典.(Objects, vectors, and bytecode objects are all represented by an array of value structures. In the case of bytecode objects, the first element in the vector is a pointer to the string of bytecodes for the function, and the rest are the literals referred to by the bytecode instructions. Class objects are vectors, where the first element is a pointer to the class object and the remaining elements are the values of the nonstatic member variables for the object. Built-in functions are just pointers to the C functions that implement the built-in function. Variables are pointers to dictionary entries for the variable. There is a dictionary for global symbols and one for classes. Each class also has a dictionary for data members and member functions.)
除堆栈外,TOOL还使用堆来存储对象,向量和字符串. TOOL的当前实现使用C堆和C函数(In addition to the stack, TOOL uses a heap to store objects, vectors, and strings. The current implementation of TOOL uses the C heap and the C functions) malloc
和(and) free
管理堆空间并使用压缩内存管理器.但是,我必须承认,此版本的TOOL中的堆管理不是完全垃圾收集的.这是由于引擎的不断发展的特性以及某些新数据类型实际上是由引擎管理的C ++类的事实.(to manage heap space and uses a compacting memory manager. However, I must admit that the heap management in this version of TOOL is not completely garbage collected. This is due to the evolving nature of the engine and the fact that some of the new data types are actually C++ classes managed by the) VMVariant
目的.这就是为什么TOOL API调用"(object. This is why the TOOL API call “) FreeObject
“已进入TOOL API.包含此函数,以便在使用脚本完成脚本时清理分配给堆的C ++类.我完全认识到这是当前实现中的一个弱点,并且我确实计划在较新版本的引擎中解决这个弱点.但是,到目前为止,我已经习惯了它的"邪恶之处”,因此在解决当前实施方案的缺点时并没有将其作为优先事项.(” has crept into the TOOL API. This function is included so that the heap allocated C++ class can be cleaned up when the script is done using it. I recognize fully that this is a weakness in the current implementation, and one that I do plan to address in a newer version of the engine. But, for now, I’ve gotten used to the “evil of it” and have not therefore made a priority out of addressing the shortcoming of the current implementation.)
运算符重载(Operator Overloading)
在TOOL解释器中,操作员可以根据操作员的LHS/RHS值具有几种不同的含义.考虑数组索引运算符"(In the TOOL interpreter, an operator can have several different meanings based on the LHS/RHS values for the operator. Consider the case of the array index operator “) []
“.在这种情况下,操作员可以有几种不同的解释.如果LHS是(”. In this case, the operator can have several different interpretations. It should perform one action if the LHS is a) Map
对象,如果LHS是(object, and a complete different operation if the LHS is a) Vector
目的.数学运算也需要类似的方法,例如”(object. A similar approach is needed for mathematical operations, such as “) +
" 要么 “(” or “) -
如果对不同类型的变量执行运算,则必须将其中一个变量"提升"为另一种变量类型.鼓励有兴趣的读者学习VMInterpreter对(” where if the operation is performed on variables of different types, then one of them must be “promoted” to the other variable type. The interested reader is encouraged to study the VMInterpreter’s handling of the) OP_ADD
和(and) OP_VREF
操作码,以获取有关如何扩展运算符重载的更多详细信息.如果您希望向工具引擎添加更多变量类型,并希望针对不同的"标准运算符"具有特定的行为,那么这是一个重要的主题.(op-codes for more details on how to extend operator overloading. This is an important topic to have a grasp of if you wish to add more variable types to the TOOL engine and wish to have specific behaviors for different “standard operators”.)
功能重载(Function Overloading)
在TOOL API中,内在函数可以被"重载”,因为它们可以被编码为接受不同的参数计数和类型.一个例子(还有很多其他例子)是(In the TOOL API, intrinsic functions can be “overloaded” in that they can be coded to accept different parameter counts and types. One example of this (and there are many others) is the) HandlerNewDateTime(
)
中的方法(method in the) VMCoreVirtualOpSys
类.该处理程序被编码为接受1个或6个参数.如果传递了一个参数,则将检查其类型以确认它是一个(class. This handler is coded to accept either 1 or 6 arguments. If one argument is passed, then its type is examined to verify that it is a) DT_DATETIME
类型.如果传递了六个参数,则将检查每个参数以验证它们是否正确.(type. If six arguments are passed, then each of these arguments are examined to verify that they are) DT_INTEGER
类型.(types.)
该功能的最极端实现是在函数中(The most extreme implementation of this feature is found in the function) HandlerStringFormat(
)
这是椭圆函数的实现;表示它可以接受任意数量和类型的参数,这是您期望的(which is an implementation of an elliptical function; meaning that it can accept any number and type of arguments, which is what you’d expect from a) sprintf
功能类型.(type of function.)
定义函数的这种灵活性是解释器将函数参数推入堆栈的自然结果.解释器没有任何内在函数所需参数的数量和类型的信息.它只会将为调用找到的所有参数推入堆栈.此功能使任何内在函数都可以被编码为接受来自解释器的所有类型的参数.(This flexibility in defining functions is a natural outcome of the way the interpreter pushes function arguments on to the stack. The interpreter has no information regarding the number and type of arguments needed by any intrinsic function. It will simply push all arguments found for the call on to the stack. This feature enables any intrinsic function to be coded to accept all types of arguments from the interpreter.)
但是,这种灵活性有一个"缺点",那就是所有内在函数都应该对所有自变量和自变量计数进行完整性检查,以验证其将要执行的操作是否符合功能的预期.(There is a “flipside” to this flexibility however, and that is that all intrinsic functions should perform sanity checks on all arguments and argument counts to verify that what it is about to operate on is what is expected in the function.)
全部上岸,即将上岸(All Ashore, That’s Going Ashore)
好吧,这是我可以提供的游览旋风,而不必完全陷入将TOOL整合到一起的所有细节中.希望我已经对TOOL引擎的基础知识提供了足够的介绍,以便您在分析TOOL的内部工作原理时可以有坚实的基础.但是,请放心,您可以轻松使用和扩展工具,而无需了解内部所有零件的工作方式.我只是想向您展示一下该项目的周围情况,这样当您自己尝试踩踏这头野兽的轮胎时,您会知道在哪里可以找到挡泥板.(Well that’s about a whirlwind of a tour that I can provide without getting completely bogged down in all the details that bring TOOL together. Hopefully, I have provided enough of an introduction to the lay of the land in the TOOL engine so that you can have some solid ground to stand on while you analyze the inner workings of TOOL. However, take comfort in knowing that you can easily use and extend TOOL without knowing how all the inside pieces and parts work. I just thought I’d show your around the project a bit, so that when you go to kick the tires on this beasty yourself, you’ll know where to find the fenders.)
用工具编程(Programming in TOOL)
下面的示例提供了一个简单的示例程序;用于计算使用TOOL语言编写的阶乘的函数.(The examples below present a simple example program; a function for computing factorials written using the TOOL language.)注意(Note):还鼓励读者阅读此发行版随附的示例脚本.(: Readers are also encouraged to review the sample scripts included with this distribution.)
// factorial program
//
Factorial( iValue )
{
return( ( iValue == 1 ) ? 1 : ( iValue * Factorial( iValue - 1 ) ) );
}
Run( ;iLoop )
{
iLoop = Long( 0 );
for ( iLoop = 1; iLoop < 10 iLoop++ )
{
Echo( StringFormat( "Factorial for % is %", iLoop, Factorial( iLoop ) ) );
}
}
您可以看到该程序看起来很像其C对应程序.唯一明显的区别是缺少参数类型的声明(You can see that this program looks a lot like its C counterpart. The only noticeable difference is the lack of a declaration for the type of the parameter) iValue
在传递给(in the arguments passed to the) Factorial
函数以及该函数的返回类型.这是由于不需要在TOOL中声明变量类型.尽管为了清晰和维护起见,声明和键入所有变量仍然被认为是"好的样式".这样做的副作用是,任何变量都可以采用任何类型的值.(function and for the return type of this same function. This is due to the fact that variable types do not need to be declared in TOOL; although it is still considered “good style” to declare and type all variables for clarity and maintenance purposes. A side effect of this is that any variable can take on a value of any type.)
在第一个示例中值得注意的其他一点是在(Other points worth noticing in this first sample are the variables declared in the) Run
函数的形式参数列表.在这里,您可以看到该变量(function’s formal parameter list. Here, you can see that the variable) iLoop
遵循分号.该语法用于定义(follows a semi-colon. This syntax is used to define the) iLoop
变量位于"本地范围"内(variable as being in “local scope” to the) Run
功能.默认情况下,TOOL脚本中的所有变量都采用全局范围,除非使用此专用本地范围语法声明它们.(function. By default all variables in TOOL scripts adopt global scope unless they are declared using this specialized local scope syntax.)
重要的提示(Important note):即使从脚本编写者的角度来看,TOOL是一种弱类型语言,但TOOL解释器仍将对传递给其例程的变量的类型进行类型检查.如果变量的类型不正确,则解释器将停止脚本的执行.(: Even though TOOL is a weakly typed language from the script writer’s perspective, the TOOL interpreter will type-check the type of a variable passed into its routines. If the variable is not of the right type, the interpreter will halt the execution of the script.)
同样,您应该主要注意的是,上面的示例程序在很大程度上看起来类似于用C编写的类似程序.此外,请注意,该程序使用了(Again, what you should notice primarily is how much the example program above looks a lot like a similar program written in C. Also, notice that the program uses the) StringFormat
函数来创建格式化的字符串,并且(function to create a formatted string, and the) Echo
功能以显示结果.这些功能将在本文档的后面部分进行详细说明.的(function to display the results. These functions will be explained in greater detail later in this document. The) StringFormat
TOOL中的function将其每个参数连续打印到输出字符串中.它能够打印任何类型的参数并自动对它们进行适当的格式化.(function in TOOL prints each of its arguments in succession into an output string. It is capable of printing arguments of any type and automatically formats them appropriately.)
除了支持类C的表达式和控件构造之外,TOOL还支持类C ++的类.同样,由于TOOL是一种无类型的语言,因此类定义的语法与C ++有所不同,但是它非常相似,因此应该很容易从一种语言过渡到另一种语言.(In addition to supporting C-like expressions and control constructs, TOOL also supports C++-like classes. Again, since TOOL is a typeless language, the syntax for class definitions is somewhat different from C++, but it is similar enough that it should be easy to move from one to the other.)
下一个示例显示了一个简单的类定义,该定义定义了一个名为(The next example shows a simple class definition that defines a class called) Foo
与成员(with members) m_A
和(and) m_B
和一个静态成员称为(and a static member called) m_Last
以及静态成员函数(as well as a static member function) GetLast
.与C ++不同,没有必要在类定义中声明所有成员函数.只需要声明静态成员函数.但是,必须在类定义中声明所有数据成员.(. Unlike C++, it is not necessary to declare all member functions within the class definition; only the static member functions need be declared. It is necessary, however, to declare all data members in the class definition.)
// class declaration
//
class Foo
{
m_A;
m_B;
static m_Last;
static GetLast();
}
Foo::Foo( AValue, BValue )
{
m_A = AValue;
m_B = BValue;
m_Last = this;
return( this );
}
Foo::GetLast()
{
return( m_Last );
}
与C ++中一样,使用构造函数初始化类的新对象,该构造函数的名称与类本身相同.在此示例中,构造函数采用两个参数,它们是成员变量的初始值(As in C++, new objects of a class are initialized using a constructor function, which has the same name as the class itself. In this example, the constructor takes two arguments, which are the initial values for the member variables) m_A
和(and) m_B
.它还记住该类型的最后一个对象实例(. It also remembers the last object instance of the type) Foo
在静态成员变量中创建(created in the static member variable) m_Last
.最后,构造函数返回新对象.对于那些不熟悉C ++的人来说,变量(. Lastly, the constructor returns the new object. For those of you not familiar with C++, the variable) this
指为其调用成员函数的对象.它是传递给每个非静态成员函数的隐式参数.在这种情况下,它是刚刚创建的新对象.(refers to the object for which the member function is being called. It is an implicit parameter passed to every non-static member function. In this case, it is the new object just created.)
在TOOL中,所有类数据成员都受到隐式保护.访问或修改成员变量的值的唯一方法是通过成员函数.如果需要访问成员函数外部的成员变量,则必须提供对成员函数的访问权限.(In TOOL, all class data members are implicitly protected. The only way to access or modify the value of a member variable is through a member function. If you need to access a member variable outside a member function, you must provide access to member functions to do this.)
我们将继续完善(We’ll continue to refine the) Foo
下面的类展示如何设置成员变量的值.最后,我们将展示一个成员函数,该函数显示之间的数字(class below to show how to set the value of a member variable. Finally, we’ll show a member function that displays the numbers between) m_A
和(and) m_B
对于任何对象(for any object of the) Foo
类类型和一个(class type, and a) Run
创建一些对象并对其进行操作的函数.的(function that creates some objects and manipulates them. The) new
运算符将创建其名称之后的类的新对象.类名后括号中的表达式是要传递给构造函数的参数.(operator creates a new object of the class whose name follows it. The expressions in parentheses after the class name are the arguments to be passed to the constructor function.)
// continuing to define the Foo class
//
Foo::GetAValue()
{
return( m_A );
}
Foo::GetBValue()
{
return( m_B );
}
Foo::SetAvalue( NewAValue )
{
m_A = NewAValue;
}
Foo::SetBValue( NewBValue )
{
m_B = NewBValue;
}
Foo::GetSpan()
{
return( m_B - m_A );
}
Run( ;poFoo1, poFoo2 )
{
poFoo1 = new Foo( 1 , 2 );
poFoo2 = new Foo( 11, 22 );
Echo( "Foo1 Span Is: " + poFoo1->GetSpan() );
Echo( "Foo2 Span Is: " + poFoo2->GetSpan() );
}
TOOL还支持类似于Java语言的继承模型,因为它允许从另一个类派生一个类.派生类将继承基类的行为,并可能添加其自身的某些行为.工具仅支持单一继承;因此,每个类别最多只能有一个基本类别.下一个代码示例定义一个类(TOOL also supports an inheritance model similar to the Java language, in that it allows one class to be derived from another. The derived class will inherit the behavior of the base class and possibly add some behavior of its own. TOOL only supports single inheritance; therefore, each class can have at most one base class. The next code example defines a class) Bar
从基类派生(derived from the base class) Foo
较早定义.(defined earlier.)
班级(The class) Bar
将有成员变量(will have member variables) m_A
和(and) m_B
从其父继承(inherited from its parent) Foo
以及其他成员变量(as well as the additional member variable) m_C
.的构造函数(. The constructor for) Bar
需要初始化这个新的成员变量,并为类的对象进行正常的初始化(needs to initialize this new member variable and do the initialization normally done for objects of class) Foo
.下面的示例说明了如何完成此操作:(. The example below illustrates how this is done:)
// class derivation in TOOL
//
class Bar : Foo
{
m_C;
}
Bar::Bar( AValue, BValue, CValue )
{
Foo*->Foo( AValue, BValue );
m_C = CValue;
return( this );
}
此示例说明了TOOL和C ++对象之间的另一个区别.在C ++中,不能调用构造函数来初始化已经存在的对象.但是,这在TOOL中是允许的,因此(This example illustrates another difference between TOOL and C++ objects. In C++, constructor functions cannot be called to initialize already existing objects. This is allowed in TOOL, however, so the) Foo
构造函数可用于进行常见的初始化(constructor can be used to do the common initialization of the) Foo
和(and) Bar
类.细心的读者可能会注意到(classes. Careful readers may notice the) *->
用于的操作(operation used in the) Bar
构造函数.在工具中,这称为"广播操作员".该运算符的作用是将"(constructor. In TOOL, this is called the “cast-call operator”. What this operator does is to cast “) this
“转换为在运算符左侧命名的基类类型,然后在命名基类中在运算符右侧调用该函数.此语法对于在函数重载中特别有用长类层次结构,因为可以在调用函数之前将其显式转换为适当的父类类型.(” to the base-class-type named on the left-hand-side of the operator, and then invoke the function on the right-hand-side of the operator in the named base class. This syntax is especially useful for function overloading in long class hierarchies because this can be explicitly cast to the proper parent class type before the invocation of the function call.)
快速提及编程风格(A Quick Mention of Programming Style)
由于TOOL是一种弱类型的语言,这意味着不需要指定变量的类型,因此我发现使用一些对脚本变量的数据类型进行分类的方法很有用(目的是使代码更易于理解.其他).因此,本文随附的所有脚本示例均使用了简化的匈牙利符号表示形式.以下是所用符号的示例:(Since TOOL is a weakly typed language, meaning that the type of variables is not required to be specified, I found it useful to use some means of classifying the data-type of a script variable (for the purpose of making the code more understandable to others). For this reason, a simplified form of Hungarian Notation is used in all script samples included with this article. Following are samples of the notation used:)
Data Type Hungarian "Marker" Example
String s sMyString
Number i iValue
Object o oValue
我并不是说要以任何特定的方式推广匈牙利符号,因为我知道正如很多人不喜欢我一样,我只是发现它在努力理解大型项目时非常有用.(I don’t mean to promote Hungarian Notation in any specific way, as I know that just as many folks don’t like it as I do, I simply find it to be a useful when striving to understand large projects.)
有关建议的编程样式的更多信息(More on a Suggested Programming Style)
TOOL是一种弱类型的语言,这意味着不必指定变量的类型.因此,一些对存储在变量中的数据进行分类的方法(目的是使代码对其他人更易理解)将非常有用.因此,在所有TOOL程序脚本中都鼓励使用简化的匈牙利表示法形式.以下是所用符号的示例:(TOOL is a weakly typed language meaning that the type of variables does not have to be specified. Therefore, some means of classifying the data stored in a variable (for the purpose of making the code more understandable to others) would be of great benefit. Therefore, a simplified form of Hungarian Notation is encouraged in all TOOL program scripts. Following are samples of the notation used:)
|数据类型(Data Type)
|建议标记(Suggested Marker)
|例(Example)
String |
|s
| sText
| |Long
|我或我(l or i)
| lNumber
| |Double
|dbl
| dblValue
| |Byte
|b
| bFlag
| |Stack
|ostk
| ostkOfItems
| |Vector
|ovec
| ovecOfItems
| |Map
|omap
| omapOfItems
| |DateTime
|dt
| dtNow
| |Queue
|oqueue
| oqueueOfItems
| |Handle
|h
| hFile
| |Color
|clr
| clrBlack
| |DWORD
|dw
| dwValue
| |数据库连接(Database Connection)
|oDB
| oDBSource
| |Calculator
|ocalc
| ocalcFormula
| |类实例(Class Instance)
|po
| poFoo
|
创建和分配工具变量(Creating and Allocating TOOL Variables)
工具中有几种类型的变量.它们与创建变量的TOOL调用一起列在下面.熟悉C ++/C#/Java的读者可以将这些API调用视为对变量的构造函数的调用.(There are several types of variables in TOOL. They are all listed below along with the TOOL call for creating the variable. Readers familiar with C++/C#/Java can think of these API calls as calls to a constructor for a variable.)
Vector(); // accepts no arguments
String( 255 ); // reserve 255 characters for length
String( "Hello" ); // create and assign value to string
Queue(); // accepts no arguments
Stack(); // accepts no arguments
Map(); // accepts no arguments
Tokenizer( sToTokenize, sDelimiter );
ByteArray(); // create byte array with default parameters
ByteArray( Long( 25 ) ); // create byte array with 25 elements
ByteArray( Long( 25 ), Long( 5 ) ); // create byte array with 25
// elements and grow factor of 5
// elements
Long(); // create a long with value zero
Long( 50 ); // assign value to a long variable
Handle( 0 ); // create a handle type MUST have init value
DateTime(); // create a datetime value equal to 'now'
DateTime( dtOther ); // copy construct a date time variable
DateTime( 2004, 01, 01, 12, 15, 0 ); // create date time with init
// value of 01/01/2004 12:15:00
Color(); // create color with RGB 255,255,255
Color( 0, 0, 0, ); // create color with RGB 0,0,0
Color( clrOther ); // copy construct a color variable
DWORD(); // create a DWORD with value of zero
DWORD( dwOther ); // copy construct a DWORD variable
DWORD( Long( 5 ) ); // construct a DWORD from a number value
Database(); // create an ODBC database variable
Calculator(); // create an calculator / equation handler
Double(); // create a double with a value of 0.00
Double( Long( 6 ) ); // create a double from a long value
Double( dblOther ); // copy construct a double variable
Double( "123.45" ); // create a double from a string value
Byte(); // create a byte initialized to zero/false
Byte( bOther ); // copy construct a byte type variable
Byte( Long( 10 ) ); // set byte variable from number
// NOTE: limit of 0 - 255 for the number
Page(); // report page object
Table(); // table object in a report page
Color(); // color object in a report page
Database(); // odbc database connection object
Calculator(); // function evaluation object
NullValue(); // TOOL NULL Value
MiniDatabase(); // SQL LITE Wrapper Object
UnZipper(); // zlib Wrapper Object for unzip operations
ZipMaker(); // zkib Wrapper Object for zip operations
FileBuilder(); // string collection object for creating file output
DelimitedFile(); // wrapper object for delimited file I/O
ExcelExporter(); // Wrapper for an excel workbook export object
工具运行时类型检查(TOOL Run Time Type Checking)
由于TOOL是一种相对较弱的语言,如先前在另一主题中所述.同时,TOOL解释器会验证传递给TOOL API函数的所有参数是否为正确的类型,TOOL为脚本编写者提供了一整套RTTI(运行时类型标识)函数,以帮助他们编写可靠的内容.可靠的脚本程序.(Since TOOL is a relatively weakly-typed language, as was explained previously in another topic; while at the same time the TOOL interpreter validates that all arguments passed to the TOOL API functions are of the proper type, TOOL offers an entire suite of RTTI (Run Time Type Identification) functions for script writers in order to assist them in writing solid and reliable script programs.)
由于可以对所有变量执行参数验证,因此使用这些功能可以创建更强大的脚本.这些功能的另一个用途是编写脚本,这些脚本将根据要测试的变量的类型有条件地分支.本文档后面将提供这两种用法的示例.下面列出了RTTI功能的完整列表:(Use of these functions allows the creation of more robust scripts due to the argument validations that can be performed on all variables. An additional use of these functions may be to write scripts that will conditionally branch based on the type of variable being tested. There will be examples of both types of usage later in this document. The complete list of RTTI functions is listed below:)
IsNull( oVarToTest ); // is the variable NULL?
IsClass( oVarToTest ); // is the variable a TOOL script class?
IsVector( oVarToTest ); // is the variable a vector type?
IsNumber( oVarToTest ); // is the variable a long number?
IsString( oVarToTest ); // is the variable a string?
IsFile( oVarToTest ); // is the variable a file?
IsKernelObject( oVarToTest ); // is the variable an NT kernel object?
IsHandle( oVarToTest ); // is the variable an NT style handle?
IsDateTime( oVarToTest ); // is the variable a TOOL date time type?
IsDWord( oVarToTest ); // is the variable a DWORD?
IsNTHandle( oVarToTest ); // is the variable an NT handle?
IsFindHandle( oVarToTest ); // is the variable a "file find handle"?
IsQueue( oVarToTest ); // is the variable a TOOL queue type?
IsStack( oVarToTest ); // is the variable a TOOL stack type?
IsHashTable( oVarToTest ); // is the variable a TOOL map type?
IsConstant( oVarToTest ); // is the variable declared as const?
IsConstCast( oVarToTest ); // is the variable cast as const?
IsDouble( oVarToTest ); // is the variable a double?
IsByte( oVarToTest ); // is the variable a byte?
IsByteArray( oVarToTest ); // is the variable a byte array type?
IsDatabase( oVarToTest ); // is the variable a database connection?
IsReportPage( oVarToTest ); // is the variable a report page?
IsPageRegion( oVarToTest ); // is the variable a report page region?
IsPageTable( oVarToTest ); // is the variable a report page table?
IsColor( oVarToTest ); // is the variable a report page color?
IsMiniDatabase( oVarToTest ); // is the variable a sqlite database wrapper?
IsUnZipper( oVarToTest ); // is the variable a zlib unzipper wrapper?
IsZipMaker( oVarToTest ); // is the variable a zlib zipper wrapper?
IsMiniRowSet( oVarToTest ); // is the variable a SQLite rowset wrapper?
IsFileBuilder( oVarToTest ); // is the variable a file builder object?
IsDelimitedFile( oVarToTest ); // is the variable a delimited file I/O wrapper?
IsExcelExporter( oVarToTest ); // is the variable an Excel workbook
// creator wrapper?
注意(Note):看课(: See the class) CRunTimeTypeInfo
在文件中(in the file)*工具箱(ToolBox.Tool)*用于将所有RTTI API拉入单个TOOL类的类.所示的类示例是所有TOOL框架类的基类,因为所有TOOL类都使用变量类型检查来确保将正确的变量类型从TOOL框架传递到TOOL API函数.(for a class that pulls all the RTTI APIs into a single TOOL class. The class sample shown is the base class for all TOOL framework classes, since all TOOL classes use variable type checks to insure the proper type of variables are passed to TOOL API functions from the TOOL framework.)
工具日期时间操作(TOOL Date Time Operations)
数据处理和报告通常需要处理日期或日期范围.实际上,对于数据处理/报告数据类型,日期时间处理可能是"第二最常见的”,只有字符串数据被更频繁地使用.因为DateTimes是一个很常见的要求,所以TOOL为该类型的值提供了内置的数据类型.并且还提供了一组API函数,这些函数提供了用于基于DateTime的值的最常见的操作类型.下面列出了这些函数,可以在名为的类中找到该函数系列的类包装器(Data processing and reporting often needs to deal with dates or date ranges. In fact, date time processing is likely to be “second most common” for data processing/reporting data types, with only string data being more frequently used. Because DateTimes are such a common requirement, TOOL offers a built in data type for that type of value; and also offers a set of API functions that offer the most common types of operations used for DateTime based values. These functions are listed below and a class wrapper for this family of functions can be found in the class called) CDateTime
可以在(which can be found in the)*工具箱(ToolBox.Tool)*文件.(file.)
dtNow = GetDate();
dtLater = DateAdd( sModPart, dtBase, iValue );
dtEarly = DateSub( sModPart, dtBase, iValue );
iPart = DatePart( sDatePart );
dtNew = DateSetHourPart( dtToChange, iHour );
dtNew = DateSetMinsPart( dtToChange, iMins );
dtNew = DateSetSecsPart( dtToChange, iSecs );
dtNew = DateSetHMS( dtToChange, iHour, iMins, iSecs );
sText = DateToString( dtDate );
dtNew = StringToDate( sText );
bLess = DateCompare( dtBase, dtOther );
iSpan = DateDiff( dtBase, dtOther );
工具文件和目录操作(TOOL File and Directory Operations)
目录相关功能(Directory Related Functions)
目录操作对于某些类别的数据处理应用程序非常普遍.为了启用这些类型的作业,TOOL包括一些特定于那些相关任务的功能.以下类说明了这些类型的功能的用法.(Directory operations are very common for certain classes of data processing applications. To enable these types of jobs, TOOL includes several functions specific to those related tasks. The following classes illustrate the use of these types of functions.)
可在以下网址找到第一个针对TOOL API本部分研究的TOOL类:(The first TOOL class to study for this section of the TOOL API is found in the)*工具箱(ToolBox.Tool)*文件名为(file called) CCurrentDirectory
它在与当前进程目录操作相关的TOOL函数上实现了类包装.当前进程目录通常由那些主要在单个目录中运行并将这些目录用作所有与文件相关的操作的"默认路径根目录"的脚本来设置.(which implements a class wrapper over the TOOL functions related to manipulation of the current process directory. The current process directory is typically set by those scripts that are going to operate primarily from a single directory and use that directory as the “default path root” for all file-related operations.)
下一个与目录有关的TOOL类包装了实际上操纵磁盘目录结构的所有那些函数.有TOOL API函数可用于移动,复制,比较,重命名和删除硬盘上的整个目录树.见(The next directory related TOOL class wraps all those functions that actually manipulate the directory structure of a disk. There are TOOL API functions for moving, copying, comparing, renaming, and deleting entire directory trees on hard disks. See the) CDirectory
中的课程(class in the)*工具箱(ToolBox.Tool)*文件以获取更多信息.(file for more information.)
目录和文件遍历相关功能(Directory and File Traversal Related Functions)
某些数据传输任务需要定期扫描文件和目录结构,作为其处理的一部分.为了支持这些类型的作业,TOOL提供了一组用于遍历目录和文件的功能.这些类型的函数的类包装器可以在(Certain data transport tasks need to periodically scan file and directory structures as part of their processing. To support these types of jobs, TOOL offers a set of functions for traversal of directories and files. Class wrappers for these types of functions can be found in the)*工具箱(ToolBox.Tool)*文件在(file in the) CFind
类.(class.)
有关使用这些类的实际示例,请参考"工具网络驱动器操作"主题中的简单网络文件备份程序.该示例将演示除专用于数据I/O,转换和报告的任务外,TOOL还可用于与系统管理相关的目的.(For a practical example that uses these classes, refer to the simple network file backup program found in the TOOL Network Drive Operations topic. That example will demonstrate how TOOL can be put to use for purposes related to system administration in addition to tasks dedicated to data I/O, transformation and reporting.)
TOOL可以用于严格的数据传输和报告之外的各种任务,这为TOOL语言增加了附加价值,因为TOOL系统具有足够的功能,可用于所有类型的操作.(The fact that TOOL can be used for various tasks outside of strictly data transport and reporting, adds additional value to the TOOL language in that the TOOL system has enough facilities included that allow it to be used for all types of operations.)
文件相关功能(File Related Functions)
TOOL API还提供了用于处理系统文件的文件相关功能. TOOL提供了一组用于检查文件属性的功能,另一组功能用于执行基于文本的文件I/O和TOOL变量的序列化,ini文件I/O和文件解析.可以在以下网址中找到说明API各个部分的类:(The TOOL API also offers file related functions for working with system files. TOOL provides a set of functions for inspecting file attributes, another set of functions for performing both text-based file I/O and for serialization of TOOL variables, ini-file I/O and file parsing. Classes that illustrate each of these portions of the API can be found in the)*工具箱(ToolBox.Tool)*文件.(file.)
见(See the) CFileInfo
在里面(in the)*工具箱(ToolBox.Tool)*类的文件,该类实现了TOOL API中内置的文件属性和文件管理API的类包装器.见(file for a class that implements a class wrapper for the file attributes and file management APIs built into the TOOL API. See the) CTextFile
中的课程(class in the)*工具箱(ToolBox.Tool)*一个TOOL类的文件,该类封装了文本文件I/O的所有TOOL API函数.(file for a TOOL class that encapsulates all the TOOL API functions for text-file I/O.)
最后,请参阅(Finally, see the) CToolDataFile
中的课程(class in the)*工具箱(ToolBox.Tool)*该文件为TOOL变量值的序列化提供了TOOL API的包装.该类使用用于读取和写入TOOL变量的向量接口进行操作.当前TOOL序列化的工作方式是,脚本开发人员将"装载一个向量"及其所有要保存到TOOL数据文件中的变量.矢量元素将按顺序写入数据文件.为了恢复值,脚本仅从文件中读取矢量数据.(file which provides a wrapper over the TOOL API for the serialization of TOOL variable values. The class operates with a vector interface for reading and writing TOOL variables. The way TOOL serialization currently works, is that the script developer will “load a vector” with all the variables they want to save into a TOOL data file. The vector elements will be written in order to the data file. For restoring the values, the script simply reads back the vector data from the file.)
请注意,此时,数据集合(堆栈,队列,地图等)无法自动序列化.但是,如果需求允许这种功能,则可以轻松地将其添加到TOOL运行时引擎中.(Note, that at this time, collections of data (stacks, queues, maps, etc.) can not be serialized automatically. However, if demand warrants this type of functionality, it can easily be added to the TOOL run-time engine.)
TOOL文件I/O处理的最后一个专业领域是INI文件处理.由于INI文件是一种存储配置数据简单层次结构的便捷方法,因此它们仍然是存储应用程序参数的流行方法.因此,为方便起见,TOOL还为INI文件提供了一组功能.提供这些API函数包装器的类是(The final specialized area of TOOL file I/O processing is with regards to INI file processing. Since INI files are a convenient and easy to use method of storing simple hierarchies of configuration data, they are still a prevalent method of storing application parameters. So, as a convenience, TOOL also offers a set of functions for INI files. A class that provides a wrapper over these API functions is the) CIniFile
在中找到的类(class found in the)*工具箱(ToolBox.Tool)*文件.(file.)
工具网络驱动器操作(TOOL Network Drive Operations)
工具提供了一对与连接网络驱动器有关的功能.有一种功能可以连接到网络共享目录,而另一种功能可以从该目录断开连接.示例类名为(TOOL offers a pair of functions that are related to connecting to network drives. There is one function to connect to a networked shared directory and another to disconnect from the same. The sample class named) CNetworkDrive
在里面(in the)*工具箱(ToolBox.Tool)*该文件显示了这些TOOL API函数的类包装.(file shows a class wrapper for these TOOL API functions.)
在里面(In the) CNetworkDrive
类,您将看到TOOL脚本可以直接连接到网络驱动器的方式.这个示例类中还有另一个有趣的项目,那就是(class, you can see the straight-forward manner in which TOOL scripts can connect to network drives. There is another interesting item in this sample class however, and it is the) Connect
方法;具体来说,该测试用于:(method; specifically, the test for:)
sDriveLetter = "ERROR";
这显示了另一个用于字符串操作的重载运算符,其中除了可以直接测试TOOL字符串类型变量的相等性之外,(This shows another overloaded operator for string operations where TOOL string type variables can be directly tested for equality in addition to the) StringCompare
功能.作为如何使用包含在网络驱动器类(和其他类)中的示例(function. As an example of how to use the network drive class (and other classes) included in the)*工具箱(ToolBox.Tool)*文件,以下程序显示了如何编写TOOL程序以执行网络数据文件备份操作:(file, the following program shows how a TOOL program could be written to perform a network data file backup operation:)
Run()
{
CNetworkDrive poNetOut = new CNetworkDrive();
// since the '\' character is an escape character within strings
// i.e., \t for tab
// \r carriage-return
// \n for line-feed
// \" for embedded-quote
//
// we need to use \\ for each \ character that should be made
// part of a string
//
// so in the string below: "\\\\MyServer\\BackupShare"
// will resolve to: "\\MyServer\BackupShare"
//
if ( poNetOut->Connect( "\\\\MyServer\\BackupShare",
"Admin",
"Password" )
{
sNetDrive = poNetOut->GetDriveLetter();
// this example will back up three other computers to
// the single backup server
//
for ( int iLoop = 0; iLoop < 2; iLoop++ )
{
// based on the machine being backed up, set up for
// that copy operation
//
if ( iLoop == 0 )
{
BackUpSingleServer( sNetDrive,
"Workstatation1",
"ImportantWork" );
}
else
if ( iLoop == 1 )
{
BackUpSingleServer( sNetDrive,
"Workstatation2",
"FinanceData" );
}
else
if ( iLoop == 2 )
{
BackUpSingleServer( sNetDrive,
"Workstatation3",
"CustomerData" );
}
}
}
}
// notice the parameter list in the function immediately below.
// the first three parameters are inputs to this function and
// as such are provided by the calling function.
//
// the next two parameters, which follow a ; character, are
// locally scoped variables
//
BackUpSingleServer( sNetDrive,
sServerName,
sServerShare,
; sTargetCopyPath,
sLastBackUpPath )
{
CNetworkDrive poNetIn = new CNetworkDrive();
sTargetCopyPath = StringCopy( sNetDrive );
sLastBackUpPath = StringCopy( sNetDrive );
// set up the target paths for the backup file
// storage
//
sTargetCopyPath += StringFormat( "\\%.Backup",
sServerName );
sLastBackUpPath += StringFormat( "\\%.Prev.Backup",
sServerName );
if ( poNetIn->Connect( StringFormat( "\\\\%\\%",
sServerName,
sServerShare ),
"Admin",
"Password" ) )
{
sInDrive = poNetIn->GetDriveLetter();
// create a directory object to manage the data
// copy
//
CDirectory poDir = new CDirectory( sInDrive );
poDir->CopyDirectory( sTargetCopyPath,
sLastBackUpPath );
if ( poDir->CompareToOther( sTargetCopyPath ) )
{
// file backup success
}
else
{
Echo( FormatString( "Failure during backup of \\\\%\\%.\r\n",
sServerName, sServerShare ) );
}
poNetIn->Disconnect();
}
else
{
Echo( FormatString( "Failed to backup: \\\\%\\%.\r\n",
sServerName, sServerShare ) );
}
}
上面的TOOL程序说明了如何在程序中实例化和利用TOOL语言类对象.该演示程序还展示了如何将简单的网络备份程序写入TOOL,从而展示了TOOL除了可以用作组织中的数据管理和报告系统之外,还可以在组织中多种用途.(The TOOL program above illustrates how to instantiate and make use of TOOL language class objects in a program. This demonstration program also shows how TOOL can be put to multiple uses in an organization in addition to its use as a data-management and reporting system by showing how a simple network backup program can be written in TOOL.)
工具注册表操作(TOOL Registry Operations)
由于TOOL开发人员可能需要使他们的脚本与系统注册表交互,因此TOOL还提供了与主机系统这方面交互的API. TOOL提供的支持足以将注册表用于字符串和数字的I/O.由于几乎所有的TOOL数据类型都可以转换为这两种变量类型,因此TOOL API受到限制,以简化和易于使用(TOOL的重复设计目标).(Since TOOL developers may need to have their scripts interact with the system’s registry, TOOL offers APIs for interacting with this aspect of the host system as well. The support offered by TOOL is enough to use the Registry for I/O of strings and numbers. Since nearly all TOOL data types can be converted to these two variable types, the TOOL API is so constrained for simplicity and ease of use (a recurring design goal for TOOL).)
如果需要其他功能,那么扩展TOOL以支持有关使用系统注册表进行操作的任何新要求将不是一件容易的事.(If additional functionality is required, then it will not be a difficult task to extend TOOL to support any new requirements with regards to operating with the system registry.)
与本文档一样,在以下位置可以找到TOOL API本节的类包装器:(As is typical for this document, a class wrapper for this section of the TOOL API is found in the)工具箱(ToolBox.Tool)文件,并在TOOL类中调用(file and in the TOOL class called) CRegistry
.(.)
工具系统环境操作(TOOL System Environment Operations)
在某些类型的系统灌输中,定期将操作系统的环境变量用于某些目的.对于这些类型的安装中的操作,TOOL提供了允许TOOL脚本读取/写入系统环境变量的功能. TOOL类称为(Periodically, in certain types of system instillations, the operating system’s environment variables are used for certain purposes. For operations in these types of installations, TOOL provides functions that allow TOOL scripts to read/write to the system environment variables. The TOOL class called) CEnvironment
在里面(in the)*工具箱(ToolBox.Tool)*文件说明了TOOL API的这一部分.(file illustrates this portion of the TOOL API.)
工具过程控制功能(TOOL Process Control Functions)
工具提供了用于启动,检查和监视其他过程的功能.这允许TOOL与现有程序互操作,和/或通过对TOOL运行时引擎外部的过程的控制来协调系统的活动. TOOL可以通过以下调用简单地生成辅助进程:(TOOL offers functions for starting, checking-on, and monitoring other processes. This allows TOOL to inter-operate with already existing programs, and/or to coordinate the activities of the system via the control over processes which are external to the TOOL run-time engine. TOOL can simply spawn a secondary process with the following call:)
ProcStartNoWait( sProcessCommandLine );
调用此API时,TOOL运行时会将流程命令行转发给操作系统以执行,然后立即返回,以免等待新启动的流程.一个相关功能是查询操作系统,以确定特定进程是否在承载TOOL运行时的操作系统中运行.该调用如下所示:(When this API is invoked, the TOOL run-time will forward the process-command-line to the operating system for execution and then will return immediately, so as to not wait for the newly-started process. A related function is to query the operating system to determine if a particular process is running in the operating system where the TOOL runtime is hosted. This call is shown below:)
IsProcessRunning( sProcessName );
这两个功能可使TOOL用作易于使用的看门狗程序(使另一个进程保持运行的程序).以下示例说明了此概念:(These two functions can allow TOOL to be utilized as a simple-to-use watchdog program (a program that keeps another process running). The following example illustrates this concept:)
WatchDog( sProcessName, sProcessCmdLine )
{
while ( 1 )
{
if ( !IsProcessRunning( sProcessName ) )
{
// if process is not running, then start it
//
ProcStartNoWait( sProcessCmdLine );
}
//
// since this is an infinite loop, yield control on
// each pass through the loop
//
Sleep( 5000 );
}
}
用于启动外部流程的另一个有趣的TOOL API是:(Another interesting TOOL API for starting external processes is:)
StartLoggedProcess( sProcssesCmdLine, sFullPathToLogFile );
该API将启动一个进程,然后将所有数据从该进程的" stdout"捕获到传递给此函数的日志文件中.对于从TOOL系统运行简单的实用程序,然后将这些实用程序的输出捕获到文件中以供将来进行故障排除或分析,启动过程非常有用.(This API will start up a process and then will capture all data from that process' “stdout” to a log file passed to this function. This means of starting processes is very useful for running simple utility programs from the TOOL system, and then capturing the output from those utilities to a file for future trouble-shooting or analysis.)
下一个用于启动进程的TOOL API是一种允许TOOL脚本从操作系统中为刚启动的进程获取进程句柄的API.然后,可以将该句柄用作"等待句柄",以便TOOL可以在继续之前等待过程完成.(The next TOOL API for starting processes is one that allows a TOOL script to get a process handle from the operating system for the process just started. This handle then can be used as a “wait handle” so that TOOL can wait for the process to complete prior to continuing.)
hHandle = StartProcessGetHandle( sProcessCmdLine );
....
ReleaseHandle( hHandle );
的(The) ReleaseHandle
对进程句柄的兴趣停止时,应从脚本调用call. TOOL提供的最终过程控制功能是允许TOOL脚本监视和分析从属过程输出的功能.调用此API时,TOOL将启动并"附加到"进程的输出.这允许通过TOOL脚本读取和分析从属进程的" std输出"和/或" std错误"流产生的所有输出.执行此操作的API函数如下:(call should be called from the script when interest in the process handle has ceased. The final process control function offered by TOOL is one that allows TOOL scripts to monitor and analyze the output of a slave process. When this API is invoked, TOOL will start and “attach to the output from” a process. What this allows is for all output produced by the slave process' “std output” and/or “std error” streams to be read and analyzed by the TOOL script. The API function that does this follows:)
ProcStartErrorCheck( sRunLine, oVecErrorList );
以下函数说明了如何使用此TOOL API函数:(The following function illustrates how to use this TOOL API function:)
StartProgramAndCheckResults( sCommandLine; oVecErrorList, bSuccess )
{
oVecErrorList = Vector();
bSuccess = Byte( 0 );
// add a series of "error strings" to a list of phrases
// the output from the slave process will be examined against the
// error list. If the slave process produces any of these phrases
// the results of the process execution will be reported as an
// error
//
VectorAddItem( oVecErrorList , "ERROR" );
VectorAddItem( oVecErrorList , "UNAVAILABLE" );
VectorAddItem( oVecErrorList , "DOES NOT EXIST" );
VectorAddItem( oVecErrorList , "INCORRECT SYNTAX" );
VectorAddItem( oVecErrorList , "NO SUCH FILE OR DIRECTORY" );
VectorAddItem( oVecErrorList , "COLUMN DOES NOT ALLOW NULLS" );
VectorAddItem( oVecErrorList , "INVALID OBJECT NAME" );
VectorAddItem( oVecErrorList , "INVALID COLUMN NAME" );
VectorAddItem( oVecErrorList , "CHOOSE ANOTHER" );
VectorAddItem( oVecErrorList , " LEVEL" );
VectorAddItem( oVecErrorList , "FAILED" );
VectorAddItem( oVecErrorList , "FILE NOT FOUND" );
// run a slave process and verify its output.
// NOTE: The TOOL script will pause at this point until the
// slave process ends. Utility type programs that exit
// after completion of a task(s) are the most appropriate
// type of process to start with this TOOL API function.
//
bSuccess = ProcStartErrorCheck( sCommandLine, oVecErrorList );
if ( !bSuccess )
{
// since Throw will exit this, make sure to free all
// memory used here
//
VectorClear( oVecErrorList );
FreeObject( oVecErrorList );
Throw( StringFormat( "The program % returned an error.\r\n",
sCommandLine ) );
}
VectorClear( oVecErrorList );
FreeObject( oVecErrorList );
}
TOOL进程间同步基元(TOOL Inter-Process Synchronization Primitives)
TOOL提供了一套完整的过程同步变量类型.这些变量类型在TOOL脚本程序将通过这些跨过程变量的信号通知与其他外部程序/过程协调动作的安装中很重要.这些变量还将在TOOL多线程脚本中内部使用.(TOOL offers a complete set of process-synchronization variable-types. These variable types are important in those installations where TOOL script programs will coordinate actions with other external programs/processes through the signaling of these cross-process variables. These variables will also be used internally in TOOL multi-threaded-scripts.)
这些变量类型的正确使用的解释超出了本文档的范围.对于这种类型的信息,读者可以参考涉及多线程编程技术的任何参考资料.(The explanation of the appropriate use of these variable-types is beyond the scope of this document. For that type of information, the reader is referred to any reference that deals with multi-threaded programming techniques.)
每种TOOL同步变量类型的类包装都可以在(A class wrapper for each of the TOOL synchronization variable types is found in the)*工具箱(ToolBox.Tool)*文件.您可能要检查的第一个变量类型是信号量的类包装,您可能还记得它是一个进程间变量,该变量与之关联.可以同时由"计数"支持的进程/线程"获取"变量.见(file. The first variable type you may want to examine is a class wrapper for a semaphore, which as you may recall is an inter-process variable that has a “count” associated with it. The variable can be “acquired” simultaneously by as many processes/threads as the “count” supports. See the) CSemaphore
上课(class in)*工具箱(ToolBox.Tool)*对于那个班.(for that class.)
TOOL支持的另一种类型的进程间变量是Mutex.这种类型的变量只能在任何时候被单个进程和/或线程"获取".这种类型的变量是"互斥"门,在整个操作系统中用作全局"单线程控件".该变量类型的TOOL类包装器称为(The next type of inter-process variable supported in TOOL is the Mutex. This type of variable can only be “acquired” by a single process and/or thread at any time. This type of variable is a “mutual exclusion” gate and serves as a global “single threaded control” across the entire operating system. The TOOL class wrapper for that variable type is called) CMutex
可以在(and can be found in the)*工具箱(ToolBox.Tool)*文件.(file.)
TOOL支持的进程间变量的最终类型是事件.事件可以用于在进程和/或线程之间"发送信号",以协调它们之间的活动.正确理解和使用事件可以导致完全独立的流程之间进行强大的交互.的(The final type of inter-process variable supported in TOOL is an Event. An event can be used to “send signals” between processes and/or threads for purposes of coordinating activities between them. Properly understood and used, Events can lead to powerful interactions between completely separate processes. The) CEvent
中的课程(class in the)*工具箱(ToolBox.Tool)*文件实现一个事件类.(file implements an event class.)
既然我们已经介绍了TOOL支持的进程间变量的创建和简单管理,下一个主题(自然地)是如何在"等待集"中使用它们,该等待集的使用允许TOOL与其他外部对象协调其操作.流程.的(Now that we’ve covered the creation and simple management of the inter-process variables supported in TOOL, the next topic (naturally) is how to use them in “wait sets” the use of which allows TOOL to coordinate its actions with other external processes. The) CWaitSet
上课(class in)*工具箱(ToolBox.Tool)*显示了一种使用与TOOL等待相关的方法.(shows one possible way to use the TOOL wait-related.)
使用工具集合对象(Working with TOOL Collection Objects)
TOOL提供了几种数据类型来管理数据收集. TOOL安装中包括示例程序,这些程序说明了如何使用每种集合类型.希望读者在将它们引入集合API函数本身的同时,也将开始更加熟悉TOOL语言中的类声明.(TOOL offers several data types for managing collections of data. Included in the TOOL installation are sample programs that illustrate how to work with each collection type. It is hoped that the reader will start to become more familiar with class declarations in the TOOL language at the same time that they are being introduced to the collections API functions themselves.)
确定任何集合的元素计数(Determining Element Counts of any Collection)
工具提供了(TOOL offers a) SizeOf
函数,用于确定任何集合类型变量中的元素数量.可以将以下类型的变量传递给此函数:(function for determining the number of elements in any collection type variable. The following types of variables can be passed to this function:)
- 串(String)
- 向量(Vector)
- 叠放(Stack)
- 队列(Queue)
- 地图(Map)
- 字节数组(ByteArray) 如下:(as follows:)
lElements = SizeOf( oCollectionTypeVar );
工具字符串操作(TOOL String Operations)
字符串可能是几乎所有数据处理任务中使用的最常见的数据类型.由于这种情况,字符串操作可能是数据处理中最常见的操作类型.(Strings are probably the most common data types used in nearly all data processing tasks. Due to that situation, string manipulation is probably the most common types of operations performed in data processing.)
因此,用于字符串的TOOL API是专用于单个数据类型的"最大的单个部分".下面列出了与字符串相关的API函数的完整列表.由于与字符串相关的API是如此丰富,因此样本类很可能是此发行版随附的样本中包含的最大样本.(Because of this, the TOOL API for Strings is the “largest single section” dedicated to a single data type. The complete list of String-related API functions is listed below. Since the String-related API is as rich as it is, the sample class is likely to be the largest sample included in the samples provided with this distribution.)
iResult = StringCompare( sOne, sTwo ); // returns -1 if sOne < sTwo
// 0 if sOne = sTwo
// 1 if sOne > sTwo
sResult = FillString( sBase, iStartAt, iSpanFor, sFillWith );
iIndex = IndexOf( sBase, sToFind );
sResult = TrimString( sToTrim, sType ); // sType = "l" trim left
// = "r" trim right
// = "b" trim both
iLength = StringLength( sText );
sResult = ToLower( sInput );
sResult = ToUpper( sInput );
sResult = SubString( sInput, iStartAt, iLength );
sResult = StringGetAt( sInput, iIndex );
sResult = StringConcat( sInput, sToAppend );
sResult = StringReplace( sInput, sToReplace, sReplaceWith );
sResult = StringFormat( sFormatString, ... );
sResult = StringBoundedBy( sInput, sLowerToken, sUpperToken );
sResult = BoundedStringReplace( sIn, sLowToken, sUpToken, sNewText );
sResult = TrimQuotes( sInput );
sOutput = StringCopy( sInput );
sResult = PutQuote( sInput );
sResult = PadLeft( sInput, iPadSize );
sResult = PadRight( sInput, iPadSize );
bNumber = IsStringNumeric( sInput );
在基于TOOL String的API函数上实现类包装的一个相当大的TOOL类是(A rather large TOOL class that implements a class wrapper over the TOOL String-based API functions is the) CString
在中找到的类(class found in the)*工具箱(ToolBox.Tool)*文件.该类包含大量功能(由于通常对字符串类型数据执行的操作类型很多),因此它是本文档中迄今为止介绍的最大类.(file. That class contains a large amount of functionality (since there are so many types of operations typically performed on the string type data), so it is the largest one presented so far in this document.)
该类还说明了TOOL提供的一些字符串到其他数据类型的转换函数.即使这样,这也是一个直截了当的课程,因此不会给读者带来任何困难.(The class also illustrates a few of the string-to-other-data-type conversion functions offered by TOOL. Even so, it is a straight-forward class and as such should offer no difficulties to the reader.)
TOOL提供的另一个非常重要的字符串函数是"(Another very important string function offered by TOOL is the “) StringFormat
此椭圆函数非常有用,它可以根据附加到格式字符串中的任意数量的数据值来构建单个字符串.请参见以下示例:(” function. This elliptic function is very useful in building a single string from any number of data values appended into a format string. See the examples below:)
sResult = StringFormat( "Today's data is: %", GetDate() );
sResult = StringFormat( "The value mapped in the under the key % is %",
iKey,
MapFindByKey( oMap, iKey ) );
随着(With the) StringFormat
函数,格式字符串在要在输出字符串中插入下一个格式参数的"字符串值"的每个位置处都包含一个"%“字符.(function, the format string contains a ‘%’ character at each position where the ‘string value’ to the next format argument is to be inserted in the output string.)
要"转义”"%“字符并输出实际的百分号,请使用” %%",它将在该位置的结果字符串中输出单个百分号.(To ‘escape’ the ‘%’ character and output an actual percent sign, use ‘%%’ which will output a single percent character into the resultant string at that location.)
虽然可以传递给参数的参数数量没有实际限制(While there is no practical limit to the number of arguments that can be passed to the) StringFormat
函数,结果字符串的长度限制为4 KB.另一个非常重要的String运算符是'(function, the resultant string is limited to 4-KBytes in length. Another very important String operator is the ‘) +
‘符号,也可用于字符串连接,如下所示:(’ sign which can also be used for string concatenation, as follows:)
sString = "The quick brown fox";
sString += "jumped over the lazy dog";
sString += "and the cow \"jumped\" over the moon";
在上面的示例中,您还可以看到’'字符可用于"转义"应嵌入到结果字符串中的双引号.(In the example above, you can also see that the ‘' character can be used to “escape” a double-quote that should be embedded in the resultant string.)
在工具中标记字符串(Tokenizing Strings in TOOL)
为了对字符串类型变量执行简单的标记化,TOOL提供了一个内置的标记化器.在工具中找到了TOOL的String标记器函数的类包装器(To perform simple tokenization on string type variables, TOOL offers a built in tokenizer. A class wrapper for TOOL’s String tokenizer functions is found in the) CTokenizer
中的课程(class in the)*工具箱(ToolBox.Tool)*文件.(file.)
工具向量运算(TOOL Vector Operations)
工具提供了一组特定于对向量类型变量进行操作的函数.对于所有这些函数,这些函数中的每个函数的第一个参数是应对其执行操作的Vector对象.这些操作将在下面介绍.(TOOL offers a set of functions specific to operating on Vector type variables. For all these functions, the first argument to each of these functions is the Vector object upon which the function should operate against. Each of these operations will be introduced below.)
TOOL Vector对象可以包含对象的"混合集合",这意味着存储在集合中的每个元素可以具有不同的类型.(TOOL Vector objects can contain a “mixed collection” of objects, meaning that each of the elements stored in the collection can be of different types.)
VectorSetAtIndex( oVector, iIndex, vValue );
VectorGetAtIndex( oVector, iIndex );
VectorAddItem( oVector, vValue );
VectorDelAtIndex( oVector, iIndex );
VectorClear( oVector );
见(See the) CVector
文件中的类(class in the file)*工具箱(ToolBox.Tool)*用于为TOOL提供的所有与Vector相关的函数提供类包装的TOOL类.该示例再次说明了如何声明TOOL类,以及为与TOOL矢量相关的功能提供了Vector对象包装的相当完整的实现.(for a TOOL class that provides a class wrapper for all the Vector related functions offered by TOOL. That example illustrates once again how to declare a TOOL class as well as offering a rather complete implementation of a Vector object wrapper for TOOL vector-related functions.)
在检查(In examining the) CVector
在类示例中,可以看到Vector类不仅为所有TOOL向量相关的函数提供了完整的类包装器;但它扩展了(class example, one can see that the Vector class not only provides a complete class wrapper to all TOOL vector related functions; but that it extends the) CRunTimeTypeInfo
在RTTI相关功能的讨论中介绍的课程;并且对基类的调用用于验证在调用TOOL引擎之前传递的参数.对于每个验证调用,您都可以看到cast-call-operator的用法.(class introduced in the discussion of RTTI related functions; and that calls to the base class are used to validate arguments passed prior to making calls into the TOOL engine. For each of these validation calls, you can see the use of the cast-call-operator.)
**注意(Note)**注意:如果对传递给TOOL运行时引擎的变量类型有任何疑问,在调用API函数之前先验证所有参数是一种好方法.如果您将不正确的变量类型传递到TOOL API函数中,则采用这种原理将有助于使您的TOOL脚本更加健壮和可靠,并防止在运行时突然停止脚本.(: It is considered good form to validate all arguments prior to invoking the API functions if there is any doubt as to the type of variable being passed to the TOOL runtime engine. Adopting this philosophy will help to make your TOOL scripts more robust and reliable and will prevent abrupt halts of your scripts by the run-time should you pass incorrect variable types into the TOOL API functions.)
通过类设计技术,可以实现"类型检查"的工具集合,如扩展名所示.(Through class design techniques, it is possible to implement TOOL collections that are “type checked” as is shown in the extension to the) CVector
类称为(class called) CNumberSet
在里面(in the)*工具箱(ToolBox.Tool)*文件.此类显示了一个类型受约束的集合的示例,该类型将仅存储数字的集合.(file. This class shows an example of a type constrained collection that will only store a collection of numbers.)
在里面(In the) CNumberSet
示例类,可以看到(example class, one can see that the) SetAtIndex
和(and) AppendItem
类函数已被覆盖,因此可以对传递给类函数的参数执行RTTI检查.您还可以在其中一些函数调用(包括类构造函数)中再次看到cast-call-operator的用法.(class functions have been overridden so that RTTI checks can be performed on the arguments passed to the class functions. You can also see the cast-call-operator usage again in several of these function calls including the class constructor.)
除此之外(In addition, the) CNumberSet
该类还说明了三个更专业的Vector运算的用法;特别是(class also illustrates the use of three more specialized Vector operations; specifically the) Max
,(,) Min
和(, and) Avg
TOOL API函数可对Vector变量中包含的所有数字进行运算,并从该运算中返回适当的值.(TOOL API functions which operate against all the numbers contained in a Vector variable and return the appropriate value from the operation.)
工具字节数组操作(TOOL Byte Array Operations)
工具数据收集变量类型之一是字节数组变量类型.此类型类似于本文档另一部分中介绍的Vector类型,但有一个非常重要的区别:“字节数组"仅存储字节数组.此类可用作形成字节值的特定排列的"缓冲区类”.(One of the TOOL data collection variable types is the Byte Array variable type. This type is similar to the Vector type explained in another section of this document, with one very important difference: the Byte Array only stores arrays of bytes. This class is useful as a “buffer class” for forming particular arrangements of byte values.)
在查看下面显示的功能列表时,您可以看到TOOL API提供的一些与字节数组相关的功能.您可以看到(In looking over the list of functions shown below, you can see some of the Byte Array related functions offered by the TOOL API. You can see that the) ByteArrayAppend
和(and) ByteArrayInsertAtIndex
函数具有几种不同的重载,并且可以使用多种类型的形式参数.由于TOOL引擎的面向堆栈设计,这种类型的函数重载是可能的,并且在有意义的情况下提供重载实现时可以使用该技术.(functions have several different overloads and can work with several types of formal arguments. This type of function overloading is possible due to the stack-oriented design of the TOOL engine, and the technique is used when it makes sense to offer an overloaded implementation.)
ByteArrayGetAtIndex( oByteArray, iIndex );
ByteArraySetAtIndex( oByteArray, iIndex, bValue );
ByteArrayAppend( oByteArray, bAByte );
ByteArrayAppend( oByteArray, sAString );
ByteArrayAppend( oByteArray, oAnotherByteArray );
ByteArrayInsertAtIndex( oByteArray, iIndex, bAByte );
ByteArrayInsertAtIndex( oByteArray, iIndex, sAString );
ByteArrayInsertAtIndex( oByteArray, iIndex, oAnotherByteArray );
ByteArrayDelAtIndex( oByteArray, iStartDeletAtIndex, iCountToRemove );
ByteArrayClear( oByteArray );
班级(The class) CByteArray
在(in)*工具箱(ToolBox.Tool)*该文件是TOOL提供的字节数组变量类型API函数的示例类包装器.在类示例中,您将再次看到使用的强制转换操作符和RTTI函数.在复习了Byte Array类包装器之后,并通过研究了(file is a sample class wrapper for the Byte Array variable type API functions offered by TOOL. In the class example, you will again see the use of the cast-call-operators and RTTI functions being used. After a review of the Byte Array class wrapper, and through studying the other classes in the)*工具箱(ToolBox.Tool)*文件,您应该开始对如何使用TOOL语言构造一个类有一个很好的主意.(file, you should start to get a pretty good idea as to how to construct a class in the TOOL language.)
工具堆栈操作(TOOL Stack Operations)
TOOL为可以从这种LIFO集合类型中受益的脚本提供了Stack对象.与向量类型一样,堆栈可以在单个集合中存储各种变量类型.堆栈是一种简单的集合类型,因此与其他工具集合类型相比,专用于堆栈的API函数的数量受到更多限制.请参阅以下列表:(TOOL offers a Stack object for those scripts that can benefit from such a LIFO collection type. Like the Vector type, the Stack can store various variable types in a single collection. A Stack is rather a simple collection type, so the number of API functions dedicated to Stack is more limited than for other TOOL collection types. See the list below:)
StackPush( oStack, oToPush );
StackPop( oStack );
StackPeek( oStack );
StackClear( oStack );
再次参考(Refer again to the)*工具箱(ToolBox.Tool)*的档案(file for the) CStack
类,它是一个简单的TOOL堆栈类包装器,如图所示,说明了TOOL中所有与堆栈相关的函数的用法.如您所见,由于与Stack相关的API函数受Stack变量类型的直接特性的限制,因此该Stack类相对简单.(class, which is a simple TOOL Stack class wrapper as shown to illustrate the use of all the stack related functions in TOOL. As you can see, this Stack class is relatively simple due to the fact that the Stack related API functions are limited by the straight-forward nature of the Stack variable type.)
工具图操作(TOOL Map Operations)
当前,TOOL只提供一种类型的关联容器; Map,它为键值对提供了一个简单的存储容器. TOOL Map数据类型限制了可用作集合键的变量类型.但是,与所有其他TOOL容器一样,该容器中存储的值可以是任何TOOL值类型,并且多个变量类型可以存储在单个映射中.如果试图将非法密钥类型与TOOL Map一起使用,则TOOL Run-Time-Engine将停止执行TOOL脚本.(Currently, TOOL offers only one type of associative container; the Map which allows a simple storage container for keyed-value pairs. The TOOL Map data type constricts the types of variables that can be used as keys into the collection; but like all other TOOL containers, the values stored in the container can be any TOOL value type, and multiple variable types can be stored in a single map. If an illegal key type is attempted to be used with the TOOL Map, the TOOL Run-Time-Engine will halt execution of the TOOL script.)
像其他TOOL数据集合一样,Map容器被设计为简单明了,因此与Map相关的功能的数量仅限于使用数据类型所需的那些功能.下面列出了与Map相关的API调用:(Like other TOOL data collections, the Map container is designed to be simple and straight-forward, so the number of Map related functions is limited to those functions required to work with the data type. The list of Map related API calls is listed below:)
MapRemoveKey( oMap, aKey );
MapFindByKey( oMap, aKey );
MapHasKey( oMap, aKey );
MapClear( oMap );
有关TOOL Map类包装器的示例实现,请参见(For an example implementation of a TOOL Map class wrapper, see the) CMap
中的课程(class in the)*工具箱(ToolBox.Tool)*文件.为了实施(file. In order to implement a) CMap
仅限于一个键或值类型的类,则需要修改(class that is constrained to a single key or value type, one would need to revise the) IsLegalKeyType
和/或(and/or the) IsLegalValueType
类方法,以对允许存储在基础Map变量中的键和值提供一组更具限制性的测试.(class methods to provide a more restrictive set of tests on the keys and values allowed to be stored in the underlying Map variable.)
工具队列操作(TOOL Queue Operations)
TOOL提供的另一个经典数据收集是Queue数据类型.工具队列是许多编程项目中常用的FIFO数据收集的实现.整个与队列相关的API如下所示:(Another classic data collection offered by TOOL is the Queue data type. The TOOL Queue is an implementation of the FIFO data collection commonly used in many programming projects. The entire Queue related API is shown below:)
EnQueue( oQueue, oToPush );
DeQueue( oQueue );
QueueClear( oQueue );
Queue类的TOOL类实现是(The TOOL class implementation of a Queue class is the) CQueue
中的课程(class in the)*工具箱(Toolbox.Tool)*文件.像Stack数据类型一样,TOOL Queue实现非常简单,不需要大量功能即可实现.从提供的样本中,您还可以看到使用TOOL队列有多么容易.(file. Like the Stack data type, the TOOL Queue implementation is straightforward and does not require a large amount of functions to implement. From the samples provided, you can also see how easy it is to work with a TOOL queue.)
工具应用程序运行时环境(TOOL Application Runtime Environment)
可以将参数传递到TOOL运行时上下文中,以在该TOOL程序的持续时间内用作"环境变量".通过以下说明的调用,TOOL脚本可以访问这些变量.(Parameters can be passed into the TOOL runtime context for use as “environment variables” for the duration of that TOOL program. TOOL scripts can gain access to these variables with the calls explained below.)
的(The)*运行工具(RunTool.exe)*可以使用命令行参数调用程序,该参数将转发到TOOL脚本的运行时上下文.请参阅以下示例,了解如何完成此操作:(program can be invoked with command-line-arguments that will be forwarded to a TOOL script’s runtime context. See the following example for how this could be done:)
对于在TOOL运行时环境中运行的其他TOOL程序,有两个附加的API调用可用于获取和设置程序上下文中的变量.要从应用程序服务器环境中获取变量,请使用"(For other TOOL programs that are running in a TOOL run-time environment, there are two additional API calls that can be used to get and set variables within the program context. For fetching a variable from the application server environment, use the API “) GetAppEnvField
“."(”. An important distinction between “) GetAppEnvField
“和”(” and “) GetProperty
" 就是它 “(” is that “) GetProperty
“将始终返回字符串类型的变量,而”(” will always return a string type variable, whereas “) GetAppEnvField
“可以返回以下类型的变量:(” can return variables of the following types:)
- 串(String)
- 长(Long)
- 字节(布尔值)(Byte (boolean))
- 双(Double)
- 双字(DWORD)
- 约会时间(DateTime)
- 字节数组(ByteArray)
取决于通过调用” API"存储在应用程序上下文中的数据类型(depending on the type of the data that was stored in the application context with the call to the API “)
SetAppEnvField
“.(请注意,上面的变量类型列表是唯一可以通过”(”. (Note that the above list of variable types are the only ones that will be allowed to be stored in the application context through the “)SetAppEnvField
“.如果有其他类型的变量传递给”(” call. If any other type of variable is passed to the “)SetAppEnvField
“函数,将导致运行时错误并终止TOOL脚本的执行.)下面的示例演示如何使用这些API函数.(” function, a run-time error will result and execution of the TOOL script will be terminated.) The sample below shows how these API functions may be used.)
// store the variables in the application context
//
SetAppEnvField( "THE_STRING", "Hello" );
SetAppEnvField( "THE_NUMBER", 1234567 );
SetAppEnvField( "FALSE_BOOL", Byte( 0 ) );
SetAppEnvField( "TRUE_BOOL", Byte( 1 ) );
SetAppEnvField( "DOUBLE_VAL", 1234.5678 );
SetAppEnvField( "NEW_YEARS", DateTime() );
// retrieve varialbes from the application context
//
xFetched = GetAppEnvField( "THE_STRING" );
xFetched = GetAppEnvField( "DOUBLE_VAL" );
参见"工具类”(See the TOOL class “) CAppEnvironment
“在文件中(” in the file)*工具箱(ToolBox.Tool)*用于实现应用程序环境接口的类.(for a class that implements the application environment interface.)
工具数据库操作(TOOL Database Operations)
由于TOOL的主要目的之一是用于与数据收集,操作和数据库数据存储有关的任务,因此TOOL必须提供数据库I/O系统. TOOL API本部分的目的是提供足够的功能来"完成任务”,同时尝试避免许多不必要的复杂性.(Since one of the primary purposes envisioned for TOOL is use in tasks related to data gathering, manipulation and storage of database data, TOOL must offer a database I/O system. The goal of this section of the TOOL API was to provide enough functionality to “get the job done” while attempting to avoid a lot of unnecessary complexity.)
TOOL用于连接数据库的接口方法是所有主要DBMS供应商提供的ODBC驱动程序.这样做的目的是,此级别的接口是所有数据库连接的最低公分母(实际上,通常甚至将更多的"高级接口"构建为ODBC上的附加代码层).此外,这是目前无处不在的技术,因此将允许TOOL在平等的基础上"几乎连接到任何东西”.(The interface method used by TOOL to connect to databases is the ODBC drivers offered by all major DBMS vendors. The purpose for this is that this level of interface is the lowest-common-denominator for all database connections (in fact even more ‘advanced interfaces’ are typically built as additional code layers over ODBC). In addition, it is a ubiquitous technology at this point and therefore will allow TOOL to “connect to virtually anything” on an equal basis.)
在内部,TOOL具有复杂的ODBC包装技术,该技术允许访问要处理的数据库结果,而TOOL程序员无需处理动态绑定到TOOL作业一部分的"任何可能的查询"结果的"繁琐细节” .(Internally, TOOL has a sophisticated ODBC wrapper technology that allows access to database results to be processed without the TOOL programmer having to deal with the ‘grungy details’ of dynamically binding to the results of “any conceivable query” that are a part of TOOL jobs.)
TOOL提供的数据库功能旨在简单,直接地应用数据库I/O. TOOL脚本和连接的数据库之间的"命令接口"是通过SQL字符串通过SQL字符串通过TOOL管理的数据库连接传递给DBMS的.再次,主要设计考虑因素是在所有数据库类型/供应商之间易于使用和可移植性,而不是为单个DBMS供应商构建过于紧密的工具.下面的示例代码显示了TOOL提供的数据库API的最简单用法:(The database functions offered by TOOL are designed for a simple and straight-forward application of database I/O. The “command interface” between TOOL scripts and the connected databases is through SQL-strings passed to the DBMS via the TOOL-managed database connections. Again, the primary design consideration is for ease of use and portability between all database types/vendors rather than to build TOOL too tightly to a single DBMS vendor. The sample code below shows the simplest possible usage of the database API offered by TOOL:)
TestDB(;oDB,iTimeOut,iColumnCount,iLoop,oValue)
{
oDB = Database();
iTimeOut = Long( 200 );
iColumnCount = Long( 0 );
iLoop = Long( 0 );
oValue = Long( 0 );
if ( DBOpen( oDB, "DSN", "DB_USER", "DB_PASS", "USE_DB" ) )
{
DBSetLogonTimeOut( oDB, iTimeOut );
DBSetQueryTimeOut( oDB, iTimeOut );
if( DBExecQuery( oDB, "select * from TABLE" ) )
{
if ( !DBIsEmptySet( oDB ) )
{
iColumnCount = DBGetColumnCount( oDB );
DBMoveToStart( oDB );
while ( !DBIsEOF( oDB ) )
{
DBFetchRow( oDB );
for ( iLoop = 0; iLoop < iColumnCount; iLoop++ )
{
oValue = DBGetFieldByIndex( oDB, iLoop );
// do something here with the data value
//
if ( IsByteArray( oValue ) )
{
FreeObject( oValue );
}
}
DBMoveNext( oDB );
}
}
}
}
DBClose( oDB );
FreeObject( oDB );
}
在下面的示例中将包括一个示例数据复制程序,而不是在TOOL数据库API上提供类包装程序,因为它说明了该TOOL API这部分的更完整和"实际"的应用程序.(Rather than present a class wrapper over the TOOL database API, a sample data replication program will be included in the following sample as it illustrates a more complete and ‘real-world’ application of this part of the TOOL API.)
///////////////////////////////////////////////////////////
//
// entry point for this TOOL program
//
Run()
{
SetupGlobals();
if ( ConnectToSource() && ConnectToTarget() )
{
if ( PrepareSourceForDataMove() && PrepareTargetForDataMove() )
{
MoveData();
PostSourceDataMove();
PostTargetDataMove();
}
}
CleanUp();
}
///////////////////////////////////////////////////////////
//
// set up globals for this TOOL program
// recall that all variables default to global scope unless
// they are explicitely declared as local variables in a
// functions formal declaration
//
SetUpGlobals()
{
iTimeOut = Long( 200 );
oSourceDB = Database();
oTargetDB = Database();
sSourceDSN = String( "ODBC.DSN" );
oSourceUID = String( "USER" );
oSourcePWD = String( "PASS" );
oSourceDB = String( "DBNAME" );
sTargetDSN = String( "ODBC.DSN" );
oTargetUID = String( "USER" );
oTargetPWD = String( "PASS" );
oTargetDB = String( "DBNAME" );
sSourceQuery = String( "select * from T_SOME_TABLE" );
sTargetTable = String( "T_TARGET_TABLE" );
}
///////////////////////////////////////////////////////////
//
// tear down after the job is completed
//
CleanUp()
{
DBClose( oSourceDB );
FreeObject( oSourceDB );
DBClose( oTargetDB );
FreeObject( oTargetDB );
}
///////////////////////////////////////////////////////////
//
// use the global vars to connect to the source database
//
ConnectToSource()
{
if ( DBOpen( oSourceDB,
sSourceDSN,
sSourceUID,
sSourcePWD,
sSourceDB ) )
{
DBSetLogonTimeOut( oSourceDB, iTimeOut );
DBSetQueryTimeOut( oSourceDB, iTimeOut );
return( Byte( 1 ) );
}
return( Byte( 0 ) );
}
///////////////////////////////////////////////////////////
//
// use the global vars to connect to the target database
//
ConnectToTarget()
{
if ( DBOpen( oTargetDB,
sTargetDSN,
sTargetUID,
sTargetPWD,
sTargetDB ) )
{
DBSetLogonTimeOut( oTargetDB, iTimeOut );
DBSetQueryTimeOut( oTargetDB, iTimeOut );
return( Byte( 1 ) );
}
return( Byte( 0 ) );
}
///////////////////////////////////////////////////////////
//
// stub functions that could be used for more complex data
// replications that require additional set up prior to a
// data move and/or post-data-move operations
//
PrepareSourceForDataMove()
{
return( Byte( 1 ) );
}
PrepareTargetForDataMove()
{
return( Byte( 1 ) );
}
PostSourceDataMove()
{
}
PostTargetDataMove()
{
}
///////////////////////////////////////////////////////////
//
// here is a simple sample of a data move operation where
// data is selected from the source, then for each column
// of data returned from the data query, an insert statement
// is built up for the target table
//
// it is worth noting that any additional amount of processing
// could be performed as part of preparing the data for the
// target. In addition, it is possible that 'source data' could
// arrive from multiple input locations
//
MoveData( ;oValue, iColumnCount, iLoop, sTargetInsert, bInTran )
{
iLoop = Long( 0 );
iColumnCount = Long( 0 );
sTargetInsert = String( 255 );
// fetch some chunk of data from the data source
//
if ( DBExecQuery( oSourceDB, sSourceQuery ) )
{
if ( !DBIsEmptySet( oSourceDB ) )
{
// if the query returned any data
//
iColumnCount = DBGetColumnCount( oSourceDB );
// set the source result cursor to the start of
// the result set
//
DBMoveToStart( oSourceDB );
// while there is still data that has not been
// processed
//
while ( !DBIsEOF( oSourceDB ) )
{
if ( !DBIsOpen( oSourceDB ) )
{
// the source connection has failed?
//
Echo( "Source Connection Unexpectedly closed\r\n" );
return;
}
// pull the result set data into TOOL local
// buffers
DBFetchRow( oSourceDB );
// start to build up an insert string for the
// target table that will be dynamically built
// from the input data
//
sTargetInsert = "insert into ";
sTargetInsert += sTargetTable;
sTargetInsert += " values ( ";
// this is one way to get data from query results
// in this example, we walk over the data column
// by column
//
for ( iLoop = 0; iLoop < iColumnCount; iLoop++ )
{
oValue = DBGetFieldByIndex( oSourceDB, iLoop );
// now, based on the data type for the current
// column append to the target-table insert string
//
if ( IsNull( oValue ) )
{
sTargetInsert += "null ";
}
else
if ( IsNumber( oValue ) )
{
sTargetInsert += IntToString( oValue );
}
else
if ( IsString( oValue ) )
{
// strings in insert statements must be wrapped
// in quotes
//
sTargetInsert += "'";
sTargetInsert += oValue;
sTargetInsert += "'";
}
else
if ( IsDateTime( oValue ) )
{
// see above comment for string types
//
sTargetInsert += "'";
sTargetInsert += DateToString( "%x %X", oValue );
sTargetInsert += "'";
}
else
if ( IsDouble( oValue ) )
{
sTargetInsert += DoubleToString( oValue, 10 );
}
// if not the last column, then put a comma field
// seperator in the insert string
//
if ( iLoop < iColumnCount - 1 )
{
sTargetInsert += ", ";
}
}
sTarget += " )";
bInTran = Byte( 0 );
if ( !DBIsOpen( oTargetDB ) )
{
// the source connection has failed?
//
Echo( "Target Connection Unexpectedly closed\r\n" );
return;
}
// if target dbms can transact, then start a transaction
//
if ( DBCanTransact( oTargetDB ) )
{
bInTran = Byte( 1 );
DBBeginTran( oTargetTB );
}
// send the insert string to the target dbms
//
if ( DBExecQuery( oTargetDB, sTargetInsert ) )
{
// if insert worked, and in a transaction, then
// issue a commit
//
if ( bInTran )
{
DBCommitTran( oTargetDB );
}
}
else
{
// query failed....if in transaction, then rollback
//
if ( bInTran )
{
Echo( StringFormat( "\r\nWARNING! Query\r\n % \r\n FAILED",
sTargetInsert ) );
DBRollbackTran( oTargetDB );
}
}
DBMoveNext( oSourceDB );
}
}
}
}
上面的程序说明了用TOOL编写的数据复制程序的实际示例.您可以看到TOOL提供的数据库接口足够完整,可以完成许多典型的数据库I/O操作所需的99%以上的任务;甚至仍然保持简单明了.再次,TOOL设计的驱动因素是严格专注于专用数据处理语言所需的工具,而不会因"特殊情况的特殊要求"而陷入困境,因为大多数项目并没有增加重大价值,只会使复杂化几乎所有实际应用中的任务.上面的示例未涵盖一些其他的TOOL数据库API函数.这些是:(The above program illustrates a practical example of a data replication program written in TOOL. You can see that TOOL offers a database interface that is complete enough to complete better than 99% of the tasks required for many typical database I/O operations; even still managing to remain simple and straightforward. Again, the driving factor in the design of TOOL is to focus strictly on the tools required by a specialized data processing language while not getting bogged down in ‘corner case special requirements’, for most projects do not add significant value and only serve to complicate the task at hand in nearly all practical applications. There are some additional TOOL database API functions not covered in the example above. These are:)
bIsAtBeginning = DBIsBOF( oDB ); // returns true if at start of records
DBMovePrev( oDB ); // moves data cursor back one row
DBMoveToEnd( oDB ); // moves data cursor to end of records
DBMoveAheadBy( oDB, iMoveBy ); // moves data cursor ahead by n
DBMoveBackBy( oDB, iMoveBy ); // moves data cursor back by n
DBMoveFromStartBy( oDB, iIndex ); // move data cursor n away from start
DBMoveFromEndBy( oDB, iIndex ); // move data cursor n away from end
DBGetFieldByName( oDB, sFieldName ); // allows record column to be
// moved into TOOL script variable
// using the column name as a key
// to look up the field by
工具内存管理功能(TOOL Memory Management Functions)
释放工具对象(Freeing TOOL Objects)
脚本完成对它们的使用后,必须丢弃某些TOOL变量类型.这些变量类型是:(There are some TOOL variable types that must be disposed off when the script has completed use of them. These variable types are:)
- 分词器(Tokenizer)
- 叠放(Stack)
- 队列(Queue)
- 地图(Map)
- 计算器(Calculator)
- 字节数组(ByteArray)
- 向量(Vector)
- 数据库(Database)
注意(Note):预计将来的TOOL版本将托管一个改进的内存管理器,这将消除释放TOOL变量的需要,但是目前,通过调用API销毁变量很简单(: Future versions of TOOL are expected to host an improved memory manager, that would eliminate the need to free TOOL variables, but for now, it is simple to destroy variables with a call to the API)
FreeObject
如下:(as follows:)
FreeObject( oSomeVar );
注意(Note):如果(: If) FreeObject
使用不需要销毁的变量类型调用,不会有任何问题.(is called with a variable type not requiring destruction, no problem will result.)
太好了,但我只想从头开始(That’s Great, But I Just Want to Scratch Around on the Surface)
我们已经讨论了如何将TOOL集成到您的应用程序中.希望您会同意这项任务相当容易完成.下一步自然是发现如何扩展TOOL以适合您的特定目的.我们在上面已经涉及了两个方面,但是在这里我们将深入探讨该主题.有很多添加到工具上的方法,但是以我的经验,有些扩展类型可以使用一些提示来提示如何完全实现扩展.我将在这里介绍的领域是:(We’ve already discussed how to integrate TOOL into your application. Hopefully, you will agree that the task is rather easy to pull off. The next natural step is to discover how to extend TOOL to suit your specific purposes. We’ve touched on a couple of these areas above, but here we’ll delve into the topic to the next level. There are countless ways to add on to TOOL, but in my experience, there are some types of extensions that could use some hints as to how to pull off an extension completely. The areas I’ll cover here are:)
- 添加新的操作码(Adding New Op-Codes)
- 添加新的变量类型(Adding New Variable Types)
- 添加新的内部功能(Adding New Intrinsic Functions) 对于这些概述,我将总结需要扩展的代码库领域.我认为我没有足够的空间做更多的事情,因此希望所提供的信息足以使您入门.(For these overviews, I will summarize the areas of the code base that will need to be extended. I don’t think I have space to do much more than that, so hopefully the information provided will be enough to get you started.)
向系统添加新的操作码是扩展工具的最复杂动作之一.在您对动作的所有含义有非常深刻的了解之前,请勿尝试执行此操作.我已经做了一两次,这涉及大量的头部刮伤.但是,如果必须这样做,则使用以下准则添加新的操作码,需要进行以下更改:在以下位置声明操作码(Adding new op-code to the system is among the most complex actions one can do to extend TOOL. Do not attempt to do this until you have a very firm grasp on all the implications of your action. I’ve done this once and twice and it involves a serious amount of head scratching. But if you must to this, then use the following guidelines to add new op-codes, the following changes are needed: declare the op-code in)*VMCoreGlobal.h(VMCoreGlobal.h)*在/第597行附近;将条目添加到otab表中(at/around line 597; add an entry to the otab table in)VMCoreInterpreter.cpp(VMCoreInterpreter.cpp)在第100行左右(在跟踪/解码的运行期间转储操作码时使用此表);新增一个(at/around line 100 (this table is used when dumping op-codes during traced/decoded runs); add a new) switch
/(/) case
在解释器的主循环中第349行/左右插入新的操作码,并在其中放入处理操作码所需的任何特定代码(for the new op-code in the interpreter’s main loop at/around line 349 and put any specific coded needed to handle the op-code in that) case
块;添加新代码以根据需要弹出操作码(block; add new code to eject the op-code as needed in)*VMCoreScannerParser.cpp(VMCoreScannerParser.cpp)*在180号线附近如果您的新操作码还会向系统添加新令牌,则需要在(at/around line 180; if your new op-code will also add a new token to the system, then you’ll need to define that token in)*VMCoreGlobal.h(VMCoreGlobal.h)*在109行/周围;您还需要扩展(at/around line 109; you’ll need to also extend)*VMCoreScannerParser.cpp(VMCoreScannerParser.cpp)*在180号线附近最后将任何适当的代码添加到(at/around line 180; and finally add any appropriate code to the) VMCoreCompiler
构建适合于新令牌的操作码数组值.(to build op-code array values appropriate to the new token.)
向工具系统添加新的变量类型是另一项涉及的操作,但不如添加新的操作码那么多.这里要记住的关键点是,您可能需要扩展某些解释器操作码处理程序的操作,以使它们了解新的变量类型以及如何实现它们.这暗示了上面有关运算符重载的部分.牢记这一点,以下是将新变量类型添加到TOOL所需的步骤:在以下位置添加新的数据类型声明(Adding new variable types to the TOOL system is another involved operation but not nearly as much as adding new op-codes. The key points to remember here are that you may need to extend the operation of certain interpreter op-code handlers so that they are aware of your new variable types as well as how to implement them. This alluded back to the section on operator overloading above. With that notice in mind, these are the steps required to add a new variable type to TOOL: Add a new data-type declaration in)VMCoreGlobal.h(VMCoreGlobal.h)在202号线/周围;如果您的新数据类型将作为C ++类实现,则在(at/around line 202; if your new data-type will be implemented as a C++ class, then forward declare that class in)VMCoreGlobal.h(VMCoreGlobal.h)在第243行附近;适当修改(at/around line 243; modify, as appropriate, the) VMVariant
(用于所有TOOL变量的变量类)构造函数((the variant class used for all TOOL variables) constructor in)VMCoreGlobal.h(VMCoreGlobal.h)在305号线/周围;适当修改(at/around line 305; modify, as appropriate, the) ResetValue()
方法中(method in)VMCoreGlobal.h(VMCoreGlobal.h)在319号线/周围;适当修改(at/around line 319; modify, as appropriate, the) VMVariant
联合(union in)VMCoreGlobal.h(VMCoreGlobal.h)在第369行附近;适当修改(at/around line 369; modify, as appropriate, the) VMVariant
复制构造函数(copy constructor in)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)在76号线/周围;添加新的内部函数以实例化/构造新的变量类型,请参见(at/around line 76; add a new intrinsic function to instantiate/construct the new variable type, see)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)在第207行处/周围如何声明实例化处理程序;也看到了(at/around line 207 for how to declare the instantiation handler; also see the) HandlerNewString()
方法中(method in)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)在第1841行/周围,获取有关如何实现实例化处理程序的一些信息;为您的新数据类型添加新的RTTI类型处理程序,请参见(at/around line 1841 for some information on how to implement the instantiation handler; add a new RTTI type handler for your new data type, see)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)有关如何声明新类型检查功能的第308行,请参见方法(at/around line 308 for how to declare your new type check function, see the method) HandlerIsByteArray()
在(in)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)在第1234行处/周围,以了解如何实现新类型检查功能;并声明并添加任何特定于新变量类型的新内部函数,请参见(at/around line 1234 for how to implement your new type checking function; and declare and add any new intrinsic functions specific to your new variable type, see)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)对于此步骤的一些示例,请在第238行处/周围.如果您的新变量受到运算符重载的影响,那么您可能还需要扩展(at/around line 238 for some examples of this step. If your new variable is subject to operator overloading, then you’ll also possibly need to extend the) VMCoreInterpreter
适用于您的新变量类型的逻辑,数组索引和数学处理程序的类op-code-switch处理程序.可能受到影响的处理程序(如果有)如下:(class op-code-switch handlers for logical, array index, and mathematical handlers as appropriate for your new variable type. The handlers that are likely to be affected (if any) are the following:) OP_NOT
;(;) OP_NEG
;(;) OP_ADD
;(;) OP_SUB
;(;) OP_MUL
;(;) OP_DIV
;(;) OP_REM
;(;) OP_BAND
;(;) OP_BOR
;(;) OP_XOR
;(;) OP_BNOT
;(;) OP_SHL
;(;) OP_SHR
;(;) OP_LT
,(,) OP_LE
;(;) OP_EQ
;(;) OP_NE
;(;) OP_NE
;(;) OP_GE
;(;) OP_GT
;(;) OP_INC
;(;) OP_DEC
;(;) OP_VREF
;(;) OP_VSET
.(.)
接下来,我们将看到如何向TOOL添加新的内在函数.您会发现,这是将引擎扩展到此处讨论的所有方法之外的最简单方法.执行此操作时所要执行的操作是,将新的函数指针添加到解释器的函数指针到内部函数的映射器中.只要解释器识别出内部函数的名称,就会为其建立调用堆栈(将所有参数放在从左到右排列的"调用堆栈"上),然后通过指向它的指针来调用该函数.首先,您需要声明函数,请参见(Next, we’ll see how to add a new intrinsic function to TOOL. You’ll see that this is the easiest way to extend the engine out of all the ways discussed here. What you are doing when you do this is that you are adding a new function pointer to the interpreter’s map of function pointers to intrinsic functions. Whenever the interpreter recognizes the name of an internal function, it builds up the call stack for it (putting all arguments on the “call stack” ordered from left to right) and then invokes the function via the pointer to it. First, you’ll need to declare the function, see)VMCoreVirtualOpSys.h(VMCoreVirtualOpSys.h)例如,在第100行之后的任何位置.您会很快看到所有内部函数共享一个共同的签名.接下来,您需要通过调用将该处理程序添加到内部函数的内部列表中(anywhere after line 100 for an example of this. You’ll quickly see that all intrinsic functions share a common signature. Next, you’ll need to add the handler to the internal list of intrinsic functions with a call to) ConfigureBuiltInFunction()
.看到(. See)*VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)*从200行开始的许多示例. configure函数的两个参数是:(starting at line 200 for many examples of this. The two arguments to the configure function are:)
- 该功能的"名称",将出现在脚本文件中;(the “name” of the function as it will appear in script files;)
- 指向该处理程序实现的函数指针.(the function pointer to your implementation of that handler.)
回想一下上面的讨论,解释器在调用内部函数之前立即"建立了一个调用栈".然后,处理程序必须(如果考虑到安全性考虑实施)必须验证传递给处理程序的值的数量和类型.让我们来看看(Recall from the discussion above that the interpreter “built up a call stack” immediately prior to invoking the intrinsic function. The handler must then (if it were implemented with safety in mind) validate the number and types of values passed to the handler. Let’s examine)
HandlerStringCompare()
在(in)VMCoreVirtualOpSys.cpp(VMCoreVirtualOpSys.cpp)有关如何验证入站参数的一些示例,请参见第4311行.参数(at/around line 4311 for some examples of how to validate the inbound arguments. The parameter)iArgc
是解释器放入函数调用堆栈的参数数量.因此,如果内部函数对应该传递的参数数量有期望,则应首先验证该事实.您可以看到,只需致电即可轻松完成(is the count of arguments the interpreter placed on to the call-stack for the function. So, if the intrinsic function has expectations about the number of arguments that should be passed, it should validate that fact first. You can see this is easily done with a call to)VerifyRunLineArguments()
.接下来,如果该函数对传递的变量类型有期望,则应接下来验证这些变量.您也可以致电以下地址轻松检查(. Next, if the function has expectations regarding the types of variables passed, it should validate those next. This can also be easily checked with a call to)VerifyElementType()
.该函数的第一个参数是要检查的参数的"堆栈偏移量",第二个参数是在该堆栈位置处期望的变量类型.回想一下,解释器将从"左到右"加载堆栈,这意味着"函数的第一个参数"将在调用堆栈上具有最高的堆栈偏移,而传递给内在函数的最后一个参数将具有堆栈偏移零在编写自己的验证码时,您需要牢记这一事实.最后,如果您需要将任何值从函数返回给解释器,则需要调用(. The first parameter to this function is the “stack offset” of the parameter to check, and the second argument is the type of variable expected at that stack location. Recall that the interpreter will load the stack from “left to right” which means that the “first parameter to the function” will have the highest stack offset on the call stack and that the last argument passed to the intrinsic function will have a stack offset of zero. You’ll need to keep this fact in mind as you write your own verification code. Finally, if you need to return any value back to the interpreter from your function, you’ll need to do so calling the)SetValue()
与您的结果一起发挥作用.解释器希望函数返回后,函数的所有返回值都将保留在堆栈上.这意味着您将始终将结果返回到堆栈位置:(function with your results. The interpreter expects that any return value from your function will be left on the stack after the function returns. This means that you will always return results to the stack location:)m_poVirtualChip->m_psStackPointer[iArgc]
.解释器还期望该函数将通过适当地调整堆栈指针来"擦除调用帧".这意味着任何内在函数的最后一行可能是:(. The interpreter also expects that the function will “erase the call frame” by adjusting the stack pointer appropriately. This means that the last line in any intrinsic function will likely be:)m_poVirtualChip->m_psStackPointer += iArgc;
这具有"清除调用堆栈"的效果.(which has the effect of “erasing the call stack”.)
编写工具程序(Writing TOOL Programs)
我将引导读者阅读(I’ll refer the reader to the)*CoreClasses.Tool(CoreClasses.Tool)*有关如何使用TOOL语言编写程序的教程的文件.该文件包含许多TOOL样式类,这些类通过TOOL API创建类包装.这组类以及此文件中的功能是我对TOOL的"测试夹具",因此几乎可以在一个脚本中练习所有TOOL API.然后,此脚本文件将成为如何使用TOOL语言进行编程的绝佳示例.此外,它还提供了一组用TOOL编写的类,并提供了一组可放入您自己的脚本中的经过测试且可立即使用的TOOL类.(file for a tutorial on how to write programs in the TOOL language. This file contains a number of TOOL style classes that create class wrappers over the TOOL API. This set of classes, as well as the functions in this file, are my “test jig” for TOOL and as such exercise nearly all of the TOOL API in a single script. This script file then, serves as an excellent example of how to program in the TOOL language. In addition, it also offers a set of classes written in TOOL and which provides a set of tested and ready-to-use TOOL classes that can be put into your own scripts.)
您还将看到TOOL函数定义看起来很像它们的C语言对应.唯一值得注意的区别是,缺少传递给任何函数的参数类型以及函数的返回类型的声明.工具中变量类型的这种"松散"是我将所有RTTI类型函数放入引擎的原因之一.这种类型的未声明变量类型的另一个结果是,任何变量都可以采用任何类型的值.在当前的TOOL实施中,我预见到这可能会导致内存泄漏,因此我尝试解决这种情况.(You’ll also see that TOOL function definitions look a lot like their C counterpart. The only noticeable difference is the lack of a declaration for the type of the parameters passed to any function as well as for the return type of the function. This “looseness” in variable types in TOOL is one of the reasons that I put in all the RTTI type functions into the engine. Another consequence of this type of undeclared variable type is that any variable can take on a value of any type. In the current implementation of TOOL, I foresaw that this could lead to memory leaks, so I attempted to address this situation in the) VMVariant
复制构造函数,如果变量已经包含任何堆分配,它将在采用新值之前将其删除.(copy constructor, where if a variable already contains any heap allocations, it will delete them prior to adopting the new value.)
重要的提示(Important note):即使从脚本编写者的角度来看,TOOL是一种弱类型语言,但TOOL解释器仍将对传递给其内在例程的变量的类型进行类型检查.如果变量的类型不正确,则解释器将停止脚本执行并抛出异常.(: Even though TOOL is a weakly typed language from the script writer’s perspective, the TOOL interpreter will type-check the type of a variable passed into its intrinsic routines. If the variable is not of the right type, the interpreter will halt execution of the script and will throw an exception.)
TOOL和C程序之间的另一个主要区别是TOOL中的"局部变量"出现在函数的参数列表中.局部变量声明以逗号分隔的列表形式出现在函数声明中的分号之后.不在此范围内的临时变量将提升为全局名称空间.尽管TOOL函数可以很好地与全局命名空间中的变量一起使用,但是在脚本执行过程中,它可能导致某些"奇怪而出乎意料的"行为(例如:“该值如何到达那里?").尽管在TOOL中不需要临时变量声明,但是采用这种方法是有用的.(Another key difference between TOOL and C programs is that “local variables” in TOOL appear in a function’s parameter list. Local variable declarations appear in a comma-separated list after a semi-colon in the function declaration. Temporary variables that are not scoped in this fashion are promoted to the global namespace. While TOOL functions will work with variables in the global namespace just fine, it can lead to some “strange and unexpected” behaviors (as in: “how did that value get there?”) during script execution. While temporary variable declarations are not required in TOOL, adopting this approach is a useful one.)
与C ++中一样,TOOL类的新对象使用构造函数初始化,该函数与类本身具有相同的名称.再次,请参阅(As in C++, new objects of a TOOL class are initialized using a constructor function, which has the same name as the class itself. Again, refer to the)CoreClasses.Tool(CoreClasses.Tool)有关如何在TOOL中声明类的几个示例. TOOL类构造函数中的最后一个操作是,它通过”(for several examples of how classes are declared in TOOL. The last operation in a TOOL class constructor is that it returns the new object through “) return( this )
“.对于那些不熟悉C ++的人来说,变量(”. For those of you not familiar with C++, the variable) this
指为其调用成员函数的对象.它是传递给每个非静态成员函数的隐式参数.在这种情况下,(refers to the object for which the member function is being called. It is an implicit parameter passed to every non-static member function. In this case,) this
是刚创建的新对象.(is the new object just created.)
在TOOL类中,所有数据成员都是隐式的(In TOOL classes, all data members are implicitly) protected
.访问或修改成员变量的值的唯一方法是通过成员函数.如果需要访问成员函数外部的成员变量,则必须提供对成员函数的访问权限.再次看到(. The only way to access or modify the value of a member variable is through a member function. If you need to access a member variable outside a member function, you must provide access to member functions to do this. Again see the)*CoreClasses.Tool(CoreClasses.Tool)*文件中的几个示例.的(file for several examples. The) new
运算符将创建其名称之后的类的新对象.类名后括号中的表达式是要传递给构造函数的参数.(operator creates a new object of the class whose name follows it. The expressions in parentheses after the class name are the arguments to be passed to the constructor function.)
工具还允许从一个类派生另一类.派生类将继承基类的行为,并可能添加其自身的某些行为.工具仅支持单一继承;因此,每个类别最多只能有一个基本类别.核心类文件中的类都表达了该语言的属性.您还可以在TOOL中看到派生类构造函数如何直接调用其父代的构造函数.这是因为父类的整个接口可用于派生类.(TOOL also allows one class to be derived from another. The derived class will inherit the behavior of the base class and possibly add some behavior of its own. TOOL only supports single inheritance; therefore, each class can have at most one base class. The classes in the core classes file all express this attribute of the language. You can also see in TOOL how derived class constructors can call directly into their parent’s constructor. This is because the entire interface of the parent class is available to the derived class.)
工具脚本文件布局(TOOL Script File Layout)
可以设计TOOL脚本布局,以便运行时引擎可以对TOOL Script程序进行简单的自省,以确定程序的性质.空脚本程序如下所示.这种方法在TOOL Runtime Engine和它运行的脚本之间提供了一种"对象接口方法”.(It is possible that the TOOL Script Layout can be designed so that the run-time engine can perform a simple introspection on the TOOL Script Program to determine the nature of the program. An empty script program is shown below. This approach provides an “object interface approach” between the TOOL Runtime Engine and the scripts it runs.)
实际上,对于任务操作的每个阶段,运行时都会向脚本"发送消息".与此概念等效的过程是,运行时将在脚本程序中最多调用五个不同的顶层函数;每个调用代表任务完成中的特定阶段.有关工具脚本的自省方法的示例,请参见下面的示例程序.但是,您可以设计最适合您的应用程序的任何形式的相似方法.由于TOOL Engine易于由主机环境驱动,因此您可以定义更具体的文件布局以满足您的特定需求.(In effect, the Runtime will “send a message” to the script for each stage of task operation. The procedural equivalent to this concept, is that Runtime will invoke up to five different top-level-functions in the script program; each call representing a specific stage in the completion of the task. See the sample program below for an example of an introspective approach to TOOL Scripts. However, you can design any form of similar methodology that suits your application best; since the TOOL Engine is easily driven by the host environment, it is possible for you to define even more specific file layouts that suit your particular needs.)
/*********************************************************/
/* TOOL SOURCE FILE */
/*********************************************************/
/*
$Revision: $
$Date: $
$Author: $
Description: This shows a simple example of a TOOL
script program in order to illustrate the
"object-interface" on TOOL scripts.
*/
/*********************************************************/
///////////////////////////////////////////////////////////
//
// constants used in File Output
//
#const FILE_ACCESS_MODE_WRITE 1
#const FILE_SHARE_READ 1
#const FILE_OPEN_ALWAYS 3
#const FILE_POINTER_REF_END 2
///////////////////////////////////////////////////////////
//
// This function is called by the TOOL Run-Time to "discover"
// the interface to this script. The script can have up to
// five entry points in order to define the task. However,
// the script does not have to support all five entry points.
//
// For some tasks, it may make sense to only have a single
// entry point (the script developer can select any one of
// the five; or any number of them as appropriate)
//
// The TOOL Runtime will invoke each defined entry point
// in order. The script can have any number of "private"
// functions that can be called from any of the entry point
// functions.
//
// This design was implemented in order to provide a maximum
// level of flexibility in the definition of the script
// program and recognizes that many jobs are completed in
// several discrete stages/operations.
//
GetScriptInterface()
{
// Runtime Name of the
// Entry Point Local Function
// Name For That Task Stage
//
SetAppEnvField( "InitFunction", "Init" );
SetAppEnvField( "PreRunFunction", "PreJob" );
SetAppEnvField( "RunJobFunction", "RunJob" );
SetAppEnvField( "PostRunFunction", "PostJob" );
SetAppEnvField( "ExitFunction", "Exit" );
}
///////////////////////////////////////////////////////////
//
// simple logging function. Also provides an example of
// a "script private" function. Meaning that the function
// is not invoked by the TOOL Runtime, but rather from
// other functions inside this program.
//
MakeLogRecord( sLogText ;dtNow, hFile, sOutput )
{
dtNow = GetDate();
sOutput = StringFormat( "[ % ]--> % \r\n",
DateToString( "%a, %b %d, %Y %X", dtNow ),
sLogText );
Echo( sOutput );
if ( !PathExists( "c:\\tool.task.log" ) )
{
CreateDirectory( "c:\\tool.task.log" );
}
hFile = OpenCreateFile( "c:\\tool.task.log\\all.tasks.log",
FILE_ACCESS_MODE_WRITE,
FILE_SHARE_READ,
FILE_OPEN_ALWAYS );
if ( 0 != hFile )
{
SetFilePointer( hFile, 0, FILE_POINTER_REF_END );
WriteLineToFile( hFile, sOutput );
CloseFile( hFile );
}
}
///////////////////////////////////////////////////////////
//
// Perform any global initializations here
//
Init()
{
MakeLogRecord( "Task -- Init() Called." );
}
///////////////////////////////////////////////////////////
//
// Perform any further preparatory work here
//
PreJob()
{
MakeLogRecord( "Task -- PreJob() Called." );
}
///////////////////////////////////////////////////////////
//
// Perform the primary work for the task here
//
RunJob()
{
MakeLogRecord( "Task -- RunJob() Called." );
}
///////////////////////////////////////////////////////////
//
// Perform any post processing work here
//
PostJob()
{
MakeLogRecord( "Task -- PostJob() Called." );
}
///////////////////////////////////////////////////////////
//
// Perform final clean up or other related work here
//
Exit()
{
MakeLogRecord( "Task -- Exit() Called." );
}
工具脚本快速退出(TOOL Script Quick Exits)
如果TOOL脚本遇到无法(或不应)继续执行的条件,则TOOL脚本可以调用该工具.(If a TOOL script encounters a condition under which it can not (or should not) continue execution, then the TOOL script can make a call to the) Throw
API,这将导致TOOL程序立即停止.此API接受消息字符串,可以将其传递回以报告错误.该调用的示例如下所示:(API which will cause an immediate halt of the TOOL program. This API accepts a message string that can be passed back for error reporting purposes. An example of this call is shown below:)
Throw( "Goodbye Cruel World" );
进入工具(Getting into TOOL)
为了传达我如何将David Betz的BOB带入新的领域,我在这里做了很多介绍.我对TOOL的目标是扩展原始引擎,并使其"简单"地使用.在本文中,我尝试对项目的组织方式进行了充分的解释.我还希望我能提供足够的信息,说明如何根据自己的特定功能和/或项目需求扩展TOOL引擎的功能.最后,我提供了一套相当完整的TOOL类,这些类说明了TOOL API的全部范围,并提供了一些"通用类",可以在您开发的任何TOOL脚本中使用.(I’ve covered a lot of ground here in attempting to convey how I’ve taken David Betz’s BOB into new territory. My goal for TOOL was to extend the original engine and to make it “drop in simple” to use. In this article, I’ve attempted to explain enough about the way the project is organized. I also hope that I provided enough information as to how one would extend the capabilities of the TOOL engine for their own specific capabilities and/or requirements to the project. Finally, I’ve provided a rather complete set of TOOL classes that illustrate the full range of the TOOL API and have provided some “general classes” that can be used in any TOOL script that you develop.)
对于TOOL的不断发展,我还有更多计划.确实需要解决一些问题,例如我先前介绍的内存管理和垃圾回收问题.此外,我已经采取了一些措施,以便能够将调试器附加到TOOL引擎,并计划继续进行这种开发.我的待办事项清单还包括向引擎添加更多内在函数,添加多线程脚本功能,将其与我也开发的元格式语言集成等,等等.所以,我想我还没有完成越来越多的工具.即使这样,我仍认为有些人会喜欢另一个解释器项目,以增加您的工具箱.(I have many more plans for my continuing evolution of TOOL. There are some items that really need to be addressed, such as the memory management and garbage collection issues I introduced earlier. Further, I’ve taken some steps towards having the ability to attach a debugger to the TOOL engine, and have plans to continue this pursuit as well. My to do list also includes adding many more intrinsic functions to the engine, adding multi-threaded script capabilities, integrating it with a meta-forms language I’ve also developed, etc. etc. So, I guess I’m not yet done growing TOOL. Even so, I thought that some of you folks out there would enjoy another interpreter project to add to your tool-chests.)
由于已经有许多其他的解释器引擎,因此TOOL可能晚了一些,但我认为TOOL引擎易于集成和扩展使它比其他解释器更具优势.尽管我完全认识到它不像其他许多脚本引擎那样强大或完整,但是我可以将其完全包含在自己的程序中并可以相对轻松地进行扩展,这一事实使它成为了我的许多项目在最爱上的赔率都很高.(While TOOL may be a little late to the party since there are so many other interpreter engines out there already, I think that the ease of integration and extension of the TOOL engine gives it a bit of an edge over other interpreters. And while I fully recognize that it is not as powerful or as complete as many other script engines as well, the fact that I can include it completely in my own programs and can extend it any way I want to with relative ease, makes it an odds on favorite for many of my projects.)
另外,我只是真正了解了可编程程序的整个概念,并且在我的工具箱中有一个类似TOOL引擎的工具,仅凭嘶嘶声就给了我一些爵士乐.无论如何,我希望你们中的一些人也会发现值得紧追其后的TOOL,并且可以像我一样在完全由您控制的情况下尽情享受引擎的乐趣.(Plus, I just really groove to the whole concept of programmable programs, and have a tool like the TOOL engine in my toolbox gives me a certain amount of jazz from the sizzle factor alone. Anyhow, I hope that some of you will also find TOOL worth catching on to, and can enjoy dabbling in an engine completely under your control to the same extent that I have.)
但是等等…还有更多….工具中包含奖金材料(But Wait…There is More….Bonus Material Included in TOOL)
在工具中内置的其他值得注意和强大的功能是一个完整的功能.(Among the other noteworthy and powerful features built into TOOL is a complete)运行时表单引擎(Runtime Forms Engine),我称之为XMLForms.这将"运行时解释表单引擎"直接放入解释器,并允许创建带有自己的GUI的TOOL应用程序.(, that I call XMLForms. This puts a “runtime interpreted form engine” right into the interpreter and allows the creation of TOOL-applications that come packed with their own GUI.)
此功能本身值得进行大量研究,我现在将尝试从很高的角度来解释此功能.再说一次,由于这个项目太大了,所以我不可能在这里描述该项目的所有方面(我认为我几乎需要一本书来进行这样的解释).(This feature itself is worth significant study and I will now try to explain this feature from a very high level. Again, since this project is so large it is impossible for me to describe all aspects of the project here (me thinks I’d almost need a book for that level of explanation).)
XML-Forms的整个代码库位于(The entire code base for XML-Forms is in the)*GUI.工具/XMLForms(GUI.Tools/XMLForms)*分发中包含的目录.该目录中甚至有一个小的示例应用程序仅用于测试XML Form脚本.(directory included in the distribution. There is even a small sample application in that directory just for testing XML Form scripts.)
就像TOOL项目一样,有一个外观类封装了整个XML表单系统类.此类的定义在文件中找到(Just like the TOOL project, there is a single façade class that encapsulates the entire XML form system of classes. The definition of this class is found in the file)XMLFormFacade.h(XMLFormFacade.h)的实现可以在(and the implementation can be found in)XMLFormFacade.cpp(XMLFormFacade.cpp).(.)
XML表单的创建包含两个主要操作:解析XML表单定义,然后运行根据该定义构建的窗口.您可以通过研究XmlFormTest项目来了解如何简单地完成此工作,该项目是我在无数次用于在项目中测试自己的XML表单的小型实用程序.具体来说,请参见(Creation of an XML form consists of two main operations: parsing an XML form definition, and then running a window that is built from that definition. You can see how simply this can be done by studying the XmlFormTest project which is a small utility I have used on countless occasions for testing my own XML forms in my projects. Specifically, see the) OnButtonTestIt()
在样本中的功能(function in the sample in the)XMLFormTestDlg.cpp(XMLFormTestDlg.cpp).(.)
一般来说,XML表单的体系结构使用XML解析器从文件(或字符串缓冲区)读取表单定义.读取XML Form数据时,会创建一系列屏幕控制元对象并将其存储在(Generally speaking, the architecture of XML Forms uses an XML parser to read the form definition from a file (or a string buffer). As the XML Form data is read, a series of screen-control meta-objects is created and stored in collections in the) XMLFormFacade
目的.当需要显示表单时,将控件元对象的集合提供给控件工厂,该工厂创建屏幕控件并将其作为子控件放置在容器窗口中.附加的解析服务在(object. When it is time for the form to be displayed, the collection of control meta-objects is given to a control factory that creates the screen controls and places them as child controls in a container window. Additional parsing services are provided in the) XMLStyleDecoder
允许在XMLForm数据中显式定义控件样式和包含窗口的对象.(object that allows the style of the controls and the containing window to be explicitly defined in the XMLForm data.)
其他对象支持数据交换和验证,其规则也在表单的XML数据字符串中定义.数据验证任务包含在DDV包装器对象中(在(Other objects support data exchange and validation, the rules of which are also defined in the XML data string for the form. The task of data validation is contained in the DDV Wrapper object (implemented in)XMLFormDDVWrapper.cpp(XMLFormDDVWrapper.cpp)).数据交换服务包含在DDX Wrapper对象中(在(). Data exchange services are contained in the DDX Wrapper object (implemented in)XMLFomrDDXWrapper.cpp(XMLFomrDDXWrapper.cpp)).().)
选项卡控件管理是XMLForms项目中实现的非常专业的功能之一.通常,将制表符顺序定义为Z-Order的函数,但这不适用于像这样的项目.因此,可以在XMLForm数据文件中定义制表符顺序,并在(Tab-control management is one of the very specialized functions implemented in the XMLForms project. Normally, tab order is defined as a function of Z-Order, but this does not work well with a project like this one. So, tab-orders can be defined in the XMLForm data file and that data is processed in the) XMLFormTabSetManager
目的.为了协助完成此任务,所有XMLForm控件都将参与焦点((object. In order to assist with this task, all XMLForm controls will participate in focus () OnGainFocus(
)
和(and) OnLoseFocus()
)通知到包含窗口.在大多数XMLForm控件的实现中,您可以看到这些专门任务的示例.() notification to the containing window. You can see examples of these specialized tasks in the implementation of most of the XMLForm controls in the)*XMLFomrControls.cpp(XMLFomrControls.cpp)*文件.(file.)
渲染数据(Rendering Data)
作为附加功能,XMLForms的背景可以由XMLForms项目中包含的另一个功能来驱动,以该项目与TOOL解释器集成的方式.如果看到文件(As an added feature, the background of XMLForms can be driven by another feature included in the XMLForms project, in the way that this project is integrated with the TOOL interpreter. If you see the file)*GiftCardPurchase.form(GiftCardPurchase.form)*在里面(in the)*样本(samples)*在目录中,您可以看到有关如何将TOOL脚本嵌入XMLForm数据文件本身的示例.特别注意(directory, you can see examples of how TOOL script is embedded in the XMLForm data file itself. Pay particular note to the) PAGE_BACKGROUND
此样本文件的"部分".如果XMLForm文件中存在该节点,则XMLForm系统将运行该XML节点中包含的渲染脚本,以执行XMLForm的背景绘制.将渲染脚本与XMLForm窗口混合使用,可以为模拟"现实世界"业务表单外观的窗口创造一些非常有趣的可能性.虽然,我不得不承认创建和调试这些屏幕是手动完成的繁琐工作.(section of this sample file. If this node is present in the XMLForm file, then the XMLForm system will run the render script contained in that XML-node to perform the background paint of the XMLForm. Mixing render scripts with the XMLForm windows can create some very interesting possibilities for windows that emulate the appearance of ‘real-world’ business-forms. Although, I have to admit that creating and debugging these screens is bit of a chore to pull off by hand.)
样品包括(Samples Included)
我在首次交付时已包括以下样品:(I have included the following samples with this initial delivery:)
- ToolDll项目:该项目将TOOL Interpreter打包到DLL中.(ToolDll project: This project packages the TOOL Interpreter into a DLL.)
- ToolDllTester项目:该项目利用TOOL DLL,并提供了如何与TOOL DLL集成的示例.(ToolDllTester project: This project utilizes the TOOL DLL and provides a sample of how to integrate with the TOOL DLL.)
- ToolTester项目:该项目提供了有关如何将TOOL直接合并到您的应用程序中的示例.(ToolTester project: This project provides a sample of how to incorporate TOOL directly into your applications.)
- ToolExtender项目;该项目是用于TOOL Extender DLL的DLL Shell项目,该DLL可以绑定到运行时并在运行时执行.(ToolExtender project; This project is a DLL Shell project for a TOOL Extender DLL that can be bound to and executed at runtime.)
- XMLFormTest项目:该项目提供了有关如何直接在您的应用程序中包括XMLForm(和TOOL)的示例.(XMLFormTest project: This project provides a sample of how to include XMLForm (and TOOL) directly in your applications.)
- 示例脚本:位于(Sample Scripts: are in the)*工具类(Tools)*夹.(folder.)
- XMLForms示例:位于(Sample XMLForms: are in the)*XML表格(XMLForms)*夹.这里定义的最有趣的屏幕是(folder. Among the most interesting screens defined here is the)*PayByCreditCard.form(PayByCreditCard.form)*文件,因为它是迄今为止开发的最复杂的XMLForm,并且显示了该库的许多高级功能,包括为表单定义的高级事件处理(请参见(file as it is about the most complex XMLForm yet developed and shows a lot of the advanced features of this library including advanced-event-handling defined for the form (see the)
ACTIONS
文件中的块). XMLForm的另一个有趣的示例是(blocks in the file). Another interesting sample of an XMLForm is the)*GiftCardPurchase.form(GiftCardPurchase.form)*在里面(in the)*工具类(Tools)*目录.此示例显示了XML表单的自定义背景渲染,类似于"实际纸质表单".(directory. This sample shows custom background rendering of an XML Form to resemble an ‘actual paper form’.) - 样本报告生成:位于(Sample Report Generation: is in the)*样品报告(Sample.Report)*文件夹(这显示了如何格式化TOOL模板文件(一种特殊形式的TOOL脚本)),并说明了如何从ODBC数据源收集数据并将该数据加载到SQLite数据库中以进行中间存储和/或数据操作.(folder (this shows how to format a TOOL Template File (a specialized form of TOOL script)) and illustrates how to gather data from an ODBC data source and load that data into an SQLite database for intermediate storage and/or data manipulation.)
如何使用RunTool程序(How do I Use the RunTool Program)
RunTool程序的用途是什么?(What is the RunTool Program for?)
提供了RunTool程序,以便用户可以使用TOOL运行时运行TOOL脚本和交互式TOOL程序. RunTool允许使用TOOL语言开发小的"批处理文件",以用于任何特定用途.(The RunTool program is supplied so that the user can run TOOL Scripts and interactive TOOL programs using the TOOL runtime. RunTool allows small “batch files” to be developed in the TOOL language for any particular use.)
RunTool程序为用TOOL编写的"桌面应用程序"提供了独立的运行时环境.这些桌面应用程序的性质和范围没有任何限制.意味着TOOL中支持的任何内容都可以成为用TOOL编写的桌面实用程序的一部分.(The RunTool program provides a stand-alone runtime environment for “desktop applications” written in TOOL. The nature and scope of these desktop applications are not limited in any way; meaning that anything that is supported in TOOL can be made a part of a desktop utility written in TOOL.)
如何使用?(How is it Used?)
只需启动RunTool程序.(Simply start the RunTool program.)
如果应将任何运行时参数传递给TOOL程序(出于任何初始化或控制目的),请使用以下格式在"参数"编辑框中输入这些参数:(If any run-time parameters should be passed to the TOOL program (for any initialization or control purposes) then enter those in the “Parameters” edit box using the following format:)
PARAM=SomeParam=SomeValue PARAM=SomeOtherParam=SomeOtherValue
注意:参数字符串的长度当前限制为255个字符.(Note: The length of the parameters string is currently limited to 255 characters.)
此时,只需单击"选择脚本"按钮,这将打开文件对话框.使用文件对话框选择要运行的工具脚本程序.由TOOL脚本产生的任何消息都将显示在RunTool程序上.单击"清除消息"按钮可以清除此显示.(At this point, simply click on the “Select Script” button which will open a file dialog. Select the TOOL Script Program that you desire to run using the file dialog. Any messages produced from the TOOL Script will be shown on the RunTool program. This display can be cleared by clicking on the “Clear Messages” button.)
我如何使用ToolForge程序(How do I Use the ToolForge Program)
ToolForge程序的用途是什么?(What is the ToolForge Program for?)
TOOLForge程序是TOOL的IDE.因此,它可以用作TOOL Scripts的可识别语法的着色编辑器.它也可以用作TOOL脚本的交互式调试器.这个版本的TOOLForge仍然有些粗糙,因为它仍在开发中.因此它可能会不时崩溃.(The TOOLForge program is the IDE for TOOL. As such it can be used as a syntax-aware colorizing editor for TOOL Scripts. It can also be used as an interactive debugger for TOOL Scripts. This version of TOOLForge is still a bit rough around the edges, as it is still a work in progress; so it may crash from time to time.)
如何使用?(How is it Used?)
要编辑TOOL脚本,只需启动TOOLForge程序并像使用其他任何编辑器一样使用它即可.以下是在Forge中调试TOOL脚本的步骤:(To edit TOOL scripts, simply start the TOOLForge program and use it like you would any other editor. Here are the steps one would use to debug TOOL Scripts in the Forge:)
- 首先注意程序中的工具栏.在工具栏的最右侧是三个驱动调试会话的控件.右侧的复选框用于控制调试器的单步操作.您应该首先保留此复选框.(First notice the Toolbar in the program. On the right most side of the toolbar are three controls that drive the debugging session. The checkbox on the right is used to control the single step operation of the debugger. You should leave this checked initially.)
- 接下来,打开"调试"菜单,然后选择调试脚本命令.这将显示一个文件选择对话框,供您选择要调试的脚本文件.首次使用调试器时,您可能需要使用(Next, open the ‘Debug’ Menu and select the debug script command. This will display a file selection dialog for you to select the script file that you would like to debug. For your first use of the debugger, you may want to use the)*FileBuilder.tool(FileBuilder.tool)*包含在(file included in the)*样本(samples)*目录.(directory.)
- 选择文件后,调试器将通过代码窗口以绿色条显示脚本的当前执行点.(After selecting a file, the debugger will show the current execution point of the script with a green bar through the code window.)
- 要切换断点,请在代码窗口中单击一行代码.打开编辑菜单,然后选择切换断点命令.断点在文本窗口中以深蓝色条突出显示,在代码窗口的装订线中以"停止符号"突出显示.(To toggle a break-point, click on a line of code in the code window; open the edit menu and select the toggle-break point command. Breakpoints are highlighted with a dark-blue bar through the text window, and a ‘stop sign’ in the gutter of the code window.)
- 选中"单步"复选框后,单击工具栏上的"蓝色向下箭头代码页"将在脚本中执行单步.如果未选中此复选框,则脚本将以"动画模式"运行,这意味着编辑器窗口将跟随程序执行.要中断以动画模式运行的脚本,只需检查单步控件即可发出程序中断.(When the single step check box is checked, clicking on the the ‘blue down arrow code page’ on the toolbar will execute a single step in the script. When this check box is not checked, then the script will run in ‘animation mode’ which means that the editor window will follow the program execution. To interrupt a script running in animation mode, simply check the single-step control to issue a program break.)
- 程序底部的三个窗口是(从左到右):“脚本输出"窗口,“调用堆栈"窗口和"变量显示"窗口.请注意,并非所有TOOL变量都在变量窗口中完全展开.这是该项目上仍在进行的项目之一.但是,其中一些有效,您将对使用此功能的去向有所了解.(The three windows along the bottom of the program are (from left to right): Script Output Window, Call Stack Window, and Variables Display Window. Note that not all TOOL variables are completely expanded in the variable window. This is one of the items that are still in progress on this project. But, some of them work, and you’ll get an idea of where I’m going with this feature.)
- 调用堆栈和变量窗口在每个断点处更新.脚本输出窗口显示脚本运行时回显的内容.(The call stack and variables windows are updated at each breakpoint. The script output window shows whatever was Echo’ed from the script as it runs.)
- 要运行脚本以完成脚本,请单击工具栏上的"红色向下箭头代码页"按钮.(To run a script to completion, click the ‘red down arrow code page’ button on the toolbar.)
已知问题(Known Problems)
使用TOOL DLL实例化多个XML表单会导致应用程序消息泵混乱.原因尚不清楚,当直接在应用程序中使用XMLForms时,该行为不会显现.(Instantiating multiple XML Forms using the TOOL DLL causes the application message pump to mess up. The cause of this is not yet known, and the behavior does not manifest when XMLForms is used directly in the application.)
到处肯定还有许多其他小错误.由于该项目已包含在我的许多项目中,因此已经对该项目进行了合理的完整测试,但在这种规模的项目中仍然很有可能发现了一些错误.(There are sure to be many other little bugs here and there. While the project has been tested reasonably completely due to its being included in many of my projects, there is still a great likelihood that there are uncovered bugs in a project of this size.)
如果您发现某些行为可能是一个错误,请告诉我.如果该行为确实被证明是一种缺陷,我将努力予以纠正.(If you find some behavior that you think may be a bug, then please do let me know. If the behavior really does prove to be a flaw, I will strive to correct it.)
帮助需求和功能请求(Help Wanted and Feature Requests)
工具和XML表单是相当大的项目的核心.而且仍有很大的改进和增强空间.待办事项列表中的项目包括:(TOOL and XML Forms are the core of a pretty sizeable project. And there is still a lot of room for improvements and enhancements. Among the items on the to-do list are:)
- 添加其他DSN创建选项.当前,仅定义了用于SQL Server和Access的DSN创建字符串.(Add additional DSN creation options. Currently only DSN creation strings for SQL Server and Access are defined.)
- TOOL的增强功能,可以与"开始"按钮菜单和用于创建快捷方式的Shell函数进行交互(在其中创建条目).(Enhancements to TOOL to interact with (create entries in) the Start button menus and shell functions for creating shortcuts.)
- TOOL RunTime中更好的内存管理,包括在解释器运行时更好的垃圾回收.(Better memory management in the TOOL RunTime including better garbage collection while the interpreter is running.)
- 满足您特定要求的新功能,此版本未解决.(New features that fit your specific requirements that are not addressed in this release.) 简而言之,我认识到虽然TOOL已经是功能丰富的解释器,但它可以扩展到更多地方.这就是我向整个社区发布TOOL的原因之一;我希望,随着其他人尝试将TOOL用于自己的目的,图书馆本身的广度将不断增长.另外,我还意识到那里有一些非常聪明的人,他们可能愿意帮助我发现并解决代码中肯定存在的小问题.(In short, I recognize that while TOOL is already a feature rich interpreter, there are many more places it can grow into. That is one of the reasons why I am releasing TOOL to the community at large; it is my hope that with other folks trying to use TOOL for their own purposes, the library itself will grow in breadth. In addition, I also realize that there are some very bright folks out there who may be willing to help me find and fix the little problems that are sure to exist in the code.)
关于源代码的注释(A Note on the Source Code)
我是一个狂热的网络爬虫,总是在寻找可以添加到工具(以及其他项目)中的新的有趣代码.这个项目中有很多工作来自各地.但是,我通常不照原样采用代码,而是通过标准做法对所有"采用的代码"进行重新格式化.这些重新格式化练习包括以下任务:(I am an avid net crawler and always on the look out for new and interesting code I can add to TOOL (as well as other projects). And there is a lot of work in this project that has arrived from all over the place. However, I don’t usually just adopt code as is, and instead I make a standard practice to heavily reformat all ‘adopted code’. Included in these reformat exercises are the following tasks:)
-
类变量重命名:转换为匈牙利表示法,并且还与我自己的命名约定保持一致.(Class variable renaming: converted to Hungarian notation and also made consistent with my own naming conventions.)
-
标题文件/实现文件的标题和页脚.(Header File / Implementation File headers and footers.)
-
功能页眉和页脚.(Function headers and footers.)
-
带有空格的标签的移除/替换.(Removal / Replacement of Tabs with spaces.) 我为什么要这样做?好吧,原因如下:(Why do I do this? Well, for the following reasons:)
-
代码审查:由于我经常会最终扩展这些文件,或者在某些情况下会修复它们,因此我发现使用"细齿梳"浏览这些文件使我对文件的组织更加熟悉,因此,如果我确实需要对它们进行处理,我对它们如何组合有一些了解.(Code Review: Since I will often end up extending these files, or in some cases repairing them, I find that going through these files with a ‘fine tooth comb’ provides me with additional familiarity with the organization of the files, so that if I do need to work on them, I have some idea of how they are put together.)
-
一致性:就我而言,这可能是一个主观的和/或美学的选择,但我认为,任何项目的"内部实施"都应力求尽可能"内部一致”.我认为,这种方法会使其他人熟悉该项目,从而更容易理解总体.换句话说,如果一个项目是由一群个人的工作创建的,那么每个文件都有自己的样式创建代码的方法,那么我认为这没有帮助.例如,如果我包括一个主要的开放源代码库(如SQLite或zlib),则不会有此规则的例外,因此,我不会如此关注所有文件,但我用来访问这些库的任何包装对象都将使用我喜欢的风格.(Consistency: It is probably a subjective and/or aesthetic choice on my part, but it is my opinion that the ‘internal implementation’ of any project should strive to be as ‘internally consistent’ as possible. In my opinion, this approach makes the overall more readily understood by others trying to get familiar with the project. In other words, I don’t think it helps if a project is created from a bunch of individuals’ work where each file has its own stylistic approach to code creation. There are exceptions to this rule, for example, if I include a major open source library like SQLite or zlib, I don’t go through all their files with this level of attention, but any wrapper object I use to access those libraries will use my preferred style.) 因此,尽管向其他以这种方式修改了文件的开发人员"道歉"并没有多大好处,但至少您对这些更改背后的原因有一个解释.我尝试将原始作者的评论保留在我集成到TOOL并经过如前所述的编辑印刷的任何文件中,但是由于该项目已经开展了很长时间,因此在某些情况下,我什至没有回想一下,其中的某些来源是否基于我在旅途中偶然发现的某些项目/样本.如果您认为该项目中的任何文件是您其中一个项目的衍生文件,并且您认为该项目的任何部分都应得到认可,请与我联系并提出案例.(So, while it will do little good to ‘apologize’ to any other developer whose files have been modified in this manner, at least you have an explanation as to the rationale behind these changes. I try to retain the original author’s comments in any file that I integrate into TOOL and put through the edit-press as I have described, but as this project has been in motion for a really long time, there are cases where I do not even recall if some of the source is based on some project/sample that I stumbled on in my travels. If you think that any file in this project is a derivative of one of your projects and you think that you deserve credit for any part of this project, then contact me and make a case.)
最后,仔细阅读此代码,您将发现包裹在”(Finally, careful review of this code will uncover a few instances of conditional compilation wrapped in “) ifndef TOOL_PUBLIC_BUILD
“其最终结果是从此版本中"删除某些功能”.这些条件编译区的背后原因是,某些功能依赖于我在产品的"私有版本"中使用的商业库,但我确实如果有人想为这些功能领域提出基于非商业图书馆的替代品,那么我将对此表示欢迎,因为它将使该项目的效用增加到其"私有版本"的水平.(” which has the end effect of ‘removing some functionality’ from this release. The reason behind these conditional compiled areas is that some functionality relied on commercial libraries that I make use of in my “private build” of the product, but that I do not have the right to release the source for. If anyone wants to come up with non-commercial-library-based replacements for these functional areas, then I would welcome that, as it would increase the utility of this project to its ‘private build’ levels.)
即使您不想使用TOOL或XMLForms,您仍然可能希望对这堆代码进行长时间的研究,因为这里实现的某些类和技术可以其他方式使用.(Even if you don’t want to use TOOL or XMLForms, you may still want to take a good long look at this pile of code as there are some classes and techniques implemented here that can find use in other ways.)
关于本文件的注释(A Note on this Document)
工具继续不断发展.由于这种发展,关于我添加到API的最新/最新功能,本文档可能并不完整.因此,如果本文档未涵盖API的某些区域,那么希望提供的示例将提供一些其他信息.(TOOL continues to evolve constantly. And because of that evolution, this document may not be entirely complete with regards to the latest/newest features I’ve added to the API. So, if there is some area of the API not covered in this document, then hopefully the samples provided will provide some additional information.)
TOOL的下一步是什么?(What is Coming Next for TOOL?)
有成千上万的新功能可以添加到工具中,我什至无法想象.因此,我将这些方向性决策留给大家.另外,我已经开发了其他使用TOOL的产品,例如:报告生成器,安装程序,应用程序服务器等.由于对这些领域中的任何一个感兴趣,我还可能以一种或另一种形式发布这些产品.(There are countless thousands of new features that could be added to TOOL that I can not even begin to imagine. So, I’ll leave those directional decisions up to you all. Also, I have already developed other products that make use of TOOL, such as: report generators, installers, application servers, etc. Pending interest in any one of these areas, I may also release these products in one form or another.)
票价您好(Fare Thee Well)
如果您在阅读本文时已经走了这么远,那么感谢您忍受阅读(有时是漫长的)提交内容.希望您对此项目有所帮助.如果您对TOOL的使用有任何想法,或者您希望看到一些特定的要求,请与我联系,也许我会有所帮助.(If you have gotten this far in reading this article, then thanks for putting up with reading this (at times rambling) submission. I hope that you find some use for this project. And if you have any ideas for where TOOL may find use, or if you have some specific request that you’d like to see, then please contact me and perhaps I can be of some kind of assistance.)
更新记录(Update History)
-
2006/10/16:天哪,时间飞逝.很难相信,自该项目更新以来已经一年了.如2b |!2b ==所示.促使我下车.因此,这里我们终于有了更新.此版本包含修复程序,更新和增强功能(要回忆的东西太多了,但主要的是紧随其后的):(2006/10/16: Gosh, how the time does fly. It is hard to believe that it has already been a year since this project was updated. As evidenced by 2b|!2b==? prompting me to get off the stick. So, here we are at last with an update. This release includes fixes, updates, and enhancements (too many to recall, but the key ones follow):)
- 用(With)很大的帮助(great assistance)从(from)2B |!2b ==?(2B|!2b==?)(谁做了"移植"工作的很大一部分),现在已经为较新的Microsoft编译环境更新了TOOL项目.((who did a large part of the “porting” work), the TOOL projects now have been updated for newer Microsoft compile environments.)
- 分析错误(由yiqianfeng报告)已得到纠正.(The parsing error (reported by yiqianfeng ) has been corrected.)
- 现在,TOOL引擎已内置支持SQLite 2.x和3.x数据库格式.随之,为SQLite 3.x管理添加了几个新的API调用.自从进行这种集成以来,旧的" minidb"调用已被重命名(如果您的脚本已被此重命名操作破坏,我先致歉),因为它们不再有意义.所有" minidb"调用都已被" SqlLite2"调用替换.(The TOOL engine now has built in support for both SQLite 2.x and 3.x database formats. Along with this, several new API calls have been added for SQLite 3.x management. Since this integration took place, the old “minidb” calls have been renamed (I apologize in advance if your scripts have been broken by this renaming action) since they no longer made much sense. All “minidb” calls and have been replaced with “SqlLite2” calls.)
- ODBC类的"动态绑定"行为已经过修订/改进,可以更好地(更可移植)支持更多SQL数据类型.(The ODBC class “dynamic binding” behavior has been revised/improved to allow better (more portable) support for more SQL data types.)
- TOOLForge应用程序已得到增强,可以直观地创建和测试XMLForms.这可以通过使用非常优秀的屏幕设计器类的大量修改版本来实现.(The TOOLForge application has been enhanced to allow visual creation and testing of XMLForms. This is made possible through use of heavily modified version of very excellent screen designer classes available) 这里(here) 通过(by) 约翰`罗森格伦(Johan Rosengren)(Johan Rosengren) .(.)
- TOOL Calculator对象已得到增强,包括几个新的内置函数:(The TOOL Calculator object has been enhanced to include several new built in functions:)
sin
,(,)cos
,(,)exp
,(,)sqrt
,(,)ln
,(,)tan
,(,)ctg
,(,)asin
,(,)acos
,(,)atan
,(,)sinh
,(,)cosh
,(,)tanh
,(,)asinh
,(,)acosh
,(,)atanh
,(,)log10
,(,)log2
,(,)abs
,(,)rounddown
和(, and)roundup
.(.)
-
2005/09/23:此发行版包含对我迄今为止发现的所有问题的修复,更新和编辑.如下:(2005/09/23: This release includes fixes, updates, edits to all problems that I was made aware of to date. As follows:)
- 阶乘示例错误(由Abf23报告)已得到纠正.包含示例脚本也证明该示例是正确的.(Factorial Example error (reported by Abf23) has been corrected. The example was also proven correct with the inclusion of a sample script.)
- Build错误(Abf23报告)为不良(Build errors (reported by Abf23) for bad)*stdafx.h(stdafx.h)*路径.违规文件已得到纠正.(path. The offending files have been corrected.)
- 更新的交付内容包括已编译的示例DLL文件和可执行文件(响应Manuele Turini).(The updated delivery includes compiled sample DLL files and executables (in response to Manuele Turini).)
- 由于当我尝试更改代码以解决他的报告时,无法再构建该发行版,因此无法完全解决hero3blade报告的错误.这里可能存在一些尚未确定的平台问题.(The errors reported by hero3blade are not entirely addressed in this release, due to the fact that when I try to change the code to address his reports, I can no longer build the release. There could to be some as yet undetermined platform issue at play here.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C++ VC6 Windows Visual-Studio Dev virtual-machine virtualization 新闻 翻译