Tea-功能细节一览

优质
小牛编辑
134浏览
2023-12-01

本文读者应该首先会一门C风格的语言(如JavaScript、C++、Java 和 C#)

一、概述

Tea 语言代码最终被编译为机器码(汇编语言)执行,不需要任何虚拟机。这个功能决定了 Tea 语言语法的基本特色:

  1. 强类型。 所有变量都应有明确的类型。同时语言提供强大的类型推导功能,以减少编码复杂度。
  2. 支持面向对象。 这为语言增加很多复杂度,但对于本来学过面向对象的读者,仍会觉得很轻松。

二、hello world

hello world 代码如下:

void main(){
    system.Console.writeLine("hello world");
}

其中,main 是一个函数定义,其返回类型是 void,其参数是空的。任何程序都是从 main 函数开始执行的。
其中,system 是提供底层 API 支持的系统模块。

代码中的所有点都可以省略,如:

import System;

void main(){
      Console.writeLine("hello world");
}

更可以写成:

import System.Console;

void main(){
      writeLine("hello world");
}

三、 基本开发模型

1. 编译和执行

使用如下命令,可以直接编译源码

tea hello.tea

Windows 下,将生成一个独立的 hello.exe 文件,复制此文件到任何电脑都可以直接运行。

2. 开发一个模块

实际的项目不会只有一个源码。需要通过模块的方式组织源码:

src1.tea           源码1
src2.tea           源码2
...
hello.teamod         存储模块配置

使用如下命令,可以直接编译模块

tea hello.teamod

Windows 下,将根据配置生成一个 hello.dll 库文件。

3. 开发一个项目

将多个模块组织在一起,可以成为一个项目。 如开发一个扫雷游戏时,目录结构为:

root/                 存储整个项目
    gui/                  GUI 模块(第三方库)
        gui,teamod            
    saolei/               扫雷游戏核心模块
        src.tea               实际的源码
        saolei.teamode        
    saolei.teaproj        

使用如下命令,可以直接编译项目

tea saolei.teaprj

Windows 下,将根据配置生成 saolei.exe, gui.dll 文件。

小结

要开发一个完整的产品, 需要新建一个项目(Project), 一个项目中由多个模块(Module)组成, 一个模块由多个源文件(SourceUnit)组成。

四、语句

1. 变量定义语句

int i;  // 创建一个类型为 int 的变量 i。

int a = 1, b = 2; // 同时创建 2 个变量。

var k = 1; // 让编译器自动推导 k 的类型。

2. if 语句

if (a) {}
if (a) {} else {}
if (a) {} else if (b) else {}

3. for 语句

for (int i = 0; i < 100; i++) {}   

for (int i in arr) {}  // 快速遍历一个数组

for (int i = 0 to 100) {}  // 等效于: i = 0 ; i < 100 ; i++
for (int i = 100 to 0; i--) {}

4. while 语句、 until 语句和 loop 语句

while (a) {}
do {} while(a);

until (a) {} // 同 while(!a) 
do {} until(a);  

loop {} // 死循环
loop(100) {} //  循环100次

5. switch 语句

每个 case 之间不会互相贯穿。

switch(val) {
    case 1:
        b = 1;
    case 2:
        goto case 1; // 使用 goto 强制跨 case 。
    case 3, 4:   // 允许多个值使用逗号分开,而不是写多个 case 。
        b = 2;
    case else: // 匹配剩余情况。注意没有 default 关键字。
        b = 4;
}

switch { // 等效于多个 if else if 。
    case a > 1:

    csae a < 0 && a > 5:

}

6. try 语句

一个 try 语句最多只能有一个 catch 段和一个 finnaly 段。

try {} catch {}
try {} catch(Exception e) {}  // 只接受指定类型的错误。

try {} finally {}
try {} catch {} finally {}
try {} catch(Exception e) {} finally {}

throw "Error";  // 所有的异常都是 Exception 对象,如果 throw 的参数是一个字符串,会自动包装为 Exception 对象。

7. 标签语句和跳转语句

label: 
    goto label; // 跳转到 label 。

return 1; // 返回值。
yield 2: // 在迭代器中返回值。

8 with 语句

with 语句可以保证资源被正确释放。

with (SqlConnection sql = MySql.open("连接字符串")){ // 打开一个数据库连接。
    sql.execute("SQL 语句"); 
} // 在代码执行完后,系统自动调用 sql.close() 关闭数据库连接资源。

9. lock 语句

lock 语句控制一段代码不会在多个线程内同时执行。

lock { } // lock 里面的代码不会同时执行。
lock (myLockObj) {} // 使用自定义的线程锁标记。

10. runonce 语句

runonce 语句控制一段代码,并确保它在程序声明周期内执行一次。

runonce { /*  一些初始化代码 */ } 

11. checked 语句和 unchecked 语句

unchecked 语句内所有的溢出异常都被忽略。checked 相反。

unchecked { /*  不测试溢出      */ } 
checked { /*  测试溢出      */ } 

12. 调试语句

> "hello world"; // 相当于 Debugger.trace("hello world");

: a > 0;   // 相当于 Debugger.assert(a > 3);

四、 表达式

1. 常量

1.1 数字

int a = 0  
int b = 0xFFFFFF
double c = -1.2 

1.2 布尔和空

bool a = true
bool b = false
object c = null

1.3. 字符串

string a = ''             // 单引号是无任何转义的字符串。
string b = ""             // 双引号是支持\转义的字符串。
string c = ‘’‘aaa'''      // 三引号:长字符串。

int a = 1;
string s = `my value is $a`; // 输出 my value is 1

1.4 列表和字典

List<int> a = [1, 2, 3]      // List
Dict<int> b = {aa:1, bb:2}   // Dict

1.5 函数

Func<bool, int> = x -> x > 0;

Func<int int, int> = (x, y) -> {
    return x + y;
};

2. 操作符

2.1 四则运算

+, -, *, /, %(取余), ^(次方),++, --

2.2 比较运算

==, !=, <=, >=, <, >

2.3 比较运算

==, !=, <=, >=, <, >

2.4 逻辑运算

&&, ||, !

2.5 赋值运算

=, +=, -=, *=, /=, %=, =>, :=, &= |=

其中,=> 叫反向赋值操作符 a => b; // 等效于 b = a;

2.6 流程控制

true ? a : b 
a = value | "a"; // a = value == null ? "a" : value;
a = value & "a"; // a = value != null ? "a" : value;

2.7 位运算

^^、^&、^|、^<、^>、^~

2.8 区间操作符

List<int> a = 1 ~ 6; // 等效于 a = [1, 2, 3, 4, 5]

a[0]  // 访问索引 0  的值。
a[0 ~ 4] // 访问索引 0 到 4 的子数组。
a[0 ~ ] // 访问索引 0 到最后的字数组。

2.9 成员访问操作符

a.b 
a..b()..c()..d()  // 链式点操作,相当于  a.b();a.c();a.d();return a;

2.10 类型操作符

a is string  // 判断 a 是否是 String 
a as string  // 将 a 转为 String 

2.11 or 操作符

fn() or null // 相当于    `try{return fn()}catch{return  null}` 

2.12 await 操作符和 async 操作符

var s = await fn(); // 等待异步执行函数。
var a = async load(); // 异步执行函数。

2.13 指针操作

int* a = &b;
*a++ = 6;

3. 函数调用和创建实例

fn1(4);  // 基本调用方式
fn2(p:4); // 命名参数
fn3(ref p); // 引用参数
fn(out p); // 输出参数

MtObj a = new MtObj();
string[] s = new string[5];
var a = new MyClass (a: 1, b:2);

4. 特殊变量

1. this & base

this 表示当前类 base 表示基类

2. 魔法变量

代码中可以直接使用一些魔法变量,这些变量的值由编译器来提供。

@outputPath 当前生成的文件名 @version 当前编译的版本号 @line 当前行号 @sourcePath 当前源码位置 @global 全局对象 @self 当前类 @callee 当前函数

void fn(){
    fn(); // 递归调用 fn
}

void fn(){
    @callee(); // 同上,只是用 @callee 代替 fn
}

@index 当前循环的次数 @variables 当前块的所有变量集合。 @arguments 当前函数的实参集合。

五、函数和类

1. 函数定义

void fn(int value){}
func fn(value){}  // 弱类型函数定义

参数默认值

void fn(int value = 2){}
void fn(int value?){}    // 即  value = 0

可变参数

void printf(string format, params string[] args) {

}

// 调用时, printf 第2个参数可以是任意个。

输出参数

void fn(ref int value){}  
void fn(out int value){} 

//    输出参数表示这个参数是返回值,不是输入值。

2. 内嵌函数

void fn(){
    void sub(){

    }

    sub(); //    内嵌函数
}

3. 类定义

 class Cat : Animal {

    // 这是一个方法。语法同函数定义。
    void say(){

    }

    // 这是一个字段。语法同变量定义。
    int _prop = 1;

    // 这是一个属性。
    int prop {
        get {
            return _prop;
        }
        set {
            _prop = value;
        }
    }

}

静态成员

在成员前加 static 表示静态成员。静态方法必须通过类名访问。

访问修饰

public: 全部可以访问
internal: 同一个模块可以访问
protected: 当前类及子类可以访问
private: 当前类内部访问

Tea 语言默认设置全部成员为 public,但设置名字前有 _ 的成员为 private 。

字段修饰符

const  指示字段是常量
readonly 指示字段对外只读
final 指示字段全部只读
volatile  指示字段为可变的

虚函数和抽象函数

virtual int fn(){ return 3};
abstract int gn();
override int fn(){return 5;};

3. 其它类型定义

除了 class, 还可以声明以下成员:

namespace: 如果一个类全部是 static 成员,应该改成 namespace 。
interface: 如果一个类全部是 abstract 成员,应该改成 interface 。
struct: 代表值类型的类。
enum:代表枚举类型的类。

4. 泛型

class List<T> {}

5. 注解

@inline void fn(){}

6. 内置类型

内置基本数据类型是所有类型的基础,其它类型都是这些类型的组合。

常用: Char       Bool
整数: SByte      Short      Int      Long   
    Byte       UShort    UInt     ULong   
小数: Float      Double
引用: Void       Object     String

为了方便使用这些内置类型,Tea 提供了一些关键字来代表这些类型。

char 
bool
byte 
short
int 
long 
float
double
void
object
string

7. 数组

类型后加 [] 即该类型对应数组类型。、

int[] // int 数组,其实是 Array<int> 简写。

8. 指针

int a;
Ptr<int> pa = &a; // 获取 a 的指针。

9. 扩展类成员

class Array{

}

extend Array {
    void fff(){}
}

// 所有 Array 类都可以访问 fff 函数

10. 反射

Type s = typeof(String);

int a = s.getProperty("length").call("aaa") as int;

六、懒人模式

1. 自动追加分号

每个独立语句都允许省略分号,但是这会引起一些歧义。因此 Tea 采用的策略是尽量认为分号是没有省略的。如以下代码,编译器不会自动添加分号。

return // 分号不会自动添加。因此空 return 需要用户强制添加分号。
fn() 

2. 自动追加括号

大部分语句块在只有一行语句时,可以省略大括号。 大部语句均允许省略小括号。 但是,请不要同时省略大括号和小括号,虽然编译器对此不作限制。

七、文档注释和预处理器

1. 文档注释

/// 表示后面是文档注释,文档注释会提取为 API 文档

/// 输出值
/// @param a 要输出的内容
/// @return 输出的个数
int print(string s){

}

2. 预处理器

所有源码内的预处理符只对当前文件有效,项目文件里的预处理符对整个项目有效。

4.1 宏

宏主要用于编译代码时根据不同的平台使用不同的代码。

#define WIN32
#undef WIN32

#if WIN32
#elif LINUX
#else
#endif

4.2 错误和警告控制

#error 强制编译器显示一个错误
#warning 强制编译器显示一个警告

4.3 编译器宏

编译器宏是预留的用于告诉编译器一些编译信息的宏。

#pragma 宏名字 宏参数

4.3.1 警告信息控制

#pragma warning disable          // 通知编译器忽略所有警告
#pragma warning disable 1023     // 通知编译器忽略此警告
#pragma warning enable 1023      // 通知编译器重新提示此警告
#pragma error 1                  // 通知编译器将警告1作为错误处理

4.3.2 检验码

检验码用于标识代码文件。具有相同校验码的代码被认为是同一份代码。 如果用户没有指定检验码,编译器会自动根据文件生成。

#pragma checksum 检验码          // 设置当前文件的检验码
#pragma checksum 检验码 文件名    // 设置其他文件的检验码

4.4 代码行

代码行用于告诉编译器源码的实际位置。此命令主要用于自动生成的代码。

#line 1                          // 设置当前文件的行号              
#line 1 文件位置                  // 设置当前文件的行号及映射的文件
#line hidden                     // 设置隐藏当前行
#line default                    // 设置恢复默认行

4.5 代码区域

代码区域和代码无关,它的作用是告诉代码编辑器产生一个折叠块。

#region 区域名                  // 其中区域名可以省略
#endregion 区域名               // 其中区域名可以省略

4.6 代码标记宏

代码标记宏仅用于代码注解。

#todo 这是一个未完成的代码
#tofix 这是一个需要修复的代码
#mark 标记代码(比如读源码时,可以标注一些位置)

4.7 代码宏

代码宏主要用于让编译器自动复制代码。

#sippet 代码片段名字
// 这里是代码片段
#endsippet

#usesippet 代码片段名字 // 会自动填充定义的代码宏内的代码片段。

八、 运行库支持

[本节内容暂不提供]

扩展阅读

从关键字看语法
Tea 模块化编程
真正的面向接口