RPA俱乐部

 找回密码
 立即注册
查看: 690|回复: 0

UiBot编程语言说明

[复制链接]

1

主题

1

帖子

115

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115
发表于 2019-3-11 16:26 | 显示全部楼层 |阅读模式
1.     背景
UiBot的设计理念是两个“快”:1) 让没有计算机基础的初学者,通过简单的学习,即可快速掌握脚本的编写方法;2)让有一定编程基础的专业人员,能够以最快的速度实现自己的脚本。
为了做到这两个“快”,UiBot提供了可视化的脚本编写界面,便于初学者快速掌握;同时提供了一种简单、易学、接近自然语言的编程语言,便于专业人员的快速实现。当然,同一个脚本,可以用两种界面来显示,并可以在开发过程中随时切换。
本文档主要介绍UiBot提供的编程语言的基本语法规则。具有基本编程基础的读者,大约在一小时内即可掌握此规则,经过数个小时的熟悉,即可灵活运用。对于有按键精灵基础的读者,还能进一步缩短学习时间。
对于UiBot来说,编程语言只是表达逻辑的工具,关键的功能还是由函数库或插件来实现。所以,语言设计只包括基本的逻辑,所有具体的功能,哪怕是最常用的“延时”功能,都不列入语言设计中,而在函数库中单独设计。本文档亦不包括函数库的介绍,由其他文档另行详解。

2.     宗旨
UiBot的编程语言是专门设计的(下文简称UB语言),而不是市面上流行的编程语言如Python、JavaScript等,是因为UiBot的主要受众是那些非计算机专业科班出身,但足够熟悉业务流程的非技术人员。UB语言的设计尽可能的接近自然语言,对于理解基本英文单词的人来说,即使没有学习过,也能大致读懂。
相比之下,以JavaScript为例,虽然在专业的程序员手里能发挥出很高的效率,甚至UiBot本身的很多代码都是使用JavaScript编写的。但这种语言里面大量使用的括号,容易给非专业人员的学习带来障碍。如下图:
1.png
因此,我们设计了专门的UB语言,并使这门语言尽可能简化,甚至尽可能少用除了字母和数字之外的元素,而不是使用市面上流行的编程语言。非不能也,是不为也。
但是,在UB语言中,吸取了很多其他编程语言的优点。你会在UB语言的设计中看到Basic语言、Python语言、JavaScript语言的一些特点。因为我们在充分理解的基础上,博取众家之长,吸取最容易理解且常用的部分,删去复杂、不常用的部分,使UB语言精简、简单、易学、易用。
我们认为UB语言是目前最适合RPA领域的编程语言。

3.     表达方式
一般在描述编程语言时,都会用方括号、花括号等符号来代表可选的内容,或者可重复的内容,等等。但这种方式往往难以阅读。在本文中,我们采用不同的颜色来表示不同的内容:
这种颜色代表关键字、运算符等需要在脚本中原样照写的内容;
这种颜色代表要用实际内容替换,如 表达式 可以用 x+1 来替换;
这种颜色代表可以重复多次的内容;
这种颜色代表可选的内容。
所有的例子都用虚线框包围。

4.     基本结构
UB语言的源代码文件是纯文本格式,扩展名不限,一律采用UTF-8编码。
UB语言的源代码由多条语句组成,和一般的脚本语言,如Python、JavaScript等一样,UB语言并没有严格的结构和显式指定的入口。执行一个脚本时,从第一行开始执行,遇到函数定义暂时跳过,然后继续从函数结束后的一行开始执行(函数的概念请参考第9节)。

一般来说,我们推荐一行只写一个语句。如果一定要写多个语句,则用冒号分隔符(:)进行分隔。
如果一行内容不够,需要折行,可以在任意语句中出现的逗号(,)或二元运算符(“二元运算符”的概念见第7节)之后直接折行,不需要增加其他额外的符号,也不推荐在其他地方折行。但如果一定要在其他地方折行,则用反斜杠(\)作为折行符号。例如:
Dim a= \
1
当一行中存在 // 时,表示从这以后的内容都是注释。包含在 /*   */ 中的内容,无论多少行都视作注释。例如:
//这里是注释
/*
这里
也是
注释
*/
注释在脚本运行过程中没有任何作用,仅供我们阅读方便。

UB语言中所有关键字、变量名、函数名均不区分大小写。例如:变量名abc、ABC或者Abc都被认为是同一个变量。

5.     变量和常量
变量是编程语言中最基础的功能,变量中可以存放数字、字符串等值,并且可以在脚本运行的过程中,随时改变变量中的值。UB语言中的变量是动态类型的,即变量的值和类型都可以在运行过程中动态改变。这符合一般脚本语言如Python、JavaScript的习惯。变量的类型分为以下几种:整数型、浮点数型、布尔型、字符串型、函数型、复合型和空值型。
整数型的值可以以十进制或者十六进制的方式表示,其中十六进制需加前缀 &H &h
浮点数的值可以用常规方式或者科学计数法方式表示。如0.01或者1E-2或者1e-2均代表同一个浮点数。
布尔型的值仅有True或者False,两者皆不区分大小写。
字符串型的值用一对单引号()或一对双引号()所包围,字符串中可以用 \t 代表制表符,用 \n 代表换行,用 \’ 代表单引号,用 \” 代表双引号,用 \\ 代表反斜杠本身。字符串中间可以直接换行,无需增加任何其他符号,换行符也会作为字符串的一部分。
也可以用前后各三个单引号来('’’)表示一个字符串,这种字符串被称为长字符串。在长字符串中,可以直接写回车符、单引号和双引号,无需用\n\’或者\”
函数型的值只能是已经定义好的函数,在第9节详述。
复合型的值包括数组、字典等,在下一节详细阐述。
空值型的值总是Null,不区分大小写。
例如:
a = 1   // a是整数型变量
a =     &HFF  // a还是整数型变量
a =     True   // a是布尔型变量。作为动态类型语言,a的类型可以随时变化
a =     FALSE  // a是布尔型变量,注意True和False都不区分大小写
a =     ‘UiBot’  // a是字符串型变量
a = “UiBot
RPA”              // a是字符串型变量,字符串中可以换行
a = null          // a是空值型变量,可以写为Null、NULL或null(不区分大小写)
变量的定义方式是:
Dim 变量名
定义变量名的同时,可以给变量赋值一个初始值:
Dim 变量名=
想要定义多个变量的话,可以这样定义:
Dim 变量名1=值1, 变量名2
Dim 变量名1=值1, 变量名2=值2
常量的定义方式和变量类似,只是把Dim改为Const,并且必须在定义时就指定值:
Const 常量名=, 常量名=值
常量和变量的唯一区别是,常量只能在定义时指定一次值,后面不允许再修改。
例如:
Dim a      // 定义名为a的变量,暂不赋值
Dim b =     1  // 定义名为b的变量,并赋值为1
Dim c, d =     True   // 定义名为c和d的两个变量,为d赋值True
Const e = ‘UiBot’  // 定义名为e的常量,为其赋值为字符串’UiBot’
Const f                   // 错误:常量必须有初始赋值
变量名可以用英文字母、下划线(_),任意UTF-8编码中包含的除英语以外其他语言的字符(当然,也包括汉字)表示,除了第一个字符外,后面还可以使用0-9的数字。变量名不区分大小写。
UB语言规定变量必须经过定义才能使用(除了For语句中的循环变量、Try语句中的异常变量、函数参数等)。变量在函数范围内定义时,属于局部变量,在函数退出时即清空。在函数范围之外任何位置定义时,属于全局变量,在脚本运行过程中不会清空。全局变量可以定义在函数范围外任何位置,不影响其使用,甚至可以在使用变量之后定义。

6.     复合类型
除了常用的整数型、字符串型等数据类型之外,UB语言还包括两种复合类型:数组、字典。两者在定义时和普通变量并无区别。
数组的值可以采用这种方式书写:
[值1, 值2, 值3 ]
其中值可以是任意类型,同一个数组中的不同值也可以是不同类型,值甚至可以是另外一个数组,这样就构成了一般意义上的多维数组。
字典的值可以采用这种方式初始化:
{ 名字1:值1, 名字2:值2, 名字3:值3 },其中 名字 只能是字符串,值可以是任意类型的表达式。如果您熟悉JavaScript或者JSON,会发现这种初始化方法和JSON的表示形式高度相似。
无论是数组还是字典,要引用其中的元素,均采用方括号作为索引,如果要引用数组中的数组(即多维数组),或字典中的数组,可以继续在后面写新的方括号,如:
变量名[索引1][索引2]
用这种方式表示的数组中的元素或者是字典中的元素,既可以作为左值也可以作为右值,也就是说,既可以使用其中的值,也可以为其中的内容赋值,甚至可以在其中增加新的值。
例如:
Dim 变量 = [486, 557, 256]    // 变量可以用中文命名,初值是一个数组
a = 变量[1]                                 //     此时a被赋值为557
变量 =     {"key1":486, "key2":557, "key3":256}   // 变量的类型改为一个字典
a = 变量["key1"]                         //     此时a被赋值为486
变量["key4"]     = [235, 668]             // 往字典中增加一个新值,可以是一个数组
//此时,字典中的内容为     {"key1":486, "key2":557, "key3":256,     "key4":[235, 668]}
a = 变量["key4"][0]                     //     此时a被赋值为235
注意:在引用数组或字典中的元素时,数组的索引只能是整数类型,用0作为起始索引;字典的索引只能是字符串类型。如果未能正确的使用,会在运行时报错。

7.     运算符
UB语言中的运算符及其含义如下表:
2.png
       把变量、常量和值用运算符和圆括号 ( ) 连接到一起,称为表达式。在上述运算符中,Not是一元运算符、- 既可以用作一元运算符,也可以用作二元运算符,其他都是二元运算符。一元运算符只允许在右边出现一个元素(变量、常量、表达式或值),二元运算符只允许在左右两边同时出现两个元素。
注意:当 = 出现在表达式内部时,其含义是判断是否相等。当 = 构成一个独立的语句时,其含义是赋值。这里 = 的设计虽然具有二义性,但能更好的被初学者所接受。
       UB语言中删掉一些其他语言中具备、但不常用的运算符,如整数除运算符、位操作运算符等等。因为这些运算符的使用场景较少,即使需要,也可以采用其他方式实现。
表达式常用于赋值语句,可以给某个变量赋值,其形式为:
变量名 = 表达式
注意,当表达式为一个独立的(没有使用任何运算符计算)数组、字典类型的变量时,赋值操作只赋值其引用,也就是说,只是为这个变量增加一个“别名”。当一个数组、字典中的元素发生改变时,另一个也会改变。
例如:
a = [486,     557, 256]    // a是一个数组
b = a                            // b是a的“别名”
b[1] = 558                     // 改变b里面的值,a里面的值也会跟着改变
c = a[1]                         // 此时c的值是558,而不是原来的557
a = 557                         // 此时a被赋值为557(变为整数型)
b = a                            // 此时b里面的值也是557,但和a分别保存
b = 558                         // b里面的值发生改变,a的值不改变
c = a                             // 此时c的值仍然是原来的557,因为a不是字典、数组

8.     逻辑语句
l  条件分支语句
即一般编程语言中最常用的If…Else语句,主要用于对某一个或者多个条件进行判断,从而执行不同流程。在UB语言中,有以下几种形式:
If 条件
语句块1
End If
或者

If 条件
语句块1
Else
语句块2
End If
或者

If 条件1
语句块1
ElseIf 条件2
语句块2
Else
语句块3
End If
当条件满足时,会执行条件之后的语句块,否则,语句块不会执行。Else后面的语句块则会在前面所有条件都不满足的时候,才会执行。
例如:
//     Time.Hour() 可以取得当前时间中的小时数
//     TracePrint() 可以把指定的内容输出到UiBot的界面中

If     Time.Hour() > 18                      //     取得当前时间中的小时数
    TracePrint("下班时间")          // 如果大于18,则执行这里的语句
Else
    TracePrint("上班时间")          // 如果不满足前面的条件,则执行这里的语句
End If

l  选择分支语句
根据一定的条件,选择多个分支中的一个。先计算Select Case后面的表达式,然后判断是否有某个Case分支和这个表达式的值是一致的。如果没有一致的Case分支,则执行Case Else(如果有)后面的语句块。
Select Case 表达式
Case 表达式1, 表达式2
语句块1
Case 表达式3, 表达式4
语句块2
       Case Else
              语句块3
End Select
例如:
Select Case Time.Month()            // 取得当前时间中的月份
    Case 1,3,5,7,8,10,12               // 如果是1、3、5、7、8、10、12月
        DayOfMonth = 31         // 当月有31天
    Case 4,6,9,11                        // 如果是4、6、9、11月
        DayOfMonth = 30         // 当月有30天
    Case Else                             // 如果是其他(也就是2月)
        DayOfMonth = 28         // 当月有28天(不考虑闰年的情况)
End Select
TracePrint(DayOfMonth)

l  条件循环语句
在UB语言中,使用DoLoop语句来实现条件循环,即满足一定条件时,循环执行某一语句块。DoLoop语句有以下五种不同的形式,用法较为灵活:
(1)前置条件成立则循环:先判断条件,条件成立则循环执行语句块,否则自动退出循环。
Do While 条件
        语句块
Loop
(2)前置条件不成立则循环:和前一条相反,条件成立则退出循环,否则循环执行语句块。
Do Until 条件
        语句块
Loop
(3)后置条件成立则循环:先执行语句块,再判断条件,条件成立则继续循环执行语句块,否则自动退出循环。
Do
        语句块
Loop While 条件
(4)后置条件不成立则循环:先执行语句块,再判断条件,条件成立则自动退出循环,否则继续循环执行语句块。
Do
       语句块
Loop Until 条件
(5)无限循环:该循环语句本身不进行任何条件的判断,需要在语句块中自行做判断,如果语句块中没有跳出循环的语句,则会无限的执行该循环
Do
       语句块
Loop
例如:
Do Until Time.Hour()     > 18           // 判断当前时间中的小时数,只要不大于18就循环
    TracePrint("还没有到下班时间")  // 每次循环,都会执行这里的语句
        Delay(1000)                         //     每判断一次,休息一秒钟
Loop
TracePrint("下班时间到啦")          // 如果大于18,则跳出循环,执行这里的语句

l  计次循环语句
计次循环语句主要用于执行一定次数的循环,其基本形式为:
For 循环变量 = 起始值 To 结束值 Step 步长
       语句块
Next
在计次循环语句中,起始值、结束值、步长都只允许是整数型或者浮点数型;步长可以省略,默认值为1。变量从起始值开始,每循环一次自动增加步长,直到大于结束值,循环才会结束。
在计次循环语句中,循环变量可以不用Dim语句定义,直接使用,但在循环结束后就不能再使用了。
例如:
Dim count =     0                     // 定义变量count
For i=1 To     100                     // 每次循环,变量i都会加1。这里变量i不需要定义
    count = count + i
Next
TracePrint(count)                  // 这里会显示1+2+3+…+100的结果,即5050

l  遍历循环语句
遍历循环语句可以用于处理数组、字典中的每一个元素。遍历循环语句有以下两种形式:

For Each 循环变量 In 数组或字典
       语句块
Next
在这种形式的循环语句中,会自动遍历数组、字典中的每一个值,并将其置入循环变量中,直到遍历完成为止。
或者:
For Each 循环变量1, 循环变量2 In 数组或字典
       语句块
Next
在这种形式的循环语句中,会自动遍历数组、字典中的每一个索引和值,并将其分别置入循环变量1循环变量2中,直到遍历完成为止。
和计次循环语句类似,在遍历循环语句中,循环变量可以不用Dim语句定义,直接使用,但在循环结束后就不能再使用了。
例如:
Dim days = [31,     28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]       // 定义数组型变量days
Dim count =     0
For Each i     In days              // 每次循环,变量i的值分别为days中的每个值
    count = count + i     // 把数组中的每个值依次加起来
Next
TracePrint(count)                  // 这里会显示一年中每个月的天数的累加和,即365

l  循环跳出语句
在UB语言中,支持以下形式的循环跳出语句:
Break
只能出现在条件循环、计次循环或遍历循环等循环语句的内部语句块中,其含义是立即跳出当前循环。
Continue
只能出现在条件循环、计次循环或遍历循环等循环语句的内部语句块中,其含义是立即结束当前循环,并开始下一次循环。
例如:
Dim days = {     '一月':31, '二月':28, '三月':31,
    '四月':30, '五月':31, '六月':30,
    '七月':31, '八月':31, '九月':30,
    '十月':31, '十一月':30, '十二月':31 }       // 定义字典型变量days
For Each i,j     In days            // 每次循环,变量i, j分别为days中每个名字和值
    If j Mod 2 = 0                // 如果j是偶数
           Continue                // 结束本次循环,开始下一次循环
    End If
    TracePrint(i)          // 把days中的名字(其值不是偶数)显示出来
Next

l  其他语句
Exit语句:在脚本任何地方,只需要书写
Exit
不需要任何参数,即可在执行到此行的时候自动结束脚本执行。

9.     函数
所谓函数,是指把一组常用的功能包装成一个语句块(称之为“定义”),并且可以在其他语句中运行这个语句块(称之为“调用”)。使用函数可以有效的梳理逻辑,以及避免重复代码的编写。
函数的定义和调用没有先后关系,可以先出现调用,再出现定义。但函数必须定义在全局空间中,也就是说,函数定义不能出现在其他函数定义、分支语句、循环语句下面的语句块中。
函数定义中可以包含参数,参数相当于是一个变量,但在调用时,可以由调用者这些变量的值。
定义函数的格式如下:
无参数的函数:
Function 函数名( )
       语句块
End Function
或者有参数的函数:
Function 函数名(参数定义1, 参数定义2)
       语句块
End Function
其中,参数定义的形式是:
变量名=表达式
也就是说,参数定义可以只是一个变量名,也可以带有默认值,其默认值由“表达式”来确定。如果函数有参数,则参数中的每个变量名都被认为是此函数内已经定义好的局部变量,无需使用Dim语句定义。
在函数定义中,要退出函数并返回,采用以下写法:
Return 返回值
当执行到这一语句时,将跳出函数并返回到调用语句的下一行。返回的时候可以带一个返回值(具体作用下文叙述)。返回值可以忽略,默认为Null。当执行到函数末尾的时候,无论有没有写Return语句,都会返回。
例如:
Function Add(x,     y=1)                   // 定义了两个参数的函数,第二个参数有默认值
    Return x + y                         // 返回值为x+y的值
End Function
调用函数的格式如下:
返回 = 函数名(表达式1, 表达式2)
或者
函数名 表达式1,表达式2
按照第一种格式调用,可以指定一个变量作为返回,当函数调用完成后,函数的返回值会自动赋值给这里的返回变量,调用者可以通过返回值,了解到函数调用的情况。此时,必须在被调用的函数名后面加圆括号。而当按照第二种格式调用时,调用者不需要返回值,则可以省略圆括号,使语句更符合自然语言习惯。
当调用时,相当于对函数中的参数进行了一次赋值运算,用表达式的值对其赋值。与赋值运算的规则相同,当表达式为一个独立的(没有使用任何运算符计算)数组、字典时,赋值操作只赋值其引用,也就是说,只是为变量增加一个“别名”。
调用函数时,传入的表达式的数量可以少于参数的数量,如果某个参数没有传入值,则采用其默认值,如果连默认值也没有,则其值自动为Null。
例如,对于上面定义的函数,可以按照如下的方式调用:
a = Add(100)                 // 调用Add函数,第二个参数取默认值1,所以a的值是101
b = Add(100,     200)         // 调用Add函数,指定了两个参数,所以b的值是300
Add 100, 200                // 调用Add函数,不关心返回值,所以可以不写括号
当函数定义完成后,其名称可以作为一个函数类型的常量使用,也可以把函数名称赋值给某个变量,用这个变量也可以调用这个函数。
例如,对于上面定义的函数Add,可以按照如下的方式使用:
Dim Plus = Add
TracePrint     Plus(100, 200)  // 相当于先调用了Add函数,再用其返回值调用了TracePrint函数,结果是300
除了在脚本中定义的函数之外,UB语言中也已经内置了很多函数,可以完成各种丰富的功能。比如上面例子中的TracePrint就是一个内置函数。

10.  多模块
UB语言支持多模块,可以用其他语言实现扩展模块,并在源码中使用。目前支持以下几种类型的模块:1)UiBot语言的脚本;2)Python语言的模块;3)C/C++语言的模块;4).Net的模块;5)Lua语言的模块。不同的模块有不同的扩展名,去掉扩展名以后,剩下的文件名就是模块的名字。比如某个Python语言的模块,文件名为Rest.py,则其模块名为Rest。
在UiBot语言中,采用以下方式导入一个模块:
Import 模块名
注意这里的模块名的书写规则和变量名一致,不需要采用双引号,也不需要加扩展名。如Import Rest。UiBot在编译和运行时会自动按照Lua语言模块、C语言模块、.Net语言模块、Python语言模块、UB语言脚本的先后顺序,依次加上相应的扩展名进行查找。在Windows中,由于文件名不区分大小写,所以Import语句后面的模块名也可以不区分大小写。在其他操作系统中,需要注意模块名的大小写要和文件一致。
每个导入的模块,都会被放置在一个与模块名同名的“命名空间”中,可以通过下面这种方式来调用导入模块中的函数:
命名空间.函数名
即在命名空间和函数名之间加一个点号(.)进行分隔。
对于Python、Lua语言的模块,只会保留其中的全局变量定义和函数定义,其他内容都会被忽略。对于C语言的模块和.Net模块,只能调用其中定义的函数。
对于UB语言的模块,既可以调用其中定义的函数,又可以直接以模块名作为函数名,直接调用这个UiBot语言的脚本。例如,有一个脚本 ABC.task。在其他脚本中Import之后,直接采用下面的格式即可调用ABC.task:
ABC ( )

11.  异常
作为动态类型语言,有很多错误在编译时难以检查,只能在运行时报错。而且,由于UiBot不强调运行速度,而更强调运行的稳定性,也会在运行时加入比较多的检查。运行时出错,比较合适的报错手段是抛出异常。
在脚本中,可以采用Throw语句抛出一个异常:
Throw 字符串
在抛出异常时,可以把异常相关信息以字符串的形式一起抛出,也可以省略这个字符串。
如果脚本中没有对异常进行处理,当出现异常时,脚本将终止执行。
可以采用以下语句对异常进行处理:
Try
       语句块
Catch 变量名
       语句块
Else
       语句块
End Try
       如果在Try中发生了异常,会跳到Catch语句块中执行。如果在Try中没有发生异常,且定义了Else语句块(当然,也可以省略Else语句块),则会跳到Else语句块中执行。
Catch语句后面的变量名可以省略。如果不省略,可以不用Dim语句提前定义,当发生异常时,这个变量的值是一个字典,其中包含“File”、“Line”和“Message”三个字段,分别代表发生异常的文件名、发生异常的行号、异常包含的信息。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /2 下一条

手机版|小黑屋|RPA俱乐部 ( 闽ICP备15006840号-20 )

GMT+8, 2019-3-19 08:18 , Processed in 0.048404 second(s), 22 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表