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)。
  • 布尔值: truefalse
  • 空值: 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>: 有序列表
      • listlist<any> 的简写。
    • map<K, V>: 键值对集合
      • mapmap<any, any> 的简写。
    • coroutine: 协程
  • 标注类型与真实类型:
    • 一个变量总是存在真实类型和标注类型(Type Hinting)。
    • 变量的真实类型永远取决于对其赋值的类型。
    • 标注类型在编译时会被擦除(类型擦除)。
    • 标注类型主要用于提高代码可读性和提供给静态分析工具(如 IDE,LSP)信息。
    • 强烈建议使用 any 来标注那些可能在运行时改变类型的变量,以避免误导(不要用 auto 代替)。
    • 类型推断: 对于字面量, 编译器会自动推断其类型:
      • auto x = 5; // x 的类型是 int
      • auto y = 3.14; // y 的类型是 float
      • auto z = "hello"; // z 的类型是 string
      • auto a = true; // a 的类型是 bool
      • auto b = null; // b 的类型是 null
      • auto lst = []; // lst的类型是list
      • auto 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();
    
  • 模块查找顺序:

    1. 当前目录
    2. 标准库目录 (由环境变量或编译时设置指定)
    3. 用户自定义搜索路径(通过编译器选项或环境变量指定)

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. 内置类型方法

listlist<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 的异常处理机制,使用 pcallxpcall 进行受保护调用,并允许抛出和捕获错误。

  • 错误值: 任何值都可以作为错误被抛出(类似于 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 返回的 listfalse 之后的元素)。
    • ...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为实际函数返回值。
  • 工作原理:

    • pcallxpcall 会捕获 func 执行期间发生的任何错误 (包括由 throw 函数抛出的错误, 以及运行时错误, 如除以零、类型错误等)。
    • pcallxpcall 总是返回一个 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 的上下文中将其作为错误值处理。
  • 运行时错误列表:(与之前相同,但现在这些错误会被 pcallxpcall 捕获)

    • 除以零
    • 无效类型操作 (例如, 字符串和数字相乘)
    • 数组/映射 索引越界
    • 调用 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 指令集采用三种基本格式:iABCiABxiAsBx,均为 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: 循环变量寄存器,`