LogicStep 是一种面向无编程基础用户的极简单脚本语言。它的设计灵感来自 Scratch 积木式编程,采用顺序步骤、明确结尾、自然语言风格的语法,适用于任何需要“一步步说明”的场景——游戏逻辑、自动化流程、数据处理、交互式问答、教学演示等等。本规范定义了 LogicStep 的核心语法、语义和标准通用指令集。
脚本文件扩展名为
.lgs。
本规范仅定义语言本身,不绑定任何特定应用领域。具体宿主程序(如游戏服务器、自动化工具)可通过指令扩展和多语言包定制自己的 LogicStep 环境。
类比:如果 LogicStep 是一套乐高积木的连接协议,那么每个宿主程序就是提供不同主题积木包的套装。学会本规范,你就可以在任何兼容 LogicStep 的环境里编写脚本。
-
步骤即积木
脚本由多个**步骤(Step)和模块(Module)**组成,每个步骤/模块内部是一条条按顺序执行的指令,就像拼积木一样。 -
对缩进不敏感
代码块使用明确的结束标记(end if、end while、end step等),缩进仅用于提高可读性,不影响解析。推荐每条语句独占一行,但也可用分号分隔。 -
接近自然语言的简单单词
所有关键字均使用最常见的英文单词(如if、repeat、for),并内置多语言翻译支持。宿主程序可提供语言映射表,将母语关键字(如中文“如果”、“那么”)映射到标准英文关键字,使非英语用户能够用母语编写脚本。 -
通过点号调用模块和跳转
mod.模块名(参数)– 调用一个可复用的模块(相当于函数),执行完毕后返回调用处继续执行。jump.步骤名(参数)– 跳转到另一个步骤并传递参数,当前步骤不再返回。
-
动态类型,极简表达式
变量无需声明类型,支持数字、字符串、布尔值、列表。表达式采用中缀数学形式,支持常见运算符。字符串拼接使用+,数字与字符串相加自动转换数字。
一个 LogicStep 脚本是纯文本文件(扩展名 .lgs),由一个或多个步骤定义和模块定义组成。可指定一个起始步骤,默认为第一个被定义的步骤,或显式指定名为 main 的步骤。
step 步骤名(参数1, 参数2, ...)
语句
...
end step
- 标识符规则:字母、数字、下划线、中文(由语言包决定),不能以数字开头,不能与关键字冲突。
- 参数在步骤内部作为局部变量直接使用。
- 步骤可由宿主程序直接调用作为入口,也可通过
jump.步骤名(...)跳转。
module 模块名(参数1, 参数2, ...)
语句
...
return 表达式
end module
- 模块必须有
return语句返回一个值。调用方式为mod.模块名(参数...),执行后返回原处继续执行。 - 模块内部定义局部变量,不会污染调用者作用域。
- 单行注释:
//开头,到行尾结束。 - 多行注释:
/* ... */,可跨行。
每条语句占一行。可以是:
- 指令调用(有/无输出)
- 变量赋值
- 控制流结构
- 模块调用或跳转
- 数字:整数或小数,如
5,3.14,-2.5 - 字符串:用双引号包围,如
"Hello",支持转义\"和\\ - 布尔值:
true或false - 列表:用方括号,元素逗号分隔,如
["a", "b", 3],可为空列表[] - 空值:
null
set 变量名 = 表达式
变量名允许字母、数字、下划线和中文,不能以数字开头。变量无需声明类型,可随时重新赋值为不同类型。
- 步骤和模块均创建独立的作用域,内部定义的变量仅在该作用域内可见。
- 参数也是局部变量,由调用者传入值。
- 不能跨作用域访问其他步骤/模块的局部变量,数据传递必须通过参数和返回值。
- 宿主程序可提供全局只读变量(如
player、world),在所有作用域可直接访问。
使用 -> 箭头语法将结果存入变量:
指令名 参数... -> 变量名
mod.模块名(参数...) -> 变量名
箭头右侧的变量可以是已存在或新的局部变量。不写 -> 变量 则丢弃输出。
表达式支持以下运算符,按优先级从高到低排列:
| 优先级 | 运算符 | 含义 |
|---|---|---|
| 1 | () |
括号分组 |
| 2 | ! - |
逻辑非、负号 |
| 3 | * / |
乘、除 |
| 4 | + - |
加、减 |
| 5 | < > <= >= |
大小比较 |
| 6 | == != |
等值比较 |
| 7 | and |
逻辑与 |
| 8 | or |
逻辑或 |
- 字符串可用
+拼接。数字与字符串相加时,数字自动转为字符串。 - 比较运算符可用于数字(按数值)或字符串(按字典序)。
- 条件判断中可省略
== true,直接写变量名:if has_perm then。 - 列表索引访问请使用
list_get指令,表达式中不直接支持[]索引。
每条指令由指令名和参数组成,部分指令有输出。宿主程序应至少提供以下通用指令,以构成 LogicStep 的基础能力。宿主可根据需要添加更多领域专用指令。
- 无输出:
指令名 参数1, 参数2, ... - 有输出:
指令名 参数1, 参数2 -> 输出变量 - 跳转:
jump.步骤名(参数1, 参数2) - 模块调用:
mod.模块名(参数1, 参数2)或mod.模块名(参数1, 参数2) -> 变量
以下指令为 LogicStep 核心通用指令集,任何兼容实现都应支持。
| 指令 | 参数 | 输出 | 描述 |
|---|---|---|---|
print |
值 | 无 | 向用户输出信息(显示在控制台、聊天框或界面) |
input |
提示字符串 | 字符串 | 向用户显示提示并等待输入,返回输入的字符串 |
| 指令 | 参数 | 输出 | 描述 |
|---|---|---|---|
random_int |
最小值, 最大值 | 数字 | 生成区间内随机整数(含两端) |
random_chance |
百分比数字 | 布尔值 | 有指定概率返回 true |
floor |
数字 | 数字 | 向下取整 |
ceil |
数字 | 数字 | 向上取整 |
round |
数字 | 数字 | 四舍五入 |
abs |
数字 | 数字 | 绝对值 |
| 指令 | 参数 | 输出 | 描述 |
|---|---|---|---|
list_create |
无 | 列表 | 创建一个空列表 |
list_add |
列表变量, 值 | 无 | 向列表尾部添加元素 |
list_get |
列表变量, 索引 | 元素值 | 获取列表指定位置的值(索引从0开始) |
list_set |
列表变量, 索引, 新值 | 无 | 修改列表指定位置的值 |
list_remove |
列表变量, 索引 | 无 | 移除列表指定位置的元素 |
list_size |
列表变量 | 数字 | 返回列表长度 |
list_contains |
列表变量, 值 | 布尔值 | 检查列表是否包含指定值 |
| 指令 | 参数 | 输出 | 描述 |
|---|---|---|---|
split |
字符串, 分隔符 | 列表 | 将字符串按分隔符拆分为列表 |
| 指令 | 参数 | 输出 | 描述 |
|---|---|---|---|
to_number |
字符串 | 数字 | 将字符串转为数字,失败返回 null |
to_string |
任意值 | 字符串 | 将值转为字符串 |
typeof |
值 | 字符串 | 返回值的类型名称("number","string","boolean","list","null") |
宿主程序可通过注册新指令扩展 LogicStep 的能力。新指令名不应与标准指令或关键字冲突。领域专用指令的详细说明由宿主程序提供。
所有控制结构都有明确的结束标记。
if 条件 then
语句
...
else if 条件 then
语句
...
else
语句
...
end if
else if和else块可选,可有多条else if。
while 条件 do
语句
...
end while
repeat
语句
...
until 条件
- 循环体至少执行一次。
for 元素变量 in 列表表达式 do
语句
...
end for
元素变量依次取列表中的每个元素;不可直接修改原列表(修改请用list_set)。- 列表表达式可以是变量、列表字面量或返回列表的调用。
exit while跳出当前最内层while循环。exit for跳出当前最内层for循环。exit repeat跳出repeat-until循环。
- 宿主程序可调用入口步骤(如
main)并传入参数。 - 脚本内部使用
jump.步骤名(参数...)跳转,当前步骤终止,不返回。
- 调用:
mod.模块名(参数...) -> 输出变量。模块执行完毕后返回return的值。 - 模块内部可
return 表达式提前退出;若未遇到return,隐式返回null。
模块支持递归,但宿主程序应限制最大调用栈深度。
语法错误、未闭合的块等在加载时报错,指出行号与原因。
除零、索引越界、未定义变量等会终止脚本,并将错误信息返回宿主。宿主可选择捕获并降级处理。
try
语句
catch
语句
end try
如果 try 块内发生运行时错误,跳转至 catch 块,错误信息可通过 _error 变量获取。此特性为可选。
LogicStep 的关键字和内置指令名均可通过语言映射表进行本地化。宿主程序在加载脚本时,先将本地化关键字替换为标准英文关键字,再交给解析器。
映射表示例(中文 -> 英文):
如果 -> if
那么 -> then
否则 -> else
否则如果 -> else if
结束如果 -> end if
当 -> while
重复 -> repeat
直到 -> until
步骤 -> step
模块 -> module
跳转 -> jump
设置 -> set
返回 -> return
遍历 -> for
在...里 -> in
结束遍历 -> end for
退出循环 -> exit while / exit for / exit repeat
内置指令名同样可映射,例如:
输出 -> print
输入 -> input
随机整数 -> random_int
取整 -> floor
列表添加 -> list_add
宿主程序提供语言包后,用户即可用母语编写 LogicStep 脚本。
LogicStep 脚本运行在宿主程序提供的环境中。
宿主程序调用指定步骤(如 main)并传入参数启动脚本。脚本执行完毕或遇到未捕获错误时返回宿主。
宿主程序可预定义全局只读变量供脚本使用,例如:
player:当前用户标识world:当前场景名称- 任何其他上下文信息
宿主程序必须将自身功能包装为指令并注册到 LogicStep 运行时。指令应尽量遵循本规范的标准语义,领域专用指令由宿主自行定义。
- 脚本在沙箱中运行,默认无文件系统/网络访问,除非宿主提供相关指令。
- 可限制最大执行步数、最大内存、最大递归深度等。
程序 ::= { 步骤定义 | 模块定义 }
步骤定义 ::= "step" 标识符 ["(" 参数列表 ")"] 语句序列 "end" "step"
模块定义 ::= "module" 标识符 ["(" 参数列表 ")"] 语句序列 "end" "module"
语句序列 ::= { 语句 }
语句 ::= 简单语句 | 控制流结构
简单语句 ::= 赋值语句
| 指令调用
| 跳转语句
| "return" 表达式
| "exit" ("while"|"for"|"repeat")
| 空
赋值语句 ::= "set" 标识符 "=" 表达式
指令调用 ::= 标识符 [ 实际参数 ] ["->" 标识符]
跳转语句 ::= "jump." 标识符 "(" [ 实际参数 ] ")"
模块调用 ::= "mod." 标识符 "(" [ 实际参数 ] ")" ["->" 标识符]
控制流结构 ::= if结构 | while结构 | repeat结构 | for结构 | try结构
if结构 ::= "if" 表达式 "then" 语句序列
{ "else" "if" 表达式 "then" 语句序列 }
[ "else" 语句序列 ]
"end" "if"
while结构 ::= "while" 表达式 "do" 语句序列 "end" "while"
repeat结构 ::= "repeat" 语句序列 "until" 表达式
for结构 ::= "for" 标识符 "in" 表达式 "do" 语句序列 "end" "for"
try结构 ::= "try" 语句序列 "catch" 语句序列 "end" "try"
参数列表 ::= 标识符 { "," 标识符 }
实际参数 ::= 表达式 { "," 表达式 }
表达式 ::= 逻辑或表达式
逻辑或表达式::= 逻辑与表达式 { "or" 逻辑与表达式 }
逻辑与表达式::= 比较表达式 { "and" 比较表达式 }
比较表达式 ::= 加减表达式 [ (">"|"<"|">="|"<="|"=="|"!=") 加减表达式 ]
加减表达式 ::= 乘除表达式 { ("+"|"-") 乘除表达式 }
乘除表达式 ::= 一元表达式 { ("*"|"/") 一元表达式 }
一元表达式 ::= [ "!" | "-" ] 主要表达式
主要表达式 ::= 字面量 | 标识符 | "(" 表达式 ")" | 列表字面量 | 指令调用 | 模块调用
列表字面量 ::= "[" [ 表达式 { "," 表达式 } ] "]"
字面量 ::= 数字 | 字符串 | "true" | "false" | "null"
LogicStep 可以作为现有 YAML/JSON 逻辑配置的前端语言。通过编译器可将 LogicStep 脚本转换为后端可执行的步骤序列;反之也可将旧配置转换为 LogicStep 脚本。
任何通过 LogicStep 核心语法测试套件的实现,均可称为 LogicStep 兼容实现。
step main
input "请输入原价:" -> 原价_str
to_number 原价_str -> 原价
if 原价 == null then
print "输入的不是有效数字"
return
end if
mod.计算折扣后价格(原价) -> 折后价
print "折后价格:" + 折后价
end step
module 计算折扣后价格(原价)
if 原价 >= 500 then
set 折扣 = 0.7
else if 原价 >= 200 then
set 折扣 = 0.8
else
set 折扣 = 0.9
end if
return floor(原价 * 折扣)
end module
module 随机点名(学生列表)
list_size 学生列表 -> 人数
if 人数 == 0 then
return "无学生"
end if
random_int 0, 人数 - 1 -> 索引
list_get 学生列表, 索引 -> 被点名学生
return 被点名学生
end module
// 使用示例
set 班级 = ["小明", "小红", "小刚"]
mod.随机点名(班级) -> 结果
print "今天回答问题的同学是:" + 结果
module 统计及格人数(分数列表)
set 及格数 = 0
for 分数 in 分数列表 do
if 分数 >= 60 then
set 及格数 = 及格数 + 1
end if
end for
return 及格数
end module
以下为 LogicStep 保留关键字,不可用作变量名/步骤名/模块名:
| 关键字 | 用途 |
|---|---|
step |
步骤定义 |
module |
模块定义 |
end |
块结束 |
if |
条件判断 |
then |
条件后接 |
else |
否则分支 |
while |
while 循环 |
do |
while 条件后接 |
repeat |
repeat-until 循环 |
until |
repeat 结束条件 |
for |
for 遍历循环 |
in |
for 变量与列表之间 |
return |
返回值 |
exit |
跳出循环 |
set |
变量赋值 |
try |
错误捕获 |
catch |
错误处理块 |
and |
逻辑与 |
or |
逻辑或 |
true / false |
布尔字面量 |
null |
空值 |
mod |
模块调用前缀 |
jump |
步骤跳转前缀 |