第 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
) 的面向对象编程(无继承)。 - 模块系统:
require
和import
机制。 - 错误处理:
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
: 通用数字类型提示 (运行时值为int
或float
)。string
: 文本字符串。bool
: 布尔值 (true
或false
)。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(); // 或者使用更具体的列表类型
list
和map
支持泛型<...>
来提供更精确的元素或键值类型信息。
3.5.2
list定义方法:
list a = [123, "124"];
print(#a);
查看list长度,输出2print(a[0]);
输出123a[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 ( 变量声明 : 集合表达式 ) { ... }
变量声明
: 必须为单个循环变量提供type
或auto
。不支持在一次迭代中解包多个变量 (如for (mutivar key, value : map)
)。集合表达式
: 列表、映射或其他可迭代对象。- listIter 遍历
list
时,可以使用内置函数listIter,令循环变量被赋值为元素值。 - mapIter 遍历
map
时,可以使用内置函数mapIter,令循环变量被赋值为键(key)。 内置函数listIter、mapIter用于迭代两种容器:
- listIter 遍历
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
源文件都被视为一个独立的模块。模块间的交互主要通过 export
和 import
关键字进行。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
语句不仅指导静态分析,也会在程序运行时触发模块的加载和执行(如果尚未加载,且导入了非类型成员)。
加载与缓存机制:
- 缓存检查: 运行时环境维护一个已加载模块的内部缓存。当通过
import
(或后续描述的动态加载函数)请求加载某个模块路径时,运行时首先检查该路径是否已存在于缓存中。 - 缓存命中: 如果模块已在缓存中,运行时将直接返回缓存的模块结果(即该模块首次执行后返回的值),不会再次执行模块的代码。
- 缓存未命中: 如果模块不在缓存中,运行时将继续执行加载过程。
- 缓存检查: 运行时环境维护一个已加载模块的内部缓存。当通过
模块路径解析:
- 运行时加载器会根据一套定义好的搜索规则和路径来查找由
import
语句中的字符串指定的模块文件。 - 这个搜索过程通常涉及检查一系列目录或路径模板。路径模板中可能包含特殊占位符(例如
?
),加载器会用请求的模块路径字符串(通常会将路径中的.
替换为平台相关的目录分隔符)替换这些占位符,生成候选文件路径。 - 加载器会按顺序尝试这些候选路径,直到找到对应的源文件为止。这些搜索路径通常由运行环境或配置决定。
- 运行时加载器会根据一套定义好的搜索规则和路径来查找由
模块执行:
- 一旦找到源文件且该模块不在缓存中,加载器会执行该模块的代码,且仅执行这一次。
- 模块代码执行完毕后,其返回值(通常是包含了
export
成员的那个对象)会被存储到模块缓存中,关联到其模块路径。 - 最后,这个返回值被提供给请求加载的代码(即
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)
- 行为:
- 接收一个表示模块路径的字符串
modulePath
。 - 执行与
import
相同的运行时加载和缓存机制:检查缓存 -> (如果未命中) -> 搜索路径 -> 执行模块代码一次 -> 缓存结果。 - 返回缓存中或新加载执行后得到的模块结果(这是一个
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)
- 描述: 抛出运行时错误。如果
message
为null
,抛出默认错误信息。 - 示例:
if value < 0 { error("Value negative"); }
- 签名:
pcall(func, ...)
- 签名:
list<any> pcall(function func, ...)
- 描述: 保护模式调用。返回
[true, result]
或[false, errorMsg]
。func
为null
会导致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]
。func
或handler
为null
会导致错误(对func
的错误会被捕获)。 - 示例: (见 第 14 章)
- 签名:
require(module_name)
- 签名:
any require(string module_name)
- 描述: 加载模块。
module_name
为null
出错。失败抛错。 - 示例:
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])
- 描述:
value
为false
或null
时抛错。 - 示例:
assert(input != null, "Input cannot be null");
- 签名:
tonumber(value [, base]) -> number
:- 签名:
number tonumber(any value [, int base])
- 描述: 尝试将值转为数字 (
int
或float
)。base
默认为 10 (2-36)。转换失败或value
为null
返回null
。 - 示例:
auto num = tonumber("123"); if (num != null) { ... }
- 签名:
tostring(value) -> string
:- 签名:
string tostring(any value)
- 描述: 转字符串。
tostring(null)
返回"null"
。
- 签名:
15.2 string
内置函数 (0 基索引)
函数通常期望字符串参数 s
非 null
,否则会出错。
#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(...)
- 描述: 查找子串
pattern
。init
为 0 基搜索起始位置 (默认为 0)。成功返回包含 0 基start
和end
(不含) 索引的列表[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
(或i
到j
包含) 处的字节值 (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
内置函数 (操作 list
和 map
, 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 - 1
。sep
默认""
。
- 签名:
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
模块函数
系统交互。函数失败可能返回 null
或 list<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 调用 FlexScript 函数时,函数将返回单个
- 扩展: C 函数遵循特定签名 (如
FlexValue my_c_func(FlexVM* vm, int argc, const FlexValue* argv)
)。- C 函数必须返回单个
FlexValue
。C 函数不支持返回多个逻辑值,如果需要返回多个逻辑值,C 函数应创建一个 FlexScript 列表 (FlexValue
类型),将结果放入列表,然后返回该列表FlexValue
。 - 使用 API 操作参数和返回值 (注意
null
处理),并通过 API (如flexRegisterFunction
) 注册到 FlexScript 环境。
- C 函数必须返回单个
第 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") // 返回函数的多个信息字段