概述
脚本为用户提供了一种在模拟中基于发生的事件执行复杂指令集的方式。该语言类似于 C# 和 Java,对于具备基本编程技能的人来说应该会很熟悉。它采用块结构,包含熟悉的声明、赋值和控制流语句,允许用户检查和操作模拟环境。
脚本本质上是由脚本编译器生成的一系列指令,脚本编译器理解脚本语言的语法并能够将其翻译成相应的指令。一旦编译完成,脚本可以在脚本上下文(执行上下文)中执行,该上下文负责解释脚本的指令并提供脚本与应用程序(即模拟)层之间的接口。在 WSF 中,执行上下文是链式结构,形成树状结构,允许子上下文继承由其父上下文定义的脚本(参见下图)。
如所示,全球仿真实例、平台及某些平台部件包含一个脚本上下文,使它们能够执行脚本。在仿真的“全局”上下文中定义的脚本对所有平台和平台部件可用。正如在特定平台中定义的脚本对其所有平台部件可用一样。然而,反向情况并不适用。平台无法访问其平台部件上定义的脚本。
为了完整起见,这里定义了脚本语法:脚本语言语法。
基本类型
脚本有 4 种基本类型。基本类型在赋值给变量或传递给函数时会被复制。基本类型包括:
int
一个 32 位整数:
int prime5 = 11;
bool
一个布尔值。可以是“true”或“false”:
bool isTrue = true;
double
一个双精度浮点值:
double gravity = 9.8;
string
一串字符:
string text = "Hello World";
基本类型支持多种运算符。‘int’ 和 ‘double’ 都支持基本的算术运算和比较:
// 算术 ( +, -, *, / )
double abc = (5 + 2.5) / 2.0 - 1.0;
// 算术赋值: ( +=, -=, *=, /= )
abc += 5.0; // 将 5 加到 abc
// 比较 ( >, >=, <, <=, ==, != )
bool isPositive = abc > 0.0;
‘string’ 支持比较运算符和 ‘+’ 作为连接:
string alphabet = "alphabet";
string zoo = "zoo";
bool trueVal = alphabet < zoo;
string combine = alphabet + " " + zoo;
‘bool’ 支持比较运算符,如预期的那样:
bool trueVal = true != false;
脚本
用户定义的函数称为“脚本”。通常脚本具有以下语法:
script <type> <script-name>([<variable-declaration-list>]) <script-commands...> end_script
脚本可以调用在同一上下文中定义的其他脚本或在父上下文中定义的脚本。请看这个例子:
script bool global_script() writeln("Global script called"); return true; end_script platform plat WSF_PLATFORM script void local_script() writeln("Local script"); end_script // on_initialize is an example of a different script syntax. on_initialize // Call a global script, accessible anywhere global_script(); // Call a local script, only accessible on this platform local_script(); // To call a script defined later, you must declare it with 'extern' extern bool global_later(double); global_later(123.0); end_on_initialize end_platform script bool global_later(double value) writeln("global_later(", value, ")"); end_script platform plat2 WSF_PLATFORM // this platform doesn't have direct access to scripts on 'plat' // script void call_plat1() // Find the platform defined above WsfPlatform plat = WsfSimulation.FindPlatform("plat"); // Here, the '.' operator cannot be used to call local_script() on 'plat' // The '->' operator allows access to other objects, but that access is not // checked at startup. plat->local_script(); // If a mistake is made, an error is issued at run-time. // This would result in a run-time error: // plat->bad_script(); end_script end_platform
全局变量
全局变量允许在多个脚本中存储和访问值。当定义一个全局变量时,它仅对其父上下文是全局的。例如,在一个平台上定义一个全局变量只允许从该特定平台访问该变量。定义全局变量的方式有多种:
// variables defined in a 'script_variables' block outside of a platform are always global. // 'x' is a true 'global' variable available anywhere script_variables double x = 1.0; end_script_variables platform plat WSF_PLATFORM // y is a global variable only available to scripts located on 'plat' script_variables double y = x; end_script_variables script void test() // global variables can be used just like regular variables y += x; // global variables can also be defined in any script using the 'global' keyword: global double z = y; end_script end_platform script void test2() // Find the platform defined above WsfPlatform plat = WsfSimulation.FindPlatform("plat"); // the '->' operator can be used to access variables belonging to an object. plat->y -= x; end_script
静态变量
静态变量是只有一个实例且仅初始化一次的变量。、
script void test_static() // In this example, x is initialized to 1.0 the first time in this script. // x's value is maintained between calls to test_static(). // This would output 1234... one number for each call to test_static(). static double x = 1.0; write(x); x += 1; end_script
类型转换
类型转换将一个值从一种类型转换为另一种类型。类型转换的语法为:(<type>)value
。基本类型之间可以自由地进行类型转换:
int five = (int) 5.5; string fivePointFive = (string)5.5;
允许在非基本类型之间进行类型转换,但用户应谨慎操作。
WsfMessage msg = GetControlMessage(); ((WsfControlMessage)msg).SetResource("my_resource");
操作符号
.
The ‘.’ operator is used to call Methods on script class objects, or static methods on script classes.
->
The ‘->’ operator is used to call user-defined scripts on objects, and get/set user-defined script variables on objects.
+
Add numeric values, and concatenate strings
-
Subtract numeric values; 1 - 1
*
Multiply numeric values; 2 * 2
/
Divide numeric values; 4 / 2
>
Test for greater-than; 1 > 0
>=
Test for greater-than or equal to; 1 >= 1
<
Test for less-than; 0 < 1
<=
Test for less-than or equal to; 0 <= 1
==
Test for equal-to; 1 == 1
!=
Test for not-equal-to; 1 != 0
!
Boolean-not; true == !false
()
Ordering expressions; (1+1)*2 == 4
(<type>)
Casting operator
=
Assignment operator; double x = 2.0;
详细信息
这是关于脚本语法的详细文档。
符号说明
尖括号(< >)包围类别标签。
方括号([ ])包围可选项。
大括号({ })包围重复项。
单引号(‘ ’)包围字面量项。
粗体文本表示保留字。
注意:该语言的完整语法可以在《脚本语言语法》中找到。
命令模板
脚本使用以下序列定义:
script <type> <script-name>([<variable-declaration-list>])
<script-commands>
end_script
语言描述
该语言由以下构造组成:
<标识符>
标识符表示变量或函数的名称。标识符以字母(大写或小写)开头,后面可以跟零个或多个字母、数字(0-9)或下划线(‘_’)。标识符是区分大小写的。因此,标识符‘x1’和‘X1’代表不同的变量。有效标识符的示例:
i
X1
aLongIdentifier
x_2
<类型>
每个变量都有一个‘类型’,它定义了变量可以包含的数据类型。数据主要分为两种类型:<基本类型>和<复杂类型>。所有类型都派生自一个称为Object的‘基’类型。
Object
一个所有其他类型都兼容的‘基’类型。
例如:
Object myObject;
myObject = 'hello';
myObject = 19;
<基本类型>
脚本语言的类型与大多数现代编程语言提供的类型匹配:
int
double
bool
string
<复杂类型>
这些是更复杂的类型,通常由多个基本类型或其他复杂类型组成,并通常包括可以访问和操作该类型内数据的函数。复杂类型无法在脚本中定义;它们在C++中定义并导出以供脚本使用。复杂类型的完整列表可在脚本类型部分找到。
<存储类型>
当声明变量时(见下文),内存存储类型会被隐式或显式设置。默认情况下(如果未指定存储类型),变量被认为是自动变量,意味着它们在当前<块>的内存空间中创建。这也意味着它们仅在当前<块>及其内部嵌套的<块>中可用。 除了自动变量,还有全局变量和静态变量。全局变量在全局内存中分配,并在所有脚本中可用。静态变量的工作方式与自动变量相同,只是它们的内存(及其当前值)在对给定脚本的多次调用之间被保留。
<表达式>
表达式是任何结果为单一值的内容。
例如:
10 * 3
('platform-1' == platform.Name()) && (5 < mX)
Foo()
mX
MATH.Pi()
(9.99 >= 1.0)
1.23
<表达式列表>
用逗号分隔的<表达式>列表。
<强制转换>
表达式可以使用强制转换操作转换为另一种类型。这在某些情况下是必要的(请参见脚本类型部分中的Iterator、ArrayIterator和MapIterator)。
'(' <类型> ')' <表达式>
例如:
Object obj = 'my string';
string = (string)obj;
int i = 99;
double d = (double)i;
WsfMessage msg = GetControlMessage();
(WsfControlMessage)msg.SetResource('my_resource');
<语句>
语句被定义为以下之一:
<变量声明>
每个变量在使用前必须声明。变量可以简单声明,也可以声明并赋初值。变量声明可以是以下之一:
[<存储类型>] <类型> <标识符> ';'
[<存储类型>] <类型> <标识符> = <表达式> ';'
前者的示例包括:
int i;
static j;
WsfSensor thisSensor;
后者的示例包括:
int i = 0;
global double x = 10.0 * i;
string s = 'Hello, world';
WsfSensor thisSensor = PLATFORM.Sensor('this_sensor');
<变量赋值>
可以使用赋值运算符将简单值、复杂表达式和脚本/函数返回值赋给变量。
<变量> = <表达式> ;
例如:
int x;
x = 10;
<if-else>
if-else语句允许用户根据一个或多个计算为布尔值的表达式选择要执行的语句。第一个计算为true的条件会执行其<block>中的语句。
if '(' <expression> ')' <block> { else if '(' <expression> ')' <block> } [else <block>]
例如:
string name = 'platform-1';
if (name == 'platform-2')
{
print('Found platform-2');
}
else if (name == 'platform-1')
{
print('Found platform-1');
}
else
{
print('Couldn\'t find platform 1 or 2');
}
<while-loop>
while语句允许用户根据一个计算为布尔值的表达式进行迭代。迭代会持续直到表达式计算为false。
while '(' <expression> ')' <block>
例如:
int i = 0;
while (i < 10)
{
print('i is ', i);
i = i + 1;
}
<do-while-loop>
do-while语句允许用户根据一个计算为布尔值的表达式进行迭代。迭代会持续直到表达式计算为false。do-while和while循环之间的区别在于,条件在do-while循环的底部检查,这保证至少执行一次迭代。
do <block> while '(' <expression> ')'
例如:
int i = -1;
do
{
i = i + 1;
print('i is ', i);
}
while (i < 10)
<for-loop>
for语句允许用户根据一个计算为布尔值的表达式进行迭代。迭代会持续直到表达式计算为false。此外,它提供了声明循环计数器和增量操作的空间。
for '(' [<variable-declaration>] ';' [<expression-list>] ';' [<expression-list>] ')' <block>
例如:
for (int i = 0; i < 10; i = i + 1)
{
print('i is ', i);
}
<foreach-loop>
foreach循环允许用户遍历容器中的元素,同时提供对键和值的访问。
foreach '(' [<variable-declaration> ':'] <variable-declaration> in <expression> ')' <block>
例如:
Map<string, double> myMap = Map<string, double>();
myMap['a'] = 1.1;
myMap['b'] = 2.2;
// 如果声明了两个循环变量
// (用冒号分隔),第一个必须是
// 键,第二个必须是值。
foreach (string aKey : double aData in myMap)
{
print('key, data ', aKey, ', ', aData);
}
// 如果只声明了一个循环变量
// 它必须是值。
foreach (double aData in myMap)
{
Print('data ', aData);
}
<break>
break语句允许用户跳出当前块。
break ';'
例如:
while (true)
{
if (true)
{
break;
}
}
<continue> continue语句允许用户忽略循环中的其余语句并跳到循环的顶部。
continue ';'
例如:
for (int i = 0; i < 10; i = i + 1)
{
if (i == 5)
{
continue;
}
}
<return>
return语句允许用户从脚本/函数调用中返回一个值。
return <expression> ';'
例如:
double Multiply(double aA, double aB)
{
return aA * aB;
}
<block>
块是:
- 被脚本
end_script
包围的零个或多个语句 - 被花括号包围的零个或多个语句
前者的示例是:
script void my_script()
int i = 1;
print('i = ', i);
end_script
后者的示例是:
if (true)
{
int i = 1;
print('i = ', i);
}
<function-declaration>
可以使用以下语法在脚本中声明函数。函数只能在脚本内声明。如果需要一个在所有脚本中可用的函数,请将其定义为脚本。
<type> <identifier> '(' [<variable-declaration-list>] ')' <block>
例如:
double Magnitude(double aDx, double aDy)
{
return MATH.Sqrt(aDx * aDx + aDy * aDy);
}
注意,MATH是一个可在所有脚本中使用的系统变量,提供对各种数学工具的访问。