aardio 文档

aardio 编程语言快速入门 —— 语法速览

 第一个控制台程序

//导入库
import console; 

//加载动画
console.showLoading("加载动画测试");
sleep(1000); //暂停 1 秒

//输出字符串
console.log('测试字符串');

//输出变量
console.dump({a=123;b=456});

//以 JSON 格式输出变量
console.dumpJson({a=123;b=456});

//暂停并等待按键
console.pause(true);

请参考:控制台入门教程

 第一个窗口程序

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form")
winform.add(
button={cls="button";text="测试";left=435;top=395;right=680;bottom=450;color=14120960;font=LOGFONT(h=-14);note="点这里";z=2};
edit={cls="edit";left=18;top=16;right=741;bottom=361;edge=1;multiline=1;z=1}
)
/*}}*/


//按钮回调事件
winform.button.oncommand = function(){
    winform.edit.text = "测试"; //修改控件属性
}


//显示窗口
winform.show();


//启动界面消息循环,保持窗口显示,并响应用户操作
win.loopMessage();

请参考文档:创建窗口与控件

第一个网页应用程序

import win.ui; 
var winform = win.form(text="窗口标题")

import web.view;
var wb = web.view(winform);//创建 WebView2 浏览器控件,参数指定宿主窗口(win.form 或 custom 控件对象)

// 导出 aardio 函数到 JavaScript 中
wb.external = {
    add = function(a, b) {
        return a + b;
    }   
} 

// 写入网页 HTML 代码
wb.html = /******
<div id="result"></div>

<script> 
(async ()=>{

    //调用 aardio 导出的 wb.external.add 函数。
    var num = await aardio.add(1, 2)

    //显示结果
    document.getElementById('result').innerText = num;
})()
</script>
******/;

//显示窗口
winform.show();

//启动界面消息循环
win.loopMessage();

也可以用 wb.go 函数打开网址或本地文件,例如:

wb.go("https://www.example.com/");

请参考文档:web.view 入门指南

 调用原生 API

//加载 DLL
var dll = raw.loadDll("user32.dll");

//调用 API 函数
dll.MessageBox(0,"测试","标题",0);

aardio 已经默认加载了一些常用的系统 DLL 对象,例如 ::User32, ::Kernel32, ::Shell32::Ntdll 等。

所以上面的代码可以简化为:

::User32.MessageBox(0,"测试","标题",0);

参考:免声明调用原生 API

 结构体

var info = {
    INT size = 8;
    INT time;
}
::User32.GetLastInputInfo( info )

因为 aardio 函数支持多返回值,且结构体默认为传址输出参数 —— 会添加到返回值中,所以上面的代码也可以这样写:

var ok,info = ::User32.GetLastInputInfo( {
    INT size = 8;
    INT time;
} );

参考:使用结构体

 注释语句

// 单行注释


/*  
多行注释,首尾星号数目必须相同。
*/

变量与赋值

// 使用 var 语句声明局部变量
var n = null;
var str = "字符串";

// 不使用 var 语句声明的变量为当前名字空间的变量
gstr = "字符串";

在 aardio 中,默认情况下变量属于当前名字空间。使用 var 语句可以定义局部变量,使其作用域仅限于当前语句块。

 语句与语句块

{
    var n = null;
    var str = "字符串";
}

语句末尾可以加分号,也可以不加。

aardio 使用 {} 来标记语句块。

使用 var 语句定义的局部变量具有块级作用域,即在语句块内定义的局部变量,其作用域仅限于该语句块内。

 数值

var num = 123.01; //10 进制数值
var hex = 0xEFEF; //16 进制数值
var num2 = 123_456; //可用下划线作为分隔符
var num3 = 2#010; //可在 # 号前自定义进制

 算术运算

var num = 3 + 2; //加,值为 5
var num = 3 - 2; //减,值为 1
var num = 3 * 2; //乘,值为 6
var num = 3 / 2; //除,值为 1.5
var num = 3 % 2; //取模,值为 1
var num = 3 ** 2; //乘方,值为 9

 位运算

import console;


//按位取反
var n = ~2#101;
//显示2#11111111111111111111111111111010
console.printf("2#%b", n)


//按位与
var n = 2#101 & 2#100;
//显示2#100
console.printf("2#%b", n)


//按位或
var n = 2#101 | 2#110;
//显示2#111
console.printf("2#%b", n)


//按位异或
var n = 2#101 ^ 2#110;
//显示2#011
console.printf("2#%b", n);


//按位左移
var n = 2#101 << 1;
//显示2#1010
console.printf("2#%b", n);


//按位右移
var n = 2#101 >> 1;
//显示2#10
console.printf("2#%b", n);


//按位无符号右移
var n = -2 >>> 18;
//显示 16383
console.log(n);


console.pause();

 字符串

var str = "双引号包含的是原始字符串( raw string literals ),不处理转义。
可直接包含换行符,不能包含回车符。
可以用 "" 表示一个双引号。";


var str2 = `反引号的包含的是原始字符串( raw string literals ),不处理转义。
可直接包含换行符,不能包含回车符。
可以用 `` 表示一个反引号。`;


var str3 = '单引号包含的是转义字符串( escaped string literals ) ,处理转义
忽略回车换行,反斜杆作为转义符使用
例如 \r\n 表示回车换行';


var str4 = /*
段注释可以赋值为字符串。
首尾星号数目必须配对。
忽略首尾紧邻星号的第一个空行(如果有的话),
其他换行总是会规范化为回车换行符,
也就是 '\r\n'。
*/


var str5 = //行注释也可赋值为字符串,不含回车换行
//在文件路径前加 $ 符号,可将该文件编译为字符串


var str6 = $"~/aardio.exe"

 二进制字符串、文本编码

var bin = '字符串可以包含任意二进制数据,例如 \0'


var utf8 = "字符串可以包含文本,默认为 UTF-8 编码"


var utf16 = '转义字符串后加 u 字符表示 UTF-16 编码字符串'u

aardio 在很多地方都支持自动编码转换,例如调用 Unicode(UTF-16) 版本的 API 函数时,UTF-8 字符串可自动转换为UTF-16 编码字符串( 支持双向自动转换),例如以下两句代码的作用是相同的:

::User32.MessageBox(0,"内容","标题",0);


::User32.MessageBox(0,'内容'u,'标题'u,0);

在调用 ::User32.MessageBox 时,aardio 会自动检测并优先使用 Unicode 版本的 ::User32.MessageBoxW 函数。当然,也可以主动在 API 函数名后加上大写的 W 尾标声明这是 Unicode 版本函数(即使该函数名尾部并没有 W,也可以添加 W 尾标)。

 字符串连接

//使用 ++ 操作符连接字符串
var str = "字符串1" ++ "字符串2";


//如果 ++ 前后有引号,可省略一个 + 号。
var str = "字符串1" + "字符串2";


//用 string.concat() 函数连接支持多参数,支持 null 值
var str = string.concat("字符串1","字符串2")

 读写文件

//写文件
string.save("/test.txt","要保存在文件的字符串");


//读文件
var str = string.load("/test.txt");

 文件路径

/*
文件路径开始可用单个斜杠(或反斜杠)表示应用程序根目录。
应用程序根目录在开发时指工程目录,发布后指 exe 目录。
*/
var str = string.load("\res\test.txt");


//文件路径中正斜杠可自动转换为反斜杠
var str = string.load("/res/test.txt");


/*
文件路径开始用波浪线表示 exe 所在目录。
aardio 不需要生成 exe 就可以运行调试,此时波浪线表示 aardio.exe 所在目录。
*/
var str = string.load("~/res/test.txt");


/*
aardio 很多读文件的函数都兼容工程内嵌资源目录。
不需要修改代码,所以 "\res\test.txt" 可以是资源目录。
如果读取文件失败,string.load() 会返回 null 值(不报错)。
*/
var str = string.load("\res\test.txt");


/*
将文件转换为完整路径。
将路径传给第三方组件时,建议这样转换一下。
*/
var path = io.fullpath("\res\test.txt");

注意在 aardio 中双引号内不需要转义反斜杠,所以写为 "\\res\\test.txt" 是错的。单引号内才需要使用转义符,例如 '\\res\\test.txt' 等价于 "\res\test.txt"

在 aardio 中一般不必要使用 "./dir/test.txt" 这样的相对当前目录路径。因为当前目录可以任意更改,你不知道什么时候某个第三方组件是不是悄悄帮你改了当前目录。

 嵌入文件到字符串

在文件路径前加上 $ 操作符可将该文件编译为字符串对象。

在编译该代码时文件必须存在,程序发布后就不再需要这个文件了。

示例:

var str = $"\dir\test.txt";

不要用这个方法包含资源目录下的资源文件,

因为这等于将同一个文件重复包含了多次,会不必要地增加发布体积。

 名字空间

//导入其他库文件,同时也导入该库创建的名字空间
import console;


//打开名字空间
namespace test.a.b{

    //定义当前名字空间变量
    console = 123;

    //..console 等价于 global.console
    ..console.log(console);
}


console.pause(true);

默认名字空间为全局名字空间,也就是 global 名字空间。在其他名字空间访问 global 名字空间必须在变量前加上两点( .. ) 。例如 global.console 等价于 ..console 。

在 aardio 中使用 import 导入其他库文件,同时也导入该库创建的名字空间。导入名字空间只是引入模块,访问该名字空间仍然要写完整的名字空间路径。

 表 (table)

table(表)是 aardio 中唯一的复合数据类型,除了非复合的基础数据类型以外,aardio 中几乎所有的复合对象都是表,即使是变量的命名空间也是表。表的本质是一个集合,可以用于容纳其他的数据成员,并且表也可以嵌套的包含其他的表(table),在 aardio 里表几乎可以容纳一切其他对象。

  1.  表可以包含键值对

    tab = {
        a = 123;
        str = "字符串";
        [123] = "不符合变量命名规则的键应放在下标内。";
        ["键 名"] = "不符合变量命名规则的键应放在下标内。";
        键名 = {
            test = "表也可以包含表";
        }
    }
    
  2.  表可以包含有序数组

    如果表中不定个数的成员的“键”是从1开始、有序、连续的数值,那么这些成员构成一个有序数组。aadio 中如果不加特别说明,数组一般特指有序数组,所有数组函数默认都是用于有序数组。

    //在表中创建数组
    var array = {
        [1] = 123;
        [2] = "数组的值可以是任何其他对象";
        [3] = { "也可以嵌套包含其他表或数组"}
    }
    
    //有序数组的键可以省略,下面这样写也可以(并且建议省略)
    var array = {
        123;
        "数组的值可以是任何其他对象";
        { "也可以嵌套包含其他表或数组"}
    }
    

    要特别注意 aardio 的数组起始索引为 1 。

  3. 表可以包含稀疏数组

    如果表中包含的成员使用了数值作为键,但是多个成员的键并不是从1开始的连续数值 - 则构成稀疏数组。在 aardio 一般我们提到的数组 - 如果未加特别说明则是特指有序连续数组(不包含稀疏数组)。

    如果表中包含了稀疏数组 - 也就是说成员的数值键(索引)包含不连续的、中断的数值,那么不应当作为有序数组使用。 aardio 中几乎所有针对数组操作的函数或操作符 - 如果未加特别说明都要求参数是有序数组。

    下面的数组就包含了 null 值,属于数值键(索引)不连续的稀疏数组:
    var array = { "值:1", null, "值:2", "值:4", null, null,"其他值" }

  4. 表的常量字段

    注意如果键名以下划线开始,则为常量字段(值为 非 null 值时不可修改) —— 可通过元属性 _readonly 禁用此规则,详细说明请参考语法手册

  5. 表的类 JavaScript 写法

    如果表不是一个声明原生类型的结构体,那么在表构造器中允许使用类 JavaScript 语法,可用冒号代替等号分隔键值,用逗号代替分号分隔表元素,并允许用引号包含键名。

    示例:

    var tag ={"name1":123,"name2":456}
    

    上面包含键名的双引号可以省略,这种写法的表作为函数参数使用时不可省略外面的大括号 {}

    但请注意 aardio 不允许用 [] 构造数组,必须用 {}构造数组。

  6. 表作为函数参数的简写法

    如果函数的参数是一个普通的表构造器( 构造表对象的字面值 ),并且第一个元素以等号分隔键值对,则可省略最外层的 {} 。例如 console.dump({a = 123,b=456}) 可以简写为 console.dump(a = 123,b=456)

    这种写法很像一些编程语言里的命名参数,但在 aardio 里并没有命名参数,这种写法在 aardio 里创建了一个表对象作为唯一的调用实参。

    注意:

    参数表如果是结构体则不能省略外层的 {}

    如果参数表使用类 JavaScript 语法用冒号代替等号分隔键值,则不能省略外层的外层的 {}

    例如:console.dump({a : 123,b : 456}) 或者 console.dump({"a" : 123,"b" : 456}) 都不能省略外层的 {}

 成员操作符

var tab = {a = 123; b = 456};


/*
用点号访问表成员,点号后面必须跟合法的标识符
*/
var item = tab.a;


/*
用下标操符符 [] 访问表成员,[] 里可以放任何表达式。
*/
var item = tab["a"];


/*
用直接下标操符符 [[]] 访问表成员,[[]] 里可以放任何表达式。
直接下标不会触发元方法,用于 null 值也会返回 null 而不是报错。
*/
var item = tab[["a"]];

 null 值

未定义的变量值默认为 null 。

获取表对象不存在的成员值时也会返回 null 而不会报错。如果对象本身是 null 值,使用点号、普通下标获取成员值会报错,改用直接下标 [[]] 则不会报错,而是会返回 null 值。

用 # 操作符获取 null 值长度返回 0 而不是报错。

将表对象的成员赋值为 null 就可以删除该成员。

在逻辑运算中,null 值为 false。

 取数组或字符串长度

import console;


var array = {123;456};
console.log("数组长度",#array);


var str  = "abcd";
console.log("字符串长度",#str);


var n  = null;
console.log("null 值长度为 0",#n);


console.pause();

使用 # 操作取数组或字符串的值,也可以取 null 值的长度( 返回 0 )。

 if 语句

import console;
var enabled = true;


if (enabled) {
  console.log('enabled 的值为 true');
}
console.pause(true);

注意在逻辑运算中,非 0、非 null、非 falsetrue,反之为 false

如果要准确判断一个变量的值是否为 truefalse ,则应使用恒等操作符,如下:

import console;
var enabled = false;


if (enabled === false ) {
  console.log('enabled 的值为 false,不是 0,也不是 null');
}
elseif( enabled ){
  console.log('enabled 的值为真');
}
else{
  console.log('enabled 值为:',enabled);
}
console.pause(true);

上面的 elseif 也可以改为 else if

 逻辑操作符

逻辑非 既可以用 not 也可以用 !

逻辑与 既可以用 and  也可以用 && 或者 ?

逻辑或 既可以用 or  也可以用 || 或者 :  。

aardio 语句在设计时试图兼容其他语言的一些基本语法,所以很多语法有类似上面的替代写法,例如可以用 begin end 替代 { } 包含语句块。

 三元运算符

var n = 1;


// ret 值为 true
var ret = n ? true : 0;

要特别注意 ? 实际上是逻辑与操作符,: 实际上是逻辑或操作符。如果 a ? b : c 这个表达式里 bfalse,则该表达式总是返回 c。这与其他类 C 语言的三元操作符不同,请注意区分。

 定义函数

//定义函数:
var add = function(a, b) {
  return a + b,"支持多个返回值";
}


//调用函数
var num,str = add(1, 2);


/*
要特别注意,函数可以返回多个值。
可以用 () 将多个返回值转换为单个调用参数。
*/
var str = tostring( ( add(1, 2) )  );

要特别注意 aardio 函数如果指定默认参数,形参中指定的默认值只能定义为布尔值、字符串、数值之一,其他默认值必须在函数体中编码指定。

for 循环语句( Numeric for )

for 循环语句是基于数值范围的循环(Range-based for)。

for 循环语句基本结构:

for(i = initialValue;finalValue;incrementValue){
    //循环体
}

aardio 虽然使用类 C 语法并且也使用 {} 包含循环体,但是 for 循环的条件部分与其他类 C 风格编程语言完全不同。aardio 使用基于数值范围的 for 循环语法(Range-based for), for 循环范围的终止值 finalValue 是一个纯数值表达式而不是需要循环计算的条件表达式,循环增量 incrementValue 也是一个纯数值表达式而不是一个需要迭代执行的语句(例如自增自减语句)。

示例:

import console;

// 循环变量 i 从起始值 1 循环到终止值 10 ,每次递增步长为 2。
for(i=1;10;2){ 

    // 在控制台输出 1,3,5,7,9
    console.log(i); 
}

// 循环变量 i 从起始值 1 循环到终止值 10 ,每次递增步长为 1。
for(i=1;10;1){

    i++; // 修改循环变量的值,使步长变为 2

    // 循环变量设为无效数值,下次循环以前自动修复为计数器值
    i = "无效数值" 
}

// 循环变量 i 从起始值 10 循环到终止值 1 ,每次递减步长为 1。
for(i=10;1;-1){  

    // 在控制台输出 10,9,8,7,6,5,4,3,2,1
    console.log(i); 
}

console.pause()

参考:for 循环语法

for in 泛型循环语句(Generic for)

import console;


var tab = {
    a = 123;
    b = 456;
}


for(k,v in tab){
    console.log(k,v)
}


console.pause();

参考:for in 语句与迭代器

 while 循环语句

import console;


var i = 0;
while (i < 100) {  
    i++;

    console.log(i);

    if(i==10){
        break; //中止循环
    }
}

console.pause();

 while var 语句

import console;


//用法1,while条件中使用一个var语句
while(
    var i =  console.getNumber( "请输入数值,输入0退出:" )
    ) {
    console.log( i )
}


//用法2,while条件中使用var语句、循环前执行语句、条件判断语句
while(
    var i;
    i =  console.getNumber( "请输入数值,输入100退出:" );
    i != 100  ) {
    console.log( i )
}


//用法3,省略var语句,仅使用循环前执行语句、条件判断语句
var i = 0;
while( ;i++; i<10 ) {
    console.log(i)
}


//用法4,省略var语句,循环前执行语句,仅使用条件判断语句
while(i>0){
    i--;
    console.log(i);
}


console.pause(true);

参考:while var 语法

 while var 模拟 for 循环

import console;


while( var i = 0; i++ ; i < 5  ) {
    console.log( i )
}


console.pause(true);

 break label, continue label, break N, continue N 语句

break 语句中断并退出当前循环语句。

continue 语句跳过当前循环体剩下的部分,继续执行下一次循环。

aardio 也支持 break N,continue N 中断或继续指定的上级循环,N 表示循环层级(1 为本层循环,2 为上层循环,依次计数 …… )。

示例:

import console;


var i, j;
for (i = 0; 2; 1 ) {


   for (j = 0; 2; 1) {

      if (i == 1 && j == 1) {
         break 2;//退出上一层循环,1 表示本层循环
      }
      else {
         console.log("i = " + i + ", j = " + j);
      }
   }
}
console.pause(true);

 类

//定义类
class className{

    //构造函数
    ctor(name,...){
        this.name = name;
        this.value = 0;
        this.args = {...}; 
    };

    //属性
    propertyName = "value";

    //定义方法(成员函数),必须写为名值对格式,不能等略等号或 function 关键字。
    method = function(v){
        this.value = this.value + v;
        return this.value;
    }
}

//打开类的名字空间
namespace className{
    staticVar = "类的静态变量值";
    staticNum = 2;
}

//调用类创建对象
var object = className();

//调用对象的成员函数
var v = object.method(5);

//访问类的静态成员
var num = className.staticNum;

类内部可以用 this 访问当前创建的实例对象。

类总是先调用构造函数 ctor 才会初始化其他成员。

每个类都有自己的名字空间。

类创建的所有实例共享同一名字空间。

类名字空间的成员也就是类的静态成员。

self,this,owner 对象

self 表示当前名字空间。

this 在类内部表示当前创建的实例对象。

每个函数都有自己的 owner 参数( 无法使用外部函数的 owner 参数 )。如果用点号作为成员操作符获取并调用对象的成员函数,则点号前面的对象是被调用成员函数的 owner 参数,否则被调用函数的 owner 参数为 null。例如 obj.func() 调用 obj 的成员函数 func ,则 objobj.funcowner 参数。

注意一个独立的 aardio 代码文件编译后也相当于一个匿名的函数。独立运行的 aardio 代码文件 owner 参数默认为 null ,使用 import 语句加载的库文件, owner 参数为加载路径、或资源文件数据。

owner 在元方法中表示左操作数。

在迭代器函数中, owner 表示迭代目标对象。

aardio 允许用 call, callex, invoke 等函数调用其他函数并改变 owner 参数的值。

Markdown 格式