1. 简介
FlexScript 是一种强制提示类型的、解释执行的、支持协程的、可即时编译(JIT)的通用脚本语言。目标支持ios,macOS,windows,android,web,linux平台。它的设计目标是:
- 小巧: 编译器与解释器集于一体且小于1MB。
- 简单易学: 语法简洁、清晰,易于上手。
- 易于使用: 适合游戏逻辑、快速原型开发、脚本编写、自动化任务、交互式应用等。
- 可扩展性: 可以方便地与 C代码交互,允许嵌入到 C 应用程序中。
- 灵活性: 支持多种编程范式(过程式、面向对象、函数式)。
- 热更新: 支持语言级的热更新,允许在程序运行时修改代码并立即看到效果,无需重启程序。
2. 词法结构
2.1. 文件格式
FlexScript 使用 Unicode 字符集,强制采用 UTF-8 编码,并使用.flx作为后缀(以后会改名))
2.2. 大小写敏感性
FlexScript 是大小写敏感的。
2.3. 空白字符
空格、制表符、换行符(以及回车符)统称为空白字符,用于分隔 token,但在其他方面通常被忽略(字符串字面量内部除外)。
2.4. 注释
- 单行注释:以
//
开头,直到行尾。 - 多行注释:以
/*
开头,以*/
结尾。多行注释不能嵌套。
2.5. 标识符
- 定义:由字母、数字和下划线 (
_
) 组成的字符序列,必须以字母或下划线开头。 - 规范:
[a-zA-Z_][a-zA-Z0-9_]*
(正则表达式) - 示例:
myVariable
,_count
,functionName123
2.6. 关键字
以下是 FlexScript 的保留关键字,不能用作标识符:
int float string bool any null void
if else while for break continue return
true false
auto this new
__init__ __deinit__
list map
global
coroutine
typeof
static
const
print
class
private
require
throw
2.7. 字面量
- 整数: 十进制整数。例如:
123
,-45
,0
。 - 浮点数: 包含小数点或指数的数字。例如:
3.14
,-0.5
,1.0e-6
,2.5E+3
。 - 字符串: 用双引号 (
"
) 括起来的字符序列。例如:"Hello, world!"
,字符串使用..运算符拼接,字符串支持驻留,相同的字符串字面量在内存中只存储一次。- 转义字符:支持常见的转义字符,如
\n
(换行),\t
(制表符),\\
(反斜杠),\"
(双引号),\'
(单引号),\r
(回车符)、\a
(响铃符)、\b
(退格符)、\f
(换页符),\v
(垂直制表符),\xHH
(十六进制)和\uHHHH
(Unicode)。
- 转义字符:支持常见的转义字符,如
- 布尔值:
true
或false
。 - 空值:
null
。 - 列表: 用方括号 (
[]
) 括起来的、用逗号分隔的元素序列。例如:[]
,[1, 2, 3]
,["apple", "banana"]
。 - 映射: 用花括号 (
{}
) 括起来的、用逗号分隔的键值对序列,键和值之间用冒号 (:
) 分隔。例如:{}
,{ name: "Alice", age: 30 }
。
2.8. 运算符
优先级 | 类别 | 运算符 | 结合性 |
---|---|---|---|
1 | 后缀运算 | () [] . :: |
从左到右 |
2 | 一元运算 | ! ~ + - |
从右到左 |
3 | 类型转换 | (type) |
从右到左 |
4 | 乘除模 | * / % |
从左到右 |
5 | 加减 | + - |
从左到右 |
6 | 移位 | << >> |
从左到右 |
7 | 关系 | < > <= >= |
从左到右 |
8 | 相等性 | == != |
从左到右 |
9 | 按位与 | & |
从左到右 |
10 | 按位异或 | ^ |
从左到右 |
11 | 按位或 | | |
从左到右 |
12 | 逻辑与 | && |
从左到右 |
13 | 逻辑或 | || |
从左到右 |
14 | 赋值 | = += -= *= /= %= |
从右到左 |
2.9. 语句分隔符
- 语句必须以分号 (
;
) 结尾。
2.10. 代码块
- 使用大括号 (
{}
) 来定义代码块。
3. 数据类型
- 基本类型:
int
: i64有符号整数 (取值范围:[-2^63 , 2^63 - 1])float
: f64双精浮点数string
: 字符串bool
: 布尔值any
: 可以表示任何类型
- 复合类型:
list<T>
: 有序列表list
是list<any>
的简写。
map<K, V>
: 键值对集合map
是map<any, any>
的简写。
coroutine
: 协程
- 标注类型与真实类型:
- 一个变量总是存在真实类型和标注类型(Type Hinting)。
- 变量的真实类型永远取决于对其赋值的类型。
- 标注类型在编译时会被擦除(类型擦除)。
- 标注类型主要用于提高代码可读性和提供给静态分析工具(如 IDE,LSP)信息。
- 强烈建议使用
any
来标注那些可能在运行时改变类型的变量,以避免误导(不要用auto
代替)。 - 类型推断: 对于字面量, 编译器会自动推断其类型:
auto x = 5;
// x 的类型是 intauto y = 3.14;
// y 的类型是 floatauto z = "hello";
// z 的类型是 stringauto a = true;
// a 的类型是 boolauto b = null;
// b 的类型是 nullauto lst = [];
// lst的类型是listauto m = {};
// m 的类型是 map<any, any>
- 查询真实类型
string typeof(any v)函数
//对象的真实类型被nanboxing技术储存在值内,可以在运行时态调用typeof查询判定真实类型。 if(typeof(a) != "int") doSomeThing();
4. 变量
-
变量声明:
// 标注类型 Type variableName = expression; // 自动类型推断 (使用 auto) auto variableName = expression; // 全局变量 global Type variableName = expression; global auto variableName = expression;
-
变量作用域:
- 局部变量: 在函数、类方法或代码块内部声明的变量,其作用域从声明处开始到包含它的块的结束。
- 全局变量: 在模块顶层使用
global
关键字声明的变量,或者在函数内部使用global
关键字定义的变量,其作用域为整个程序(所有模块)。
-
全局变量初始化顺序: 基于requre依赖关系顺序执行,FlexScript本身不支持直接循环require,但可以通过延迟require或拆分公共模块解决循环依赖问题。
5. 表达式
FlexScript 支持常见的表达式,包括:
- 字面量
- 变量
- 运算符构成的表达式
- 函数调用
- 成员访问
- 索引访问
- 对象创建表达式
- 圆括号改变优先级
6. 控制流
-
条件语句:
if (condition) { // ... } else if (condition2) { // ... } else { // ... }
-
循环语句:
// while 循环 while (condition) { // ... } // for 循环 for (initialization; condition; increment) { // ... }
-
跳转语句:
break
: 终止循环。continue
: 跳过当前循环迭代。return
: 从函数或协程返回值。
7. 函数
-
函数定义:
返回类型 函数名(参数类型 参数名, ...) { // 函数体 return 返回值; // 如果返回类型不是 void,则必须有 return 语句 }
- 如果有返回值,必须声明返回类型。
- 如果没有返回值,应使用
void
。
-
函数调用:
returnValue = functionName(arg1, arg2, ...);
8. 类 (Class)
-
类定义:
class ClassName { // 类级静态成员 static Type staticMember; // 成员变量 Type member1; Type member2; // 构造函数 (名称为 __init__) void __init__(Type arg1, Type arg2, ...) { this.member1 = arg1; this.member2 = arg2; // ... // 注意:构造函数不需要显式返回 this } // 成员函数 Type methodName(Type param1, Type param2, ...) { // ... return value; // 可选 } // 析构函数 (名称为 __deinit__) void __deinit__() { // ... 资源清理 ... } } // 类级静态成员需类外初始化 Type ClassName::staticMember = initialValue;
-
对象创建:
auto objectName = new ClassName(arg1, arg2, ...);
-
成员访问:
objectName.member = value; Type returnValue = objectName.methodName(arg1, arg2, ...);
//静态成员仅支持类名+作用域访问符(::)+静态成员名来访问 ClassName::staticMember2 = 13; returnValue = ClassName::staticMember2;
-
特性:
- 所有成员都是公开的 (public)。
- 不支持继承。
- 构造函数名为
__init__
,析构函数名为__deinit__
。- 如果没有显式定义
__init__
,则会有一个默认的无参构造函数。 __deinit__
在对象被垃圾回收时调用(具体时机取决于垃圾回收策略)。
- 如果没有显式定义
this
关键字引用当前对象。- 静态成员仅支持类名 + 作用域访问符(::) + 静态成员名来访问。
9. 模块 (Modules)
-
模块定义:
- 每个
.flx
文件都是一个独立的模块。 - 模块内所有在顶层(全局)定义的变量、函数和类,默认都是导出的。
- 仅
private
关键字修饰的变量、函数或类将不会被导出。 - 使用
__module__
关键字访问当前模块。
// mymodule.flx // 不需要 module 声明 global int exportedVar = 10; // 默认导出 private int internalVar = 20; // 不导出 void exportedFunction() { //默认导出 // ... } private void internalFunction() { // ... } class ExportedClass{ //默认导出 } private class internalClass{ }
- 每个
-
模块导入:
- 使用
require
语句导入其他模块。 语法:auto module = require("moduleName")
require
语句返回一个表示被导入模块的map
对象。所有 未被private
修饰的全局变量、函数和类都成为该map
对象的字段。- require函数只会加载一次模块, 后续的require调用会直接返回缓存的模块对象。
// main.flx auto mymodule = require("mymodule"); // 导入 mymodule mymodule.exportedVar = 30; // 可以访问 // mymodule.internalVar = 40; // 错误!无法访问 private 成员 mymodule.exportedFunction(); // 可以访问 auto myObj = new mymodule.ExportedClass(); //可以访问
- 为了方便,你也可以将map中的成员导入到当前作用域:
// main.flx map mymodule = require("mymodule"); //遍历匹配的键值对,并打印 for(list<string, any> [key, v] : mymodule) { //将mymodule的成员名称和成员类型打印出来 print(key, typeof(v)); } exportedVar = 30; exportedFunction(); auto obj = new ExportedClass();
- 使用
-
模块查找顺序:
- 当前目录
- 标准库目录 (由环境变量或编译时设置指定)
- 用户自定义搜索路径(通过编译器选项或环境变量指定)
10. 协程 (Coroutines)
- 创建:
auto co = coroutine.create(func);
- 启动/恢复:
auto (ok, ...values) = coroutine.resume(co, ...args);
- 挂起:
auto ...values = coroutine.yield(...args);
- 状态查询:
string status = coroutine.status(co);
返回"running"
,"suspended"
,"dead"
, or"normal"
. - 获取当前协程:
coroutine co = coroutine.running();
- 错误处理:
- 如果协程内发生未捕获的错误, 协程会进入
dead
状态. coroutine.resume
会返回false
以及错误信息 (如果有).
- 如果协程内发生未捕获的错误, 协程会进入
coroutine.running()
的使用场景:- 调试。
- 在协程内部获取自身的引用,用于将自身传递给其他函数或协程。
11. 内置类型方法
list
是 list<any>
的简写。
11.1. list<T>
类型
// 返回列表的长度(元素个数)。
int size()
// 在列表末尾添加一个元素。
// item: 要添加的元素。
void push(any item)
// 返回指定索引的元素。可以用 list[index] 替代。
// index: 元素的索引。
any get(int index)
// 设置指定索引的元素值。可以用 list[index] = value 替代。
// index: 元素的索引。
// value: 新的值。
void set(int index, any value)
// 从列表中删除指定位置开始的指定数量的元素。
// idx: 起始索引。
// length: 要删除的元素个数(可选,默认为 1)。
void erase(int idx, int length = 1)
// 返回自身深拷贝的副本。
list<T> clone()
// 清空列表。
void clear()
11.2. map<TK, TV>
类型
// 返回映射中键值对的数量。
int size()
// 返回一个包含映射中所有键的列表。
list keys()
// 检查映射是否包含指定的键。
// key: 要检查的键。
bool has(any key)
// 返回指定键对应的值。可以用 map[key] 替代。
// key: 要获取的值的键。
any get(any key)
// 设置键值对。可以用 map[key] = value 替代。
// key: 键。
// value: 值。
void set(any key, any value)
// 从映射中移除指定的键值对。
// key: 要移除的键。
void erase(any key)
// 返回自身深拷贝的副本。
map<TK, TV> clone()
// 清空映射。
void clear()
12. 错误处理
-
FlexScript 支持类似于 Lua 的异常处理机制,使用
pcall
和xpcall
进行受保护调用,并允许抛出和捕获错误。 -
错误值: 任何值都可以作为错误被抛出(类似于 Lua,不仅仅是字符串)。
-
throw
函数: 用于抛出一个错误。throw(value)
value
: 要抛出的错误值(可以是任何类型)。
-
pcall
函数: 用于受保护地调用一个函数。auto result = pcall(func, ...args)
func
: 要调用的函数。...args
: 传递给func
的参数。result
: 一个list
对象。- 如果调用成功,
result
的第一个元素是true
,后续元素是func
的返回值。 - 如果发生错误,
result
的第一个元素是false
,第二个元素是错误值。
- 如果调用成功,
-
xpcall
函数: 用于受保护地调用一个函数,并指定一个错误处理函数。auto result = xpcall(func, errHandler, ...args)
func
: 要调用的函数。errHandler
: 一个函数,用于处理func
执行期间发生的错误。该函数接收错误值作为参数,并且可以返回任何值(这些值将成为xpcall
返回的list
中false
之后的元素)。...args
: 传递给func
的参数。result
: 一个list
对象。- 如果调用成功,
result
的第一个元素是true
,后续元素是func
的返回值。 - 如果发生错误,
result
的第一个元素是false
,后续元素是errHandler
函数的返回值。
- 如果调用成功,
-
异常返回列表:
list results = pcall(func, ...args) // list results = xpcall(func, errHandler, ...args)
- results [true/false, result](
true
表示成功,false
表示发生错误),result为实际函数返回值。
- results [true/false, result](
-
工作原理:
pcall
和xpcall
会捕获func
执行期间发生的任何错误 (包括由throw
函数抛出的错误, 以及运行时错误, 如除以零、类型错误等)。pcall
和xpcall
总是返回一个list
对象。
-
示例:
void mightFail(int a, int b) { if (b == 0) { throw("division by zero") } return a / b; } list result = pcall(mightFail, 10, 2); // result == [true, 5] print("Result:", result[1]) // 使用索引访问, 输出: Result: 5 list result = pcall(mightFail, 5, 0); // result == [false, "division by zero"] print("Error:", result[1]) // 输出: Error: division by zero // 抛出自定义错误值(不仅仅是字符串) void throwCustomError() { throw({ code : 123, message : "Something went wrong" }) } list result = pcall(throwCustomError) //result == [false, { code : 123, message : "Something went wrong" }] if (!result[0]) { // 检查 list 的第一个元素 bool err = result[1]; print("Custom Error Code:" , err.code, "Message:", err.message) //输出: Custom Error Code: 123 Message: Something went wrong } // 使用 xpcall 和自定义错误处理函数 string errorHandler(any err) { print("An error occurred:", err) return "error occurred"; } list result = xpcall(mightFail, errorHandler, 10, 0) // result == [false, "error occurred"] if (result[0]) { print("Result:", result[1]) } else { print("xpcall failed, error handler returned:", result[1]) // 输出: xpcall failed, error handler returned: error occurred }
-
flexCallCError
(C 绑定):- 接口声明
void flexCallCError(FlexVM* vm, const char* errorMessage):
- 在 C 绑定函数中,
flexCallCError
用于向 FlexScript 抛出 一个错误。 - 它应该返回一个表示错误的值(通常是一个包含错误信息的字符串, 但也可以是任何
FlexValue
)。 - FlexScript 虚拟机会捕获由
flexCallCError
抛出的错误, 并在pcall
/xpcall
的上下文中将其作为错误值处理。
- 接口声明
-
运行时错误列表:(与之前相同,但现在这些错误会被
pcall
和xpcall
捕获)- 除以零
- 无效类型操作 (例如, 字符串和数字相乘)
- 数组/映射 索引越界
- 调用
null
值的成员函数 - 类型转换失败
- 栈溢出 (Stack Overflow)
13. 与 C 交互
FlexScript 提供了与 C 代码交互的能力,允许将 C 函数注册为 FlexScript 函数,并在 FlexScript 代码中调用它们。
13.1. C 绑定接口
// 被调用C函数的通用函数声明
typedef FlexValue (*FlexBindFuncPtr)(FlexVM* vm, int argc, const FlexValue* argv);
// 定义的绑定结构
// flexFunctionName: FlexScript 中的函数名
// numArgs: 函数的固定参数数量
// cFunctionPtr: FlexBindFuncPtr 类型的 C 函数指针
typedef struct {
const char* flexFunctionName;
int numArgs;
FlexBindFuncPtr cFunctionPtr;
} FlexCBind;
// 注册 C 绑定函数
// vm: FlexScript 虚拟机指针
// binding: FlexCBind 结构体,包含要注册的函数信息
// return: 成功返回 0,失败返回非 0 值 (错误码)
int flexRegisterFunction(FlexVM* vm, FlexCBind binding);
13.2 C绑定例子
// 示例 1:一个简单的加法函数
static FlexValue flexBind_add(FlexVM* vm, int argc, const FlexValue* argv) {
int a, b;
// 检查参数是否为整数
if (!flexValueToInt(vm, argv[0], &a) || !flexValueToInt(vm, argv[1], &b)) {
return flexCallCError(vm, "Invalid arguments to add. Expected Int.");
}
// 返回相加后的结果
return flexCreateInt(vm, a + b);
}
// 示例 2:一个无返回值的函数
static FlexValue flexBind_noReturn(FlexVM* vm, int argc, const FlexValue* argv) {
// ... 执行一些操作,例如打印到控制台 ...
printf("This function has no return value.\n");
//最后返回null,即使无返回值也需要一个FlexValue占位
return flexCreateNull(vm);
}
static FlexCBind bindings[] = {
{"add", 2, flexBind_add},
{"noReturn", 0, flexBind_noReturn},
};
int numBindings = sizeof(bindings) / sizeof(bindings[0]);
for (int i = 0; i < numBindings; i++) {
if (flexRegisterFunction(vm, bindings[i]) != 0) {
return -1; // 注册失败
}
}
13.3 在flex调用绑定好的C函数:
//绑定后的函数在全局作用域,任何地方均可调用。
auto add_result = pcall(add, 10, 5.5);
if (add_result[0]) {
print("10 + 5.5 =", add_result[1]) // 输出: 10 + 5.5 = 15.5
} else {
print("Error in add:", add_result[1])
}
auto no_return_result = pcall(noReturn()); // 不需要参数
if (not no_return_result[0]) {
print("Error noReturn:", no_return_result[1]);
}
14 附录:示例代码
// 计算斐波那契数列
int fib(int n) {
if (n <= 1) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
// 使用 list
list<int> numbers = [1, 2, 3, 4, 5];
for (auto i = 0; i < numbers.size(); i++) {
auto f = fib(numbers[i]);
print("fib(" + numbers[i] + ") = " + f);
}
// 使用 map 容器
map<string, any> person = { name: "Alice", age: 30 };
print(person.name);
// 定义类
class Point {
int x;
int y;
void __init__(int x, int y) {
this.x = x;
this.y = y;
}
void printInfo() {
print("(" + this.x + ", " + this.y + ")");
}
}
//创建对象
auto point = new Point(1,3);
point.printInfo();
// 协程示例
string coFunc(int x)
{
print("coFunc start, x = " + x);
auto ret = coroutine.yield(1);
print("coFunc resume, ret = " + ret);
return "finish";
}
auto co = coroutine.create(coFunc);
auto (ok, val) = coroutine.resume(co, 10); // 启动
print(ok, val); // 输出 true 1
auto (ok2, val2) = coroutine.resume(co, "abc");//恢复
print(ok2, val2);
FlexScript 指令集规范
指令格式
FlexScript 指令集采用三种基本格式:iABC
、iABx
和 iAsBx
,均为 32 位定长指令。
-
iABC
格式:bit 3 2 1 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 iABC C(8) | B(8) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) |
Opcode
: 指令操作码 (7 位)。A
: 通常用作目标寄存器 (8 位)。B
: 9 位,含义根据指令而定:B[8]
(k 位): 为 0 时,B[7:0]
表示寄存器索引;为 1 时,B[7:0]
表示常量表索引。B[7:0]
: 寄存器索引或常量表索引 (8 位)。
C
: 9 位,含义根据指令而定:C[8]
(k 位): 为 0 时,C[7:0]
表示寄存器索引;为 1 时,C[7:0]
表示常量表索引。C[7:0]
: 寄存器索引或常量表索引 (8 位)。
-
iABx
格式:| Opcode (7 bits) | A (8 bits) | Bx (17 bits) | |-----------------|------------|-----------------------| | Op (7) | A (8) | Bx (17) |
Opcode
: 指令操作码 (7 位)。A
: 通常用作目标寄存器 (8 位)。Bx
: 17 位无符号整数,含义根据指令而定。
-
iAsBx
格式:| Opcode (7 bits) | A (8 bits) | sBx (17 bits) | |-----------------|------------|-----------------------| | Op (7) | A (8) | sBx (17) |
Opcode
: 指令操作码 (7 位)。A
: 通常用作条件跳转中的条件寄存器 (8 位)。sBx
: 17 位有符号整数,表示跳转偏移量。
通用符号说明:
R[x]
: 寄存器,编号为x
。K[x]
: 常量表中的第x
个常量。RK(x)
: 如果x
的最高位为 1,则表示常量表索引x & 0xFF
;否则表示寄存器R[x]
。sBx
: 有符号整数。pc
: 程序计数器。
指令列表
1. 数据传输指令:
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_MOVE |
iABC |
MOVE A B |
R[A] := R[B] |
将寄存器 B 的值复制到寄存器 A |
OP_LOADI |
iABx |
LOADI A sBx |
R[A] := sBx |
将有符号整数 sBx 加载到寄存器 A |
OP_LOADF |
iABx |
LOADF A sBx |
R[A] := (number)sBx |
将有符号整数 sBx 转换为浮点数并加载到寄存器 A (FlexScript 可能不需要) |
OP_LOADK |
iABx |
LOADK A Bx |
R[A] := K[Bx] |
将常量表中的第 Bx 个常量加载到寄存器 A |
OP_LOADFALSE |
iABC |
LOADFALSE A |
R[A] := false |
将布尔值 false 加载到寄存器 A |
OP_LOADTRUE |
iABC |
LOADTRUE A |
R[A] := true |
将布尔值 true 加载到寄存器 A |
OP_LOADNULL |
iABC |
LOADNULL A |
R[A] := null |
将 null 值加载到寄存器 A |
2. 表操作指令 (List 和 Map):
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_NEWLIST |
iABx |
NEWLIST A Bx |
R[A] := [] |
创建一个新的 list,初始容量为 Bx (可选,用于优化) |
OP_NEWMAP |
iABx |
NEWMAP A Bx |
R[A] := {} |
创建一个新的 map,初始容量为 Bx (可选,用于优化) |
OP_INDEX |
iABC |
INDEX A B C |
R[A] := R[B][RK(C)] |
通用索引访问:R[B] 必须是 list 或 map,RK(C) 是索引(整数或字符串等)。如果类型不匹配,抛出运行时错误。 |
OP_SETINDEX |
iABC |
SETINDEX A B C |
R[A][RK(B)] := RK(C) |
通用索引设置:R[A] 必须是 list 或 map,RK(B) 是索引,RK(C) 是值。如果类型不匹配,抛出运行时错误。 |
OP_LISTAPPEND |
iABC |
LISTAPPEND A B |
R[A].append(R[B]) |
将寄存器 R[B] 的值追加到R[A] 的末尾 |
OP_LEN |
iABC |
LEN A B |
R[A] := #R[B] |
获取 list 或 map 的长度,存储到 R[A] 。 |
3. 算术和位运算指令:
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_ADDI |
iABC |
ADDI A B sC |
R[A] := R[B] + sC |
整数加法 |
OP_ADDK |
iABC |
ADDK A B C |
R[A] := R[B] + K[C] |
|
OP_SUBK |
iABC |
SUBK A B C |
R[A] := R[B] - K[C] |
|
OP_MULK |
iABC |
MULK A B C |
R[A] := R[B] * K[C] |
|
OP_MODK |
iABC |
MODK A B C |
R[A] := R[B] % K[C] |
|
OP_POWK |
iABC |
POWK A B C |
R[A] := R[B] ^ K[C] |
|
OP_DIVK |
iABC |
DIVK A B C |
R[A] := R[B] / K[C] |
|
OP_BANDK |
iABC |
BANDK A B C |
R[A] := R[B] & K[C] |
|
OP_BORK |
iABC |
BORK A B C |
R[A] := R[B] | K[C] |
|
OP_BXORK |
iABC |
BXORK A B C |
R[A] := R[B] ~ K[C] |
|
OP_SHRI |
iABC |
SHRI A B sC |
R[A] := R[B] >> sC |
|
OP_SHLI |
iABC |
SHLI A B sC |
R[A] := sC << R[B] |
|
OP_ADD |
iABC |
ADD A B C |
R[A] := R[B] + R[C] |
加法 |
OP_SUB |
iABC |
SUB A B C |
R[A] := R[B] - R[C] |
减法 |
OP_MUL |
iABC |
MUL A B C |
R[A] := R[B] * R[C] |
乘法 |
OP_MOD |
iABC |
MOD A B C |
R[A] := R[B] % R[C] |
取模 |
OP_POW |
iABC |
POW A B C |
R[A] := R[B] ^ R[C] |
幂运算 |
OP_DIV |
iABC |
DIV A B C |
R[A] := R[B] / R[C] |
除法 |
OP_IDIV |
iABC |
IDIV A B C |
R[A] := R[B] DIV R[C] |
整数除法 |
OP_BAND |
iABC |
BAND A B C |
R[A] := R[B] & R[C] |
按位与 |
OP_BOR |
iABC |
BOR A B C |
R[A] := R[B] | R[C] |
按位或 |
OP_BXOR |
iABC |
BXOR A B C |
R[A] := R[B] ~ R[C] |
按位异或 |
OP_SHL |
iABC |
SHL A B C |
R[A] := R[B] << R[C] |
左移 |
OP_SHR |
iABC |
SHR A B C |
R[A] := R[B] >> R[C] |
右移 |
OP_UNM |
iABC |
UNM A B |
R[A] := -R[B] |
取负 |
OP_BNOT |
iABC |
BNOT A B |
R[A] := ~R[B] |
按位取反 |
OP_NOT |
iABC |
NOT A B |
R[A] := not R[B] |
逻辑非 |
OP_CONCAT |
iABC |
CONCAT A B C |
R[A] := R[B]..R[C] |
字符串连接 |
4. 控制流指令:
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_JMP |
iAsBx |
JMP sJ |
pc += sJ |
无条件跳转 |
OP_EQ |
iABC |
EQ A B k |
if ((R[A] == R[B]) ~= k) then pc++ |
等于比较。如果 k 为 1,则比较寄存器和常量;否则比较两个寄存器。 |
OP_LT |
iABC |
LT A B k |
if ((R[A] < R[B]) ~= k) then pc++ |
小于比较。 |
OP_LE |
iABC |
LE A B k |
if ((R[A] <= R[B]) ~= k) then pc++ |
小于等于比较。 |
OP_TEST |
iABC |
TEST A B C |
if (not R[A] == (C==1)) then pc++ |
测试寄存器 R[A] 的真假值。如果 R[A] 为真/假 (取决于 C 的值) 条件不成立,则跳过下一条指令。 |
OP_TESTSET |
iABC |
TESTSET A B C |
if (not R[B] == (C==1)) then pc++ else R[A] := R[B] |
5. 函数调用指令:
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_CALL |
iABC |
CALL A B C |
R[A] := R[B](R[B+1], ... ,R[B+C-1]) |
调用函数。B : 函数寄存器,C : 参数个数。 |
OP_RETURN |
iABC |
RETURN A |
return R[A] |
从函数返回。A : 返回值寄存器。 |
6. 循环指令:
指令 | 格式 | 汇编形式 | 语义 | 说明 |
---|---|---|---|---|
OP_FORPREP |
iAsBx |
FORPREP A sBx |
R[A] := start; R[A+1] := end; R[A+2] := step; pc += sBx (如果 start >= end) |
数值 for 循环准备。A : 循环变量寄存器,sBx : 跳转偏移量(跳过循环体)。 |
OP_FORLOOP |
iAsBx |
FORLOOP A sBx |
R[A] += R[A+2]; if R[A] < R[A+1] then { pc -= sBx; R[A+3] := R[A] } |
数值 for 循环迭代。A : 循环变量寄存器,` |