在 aardio 编辑器中仅选中特殊符号或标记,按 F1 可直接打开相关文档。
{} 表构造器、语句块标记{} 可用作包含 语句块 的首尾标记,语句块一组顺序执行的语句 组成,并可创建独立的局部变量 作用域,局部变量拥有最高的存取优先级,查找同名变量时将优先搜索当前语句块的局部变量,例如下面的代码创建了多个语句块:
import console;
//创建函数体
var func = function(cond){
//声明局部变量
var v = 1;
//条件语句
if(cond){
//声明局部变量
var v = 2;
console.log(v);
}
}
func(true);
console.pause(true);
{} 在表达式中用于构造表对象,表是 aardio 中唯一的复合数据类型,
即可用作哈希表,也可以同时包含有序数组、稀疏数组等成员。声明字段的原生类型时 —— 还可以表示结构体。
表中默认用 ; 号分隔成员( 可用逗号替代 ),默认用 = 号分隔键值对成员(非结构体字段可用 : 号替代 ),示例:
//名值对成员,可用 `:` 代替 `=` 符号分隔键值对
var object = { name1 = "abc"; name2 = 123 }
//用字符串包含键名,可用 `:` 或 `=` 符号分隔键值对
var object4 = { "name1":"abc", "name2":123 }
//可将任意表达式放在下标操作符 [] 内部表示元素的键,这种方式不在纯数组构造器 [] 内使用(纯数组内 [] 也是用于构造纯数组)。
var object2 = { ["name1"]="abc", ["name2"]=123 }
//数值后跟一个冒号表示数值索引,这种键值对不能用 `=` 符号分隔。
var object3 = { 123:"稀疏数组成员" }
//表也可以包含稠密数组成员,可以省略数值索引,以数组出现的位置作为索引(aardio 数组起始索引为 1 )
var array = { 1;2;3 } //也可以用分号分隔成员
//表可以可时包含稠密数组与其他类型的键值对成员
var objectAndArray = { name1 = "abc"; name2 = 123; 1;2;3 }
虽然在 aardio 中以上构造表的语法都是正确的,但一般建议在 {} 构造器内的规范写法应使用 ; 号分隔元素,并使用 = 号分隔键值对。
. 成员操作符用于访问对象的成员,
例如 io.open 表示 open 函数是 io对象的成员( 这里是命名空间成员 )
.. 全局成员操作符这个操作符用在自定义的命名空间里访问全局命名空间 global;
例如 ..io.open() 实际上等价于 global.io.open() 。注意 .. 后面不能紧接空白字符。
:: 保留常量操作符这个操作符用于将一个合法的标志符转换为 global 命名空间下的保留常量名 - 并保护该常量在其后加载的代码中一旦赋为非空值后即不可修改,例如:
::Kernel32 := raw.loadDll("Kernel32.dll");
保留常量需要遵守以下使用规则
保留常量名首字母不能是小写或下划线(以区别普通变量 ), 另外在 :: 后面不能紧接空白字符。
当一个变量被定义为保留常量,赋于非空值以后其值即不能再随意更改 。
保留常量一般使用 ::Name := 初始值 赋值,等价于使用 ::Name = ::Name or 初始值 以避免重复初始化。
保留常量以后即使不添加 :: 前缀仍然属于相同的保留常量。注意 :: 的作用域是根据代码的载入与编译顺序向下向后生效,但是代码文件的载入顺序存在不确定性,因此在任何时候都不应当省略保留常量的 :: 前缀。
# 计数操作符当 # 操作符 在一个数组前面时返回数组长度。
# 操作符用在字符串或 buffer 对象前面时返回字节数。注意 UTF16/UTF8 字符串用 # 取到的都是字节数而不是字符数,UTF8 字符串使用 string.len() 函数可以获取字符数,而 UTF16 字符串只要简单的用字节数除以 2 就可以得到字符数。
#null 也会返回数值 0。
在 aardio 条件表达式中
null、0等价于 false, 非false非null并且 非0的其他所有值都为true,空字符串与空数组也为true。如果要判断一个对象是否为非空字符串,更简洁的写法是使用#操作,例如if( ! #stringOrNull ) print("字符串不能为空")。
如果 #在一个单引号包含的字符后面,计算并返回字符的 ASCII 码,例如 'a'# 返回表示 a 的字节码的数值97。
当#放在数字中间时,用来表示自定义进值,例如 2#101 表示2进制的101
[] 纯数组构造器、下标操作符( 或者叫索引操作符 )[] 可以用于构造纯数组()( pure-array ),例如 [1,2,3,[4,5,6] ]。
注意用 [] 构造纯数组时,内部的 [] 也用于构造纯数组,不能用于包含元素的键。
在 {} 构造的表内部,可以将任何表达式放在 [] 内表示元素的键,
例如 { ["键名"]=123;[1+2]=3 } ,在纯数组内部不能这样用。
在表构造器之外,[] 也可以作为普通下标操作符用来访问对象的成员。
中括号里面应当是一个合法的表达式,例如 global["全局变量名"] 。
而使用 . 成员操作符 后面紧接的成员名字则不需要放到字符串里,并且必须是一个合法的标识符。但下标操作符就不同了,可以放任意的表达式,例如 io[ "o" + "pen" ] 这样写也是可以的。
当使用操作符调用成员函数时,被调用函数的 owner 参数为空。 所以一般不应当写为 io["open"]() , 应当写 io.open() 以保证正确传递 owner 参数。
下标操作符也可以用于字符串、或 buffer 对象,返回的是指定位置的字节码(数值),例如:
import console;
var str = "test测试"
var wstr = 'Unicode/UTF16宽字符串'u
var buf = raw.buffer("abc测试");
console.log( str\[1\], wstr\[1\], buf\[1\] );
console.pause(true);
要特别注意的是:
UTF16 字符串使用下标操作符 [] 返回的是 UTF-16 宽字符编码(2 字节 16 位 无符号数值)。
如果需要返回宽字符而不是字符编码(数值),需要改用下面会讲到的直接下标操作符 [[]] 。
[[]] 直接下标操作符这个操作符与 [] 的用法基本是一样的,
唯一的区别是他不会触发元方法,所以数组里实际有这个成员就是有,没有就是没有,忽悠不了这个操作符。
这个直接下标操作符可以应用于任何类型的对象( 包括null空值 )不会报错,
如果对象不包含直接下标操作符中指定的成员就简单的返回 null空值。所以 [[]] 即可以用来取值同时又可以方便地检测对象类型,例如:
if( 可能为空或任意类型的变量[["test"]] ){
print( 可能为空或任意类型的变量[["test"]] )
}
不要小看这个操作符 - 使用频率非常高,而且可以节省不少的代码。
最近Javascript, Typescript里一个炒的很火的新语法“可选链”跟这个直接下标有点像,解决的也是类似的问题,实际上 aardio十几年前就有这些了。
将普通下标操作符用于字符串时, []操作符取的是字节码、是个数值,而 [[]] 取出来的是字符。
例如定义字符串变量 var str = "abcd" 之后,str[1] 是 "a" 的 ASCII 码 97,而 str[[1]] 则返回字符"a" 本身(仍然是一个字符串对象)。
对于Unicode/UTF16字符串,[] 操作符取的是 UIF-16 宽字符编码( 以 2 个字节为单位的 16 位数值 ),而 [[]] 操作符返回的是宽字符( 以 2 个字节为单位的单个 UTF-16 字符 ),但使用 # 取长度时就总是返回 8 位字节( byte )长度 - 这个要注意区别,下面看一个完整例子。
import console;var ws = 'abc中文'u;
for(i=1;#ws/2 ){
console.log("Unicode字节码", ws[ i ], "Unicode字符",ws[[ i ]] )
}
console.pause(true);
@ 元表操作符这个操作符表来读取或设置对象的元表, 关于这个请查看帮助文档
一个简单的示例
var tab = { a = 123 };
tab@ = {
_get = function(name){ return "有没有:" + name;
}
}
print( tab.a ) // -> 显示123
print( tab.b ) // -> 显示"有没有:b"
print( tab[["b"]] ) // -> 显示 null 空值
@ 在模式匹配里还有特殊用途。
如果一个模式串的第一个字符是‘@’,表示全局禁用模式语法执行普通的二进制匹配。
如果一个模式串的第一个字符是两个'@@',同上表示禁用模式语法并且执行文本匹配(不会截断宽字符。)
也可以在模式串的尖括号中使用一个'@" 或两个 '@@' 表示局部禁用模式语法( 两个‘@@’ 表示启用文本匹配,并且忽略大小写 )
示例如下:
var str = "abc\d测试字符串"
//模式匹配
var i,j = string.find(str,"\\d")
//禁用模式匹配
var i,j = string.find(str,"@\d")
//禁用模式匹配、且启用文本匹配
var i,j = string.find(str,"@@\d")
//局部禁用模式匹配
var i,j = string.find(str,"<@\d@>:+")
//局部禁用模式匹配、且启用文本匹配、并且忽略大小写
var i,j = string.find(str,"<@@\D@>:+")
_ 下划线如果在一个成员变量的前面加上下划线,则声明该变量的值为只读,在赋值后不可修改
例如执行 _version = 123 以后你就不能在后面再执行 _version = 456 修改非 null 值的 _version, 这种习惯在其他动态语言中或许只是一种书写习惯,但是在 aardio 则是语法级别的强制约束。
如果下划线后面的变量名全部大写,则表示全局只读的常量
例如 _VERSION = 123 表示他在所有命名空间都有效。
另外数值的字面值允许加入下划线作为数值分隔符,
例如 123_456 等价于 123456, 2#1010_1100 等价于 2#10101100,
数值分隔符不能使用连续多个下划线,并且不能在字符串中使用数值分隔符,例如:
tonumber("123_456") //返回的是123
("123456") + 1 //返回的是一个数值 123457
("123_456") + 1 //会报错
“\”,"/" 应用程序根目录在 aardio 中文件路径如果以单个斜杠或反斜杠开始表示『应用程序根目录』。
『应用程序根目录』指启动程序文件所在目录,在开发时指 aardio 工程目录,在发布后指启动 EXE 目录。
如果启动文件在工程外部,或者当前没有打开工程,则以启动文件所在目录为 『应用程序根目录』。
如果在开发环境中运行没有保存的 aardio 代码,则仍以当前工程根目录为『应用程序根目录』,如果没有打开工程,则以 aardio.exe 所在目录为 『应用程序根目录』。
~ EXE 目录,按位取反运算符~ 在 aardio 里有两个用途:
在文件路径的开始出现时表示 EXE 根目录。
在 aardio 中如果文件以 "~" 右单个斜杠或反斜杠开始表示启动EXE 所在目录。
如果没有生成 EXE ,在开发环境中直接运行代码,EXE 目录指的是 aardio.exe 所在目录。
数值按位取反运算符
单个 ~ 作为数值位运算符使用时表示按位取反。
$ 包含操作符,模式匹配尾部锚点包含操作符
这个包含操作符 挺有意思,
只要在文件路径前面加上这个符号, 就会将该文件编译为一个普通的字符串对象.
例如 str = $"e:/我的图像/x.jpg" ,在程序发布后,程序即可脱离原来的文件运行,因为该文件已经被编译为一个普通字符串变量并内嵌到EXE中了。 在 aardio 编辑器里,只要将资源管理器里的文件直接往编辑器里一拖就行了,会自动加上这个包含指令符。
如果 $ 包含的文件路径以~/或~\开始,并且查找文件失败,
aardio 会移除路径前面的~,转换为\或/开头的应用程序根目录路径继续查找。
应用程序根目录在开发时指工程根目录(工程之外的 aardio 文件指启动aardio文件所在目录)。
反之,如果包含的文件以/或\开始,并且查找包含文件失败,
aardio 不会在路径前面添加 ~ 到EXE目录下查找(EXE目录在开发时指 aardio 开发环境所在目录)。
默认如果找不到包含文件会报错,但是如果包含文件路径前面添加一个问号,
找不到文件时不报错而是返回null。 例如: str = $"?找不到的文件"
模式匹配尾部锚点
在模式匹配中用于匹配字符串尾部边界。参考:模式语法 - 首尾锚点
^ 按位异或在模式匹配语法中表示字符串的 开始锚点。
++ 字符串连接操作符在 aardio 里 1 + 2 的结果是数值 3,
而 1 + "" + 2 的结果是字符串 12 这个不难理解吧?
上面的 + "" + 可以直接缩写为 ++ ,
也就是说 1 ++ 2 等价于 1 + "" + 2,相加的结果就是字符串 12 。
如果加号前面或后面出现了引号包含的字符串字面值,连接字符串就不用再写两个加号,例如:
"1" + "2" 的结果是 字符串 12,
也就是说,只有在 + 号前后没有引号包含的字符串字面值, 你就需要用这个 ++ 来告诉 aardio,你要做的是字符串连接,而不是数值加运算。
// 行注释语句这个是比较通用的语法, 不过在aardio里有一个特殊的用法:
行注释可以用于赋值语句中表示字符串,例如 var str = //这是一个字符串
这个与双引号类似,字符串都表示字面意义,没有转义符。
/* */ 块注释语句这个也类似其他类 C 语言,但注意首尾的星号可以用多个、并且首尾星号的数目一定要匹配。
aardio使用这个特性来解决注释嵌套的麻烦问题。
另外,块注释也可以用在赋值语句中表示字符串,字符串都表示字面意义,没有转义符。
而换行总是被强制解释为 '\r\n',以避免不同文本编辑器导致的可能混淆的结果。
这个特性用来包含HTTP头之类严格指定回车换行的文本很方便。
aardio中可以调用一些其他语言的源代码,通常要包含大段的其他语言的源码,放到这个块注释里赋值给字符串变量就可以了。直接复制粘帖,不需要象其他语言里一样苦闷的折腾字符串转换。
单引号包含编译时转义字符串
var backslashEscapedString = '单引号内是 JavaScript 风格字符串,识别 \ 转义符。
例如 \r\n 表示回车换行,'
注意单引号内部由源代码包含的原始换行被忽略,只能用转义符表示回车换行。
双引号里表示原样字符串、编译阶段不处理转义,将反斜杠识别为普通字符。
var rawString = "双引号用来
包含原样字符串,可以用重复的 "" 表示双引号"
双引号里面源代码包含的回车换行被格式化为单个换行符 \n,在双引号无法表示回车符。
反引号里也表示原样字符串。
var rawString = `双引号用来
包含原样字符串,可以用重复的 `` 表示双引号`
反引号里面源代码包含的回车换行也被格式化为单个换行符 \n,在反引号也无法表示回车符。
反引号按键`位于键盘左上角 ESC 键下方的。
** 幂运算符aardio 使用两个星号 ** 表示乘方运算,例如 2 ** 3
取模运算符
aardio 取模运算符 % 的计算过程为 a % b == a - ⌊a/b⌋ b,a % b 的结果与 a - ( math.floor(a/b) * b) 相同。
例如 24 小时制的 19 点转换为 12 时制就可以表示为 19 % 12,计算结果是 7 点。
对称模式匹配
在 aardio 模式匹配语法中 % 用于匹配成对的字符串。
模式语法:%<begin_subpattern><end_subpattern>
例如 %() 匹配成对的括号。
or || : 逻辑或运算符这几个运算符语义都是完全相同的,唯一的区别是 : 的优先级略低。
要特别注意,在构造表或纯数组时, : 在标识符、字符串字面量、数值字面量表示的键后面可作为健值分隔符使用。例如 [123:"稀疏数组成员",1,2,3] 。
and && ? 逻辑与运算符这几个运算符语义也是完全相同的,唯一的区别提 ? 的优先级略低
这个是三元运算符,计算规则为:
a为真则计算 b( b 为真则返回 b,否则仍然计算并返回 c ),否则计算并返回c。
当 a 与 b 条件满足时不会计算 c( c 如果是一个函数调用就不会执行 ),a 为假时不会计算 b。
注意与 C 语言有所区别:
当 b 运算结果为假的时候仍然会返回 c,aardio 里这个三元运算符是尽最大可能去取回真值。
() 括号这个用在表达式中可以改变操作符的优先级, 例如 7 * ( 2 + 3 ) 括号里面的会先运算.
放在函数名后面则表示调用执行该函数,例如
print("还可以写一个或多个参数")
定义函数的时候用来表示形参,例如
func = function(a,b,c){
print("收到参数",a,b,c )
}
... 不定参数运行下面的示例你就明白了:
func = function( a,... ){
print( "收到参数",... )
}
func( 1 )
func( 1,2 )
func( 1,2,3,4 )
... 用于表示不定个数的调用参数,注意 ... 只能放在参数列表的尾部。
我们可以使用 {...} 将不定个数的参数转换为数组对象。
λ 希腊字母 λ 在aardio中可用于替代 lambda 关键字lambda 用于定义匿名函数,示例代码:
import console;
var arr = {1;2;3;4;7}
var value = reduce(arr,λ(a,b) a + b );
console.dump(value);
console.pause(true);
代码 λ(a,b) a + b
等价于 lambda(a,b) a + b
也等价于 function(a,b) return a+b;
<? ?>,<?= ?> 模板语法aardio 源码文件自动支持模板语法,模板主要由两个部分组成:
<? 到 ?> 之间的部分为 aardio 代码。?> 之后或 <? 之前的部分为模板代码。模板代码实际上是解析为调用 print 函数的参数,而 <?= exp ?> 则被解析为 print(exp) 调用。
aardio 模板语法通常用于网站服务端生成 HTML 页面,有时候也被用调用 aardio 代码以生成其他格式的字符串。
aardio 源文件按以下规则开始解析模板语法:
?> 标记被解析为代码,遇到 ?> 则开始解析模板代码。<? 标记被解析为模板。string.loadcode 等函数支持用 aardio 模板语法解析代码并返回输出的字符串。
另外还有一些库函数的字符串参数可选支持 aardio 模板语法,这些函数通常会要求字符串以 <? 或 ?> 开始才会以 aardio 模板语法进行解析( 可参考 string.loadcode 源码 ),否则将参数作为普通字符串处理。
请参考: 模板语法
/*DSG{{*/ /*}}*/ 窗体设计器代码块aardio 窗体设计器生成的代码会置于 /*DSG{{*/ 到 /*}}*/ 中间。
例如:
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=759;bottom=469;)
winform.add(
button={cls="button";text="Button";left=221;top=297;right=340;bottom=329;z=2;};
edit={cls="edit";text="Edit";left=66;top=175;right=314;bottom=203;edge=1;multiline=1;z=1;};
)
/*}}*/
winform.show();
win.loopMessage();
在 aardio 开发环境中打开 *.aardio 源文件时,
aardio 会搜寻 /*DSG{{*/ 到 /*}}*/ 的创建窗体与控件的代码块,并可以在窗体设计器中呈现并修改生成窗体的 aardio 代码。