打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

用户:DGCK81LNN/WhatLang

来自 LNN的:not(博客)?
DGCK81LNN留言 | 贡献2026年6月30日 (二) 19:35的版本 (wtf)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

WhatLang 的原始解释器,又称Koishi运行时,使用 TypeScript 编写,可作为Koishi.js插件安装。使用bot命令 模板:LNNBot命令链接,或直接发送以 ¿ 开头的代码即可运行。Koishi运行时内置 help@ 帮助函数,可返回中文帮助信息。

WhatLang 解释器核心发布为 npm 包 whatlang-interpreter,附带一个 Node.js 可执行程序 what 可在命令行使用。命令行环境暂未实现标准输入读取和文件系统交互功能。

DGCK81LNN 还开发了另一套运行时 WhatServer,内嵌在 Koishi 机器人的网页控制台中,处理所有路径以 /what 开头的 HTTP 请求。它支持 WhatNoter 与 WhatCommands,但不支持大部分Koishi运行时特有函数。

预防计划重构新版 WhatLang,引入“拉丁-1补充”区段的字符作为新命令,新增Map数据类型与局部变量,同时会带来多处不兼容语法变更。

运行机制

WhatLang 拥有四种值类型:字符串(String)、数字(Number)、数组(Array)、未定义(Undefined)。数字为64位浮点数,包含 NaN。字符串和数字是不可变的;数组可原地修改,长度动态,可存放混合类型数据。

虚拟机内部维护一层栈中栈结构,称为帧栈(Frame Stack)。帧栈最顶层的栈称为主栈,绝大多数代码命令都针对主栈。新数据压入栈顶,读取、弹出也从栈顶操作。使用 [| 命令创建或编辑数组时,该数组会被压入帧栈,临时成为当前主栈;执行 ] 后,数组从帧栈弹出,作为数组元素放回上一级栈。 数组与栈互相转换时不会生成新对象;若复制数组引用后通过 | ] 修改数组,所有引用该数组的位置都会同步变更。

虚拟机同时内置字典,用于存储具名变量。

部分运行时存在独立的「输出栈(Output Stack)」:每次打印内容都会压入输出栈。send@sends@ 等函数可弹出输出栈内容并立即发送;程序结束时,输出栈内剩余内容会统一输出。 Koishi运行时的输出栈可存放文本片段、附件、消息回复引用;其他环境支持打印原始字节数据。

本文档统一约定:

  • 栈为空时执行弹出操作,返回未定义(Undefined);
  • 一次性弹出多个值时,描述顺序为从栈底到栈顶;
  • 「返回某值」等价于将该值压入主栈;
  • 使用参数执行字符串函数:复制当前栈生成临时栈,将参数、函数字符串依次压入临时栈,执行 @ 调用命令;执行结果为临时栈栈顶值,若临时栈为空则为未定义;
  • 字符串可视为字符串数组:常规遍历单位为**字符**,部分操作单位为**编码单元**。编码单元依实现不同可为单字符或UTF-16码元;官方实现采用UTF-16码元,一个「字符」可对应单个BMP平面Unicode字符,或一对UTF-16代理对。
 该区分至关重要:不同函数对含非BMP字符的字符串处理逻辑不同。例如 len@ 统计编码单元数量,# 按字符遍历字符串;arr@ 将字符串拆分为字符数组,""split@ 按编码单元分割字符串。
类型强制转换

将任意值转为字符串(String):按人类可读格式序列化;字符串自动添加引号并转义特殊字符;数字使用标准十进制输出;未定义转为 undef;数组元素逗号分隔、方括号包裹;自 v0.4.0 起,超长或深层嵌套数组会用省略号截断。

将任意值强制转为数字(Number)规则如下:

输入值 转换结果
字符串 去除首尾空白后尝试解析为数字;支持二进制`0b`、八进制`0o`、十六进制`0x`前缀;支持科学计数法E/e(正负尾数、正负指数,正号可省略);不支持千位分隔符;无法解析时返回NaN
未定义 NaN
数组 空数组返回0;仅含单个元素时递归转换该元素为数字;其余情况返回NaN

真值判定:除0、空字符串、未定义外均为真值;NaN、空数组均判定为真。

命令集

命令 名称 说明
0 数字零 压入数值0。
1~9开头、后跟任意位0~9数字 正数字面量 压入对应数字。
ASCII字母开头,后跟任意ASCII字母、数字、下划线 标识符字面量 将标识符转为全小写字符串压入栈。
' 后跟单个字符 字符字面量 将单个字符作为字符串压入栈。

v0.3.0 之前仅支持单个UTF-16编码单元,无法输入非BMP字符。

" 包裹文本 引号字符串字面量 将包裹文本作为完整字符串压入栈。

换行 `\n`、制表符 `\t` 可转义;自 v0.2.0 新增 `\r` `\f` `\v`;单独反斜杠会让后一个字符原样输出。

` 包裹文本 直接打印字面量 逻辑同双引号字符串,但不会压入栈,直接输出文本。
+ 加法/拼接 弹出a、b两个栈顶值,执行合并运算:
  • 双方均为数组/均为字符串:执行拼接;
  • 一方为数组,另一方转为单元素数组后拼接;
  • 一方为字符串,另一方强制转字符串后拼接;
  • 任意一方为NaN/未定义,结果为NaN;
  • 其余场景执行数值加法。

数组拼接功能于 v0.2.2 加入,此前数组相加行为未定义。

- * / % 四则运算 弹出a、b,统一转为数字,计算 `a 运算符 b` 并压入结果。
? 比较 弹出a、b进行比较:宽松相等(等价JS `==`;数组仅同一引用才算相等)返回0;a大于b返回1;a小于b返回-1;无法比较返回NaN。
~ 逻辑非 弹出单个值;假值返回1,真值返回0。
[ 新建数组栈 开辟一层空栈,用于构建数组。
| 切换至数组栈 弹出栈顶值,若非数组则抛出错误;将该数组设为当前操作栈。
] 关闭数组栈 将当前栈转为数组,退回上一级栈并把数组压入栈顶。

[ ]| ] 必须成对匹配。

() 包裹文本(允许括号嵌套) 无转义字符串 直接提取括号内全部文本为字符串,不解析转义符。
. 打印栈顶 输出栈顶元素,不弹出;栈空则输出 `undef`。
\ 交换栈顶两位 交换栈最上方两个元素;栈元素不足2个则无操作。
: 复制栈顶 复制栈顶元素压入栈;数组仅复制引用,不深拷贝;栈空无操作。
& 下沉栈顶 弹出栈顶元素,插入栈底;栈空无操作。
_ 丢弃栈顶 弹出并丢弃栈顶值。
= 赋值具名变量 弹出字符串作为变量名,将剩余栈顶值赋予该变量;若无剩余值则赋值未定义。
^ 读取具名变量 弹出字符串变量名;存在该变量则返回变量值;为内置函数名则返回「函数名@」;其余返回未定义。
@ 函数调用/代码执行 弹出字符串:若是内置函数则直接调用;若是已定义变量,则将变量值作为代码执行(变量值必须为字符串);其余情况直接将字符串作为代码运行。

未来版本计划限制变量名规则:仅允许小写ASCII字母开头,仅包含小写字母、数字、下划线;不符合规则的标识符会直接作为代码文本执行。

> 打包数组 弹出n并转为整数;取出栈顶n个元素,打包为新数组压入栈。

n为0或负数时,保留栈底 |n| 个元素,其余打包为数组。

< 展开序列 弹出数组/字符串,将内部所有元素依次压入栈;输入非数组、非字符串则报错。
{ 循环起始 弹出判断值;若为假,直接跳转到匹配的 }
} 循环结束 弹出判断值;若为真,跳转回匹配的 {

{ } 必须成对匹配。

连续多个 ! 跳出循环/终止执行 跳出对应层数嵌套循环;!数量超过循环嵌套层数时,终止当前代码块:退出当前@调用、终止#迭代、或直接结束程序。
# 映射遍历 弹出函数字符串func;遍历栈顶数组/字符串的每一项item,以item为参数执行func;将所有执行结果打包为数组返回。

栈顶必须是数组或字符串,否则报错;v0.2.0 前不支持字符串遍历。

, 读取序列下标 弹出下标n;取出栈顶数组/字符串对应下标元素。

n强制转为整数;栈顶非数组/字符串报错;字符串按下标取编码单元;负数下标从末尾倒数,-1代表最后一位;下标越界返回未定义。

; 修改序列下标 弹出n、value;修改栈顶数组下标n的元素为value。

n必须可转为整数或NaN;栈顶非数组报错;负数下标倒数;n等于数组长度/NaN时将value追加至数组末尾;其余越界下标不修改数组。

$ 删除数组下标元素 弹出下标n;删除栈顶数组对应下标元素。

n强制转为整数;栈顶非数组报错;负数下标倒数;下标越界无操作;被删除元素后方所有元素下标自动前移一位。

内置函数

核心通用函数

函数 说明
num@ 弹出一个值,强制转为数字并返回。
str@ 弹出一个值,强制转为字符串并返回。

v0.2.0 旧版本行为:字符串带引号转义;全单编码单元字符串数组直接拼接,空数组返回空串。

repr@ 弹出值,返回可复现该值的WhatLang代码字符串。
arr@ 弹出值:数组返回浅拷贝;字符串拆分为字符数组;其余类型报错。
pow@ 弹出a、b转为数字,返回a的b次幂。
band@ bor@ bxor@ 弹出两值转为有符号32位整数,依次执行按位与、按位或、按位异或并返回结果。
bnot@ 弹出值转为有符号32位整数,返回按位取反结果。
rand@ 返回0~1之间随机浮点数。
randint@ 弹出两个数字,返回区间内随机整数(向下取整)。
flr@ 弹出数字,向下取整返回整数。
range@ 弹出n转为数字,n必须小于4294967296;返回 [0, n) 整数数组,小数部分直接截断。n为负则报错。
len@ 返回栈顶数组/字符串长度;字符串统计**编码单元**数量。

v0.2.0 旧版本统计字符数。

split@ 弹出字符串、分隔符;按分隔符分割字符串为字符串数组;分隔符为空则按编码单元拆分。

v0.2.1:分隔符为数组时视作正则表达式,匹配分组全部存入结果数组。

join@ 弹出分隔符字符串;将栈顶数组/字符串全部转为字符串,以分隔符拼接为单一字符串;栈顶非数组/字符串报错。
reverse@ 将栈顶数组/字符串转为数组并反转元素顺序,返回浅拷贝。
in@ 弹出目标值;查找栈顶数组/字符串中首个匹配项下标,无匹配返回-1;栈顶非法类型报错。
filter@ 弹出函数字符串;遍历栈顶数组/字符串,执行函数筛选真值项,返回筛选后的新数组;栈顶非法类型报错。
chr@ 弹出数字/数字数组(Unicode码点),拼接为对应字符串;数值为负或超过1114111时报错。
ord@ 弹出字符串,将每个字符/未配对代理对转为码点数字,返回数字数组。
and@ 弹出a、b;a为空串/0/未定义则返回a,否则返回b。
or@ 弹出a、b;a为空串/0/未定义则返回b,否则返回a。
nan@ 返回NaN。
undef@ 返回未定义。
inf@ 返回正无穷。
ninf@ 返回负无穷。
eq@ 弹出两值,严格相等(等价JS `===`)返回1,否则0。
stak@ 将当前主栈转为数组返回;会造成数组自包含。

名称含义:stack@ 去掉c(copy,不拷贝)。

stack@ 返回主栈的浅拷贝数组。
try@ 逻辑同@调用;运行时报错则返回 [错误名, 错误信息];无错误返回 [未定义, 未定义]。
throw@ 弹出字符串作为错误信息,抛出名为Error的运行时异常。
match@ 弹出待匹配字符串、正则表达式;执行JS正则匹配,返回匹配结果字符串数组,无匹配返回空数组。

待匹配值强制转字符串;表达式为数组时对应RegExp构造参数 [正则, 修饰符];普通字符串直接作为正则模板;正则非法则报错。带g修饰符返回全部完整匹配;无g修饰符返回完整匹配+各分组捕获内容。

repl@ 弹出目标字符串、匹配模板、替换文本;全局替换所有匹配项并返回新字符串。匹配模板可为字面字符串或正则数组(同match@规则);替换文本内美元符号为分组引用,如需输出$需写两个$$。
time@ 返回当前系统时间戳(毫秒级纪元时间)。
type@ 弹出值,返回类型字符串:"String" / "Number" / "Array" / "Undefined"
b64@ 弹出字节数字数组,返回Base64编码字符串。

v0.3.3 新增。

nb64@ 弹出Base64字符串,解码为字节数字数组返回。

v0.3.3 新增。解码前自动过滤非法Base64字符,末尾补等号至长度为4的倍数。

utf8@ 弹出字符串,转为UTF-8字节数字数组返回。

v0.3.3 新增。

nutf8@ 弹出UTF-8字节数字数组,解码为字符串返回。

v0.3.3 新增。

通用扩展函数

该组函数最初为Koishi运行时专属,现已在多数环境提供。

函数 说明
you@ 返回运行环境标识字符串。小石心格式:"WhatLang/2024 Environment/messaging Framework/koishi Platform/平台标识 Id/机器人平台ID"

可在插件配置「Framework」与「Platform」之间自定义品牌字段,规范格式为 Brand/机器人名称。 2025年6月22日并入小石心插件:[1]

pr@ Koishi运行时:等待当前频道内同一用户下一条消息,超时返回未定义;用于交互式读取用户输入。
cat@ 弹出URL字符串,发起HTTP GET请求,返回响应文本;状态码异常/网络错误直接抛出异常。

命名来源LNNBot内置cat命令:传入URL则拉取网页,不传则读取下一条用户消息(逻辑近似pr@)。

ca@ 逻辑同cat@,返回原始字节数字数组而非文本。

2025-05-14 在WhatServer以catb@上线,05-29更名ca@,含义:cat去掉text的t;2025-06-22接入Koishi运行时:[2]

fetch@ 依次弹出请求方法、URL、请求头、请求体,发起HTTP请求;返回数组 [状态码数字, 状态描述字符串, 二维数组请求头, 响应文本字符串]。

2025年6月3日新增。 方法、URL必须为字符串;请求头为二维字符串数组;请求体为字节数字数组或字符串;GET/HEAD请求体必须为未定义。

fech@ 逻辑同fetch@,响应体返回原始字节数组而非文本;名称含义fetch去掉t(text)。2025-06-03新增。
reesc@ 弹出字符串,转义所有JS正则元字符后返回。
sleep@ 弹出数字n,阻塞n秒;取值范围0 ≤ n ≤ 2147483.647。
nout@ 弹出并丢弃输出栈最后存入(栈顶)的一条内容。
nouts@ n|条,其余全部清空;无穷值视为0。
send@ 弹出输出栈栈顶内容并立即发送;小石心返回发送消息的平台ID字符串数组。用于单条输出刷新。
sends@ n|条;无穷视为0。用于批量刷新输出栈。
ou@ 弹出字节数字数组,直接打印原始二进制字节;小石心无此函数。2025-05-14 WhatServer以outb@上线,05-29更名ou@;命名类比ca/fech,out去掉t代表输出字节流。

Koishi运行时专属函数

部分函数与机器人接收消息绑定;消息内容使用Koishi标准XML字符串格式。

函数 说明
help@ 帮助文档函数。弹出参数字符串,返回对应帮助主题文本;空串/未定义返回语言简介与help@使用教程。
helpall@ 生成图片,打印几乎全部内置函数名称列表。
propt@ 弹出用户ID;等待当前频道指定用户的下一条消息并返回内容;参数为假值则接收全体用户消息,支持传入单个/多用户平台ID。超时返回未定义。
prompt@ 弹出频道ID、回调函数;监听目标频道消息,每条消息传入详情数组执行回调;回调返回真值时停止监听并返回消息详情,超时返回未定义。
me@ 返回触发解释器的消息详情数组,字段定义:
  • 下标0:消息XML文本字符串
  • 下标1:消息平台ID字符串
  • 下标2:发送者用户名
  • 下标3:发送者平台用户ID
  • 下标4:Koishi数据库用户ID(数字)
  • 下标5:当前频道平台ID
  • 下标6:被回复/引用消息ID,无则未定义

该函数在WhatServer可用,但字段语义变更(尽量保持接口兼容):

  • 下标0:请求方法+URL,例 GET /whatfoo/bar;URL编码字符自动解码
  • 下标1:随机字符串
  • 下标2、3:客户端IP地址
  • 下标4:携带X-Lnnbot-Whatserver-Login-Token请求头时为数据库用户ID数字,否则未定义
  • 下标5:固定字符串 "__WHATSERVER__"
  • 下标6:未定义
outimg@ outaudio@ outvideo@ outfile@ 弹出资源URL字符串,向输出栈写入对应类型附件。
outquote@ 弹出消息ID字符串,输出对应消息的回复引用卡片。
outat@ 弹出用户平台ID,输出@提及该用户。
outimag@ 弹出文本字符串,使用等宽字体渲染为图片存入输出栈。
outksq@ 弹出文本字符串,使用Kreative Square字体渲染图片输出;字体地址:[3]
outsvg@ 弹出SVG描述数组,渲染矢量图存入输出栈;暂不支持动画。

数组规则:前两项为svg宽、高;后续每项为元素数组:首项元素类型字符串,第二项style样式,剩余参数随类型变化。

  • path / p:路径标签,第三项为d路径数据;
  • text / t:文字标签,第三项x、第四项y、第五项文本内容;
  • img / i:图片标签,依次为x、y、宽、高、资源href地址。
outhtml@ 弹出HTML文本,渲染网页截图存入输出栈。

2025年10月3日新增:[4] 当前不支持class=wikitable表格快捷语法,因渲染环境禁用JS;开发组正在修复该问题。

nsend@ 弹出消息ID,撤回当前频道对应消息。
sendsto@ 依次弹出目标频道ID、整数n;逻辑同sends@,批量发送至指定频道,返回发送消息ID数组。
findmsg@ 弹出回调函数;遍历当前频道历史消息,每条传入详情数组执行回调;回调返回真值时停止遍历并返回该消息详情;遍历至消息上限无匹配则返回未定义。
getmsg@ 弹出消息ID,返回对应消息详情数组(同me@结构)。
guildmem@ 弹出群组ID,返回群成员二维数组,每项格式 [用户名, 平台用户ID];机器人接收过该用户消息才会出现在列表中。

WhatNoter 笔记存储模块

基于Koishi数据库用户ID的简易持久化存储;每个用户ID独立拥有公开笔记、保护笔记、私有笔记。 无需用户真实存在即可读取对应ID的公开笔记;任意一条笔记创建后,同ID其余两类笔记自动初始化为空串;从未写入的ID读取笔记返回未定义。

函数 说明
notewc@ 弹出数字用户ID、字符串文本,写入该用户公开笔记。
notewd@ 弹出字符串文本,写入调用者的保护笔记。
notewe@ 弹出字符串文本,写入调用者的私有笔记。
noterc@ 弹出数字用户ID,读取其公开笔记内容。
noterd@ 弹出数字用户ID,读取其保护笔记内容。
notere@ 读取调用者私有笔记内容并返回。

WhatCommands 自定义命令模块

允许使用WhatLang编写自定义命令;每条命令包含名称、执行代码、简短描述、详细帮助文档。 三种调用方式:发送 ¿¿命令名 参数、调用机器人命令 whatcmd、WhatLang内置 cmd@ 函数。 命令接收单个字符串输入,存放于栈顶。 示例:命令greet代码 `Hello, `.`!` 发送 ¿¿greet my friends、执行机器人命令 whatcmd greet my friends、运行代码 "my friends" greet cmd@ 均输出 Hello, my friends!

使用 cmd@ 调用命令会返回执行结果,可作为退出状态码;¿¿前缀调用无法获取返回值。

函数 说明
cmdset@ 依次弹出代码字符串、命令名称,绑定命令执行代码。
cmdall@ 返回全部已定义命令名称数组。
cmdsethelp@ 依次弹出帮助文本、命令名,设置命令详细手册。
cmdseth@ 依次弹出描述文本、命令名,设置命令简短介绍。
cmddel@ 弹出命令名称,删除该自定义命令。
cmdget@ 弹出命令名称,返回命令代码,无该命令返回未定义。
cmdgethelp@ 弹出命令名称,返回命令详细手册文本,无则未定义。
cmdgeth@ 弹出命令名称,返回命令简短描述,无则未定义。
cmd@ 依次弹出输入参数字符串、命令名称,运行对应命令代码并返回执行结果。

旧版本无命令时无返回值;新版本直接抛出异常。

WhatServer 网页服务端专属函数

函数 说明
hset@ 依次弹出响应头键、值字符串,设置HTTP响应头。

除内置函数外,WhatServer预定义三个全局变量: reqh:请求头二维字符串数组; reqm:请求方法字符串; reqb:请求体,依请求方式与Content-Type分为字符串、字节数组、未定义三种类型。

惯用写法与标准范式

  • 字面量赋值变量:值 变量名 =_
    • 末尾 _ 丢弃赋值后栈中残留的变量名,一般无保留必要。
    • 自定义函数写法:用括号包裹代码作为字符串存入变量;函数可从栈顶读取参数、返回任意数量结果;调用格式 函数名@
  • 获取负一/任意负整数:01-
  • 超大浮点数(例3×10¹⁰⁰):(3e100)num@
    • 用字符串承载数字字面量,再通过num@转为数值。
  • If条件块:条件 { 执行语句 !}
    • 利用单次循环+末尾跳出实现分支。
  • If-Else分支方案1(借助临时变量):条件 c={ 真分支 !} c^~{ 假分支 !}
  • If-Else方案2(无变量):1{ 条件 { 真分支 !!} 假分支 !}
  • If-Else方案3(数组映射):条件 ~[(真分支)(假分支)] \,_@
  • 两值比较(返回真值代表条件成立)
    • 宽松相等(a?b==0):?~
    • a大于b(a?b==1):?1?~
    • a小于b:?(-1)?~\?1?~
    • 小于等于:?1\?1?~
    • 大于等于:\?1\?1?~
    • 删除末尾 ~ 可对结果取反。
    • 通用比较模板:?[相等结果 大于结果 小于结果] \,_,填入0/1自定义每种情况返回值;两值不可比较时视为相等。
      • 处理不可比较场景扩展模板:?2+[不可比较返回 小于返回 相等返回 大于返回] \,_
  • 向数组尾部追加元素(栈顶为数组):len@ 数值 ;nan@ 数值 ;
  • 代码注释写法:0{ 注释文本 }(注释文本)_

外部资源