第 1 章:引言

版本: v1.2 最后更新: 2025年4月28日 FlexScript 是一种现代、通用、跨平台的脚本语言,旨在融合静态类型提示的严谨性与动态语言的灵活性。它适用于游戏开发、应用程序扩展、自动化任务、快速原型设计等多种场景。

1.1 设计哲学

FlexScript 的核心设计理念是在提供强大功能的同时,保持语法的简洁、直观和一致性。它强制开发者使用明确的类型提示来增强代码的可读性和健壮性,同时利用高效的动态运行时环境来简化开发过程。

1.2 主要特性

  • 强制静态类型提示: 所有变量在声明时都必须提供类型注解(如 int, string, list<T>, map<K,V>)或使用 auto 进行类型推断。
  • 动态运行时: 值在运行时携带类型信息,允许灵活操作。
  • 0 基索引: 列表 (list) 等序列容器的索引从 0 开始。
  • 内置容器: list<T> (有序序列) 和 map<K,V> (键值映射)。
  • 自动内存管理: 采用垃圾回收。
  • 原生协程: 内建支持轻量级协作式多任务。
  • 简洁语法: 借鉴常见编程语言,易于学习。
  • 面向对象: 支持基于类 (class) 的面向对象编程(无继承)。
  • 模块系统: requireimport 机制。
  • 错误处理: pcall, xpcall, error
  • C 风格与 For-Each 循环: 同时支持两种 for 循环语法(注意:For-Each 循环仅支持单个循环变量)。
  • 赋值作为语句: 赋值操作 (=, += 等) 被视为独立的语句类型。支持多重赋值语法 (a, b = expr1, expr2 ...)。
  • Lambda 表达式: 支持 function(...) -> type { ... } 形式的匿名函数和闭包。
  • 函数多返回值: 函数可以使用mutivar关键字声明以返回多个值。可以使用多重赋值语句a, b = func()或mutivar声明mutivar a, b = func()来接收这些值
  • 可扩展性: 提供 C 语言接口 (API)。
  • 分离设计: 解释器不负责类型推断,和语法检测,仅负责执行和失败抛出错误,分析工作由独立的分析器执行。

本规范旨在详细介绍 FlexScript 语言的各个方面。

第 2 章:词法结构

本章描述构成 FlexScript 代码的基本元素。

2.1 文件与编码

  • 源文件推荐使用 .flx 扩展名。
  • 源文件必须使用 UTF-8 编码。

2.2 大小写敏感性

FlexScript 是大小写敏感的。

2.3 空白与分号

  • 空白字符: 空格、制表符 (\t)、换行符 (\n) 等用于分隔词法单元,通常会被忽略(字符串内除外)。
  • 分号 (;): 语句结束符,每条语句强制以分号结尾,支持单行多条语句。
int x = 1; int y = 2; // 单行多语句写法
print(x);

2.4 注释

  • 单行注释: // 直到行尾。
  • 多行注释: /* ... */,不可嵌套。

2.5 标识符

用于命名变量、函数、类等的名称。

  • 规则: 必须以字母(a-z, A-Z)或下划线 (_) 开头,后跟任意数量的字母、数字(0-9)或下划线。
  • 示例: index, _temp, userName, isValid

2.6 关键字

关键字是语言保留的、具有特殊含义的单词,不能用作标识符。

// 类型相关:
int float number string bool any void list map null coro function

// 控制流:
if else while for break continue return

// 字面量与常量:
true false const

// 声明与作用域:
auto global static import as type export private

// 面向对象:
class this new __init__ __deinit__

// 注意: print, error, require, type 等内建函数名不是关键字,是预定义在全局的函数标志符。

2.7 字面量

直接表示值的源代码文本。

  • 整数: 十进制 (123, -10) 或十六进制 (0xFF, 0x10)。
  • 浮点数: 带小数点或指数 (1.0, -0.5, 1e6, 2.5E-3),支持十六进制浮点数 (0x1.fp10)。
  • 字符串: 单引号 ('...') 或双引号 ("...") 包围。
    • 支持转义序列:\n, \t, \\, \', \", \xHH, \u{...} 等。
    • 连接使用 .. 运算符。
    • 长度使用 # (字节长度)。
  • 布尔值: true, false
  • 空值: null
  • 列表构造器: [element1, element2, ...]
  • 映射构造器: {key1: value1, "key2": value2, [exprKey]: value3, ...}

2.8 运算符

具有明确的优先级和结合性。(详细列表和优先级请参考 ANTLR 语法定义或后续章节)

  • 关键运算符:
    • 后缀: () (调用), [] (索引), . (成员/方法), : (成员查找)
    • 一元前缀: ! (逻辑非), - (负号), # (长度), ~ (位非)
    • 算术: *, /, %, +, -
    • 字符串: .. (连接)
    • 位运算: <<, >>, &, |, ^ (异或)
    • 比较: <, >, <=, >=, ==, !=
    • 逻辑: && (与), || (或) (均支持短路求值)
    • 赋值 (=, += 等) 作为独立的语句类型处理。

第 3 章:类型系统

FlexScript 采用强制静态类型提示,配合动态运行时类型。

3.1 概述

  • 强制静态提示: 声明变量时必须提供类型注解或 auto。用于编译期检查。
  • 动态类型: 运行时,值携带类型信息。
  • 类型擦除: 泛型等静态信息通常在运行时被擦除。

3.2 基本类型

  • int: 64 位有符号整数。
  • float: 64 位双精度浮点数。
  • number: 通用数字类型提示 (运行时值为 intfloat)。
  • string: 文本字符串。
  • bool: 布尔值 (truefalse)。
  • null: 表示空或无值。
  • void: 用于函数返回类型,表示无返回值。

3.3 容器类型

  • list<T> (列表): 有序的元素序列。
    • 通过 0 基整数索引 访问。
    • T 是元素类型的提示。list 等同于 list<any>
    • 大小通常是动态可变的。
  • map<K, V> (映射): 键值对的集合。
    • 通过键 (K 类型) 访问值 (V 类型)。
    • map 等同于 map<any, any>
    • 键可以是除 null 外的任何类型。

3.4 其他类型

  • union<T1, T2, ...> (联合体): 表示值允许是参数列表中的任意一个类型。

    • 联合体并非容器,仅用于类型的提示
    • 联合体至少两个类型参数
  • tuple<T1, T2, ...> (元组): 实际为List的别名,但支持多类型参数描述。

    • 元组使用方式与list完全相同,甚至可以追加元素或者删除元素,但不鼓励这么做。
    • 元组至少2个类型参数,上限16个
    • T1为tuple[0]的类型,T2为tuple[1]的类型,依此类推。
  • function: 函数类型。

  • coro: 协程类型。

  • any: 特殊类型提示,表示可持有任何类型的值。谨慎使用。

3.5 类型注解与泛型

3.5.1

  • 类型注解跟在变量名或函数参数后,或在函数名前/箭头后(返回值)。
  • 不支持类似 (int, bool) 类型作为返回类型注解。函数必须声明单个返回类型。如果需要返回多个值,使用mutiret关键字代替返回类型。
int count;
list<string> names;
map<string, int> scores;
union<string, int> nameOrAge;
function callback;
// list<any> getValues(); // 函数返回一个包含多个值的列表
// list<int, bool> checkStatus(); // 或者使用更具体的列表类型
  • listmap 支持泛型 <...> 来提供更精确的元素或键值类型信息。

3.5.2

list定义方法:

  • list a = [123, "124"];
  • print(#a); 查看list长度,输出2
  • print(a[0]); 输出123
  • a[0] = null;;
  • 迭代器会继续迭代到"124",不会因为a[0]等于null而终止,迭代函数为ipairs。
  • for(any e: ipairs(a)) { print(e);} 打印出: null 124

3.6 auto 类型推断

  • 使用 auto 声明变量时,编译器根据初始化表达式推断其静态类型。
  • 注意: auto 不能用于从单个表达式推断出多个变量的类型(多重赋值是不支持的)。
auto i = 10;      // 推断为 int
auto items = [1, 2]; // 推断为 list<int> (或 list<any>)
auto config = {};   // 推断为 map<any, any>
// list<any> result = getPosition(); // 函数返回列表
// auto x = result[0];              // 单独声明和赋值
// auto y = result[1];

3.7 const 常量意图

  • 表示变量初始化后不应再被修改的意图,主要用于静态分析。
  • const float PI = 3.14;
  • 不提供运行时的强制不可变性。

3.8 type() 运行时类型查询

  • 内建函数,返回值的运行时类型名称字符串。
print(type(1)); // "int"
print(type("a")); // "string"

第 4 章:变量与作用域

4.1 变量声明

  • 强制显式类型/auto: 声明变量必须使用类型注解或 auto
  • 初始化: 可选。使用 = 后跟一个单一表达式 (expression)。
    • 未初始化的变量默认为 null
    • 不支持使用单个表达式列表同时初始化多个变量的语法 (例如 int a, int b = 1, 2; 是错误的)。每个初始化必须明确对应一个变量。
    • 单行多声明初始化: 支持在单行声明多个变量并强制初始化(使用mutivar),多声明会使变量丢失类型信息(例如 mutivar a, b = expr; )。
// --- 正确的声明和初始化 ---
int index;
float value = 0.0;
auto name = "Default";
bool flag = true;
const int MAX = 1000;
list<int> items = [1, 2, 3];
map<string, bool> config = { enabled: true };
int v1 = 10; // 单独声明和初始化
int v2 = 20;


// --- 语法错误示例 ---
// int a, b;                // 错误: 变量 'b' 缺少类型注解
// auto p = 1, q = 2;    // 错误: 变量 'q' 缺少 'auto' (如果意图是分别初始化)
// int v1, int v2 = 10, 20; // 错误: 不支持使用表达式列表初始化多个变量
// auto x, auto y = getPosition(); // 错误: 不支持多重赋值/解包声明
// mutivar i, j = 10, 20; // ERROR: mutivar 初始化器必须是单个表达式

4.2 作用域规则

  • 局部作用域: 函数、方法或代码块 ({}) 内声明的变量。
  • 全局作用域: 使用 global 关键字声明的变量。
  • 模块作用域: 顶层(所有函数、类之外)未使用 global 声明的变量。

第 5 章:表达式与运算符

表达式是值的计算单元。

5.1 概述

表达式由操作数(字面量、变量、函数调用等)和运算符组成。求值后产生一个值。

5.2 主要表达式 (primaryExp)

表达式的基本构建块:原子字面量 (1, "s"), 列表/映射字面量 ([], {}), 标识符 (var), this, ..., 括号表达式 ((...)), new 表达式, Lambda 表达式 (function(...) -> ...)。

5.3 后缀表达式 (postfixExp)

对主表达式进行后缀操作:expr[index] (索引, List 从 0 开始), expr.member (成员访问), expr:member (成员查找), expr(args) (函数调用), expr.method(args) (方法调用, 隐式 this)。

5.4 一元运算符 (unaryExp)

作用于单个操作数:!a (逻辑非), -x (负号), #list (长度), ~bits (位非)。

5.5 二元运算符

作用于两个操作数,按明确的优先级和结合性计算。包括算术、比较、逻辑 (&&, || 短路), 位运算 (&, |, ^), 字符串连接 (..)。

第 6 章:左值 (L-Values)

左值是指可以出现在赋值操作符左侧的表达式,代表可被赋值的位置。

  • 有效左值: 标识符 (variable), 索引访问 (list[0]), 成员访问 (object.field)。
  • 无效左值: 字面量 (10), 算术表达式 (x+1), 函数调用 (func()) 等。

第 7 章:语句

语句是构成程序执行的基本单元。

  • 空语句: ;
  • 代码块语句: { ... }
  • 表达式语句: funcCall(); (通常是函数/方法调用,结果被丢弃)
  • 声明语句: int x = 1;, void func(){}, class C {}
  • 赋值语句: assignStatement, updateStatement (注意:不支持多重赋值语句)
  • 控制流语句: if, while, for
  • 跳转语句: break, continue, return

第 8 章:赋值语句

赋值操作在 FlexScript 中被视为独立的语句类型。

8.1 普通赋值 (assignStatement)

语法: lvalue [, lvalue]* = expression [, expression]* ;

  • 将右侧一个或多个表达式的值赋给左侧一个或多个左值。赋值行为遵循以下规则:
    • 如果值的数量和变量的数量相等,则按顺序赋值。
    • 如果值的数量多于变量的数量,多余的值会被丢弃。
    • 如果值的数量少于变量的数量,不足的变量会被赋为 null。
    • 如果右侧表达式列表的最后一个表达式是函数调用,它会根据需要返回尽可能多的值来匹配左侧变量。
int x; int y; string z;

x = 1;        // 单个赋值 [cite: 21]
x, y = 10, 20; // 多重赋值
x, y = getTwoValues(); // 从函数多返回值赋值 (假设 getTwoValues 返回两个值)

mutivar a, b, c = getPossiblyThreeValues(); // 声明并赋值
a, b = getPossiblyThreeValues();         // 对已声明变量赋值

x, y = 5; // x = 5, y = null
x = 10, 20; // x = 10 (20 被丢弃)
x, y, z = func1(), func2(); // 如果 func1 只返回一个值 v1, func2 返回 v2, v3 -> x=v1, y=v2, z=v3
x, y = func1(), func2(); // 如果 func1 返回 v1, func2 返回 v2, v3 -> x=v1, y=v2 (v3 丢弃)

8.2 更新赋值 (updateStatement)

  • 语法: lvalue op= expression ; (op= 包括 +=, -=, *=, /=, %=, ..=, 等)
  • 等效于 lvalue = lvalue op expression ; (但 lvalue 只求值一次)。
int count = 0; count += 1;
string msg = "a"; msg ..= "b"; // "ab"

第 9 章:控制流

9.1 if / else if / else

条件分支。代码块必须用 {} 包裹。

if (x > 0) { /*...*/ } else if (x < 0) { /*...*/ } else { /*...*/ }

9.2 while 循环

条件为真时重复。代码块必须用 {} 包裹。

while (condition) { /*...*/ }

9.3 for 循环

支持 C 风格和 For-Each 风格。代码块必须用 {} 包裹。

9.3.1 C 风格 for

  • 语法: for ( [初始化] ; [条件] ; [更新] ) { ... }
  • 初始化: 可选。变量声明 (强制显式类型/auto, 无 const/global, 每个变量单独初始化) 或表达式列表。
  • 条件: 可选。布尔表达式。
  • 更新: 可选。单个表达式。
for (int i = 0; i < 10; i+=1) { print(i); }
// 初始化多个变量必须分别进行
for (auto i = 0, auto j = 5; i < j; i+=1, j-=) {}

9.3.2 For-Each 风格 for

  • 语法: for ( 变量声明 : 集合表达式 ) { ... }
  • 变量声明: 必须单个循环变量提供 typeauto不支持在一次迭代中解包多个变量 (如 for (mutivar key, value : map))。
  • 集合表达式: 列表、映射或其他可迭代对象。
    • listIter 遍历 list 时,可以使用内置函数listIter,令循环变量被赋值为元素值
    • mapIter 遍历 map 时,可以使用内置函数mapIter,令循环变量被赋值为键(key)。 内置函数listIter、mapIter用于迭代两种容器:
list<string> names = ["A", "B"];
// 遍历列表,获取元素
for (string name : listIter(names)) {
    print(name); // 输出 "A", "B"
}

map<string, int> data = {k1: 1, k2: 2};
// 遍历映射,获取键
for (string key : mapIter(data)) {
    int value = elm[key]; // 返回value
    print(key .. ": " .. value); // 输出 "k1: 1", "k2: 2"
}


// 错误:不支持多个循环变量
// for (int i, string name : names) { /* 语法错误 */ }
// for (mutivar key, value : data) { /* 语法错误 */ }
// for (string key, int value : data) { /* 语法错误 */ }

9.4 break, continue, return

  • break;: 退出最内层循环。
  • continue;: 进入最内层循环的下一次迭代。
  • return var1, var2;: 从函数/协程返回。允许返回带零个或多个值。如果需要返回多个结果,需要用逗号隔开。

第 10 章:函数

10.1 定义函数

  • 语法: [global?] returnType qualifiedName ( [paramList] ) { body }
  • 必须指定单个返回类型 (void, int, list<any>, map<string, int> 等)。
  • 多返回值情况必须使用mutivar关键字代替returnType,使用mutivar意味着放弃此函数返回的类型推断,默认推断为返回未知个数(包括0个)的any值。
  • 函数名可以是 module.func 形式。
  • 参数必须指定类型。
  • 支持 ... 可变参数 (需用 list<any>(...) 等方式在内部捕获)。
// 无返回值
void logMessage(string msg) {
    print(msg);
}

// 返回单个 int
int calculateSum(int a, int b) {
    return a + b;
}

// 返回包含多个值的列表
list<any> checkValue(int v) {
    list<any> result = [v, v > 0]; // 将两个值放入列表
    return result;
}

// 返回多值的写法:使用mutivar,如果mutivar替代返回类型的声明,则推断为返回为未知数量的any值。
// mutivar 并不是类型
mutivar mutireturnFunc(int v) {
    if(v > 14)
        return 1;
    return "hi", true;
}
// 正确用法 1: 使用 mutivar 声明并初始化
mutivar a, b = mutireturnFunc(1);
print(a, b);

// 正确用法 2: 先声明,再使用多重赋值语句
int c; // 假设知道返回类型
c = mutireturnFunc(15); // 假设 v > 14, c=1
print(c);

any e; any f;
e, f = mutireturnFunc(1); // e="hi", f=true
print(e, f);

10.2 Lambda 表达式 (匿名函数/闭包)

  • 语法: function ( [paramList] ) -> returnType { body }
  • 必须使用箭头符号->指定单个返回类型。
  • 可捕获外部变量形成闭包。
auto multiply = function(int a, int b) -> int { return a * b; };

// 返回列表的 Lambda
auto createPair = function(int x, int y) -> list<int> { return [x, y]; };

// 返回多返回值的 Lambda
auto createPair = function(int x, int y) -> mutivar { return x, y; };

// 无返回值的 Lambda
auto createPair = function(int x, int y) -> void { print(x, y); };

//常规函数的定义不支持Lambda的写法:
//错误
function add(int a, int b) -> int {
    return a+b;
}

10.3 函数调用与一等公民特性

函数是值,可以赋值、传递、返回。使用 () 调用。调用返回列表的函数后,需要通过索引访问其元素。

list<any> valueInfo = checkValue(10); // 调用返回列表的函数
int val = valueInfo[0];
bool isValid = valueInfo[1];
print("Value:", val, "Is Valid:", isValid); // Value: 10 Is Valid: true

第 11 章:类 (Classes)

提供简洁的面向对象支持,无继承

11.1 定义类 (class)

class MyClass {
   // 实例字段 (必须有类型)
   int value = 0;
   string name;

   // 静态字段
   static int instanceCounter = 0;

   // 构造函数 (签名必须是 void __init__)
   void __init__(string n, int v) {
       this.name = n;
       this.value = v;
       MyClass.instanceCounter+=1;
   }

   // 实例方法 (隐式 this )
   void printInfo() {
       print(this.name .. ": " .. this.value);
   }

   // 静态方法
   static int getCount() {
       return MyClass.instanceCounter;
   }

   // 析构函数 (可选, 签名必须是 void __deinit__)
   void __deinit__() { /* 清理 */ }
}

11.2 创建 (new) 与使用

  • new ClassName(...): 创建实例 (必须带括号)。
  • . (点): 成员查找 (不传 this)。
  • : (冒号): 访问实例/静态成员;调用实例/静态方法 (方法调用隐式传 this)。

第 12 章:模块 (Modules)

FlexScript 通过模块系统来组织和重用代码。每个 .flx 源文件都被视为一个独立的模块。模块间的交互主要通过 exportimport 关键字进行。import 语句不仅指导运行时的模块加载,更重要的是为静态分析器提供跨模块类型检查所需的信息。

12.1 定义模块与导出 (export)

模块通过 export 关键字指定对外暴露的公共接口(变量、函数、类)。

  • 语法: export 是一个可选的前缀,可以放在变量、函数或类的声明之前。
  • 作用: 定义模块的公共 API。这些导出的成员会被收集到一个结构(通常是 map)中,作为模块执行后的返回值。
  • 范围: 强烈建议仅在模块的顶层使用 export
// 文件: utils.flx
export const PI = 3.14159;
export function calculateArea(float radius) -> float {
    return PI * radius * radius;
}
export class GeometryHelper {
    // ...
}
string internalData = "secret"; // 未导出

12.2 导入模块 (import)

import 语句用于从其他模块引入需要使用的导出成员或类型信息。所有 import 语句都必须使用字符串字面量指定模块路径,并以分号结尾。

FlexScript 支持以下导入语法:

12.2.1 命名导入 (Named Imports)

从模块中导入一个或多个指定的导出成员。可以使用 as 关键字为导入的成员指定本地别名。这种导入会同时为静态分析器提供类型信息,并指示运行时加载相应的实体。

语法与正确示例:

// 文件: main.flx
// 假设 utils.flx 定义如上

// 导入单个成员
import { calculateArea } from "./utils";

// 导入多个成员
import { PI, GeometryHelper } from "./utils";

// 导入并使用别名
import { calculateArea as computeCircleArea } from "./utils";

// 混合导入和别名
import { PI, GeometryHelper as Geom, calculateArea as areaFunc } from "./utils";

// --- 使用导入的成员 ---
float radius = 5.0;
float circleArea = computeCircleArea(radius); // 使用别名
print("圆周率:", PI);
auto helper = new Geom(); // 使用别名
areaFunc(1.0); // 使用别名

错误示例:

// 错误: 导入不存在的成员
// import { nonExistentMember } from "./utils"; // 静态分析器应报错

// 错误: 别名语法错误 (应为 original as alias)
// import { calculateArea computeCircleArea } from "./utils"; // 解析器会报错
// import { calculateArea: computeCircleArea } from "./utils"; // 解析器会报错

// 错误: 缺少花括号
// import calculateArea from "./utils"; // 语法错误 (这不是默认导入)

// 错误: 模块路径不是字符串字面量
// string path = "./utils";
// import { PI } from path; // 解析器会报错

作用: 既导入类型信息(供静态分析),也导入运行时实体(代码生成器会处理)。

12.2.2 命名空间导入 (Namespace Import)

将模块所有导出的成员聚合到一个单一的命名空间对象(别名)下。

语法与正确示例:

// 文件: main.flx
// 假设 utils.flx 定义如上
import * as Utils from "./utils";

// --- 使用导入的成员 ---
float radius = 5.0;
float circleArea = Utils.calculateArea(radius);
print("圆周率:", Utils.PI);
auto helper = new Utils.GeometryHelper();
// print(Utils.internalData); // 错误: 不能访问未导出的成员 (静态分析器/运行时均会失败)

错误示例:

// 错误: * as 后面缺少别名
// import * from "./utils"; // 解析器会报错

// 错误: 使用花括号进行命名空间导入
// import { * as Utils } from "./utils"; // 解析器会报错

作用: 导入一个包含所有导出成员的运行时对象,并为静态分析器提供该对象下成员的类型信息。

12.2.3 仅类型导入 (Type-Only Imports)

明确指示只导入用于静态类型检查的类型信息(如类名、接口、类型别名等),不会产生任何运行时代码。这有助于优化性能并明确意图。

使用 type 关键字紧跟在花括号 {} 内需要仅作为类型导入的成员名称之前

语法与正确示例:

// 文件: definitions.flx
export class UserProfile {
    string name;
    int age;
}
export auto DEFAULT_TIMEOUT = 1000;
// 文件: processor.flx
// 仅导入 UserProfile 类型,导入 DEFAULT_TIMEOUT 值
import { type UserProfile, DEFAULT_TIMEOUT } from "./definitions";

// UserProfile 只能用作类型注解
function processUserProfile(UserProfile profile) -> void {
    print("Processing user:", profile.name);
    // ...
}

// 可以使用导入的值
print("Timeout:", DEFAULT_TIMEOUT);

// 另一个例子: 只导入类型,不导入任何值
import { type UserProfile } from "./definitions";
// auto timeout = DEFAULT_TIMEOUT; // 错误! DEFAULT_TIMEOUT 未在此处导入

错误示例:

// 文件: processor.flx
import { type UserProfile } from "./definitions";

// 错误: 尝试将仅类型导入用作值
// auto profileInstance = new UserProfile(); // 运行时错误 (UserProfile 未定义)
                                            // 静态分析器也应报错

// 错误: 对非类型使用 'type' 关键字 (逻辑错误,但语法允许)
// import { type DEFAULT_TIMEOUT } from "./definitions";
// function useTimeout() {
//     int t = DEFAULT_TIMEOUT; // 运行时错误 (DEFAULT_TIMEOUT 未定义)
//                             // 静态分析器应能检测到 DEFAULT_TIMEOUT 不是类型
// }

// 错误: 'type' 关键字位置错误
// import type { UserProfile } from "./definitions"; // 语法错误 (type 在花括号内)
// import { UserProfile type } from "./definitions"; // 语法错误

作用: 仅为静态分析器提供类型定义信息,代码生成器会完全忽略 type 标记的导入项。

12.3 运行时行为

import 语句不仅指导静态分析,也会在程序运行时触发模块的加载和执行(如果尚未加载,且导入了非类型成员)。

  • 加载与缓存机制:

    1. 缓存检查: 运行时环境维护一个已加载模块的内部缓存。当通过 import(或后续描述的动态加载函数)请求加载某个模块路径时,运行时首先检查该路径是否已存在于缓存中。
    2. 缓存命中: 如果模块已在缓存中,运行时将直接返回缓存的模块结果(即该模块首次执行后返回的值),不会再次执行模块的代码。
    3. 缓存未命中: 如果模块不在缓存中,运行时将继续执行加载过程。
  • 模块路径解析:

    1. 运行时加载器会根据一套定义好的搜索规则和路径来查找由 import 语句中的字符串指定的模块文件。
    2. 这个搜索过程通常涉及检查一系列目录或路径模板。路径模板中可能包含特殊占位符(例如 ?),加载器会用请求的模块路径字符串(通常会将路径中的 . 替换为平台相关的目录分隔符)替换这些占位符,生成候选文件路径。
    3. 加载器会按顺序尝试这些候选路径,直到找到对应的源文件为止。这些搜索路径通常由运行环境或配置决定。
  • 模块执行:

    1. 一旦找到源文件且该模块不在缓存中,加载器会执行该模块的代码,且仅执行这一次
    2. 模块代码执行完毕后,其返回值(通常是包含了 export 成员的那个对象)会被存储到模块缓存中,关联到其模块路径。
    3. 最后,这个返回值被提供给请求加载的代码(即 import 语句或动态加载函数调用)。
  • import 的运行时效果(总结):

    • import { entity1, ... } from "module/path";:触发加载(如果需要且导入了非类型成员),然后从返回的模块结果对象中提取 entity1 等非类型成员,绑定到当前作用域的同名局部变量上。
    • import * as Namespace from "module/path";:触发加载(如果需要),并将返回的整个模块结果对象绑定到 Namespace 标识符上。
    • import { type T } from ...;触发运行时加载(除非同一语句还导入了值),绑定任何运行时变量。

12.4 动态模块加载函数 (require)

除了 import 语句,FlexScript 运行时通常还提供一个内置的 require 函数,允许基于程序逻辑(例如,字符串变量的值)按需加载模块。

  • 函数签名: map require(string modulePath)
  • 行为:
    1. 接收一个表示模块路径的字符串 modulePath
    2. 执行与 import 相同的运行时加载和缓存机制:检查缓存 -> (如果未命中) -> 搜索路径 -> 执行模块代码一次 -> 缓存结果。
    3. 返回缓存中或新加载执行后得到的模块结果(这是一个 map,包含了模块所有 export 的成员)。
  • 类型: 由于调用是动态的(模块路径在编译时通常是未知的),静态分析器无法推断此函数返回的 map 中具体包含哪些成员及其类型。因此,此函数的返回值在静态分析层面通常被视为 map<string, any> 类型(或更宽松的 map / any)。
    string moduleName = "my_module";
    // 运行时加载,返回值类型静态视为 map<string, any>
    map<string, any> myModuleRuntime = require("./" + moduleName);
    
    // 访问成员需要进行运行时检查或假设其存在
    if (myModuleRuntime.containsKey("utilityFunc")) {
        // 需要开发者确信该成员存在且可调用
        // 最好使用 pcall 或类型断言 (如果支持)
        // function func = (function)myModuleRuntime["utilityFunc"]; // 假设有类型断言
        // if (func != null) { func("Dynamic call"); }
    }
    
  • import 的区别:
    • import 是静态声明,其主要优势在于为编译时类型检查提供信息,同时也指导运行时加载。
    • require 函数是纯粹的运行时操作,灵活但不提供编译时类型安全

建议: 优先使用 import 语句进行模块导入,以充分利用 FlexScript 的静态类型检查带来的健壮性和可维护性。仅在确实需要根据运行时条件动态决定加载哪个模块时,才考虑使用 require 函数,并谨慎处理其 map 类型的返回值(通常需要进行运行时类型检查或假定成员存在)。

12.5 暂不支持的导入语法

为了保持语言简洁和实现的可管理性,FlexScript 目前支持以下 TypeScript 导入语法:

  • 默认导入/导出 (import Default from ... / export default ...): 请使用命名导入/导出。
  • CommonJS 风格导入 (import Name = require("...")): 与目标运行时关系不大。
  • 动态导入 (import(...) 表达式): 功能上与 require 类似但语法不同,且通常涉及异步,暂不引入。
  • 副作用导入 (import "..."): 可以通过导入后调用初始化函数实现类似效果。

12.6 静态分析与类型擦除总结

  • 静态分析: import 语句(包括仅类型导入)是静态分析器获取跨模块类型信息的关键。分析器解析 import,找到并分析依赖的模块,构建符号表,进行类型检查。
  • 类型擦除: FlexScript 在编译到目标运行时环境时会擦除所有静态类型信息。import type 的设计正是为了服务于编译/分析时的类型检查,而不增加运行时的负担。

第 13 章:协程 (Coroutines)

协作式多任务。详情见15.7 coroutine 模块函数

  • API ( coroutine 系列内置函数):
    • create(func): 创建协程。返回 coroutine 对象。
      • 签名: coroutine create(function func)
    • resume(co, arg): 启动或恢复协程 co,传递单个参数 arg (可以是 null 或包含多个值的列表)。返回一个 list<any>[success, result]success 是布尔值,指示协程运行是否成功。result 是协程 yield 的值或错误信息。
      • 签名: list<any> resume(coroutine co, any arg)
      • 示例: list<any> status = cororesume(co, "data"); bool ok = status[0]; any res = status[1];
    • yield(result): 从协程内部暂停执行,并向 resume 调用返回单个 result 值。yield 的返回值是下一次 resume 传递的单个参数。
      • 签名: any yield(any result)
      • 示例: any next_arg = coroyield(current_result);
    • status(co): 获取协程状态 (“running”, “suspended”, “normal”, “dead”)。
      • 签名: string status(coroutine co)
    • running(): 获取当前正在运行的协程。如果在主线程中调用,返回 null
      • 签名: coroutine running()

第 14 章:错误处理 (Error Handling)

  • error(message): 抛出错误。
  • pcall(func, ...): 保护模式调用。调用函数 func,并将后续参数传递给它。返回多个返回值 mutivar bool, …:
    • 成功时: true, func_return_values... (如果 func 返回 void,则为 true, null)。
    • 失败时: false, error_message
    • 签名: list<any> pcall(function func, ...)
    • 示例:
      mutiret ret1, ret2 = pcall(readFile, "f.txt");
      if (ret1 == true) {
          string content = ret1;
          // ... process content ...
      } else {
          string errMsg = ret2;
          print("Error reading file:", errMsg);
      }
      
  • xpcall(func, handler, ...): 带错误处理器的保护调用。调用 func,出错时调用 handler 并传入错误信息。返回多个返回值 mutivar any, …:
    • 成功时: true, func_return_values...
    • 失败时: false, handler_return_values...
    • 签名: list<any> xpcall(function func, function handler, ...)
    • 示例:
      string errorHandler(string msg) {
          print("Caught error:", msg);
          return "Processed error"; // 返回值将作为 xpcall 的第二个结果
      }
      mutivar ret1, ret2 = xpcall(dangerousOperation, errorHandler, arg1);
      if (ret1 == true) {
          print("Success:", ret2);
      } else {
          print("Handler result:", ret2); // 输出 "Handler result: Processed error"
      }
      

第 15 章:标准库概览

FlexScript 提供基础函数和标准库模块。(API 细节需参考官方文档)

  • 核心: print, error, pcall, xpcall, require, type, assert 等。
  • string: 子串、查找、替换、格式化等。索引基于 0#s 字节长度。
  • math: 数学运算和常量。
  • list 方法: add, insert(index, val), remove(index), # (长度), sort 等 (0-based 索引)。
  • map 方法: set, get, remove, containsKey, keys, values, size 等。
  • os: 系统交互。
  • io: 文件和标准输入输出。
  • coroutine: 协程操作。

15.1 内置函数 (全局可用)

  • print(...)

    • 签名: void print(...)
    • 描述: 输出参数的字符串表示到标准输出。参数间通常以制表符分隔,末尾加换行。
    • 示例:
      print("Log:", 10, false); // Log: 10 false
      print(); // 换行
      
  • error(message)

    • 签名: void error(string message)
    • 描述: 抛出运行时错误。如果 messagenull,抛出默认错误信息。
    • 示例: if value < 0 { error("Value negative"); }
  • pcall(func, ...)

    • 签名: list<any> pcall(function func, ...)
    • 描述: 保护模式调用。返回 [true, result][false, errorMsg]funcnull 会导致 pcall 返回 [false, "error message"]
    • 示例: list<any> status = pcall(require, "utils"); bool ok = status[0]; any mod = status[1];
  • xpcall(func, handler, ...)

    • 签名: list<any> xpcall(function func, function handler, ...)
    • 描述: 带错误处理器的保护调用。返回 [true, result][false, handler_result]funchandlernull 会导致错误(对 func 的错误会被捕获)。
    • 示例: (见 第 14 章)
  • require(module_name)

    • 签名: any require(string module_name)
    • 描述: 加载模块。module_namenull 出错。失败抛错。
    • 示例: list<any> status = pcall(require, "nonexistent"); if (!status[0]) { print("Failed:", status[1]); }
  • type(value)

    • 签名: string type(any value)
    • 描述: 返回类型字符串 (“int”, “float”, …, “null”)。
    • 示例: if (type(x) == "int") { ... }
  • assert(value [, message])

    • 签名: void assert(any value [, string message])
    • 描述: valuefalsenull 时抛错。
    • 示例: assert(input != null, "Input cannot be null");
  • tonumber(value [, base]) -> number:

    • 签名: number tonumber(any value [, int base])
    • 描述: 尝试将值转为数字 (intfloat)。base 默认为 10 (2-36)。转换失败或 valuenull 返回 null
    • 示例: auto num = tonumber("123"); if (num != null) { ... }
  • tostring(value) -> string:

    • 签名: string tostring(any value)
    • 描述: 转字符串。tostring(null) 返回 "null"

15.2 string 内置函数 (0 基索引)

函数通常期望字符串参数 snull,否则会出错。

  • #s -> int: 字节长度。

    string s = "test";
    if (s != null) { print(#s); } // 4
    
  • strsub(s, start [, end]) -> string:

    • 签名: string strsub(string s, int start [, int end])
    • 描述: 提取子串。0 基索引。start 包含, end 不包含。end 默认 #s。负索引从后计数 (-1 是最后一个)。索引无效返回 ""
    • 示例:
      string greeting = "Hello World!"; // len 12
      print(strsub(greeting, 0, 5));  // "Hello"
      print(strsub(greeting, 6));     // "World!"
      print(strsub(greeting, -1));    // "!"
      print(strsub(greeting, -6, -1)); // "World"
      print(strsub(greeting, 12));   // ""
      
  • strfind(s, pattern [, init [, plain]]) -> list<int>:

    • 签名: list<int> strfind(...)
    • 描述: 查找子串 patterninit 为 0 基搜索起始位置 (默认为 0)。成功返回包含 0 基 startend (不含) 索引的列表 [start, end]。失败返回 null
    • 示例:
      list<int> indices = strfind("abcabc", "b", 2); // 从索引 2 开始查找 'b'
      if (indices != null) {
          print("Found at index:", indices[0]); // 输出 "Found at index: 4"
          // indices[1] 将是 5
      }
      
  • strrep(s, n) -> string:

    • 签名: string strrep(string s, int n)
    • 描述: 重复 n 次。n <= 0 返回 ""
  • strlower(s) -> string, strupper(s) -> string:

    • 签名: string strlower(string s), string strupper(string s)
    • 描述: 大小写转换。
  • strreverse(s) -> string:

    • 签名: string strreverse(string s)
    • 描述: 字节反转。
  • strformat(formatstring, ...):

    • 签名: string strformat(string formatstring, ...)
    • 描述: 格式化。支持 %s, %d, %f, %% 等。
    • 示例: print(strformat("Coords: (%d, %d)", 10, 20));
  • strbyte(s [, i [, j]]) -> int (若只取一个字节) 或 list<int> (若取多个)

    • 签名 (单个): int strbyte(string s, int i)
    • 签名 (范围): list<int> strbyte(string s, int i, int j)
    • 描述: 获取 0 基索引 i (或 ij 包含) 处的字节值 (int)。索引无效返回 null
    • 示例: auto b = strbyte("Test", 1); // 获取 'e' 的字节值,b 为 int 或 null
  • strchar(...) -> string:

    • 签名: string strchar(...)
    • 描述: 将一个或多个整数字节值转换为字符串。
  • strdump(func [, strip]) -> string:

    • 签名: string strdump(function func [, bool strip])
    • 描述: 函数转字节码。
  • strmatch(s, pattern [, init]) -> any: (若支持模式匹配)

    • 签名: any strmatch(...) (返回值依赖于捕获)
    • 描述: 模式匹配 (0 基 init)。注意: 如果模式可以捕获多个值,此函数可能返回一个 list
  • strgsub(s, pattern, repl [, n]) -> list<any>: (若支持模式匹配)

    • 签名: list<any> strgsub(...)
    • 描述: 全局替换。返回包含替换后字符串和替换次数的列表 [new_string, count]
    • 示例: list<any> res = strgsub("hello world", "l", "L"); print(res[0], res[1]); // "heLLo worLd", 3

15.3 list 内置函数 (操作 listmap, 0 基索引)

函数通常期望容器参数非 null

  • listInsert(list, [pos,] value):
    • 签名: void tbinsert(list list, [int pos,] any value)
    • 描述: 在 0 基 pos 插入。省略 pos 在末尾 (#list) 插入。pos 必须在 [0, #list] 范围内。
    • 示例:
      list l = [10];
      tbinsert(l, 0, 5);  // [5, 10]
      tbinsert(l, 2, 15); // [5, 10, 15]
      tbinsert(l, 25);  // [5, 10, 15, 25]
      // tbinsert(l, 5, 30); // 错误: 索引 5 越界 (#l is 4)
      
  • listRemove(list [, pos]):
    • 签名: any tbremove(list list [, int pos])
    • 描述: 移除并返回 0 基 pos 处元素。省略 pos 移除最后一个 (#list - 1)。pos 必须在 [0, #list - 1] 范围内。对空列表操作出错。
    • 示例:
      list l = ["x", "y", "z"];
      auto y = tbremove(l, 1); // y="y", l=["x", "z"]
      auto z = tbremove(l);    // z="z", l=["x"]
      auto x = tbremove(l, 0); // x="x", l=[]
      // auto err = tbremove(l); // 运行时错误: 列表为空
      
  • listSort(list [, comp]):
    • 签名: void tbsort(list list [, function comp])
    • 描述: 原地排序。comp(a, b) 返回 true 表示 a 应在 b 前。
  • listConcat(list [, sep [, i [, j]]]):
    • 签名: string tbconcat(list list [, string sep [, int i [, int j]]])
    • 描述: 连接 list[i..j] (0 基, 含 j) 的 string/number 元素。i 默认 0, j 默认 #list - 1sep 默认 ""
  • listPack(...) -> list:
    • 签名: list tbpack(...)
    • 描述: 将所有参数打包成一个新的 list。
    • 示例: list<any> packed = tbpack(1, "a", true); // packed is [1, "a", true]
  • listUnpack:
    • 签名: mutivar tbunpack(list)
    • 描述: 将一个list解包为any类型的N个新值返回。
    • 示例: mutivar a, b, c = tbunpack([1, "a", true]); // a:1, b:"a", c:true
  • listMove(a1, f, e, t [, a2]):
    • 签名: void tbmove(list a1, int f, int e, int t [, list a2])
    • 描述: 复制 a1[f..e] (0 基) 元素到 a2 从索引 t (0 基) 开始的位置。如果 a2 省略,则默认为 a1 (在同一列表内移动)。

15.4 math 模块函数

函数通常期望数字参数,传入 null 会出错。模块使用需要使用模块名+.访问模块内部对象,例如:使用方法为math.pi,math.abs(-13)

  • 常量: pi, huge
  • 函数: abs, sqrt, pow, exp, log, log10, sin, cos, tan, asin, acos, atan, atan2, deg, rad, floor, ceil, fmod, min, max, random, randomseed
    • pow(x, y): 计算 x 的 y 次幂。
    • log(x [, base]): 计算对数,默认自然对数。
    • random([m [, n]]):
      • 无参数: 返回 [0, 1) 范围的 float。
      • 一个参数 n: 返回 [1, n] 范围的 int。
      • 两个参数 m, n: 返回 [m, n] 范围的 int。

15.5 io 模块函数

文件和标准 IO。函数失败通常返回 list<any>[null, errorMsg]。对 null 文件句柄操作会出错。 模块使用需要使用模块名+.访问模块内部对象。

  • ioopen(path [, mode]) -> list<any>
    • 签名: list<any> ioopen(string path [, string mode])
    • 描述: 打开文件。成功返回 [file_handle, null]。失败返回 [null, error_message]
    • 示例:
      list<any> result = ioopen("myfile.txt", "r");
      if (result[0] != null) {
          any file = result[0]; // file handle
          // ... use file handle ...
          file.close();
      } else {
          print("Error opening file:", result[1]);
      }
      
  • 文件句柄方法: read, write, close, seek, lines 等。这些方法本身也可能返回 list<any> 来表示成功/失败和结果/错误。
  • ioread(...), iowrite(...), ioflush() 等便捷函数。

15.6 os 模块函数

系统交互。函数失败可能返回 nulllist<any> (例如表示成功/失败状态和结果/错误信息)。 模块使用需要使用模块名+.访问模块内部对象。

  • clock, time, date, difftime, exit, getenv, execute, remove, rename, tmpname

15.7 coroutine 模块函数

模块使用需要使用模块名+.访问模块内部对象。

  • create, resume, yield, status, running。(详见 第 13 章)

15.8 debug (谨慎使用)

模块使用需要使用模块名+.访问模块内部对象。

  • traceback, getinfo, sethook

第 16 章:与 C 交互 (概念)

FlexScript 提供 C API 支持嵌入和扩展。

  • 嵌入: C/C+=1 程序创建 FlexVM,加载并执行 .flx 文件或字符串,调用 FlexScript 函数,与 FlexScript 交换数据。
    • 从 C 调用 FlexScript 函数时,函数将返回单个 FlexValue。如果 FlexScript 函数逻辑上需要返回多个值,它会返回一个 FlexValue 类型的列表,C 代码需要使用 C API 检查该值是否为列表,并从中提取元素。
    • 将数据从 C 推送到 FlexScript 栈时,也应遵循一次推入一个 FlexValue 的原则。
  • 扩展: C 函数遵循特定签名 (如 FlexValue my_c_func(FlexVM* vm, int argc, const FlexValue* argv))。
    • C 函数必须返回单个 FlexValue。C 函数不支持返回多个逻辑值,如果需要返回多个逻辑值,C 函数应创建一个 FlexScript 列表 (FlexValue 类型),将结果放入列表,然后返回该列表 FlexValue
    • 使用 API 操作参数和返回值 (注意 null 处理),并通过 API (如 flexRegisterFunction) 注册到 FlexScript 环境。

第 17 章:附录 A:语法速查

  • 变量声明:

    • 单变量声明: [const?] (type|auto) ID [ = expression ] ;
    • mutivar 多变量声明: mutivar [global?] [const?] ID [, [global?] [const?] ID]* [ = expression ] ; (右侧单个 expr)
  • 函数定义: [global?] returnType qualifiedID ( [paramList] ) { block } (单个 returnType)

  • Lambda: function ( [paramList] ) -> returnType { block } (单个 returnType)

  • 类定义: class ID { [classMember]* }

  • If: if (exp) { block } [ else if (exp) { block } ]* [ else { block } ]

  • While: while (exp) { block }

  • For (C-Style): for ( [init] ; [cond] ; [update] ) { block }

  • For (For-Each): for ( declItem : collection ) { block } (declItem单个 (type|auto) ID)

  • 赋值: ·lvalue [, lvalue]* = expression [, expression]* ;` (支持多重赋值)

  • 更新赋值: lvalue op= expression ;

第 18 章:附录 B:语法校验示例

B.1 变量声明 (variableDeclaration)

// --- 合法 ---
int x;
float y = 1.0;
auto name = "Flex";
//string s1, string s2;  错误: 声明多个同类型变量
//int count = 0, int limit = 100;  错误: 定义多个变量并初始化
bool flag = true;
const int MAX = 1000;
list<int> items = [1, 2, 3];
map<string, bool> config = { enabled: true };
int v1 = 10;
int v2 = 20;
list<any> coords = getCoords();
auto a = coords[0];
auto b = coords[1];
mutivar m1, m2; // 合法, m1=null, m2=null
mutivar x, y = getCoords(); // 合法, 用 getCoords 的返回值初始化 x, y
export mutivar g1, g2 = someFunc(); // 合法
export mutivar global g3, const g4 = someFunc(); // 合法

// --- 语法错误 ---
// var z = 10;             // ERROR: 无 'var' 关键字, 且缺少类型/auto
// int a, b;               // ERROR: 变量 'b' 缺少类型注解
// auto p = 1, q = 2;   // ERROR: 变量 'q' 缺少 'auto'
// x = 10;                 // ERROR: 如果 'x' 未声明, 这是赋值语句错误;如果这是想声明, 则缺少类型/auto
// const c;                // ERROR: const 变量需初始化 (通常)
// list numbers;           // ERROR: 缺少泛型参数 <T> (应为 list<any> 或 list<具体类型>)
// map lookup;             // ERROR: 缺少泛型参数 <K, V>
// int v1, int v2 = 10, 20; // ERROR: 声明时每个变量需单独初始化
// auto a, auto b = getCoords(); // ERROR: auto 不支持解包声明, 使用 mutivar 或赋值语句
// mutivar i, j = 10, 20; // ERROR: mutivar 初始化器必须是单个表达式 (可以是列表字面量 [10, 20])

全局变量使用global定义
global int a;//即使没有赋值,a依然会被注册到全局表_G中,不声明global int a;直接进行赋值a = 0;会再运行时报错。
变量支持不同作用域遮蔽。
int b = 0;
{
    global int b = 54;
    print(b); // 输出54
}
print(b);//输出0

B.2 函数定义与 Lambda

// --- 合法 ---
void printMsg(string msg) { print(msg); }
int add(int a, int b) { return a + b; }
list<any> checkValue(int v) { return [v, v > 0]; } // 返回列表
auto greet = function(string name) -> string { return "Hi, " .. name; };
function process(function cb) -> void { cb(10); }

// --- 语法错误 ---
// func myFunc() {}           // ERROR: 无 'func' 关键字(应为 function), 且缺少返回类型
// int add(a, b) { return a+b; } // ERROR: 参数 'a', 'b' 缺少类型注解
// myLambda = (x) -> x + 1;   // ERROR: Lambda 必须用 'function' 关键字且有返回类型
// function sub(int a, int b) { return a - b; } // ERROR: Lambda 或函数定义缺少返回类型 (-> 或在函数名前)
// (int, string) myFunc(int x) // ERROR: 不支持元组返回类型
// (int, bool) checkValue(int v) { return v, v > 0; } // ERROR: 不支持多返回值

B.3 For 循环

// --- 合法 (C-Style) ---
for (int i = 0; i < 10; i+=1) { /*...*/ }
for (auto j = 0, auto k = 5; j < k; j+=1) { /* 分别初始化 */ }
int idx = 0;
for (idx = 0; idx < 10; idx += 2) { /*...*/ } // 初始化使用表达式列表
for (; idx < 20; ) { idx+=1; } // 省略初始化和更新
for (;;) { break; } // 省略所有部分

// --- 合法 (For-Each) ---
list<string> items = ["x", "y"];
// 遍历列表值
for (string item : items) { print(item); }
map<int, bool> flags = {1: true, 2: false};
// 遍历映射键
for (int key : flags) {
    bool value = flags[key];
    print(key, value);
}

// --- 语法错误 ---
// for i = 0, 10 { ... }          // ERROR: C-Style 缺少类型/auto, 且缺少分号 ; (数值 for 已移除)
// for i < 10; i+=1 { ... }         // ERROR: C-Style 缺少括号 (), 且缺少初始化分号
// for (item : mylist) { ... }   // ERROR: For-Each 循环变量 'item' 缺少类型/auto
// for (int i; i < 10; i+=1)       // ERROR: 循环体必须用 {} 包裹
// for (int k, v : mymap) { ... } // ERROR: For-Each 不支持多个循环变量
// for (auto key, auto value : flags) { /*...*/ } // ERROR: For-Each 不支持多个循环变量

B.4 赋值语句

// --- 合法 ---
int x = 0; int y = 0; string s;
x = 10;
y += 5;
x, y = 100, 200;        // 合法: 多重赋值
list<any> pos = getPos();
x, y = pos[0], pos[1]; // 合法: 从列表元素赋值
x, y = getPos();         // 合法: 从函数返回值赋值

// --- 语法错误 ---
// x = ;                // ERROR: = 右侧缺少表达式
// x, = 1, 2;            // ERROR: = 左侧缺少左值 (需要至少一个)
// 1 = x;              // ERROR: = 左侧不是左值
// x + 1 = 10;           // ERROR: = 左侧不是左值
// x = y = 1;          // ERROR: 赋值是语句, 不能直接链式 (应分两句: y = 1; x = y;)

B.5 类与 New

// --- 合法 ---
class MyClass { int val; void __init__(int v) { this.val = v;} }
auto obj = new MyClass(10);
auto obj2 = new MyClass(); // 合法, __init__ 可以无参数或有默认值

// --- 语法错误 ---
// class { int x; }           // ERROR: 类名缺失
// auto obj = MyClass(10);     // ERROR: 创建对象必须使用 'new'
// auto obj = new MyClass;     // ERROR: 'new' 后面必须带括号 (), 即使无参数
// class MyClass { x; }         // ERROR: 字段 'x' 缺少类型注解

多返回值的内置函数:

table 内置函数(操作 list 和 map) mutivar listUnpack(list list, int i = 0, int j = #list-1) // return list[i], list[i+1], ..., list[j]; 的多个值 string 内置函数(字符串操作) mutivar strByte(string s, int i = 0, int j = 0) // 返回 s[i] 到 s[j] 位置的 ASCII 码,多个值 mutivar strFind(string s, string pattern, int init = 0, bool plain = false) // 返回匹配的起始索引和结束索引 mutivar strMatch(string s, string pattern, int init = 0) // 返回匹配到的子串(可能多个) mutivar strGsub(string s, string pattern, string|function repl, int n = -1) // 返回替换后的字符串和替换次数 math 内置函数(数学库) mutivar math.max(float x, float ...) // 返回最大值(可能多个) mutivar math.min(float x, float ...) // 返回最小值(可能多个) coroutine 内置函数(协程) mutivar coro.resume(coroutine co, any ...) // 成功时返回 true 和协程返回值,失败时返回 false 和错误信息 mutivar coro.yield(any ...) // 返回传入的参数 os 内置函数(系统库) mutivar os.execute(string command) // 返回执行结果(成功时返回 true 和退出码,失败时返回 nil、错误类型、退出码) io 内置函数(输入输出) mutivar io.read(string format = "*l") // 读取输入(可能多个) debug 内置函数(调试库) mutivar dbg.getinfo(function f, string what = "nSl") // 返回函数的多个信息字段