batScript是一个强类型, 结构化的转译语言, 以.bs作为后缀名.
就和JavaScript转译为TypeScript一样, batScript转译.bs至.bat, 为陈旧简陋的.bat脚本带来了:
- 合理的命名规范和使用: 批处理原生的语法简陋怪异,
batScript提供贴近正常语言的关键字和标识符设计. - 类型系统: 提供三种单元类型, 包括两种基本类型(
num,bool)和一种组合类型(pair), 以及两种可迭代类型(str,arr), 变量区分大小写. - 正常的控制流:
if-else-elif基本选择分支,while,loop基本循环分支, 以及延展的condition状态机选择分支和iterate可迭代体循环分支, 及loop,repeat,circle语法糖. 控制流关键字均无需括号包裹条件. - 函数和过程. 分别通过
func和proc声明, 强类型, 支持递归. - 现代的运算符. 主要运算符符合现代语言习惯, 以及和
BAT契合的特别运算符, 如取原始运算符$|. - 基本的包管理.
pkg导入包,lib导入库,lib*解包库至当前作用域, 以及bspm包管理器. - 结构化的命名空间.
group声明数据组, 支持单行简写及func/proc方法绑定语法. - 脚本特制的语法, 如
conf配置,match约束等. - 其它语法扩展. 包括最基础的异常
err, 输入输出echo和getin等, 原生BAT运行runraw, 成组group等.
从这里开始阅读完整文档.
bsc -Version :: 输出版本
bsc -Compile input_file -Output output_file :: 执行编译
bsc -Check -File check_file :: 检查代码示例:
bsc -Compile MyScript.bs -Output MyScript.batbspm /v :: 输出版本
bspm /s search_name :: 搜索库
bspm /i package_name :: 安装库
bspm /u package_name :: 卸载库
bspm /l match_name :: 列出本地库示例:
bspm /i BatKV /c ">=1.0"| 符号 | 说明 |
|---|---|
set |
声明变量 |
let |
声明不可变量 |
num |
数字类型 |
bool |
布尔类型 |
str |
字符串类型 |
arr |
一维数组类型 |
pair |
键值对类型 |
match |
约束 |
if |
如果 |
elif |
或者如果 |
else |
或者 |
condition |
匹配条件 |
while |
基本循环 |
loop |
无限循环 |
repeat |
次数循环 |
iterate |
可迭代体循环 |
circle |
可迭代体轮转循环 |
break |
终止循环 |
continue |
继续循环 |
and |
且 |
or |
或 |
not |
非 |
func |
函数 |
proc |
过程 |
return |
返回值 |
pkg |
文件导入 |
lib |
包导入 |
lib* |
解包导入(展开库命名空间至当前作用域) |
runraw |
原生内嵌 |
err |
抛出异常 |
endprogram |
终止程序 |
pause |
暂停 |
getin |
输入流获取 |
echo |
输出 |
group |
成组 |
conf |
配置项 |
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
> |
大于判断 |
>= |
大等于判断 |
== |
等于判断 |
~= |
不等于判断 |
< |
小于判断 |
<= |
小等于判断 |
= |
赋值 |
%% |
类型转换 |
! |
取反 |
? |
判空 |
& |
取类型字符串 |
@ |
可迭代体取长 |
.. |
可迭代体拼接 |
^ |
可迭代体位值 |
$| |
取原始 |
True |
真值 |
False |
假值 |
Nil |
空值 |
Inf |
无限值 |
| 库名称 | 说明 | 代表性方法/字面量 |
|---|---|---|
Math |
数学 | abs() min() max() sqrt() random floor() ceil() round() pow() PI |
Cmd |
控制台 | setColor() setTitle() restartInAdmin() |
Tools |
系统工具 | diskpart() ping() ipconfig() whoami() |
Windows |
操作系统 | version() getEnv() setEnv() |
Admin |
管理员权限 | isAdmin() requireAdmin() relaunchAsAdmin sudo() |
Iterable |
可迭代体 | arrIndexOf() strContain() |
lib*将库的命名空间展开至当前文件, 之后可省略前缀直接调用. 例如lib* Math后, PI、abs()、sqrt()等可直接使用; lib Math则仍需Math.abs()前缀.
#: 嵌入文档;: 注释"\n": 字符串转译'': 原始字符串`Hello {hello_val}`: b-string[1, 2, 3, 4][2:3]: 数组切片{,}: 代码块切分if a > b :: 单行语句(控制流关键字无需括号包裹条件)func myFunc(a: num, b: str) -> bool: 函数声明lib* Math: 解包导入,PI、abs()等直接可用group MyGroup { let X: num = 1 }: 单行组声明func MyGroup.myFunc(a: num) -> num/proc MyGroup.myProc(): 组方法绑定
- 文件: 大驼峰.bs
- 变量: 小写+下划线
- 常量: 大写+下划线
- 函数及过程: 小驼峰
- 组名: 大驼峰
- 组方法名: 小驼峰
- 配置名: 每个词以大写开头, 使用下划线连接
- AllMan风格括号
- 一切文件, 组, 函数和过程编写文档注释
最基本的示范, 演示输出与控制台初始化.
# Description: batScript HelloWorld示例程序
# Author: WaterRun
# Date: 2026-03-30
# File: HelloWorld.bs
lib Cmd ; 导入控制台库
Cmd.setTitle("batScript Demo")
Cmd.setColor("0A") ; 黑底亮绿字
echo "HelloWorld!"
以下展示了 batScript 中各个基本类型和使用, 并引入 lib* Math 解包示范.
# Description: batScript 类型示例程序
# Author: WaterRun
# Date: 2026-04-01
# File: Types.bs
lib* Math ; 解包 Math:PI、abs()、sqrt() 等直接可用
; 常量
let MAX_COUNT: num = 128
let max_count: num = 64 ; 和 MAX_COUNT 是两个变量,大小写敏感
; 字符串类型
set name_1: str = "Alice"
set name_2 = "Bol"
set name_3: str? = "Siri"
; 数字类型
set age_1: num = 20
set age_2 = 22.5
set age_3: num? = Nil
; 布尔类型
set is_alive_1: bool = True
set is_alive_2: bool = False
set is_alive_3: bool? = Nil
; 数组类型
set scores: arr<num> = [90, 88.5, 100]
set names: arr<str> = ["Alice", "Bol", "Siri"]
; 组合类型
set name_age_pair_1: pair<str, num> = (name_1, age_1)
set name_age_pair_2: pair<str, num?> = (name_3, age_3)
set name_age_pair_3: pair<str, num>? = Nil
; 一元与长度
set name_1_len: num = @name_1
set name_1_rev: str = !name_1
; 拼接
set greeting: str = "Hello, " .. name_1
; 类型转换
set age_1_text: str = %str%age_1
set name_1_len_text: str = %str%name_1_len
set scores_len: num = @scores
set scores_len_text: str = %str%scores_len
; 使用 lib* Math 解包后的直接调用
set dist: num = abs(age_1 - 100) ; 无需 Math.abs()
set root: num = sqrt(scores_len) ; 无需 Math.sqrt()
set area: num = PI * 5 * 5 ; PI 直接可用
set dist_text: str = %str%dist
set root_text: str = %str%root
set area_text: str = %str%area
echo greeting
echo `age_1 = {age_1_text}`
echo `name_1_len = {name_1_len_text}`
echo `scores_len = {scores_len_text}`
echo `name_1_rev = {name_1_rev}`
echo `abs(age-100) = {dist_text}`
echo `sqrt(len) = {root_text}`
echo `PI*5^2 = {area_text}`
# Description: batScript 控制流示例程序(含详尽注释)
# Author: WaterRun
# Date: 2026-04-01
# File: ControlFlow.bs
# Version: 1.0.1
lib Cmd
; ==============================
; 全局常量(单行 group)
; ==============================
group Constants { let DEFAULT_NAME: str = "Guest"; let MAX_CIRCLE_STEPS: num = 5 }
; ==============================
; 工具函数与过程
; ==============================
# Description: 判断字符串是否非空
# Param [v]: 待判断的字符串
# Return: 非空返回 True,否则 False
func isNonEmpty(v: str) -> bool
{
return ?v
}
# Description: 输出分节标题
# Param [title]: 标题文本
proc showTitle(title: str)
{
echo ""
echo "=============================="
echo title
echo "=============================="
}
; ==============================
; 主流程
; ==============================
# Description: 主流程,演示全部控制流
proc main()
{
Cmd.setTitle("控制流示例")
showTitle("batScript 控制流示例")
; ---------- 输入与基本 if ----------
set name_raw: str = getin "Input your name>>"
set user_name: str = Constants.DEFAULT_NAME
if isNonEmpty(name_raw)
{
set user_name = name_raw
}
else
{
set user_name = Constants.DEFAULT_NAME
}
echo `Hello {user_name}`
; ---------- if / elif / else 与 condition ----------
showTitle("if / elif / else 与 condition")
set age_raw: str = getin "Input age (10/20/30)>>"
set age: num = 0
condition age_raw
{
"10": set age = %num%age_raw
"20": set age = %num%age_raw
"30": set age = %num%age_raw
/: err "Age must be 10, 20, or 30"
}
set age_text: str = %str%age
set age_group: str = "Unknown"
if age < 18
{
set age_group = "Child"
}
elif age < 30
{
set age_group = "Adult"
}
else
{
set age_group = "Senior"
}
echo `age = {age_text}`
echo `group = {age_group}`
; if 单行语法示例
if age >= 18 : echo "Access granted"
; ---------- condition 的字符串分支 ----------
set op_raw: str = getin "Choose op (add/sub/other)>>"
condition op_raw
{
"add": echo "You chose add"
"sub": echo "You chose sub"
/: echo "You chose other"
}
; ---------- while / continue ----------
showTitle("while / continue")
set while_count: num = 0
while while_count < 5
{
set while_count = while_count + 1
if while_count == 2 : continue
set while_count_text: str = %str%while_count
echo `while_count = {while_count_text}`
}
set quick_count: num = 0
while quick_count < 2 : set quick_count = quick_count + 1
echo "while 单行语法已执行"
; ---------- loop / break ----------
showTitle("loop / break")
set loop_count: num = 0
loop
{
set loop_count = loop_count + 1
set loop_count_text: str = %str%loop_count
echo `loop_count = {loop_count_text}`
if loop_count >= 3 : break
}
loop : break ; 立即退出的无限循环示例
; ---------- repeat ----------
showTitle("repeat")
repeat 3 : echo "repeat single-line"
repeat 3 => i
{
set i_text: str = %str%i
echo `repeat index = {i_text}`
}
; ---------- iterate ----------
showTitle("iterate")
set nums: arr<num> = [1, 2, 3]
iterate nums => n
{
set n_text: str = %str%n
echo `n = {n_text}`
}
set word: str = "bat"
iterate word => ch
{
echo ch
}
; ---------- circle ----------
showTitle("circle")
set colors: arr<str> = ["R", "G", "B"]
set circle_count: num = 0
circle colors => c
{
set circle_count = circle_count + 1
echo c
if circle_count >= Constants.MAX_CIRCLE_STEPS : break
}
echo "Done."
}
main()
# Description: 交互式四则计算器(含表达式解析、优先级与括号)
# Author: ChatGPT
# Date: 2026-04-01
# File: Calculator.bs
# Version: 1.0.0
lib Cmd
; ==============================
; 解析器状态(单行 group)
; ==============================
group Parser { set g_tokens: arr<str> = []; set g_tok_len: num = 0; set g_pos: num = 0 }
; ==============================
; Parser 组方法
; ==============================
# Description: 初始化解析器状态
# Param [tokens]: 分词结果
proc Parser.init(tokens: arr<str>)
{
set Parser.g_tokens = tokens
set Parser.g_tok_len = @tokens
set Parser.g_pos = 0
}
# Description: 是否还有未读记号
# Return: 有剩余返回 True
func Parser.hasMore() -> bool
{
return Parser.g_pos < Parser.g_tok_len
}
# Description: 查看当前记号,不前进
# Return: 当前记号或空串
func Parser.peek() -> str
{
if Parser.hasMore() : return Parser.g_tokens[Parser.g_pos]
return ""
}
# Description: 游标前进一位
proc Parser.advance()
{
set Parser.g_pos = Parser.g_pos + 1
}
; ==============================
; 字符分类工具函数
; ==============================
# Description: 判断字符是否为数字 0-9
# Param [ch]: 单字符字符串
# Return: 是数字返回 True
func isDigit(ch: str) -> bool
{
return ch >= "0" and ch <= "9"
}
# Description: 判断字符是否为空白
# Param [ch]: 单字符字符串
# Return: 是空白返回 True
func isSpace(ch: str) -> bool
{
return ch == " " or ch == "\t"
}
; ==============================
; 词法分析
; ==============================
# Description: 将表达式字符串切分为记号数组
# Param [expr]: 原始表达式字符串
# Return: 记号数组(arr<str>)
func tokenize(expr: str) -> arr<str>
{
set tokens: arr<str> = []
set i: num = 0
set n: num = @expr
while i < n
{
set j: num = i + 1
set ch: str = expr[i:j]
if isSpace(ch)
{
set i = i + 1
}
elif isDigit(ch) or ch == "."
{
set num_buf: str = ""
loop
{
if i >= n : break
set j = i + 1
set ch = expr[i:j]
if isDigit(ch) or ch == "."
{
set num_buf = num_buf .. ch
set i = i + 1
}
else
{
break
}
}
tokens^ num_buf
}
elif ch == "+" or ch == "-" or ch == "*" or ch == "/" or ch == "(" or ch == ")"
{
tokens^ ch
set i = i + 1
}
else
{
echo `Invalid char: {ch}`
err "Invalid character in expression"
}
}
return tokens
}
; ==============================
; 递归下降解析器
; 支持:+ - * /,括号,一元正负号
; ==============================
# Description: 解析因子:数字字面量、括号表达式、一元正负号
# Return: 数值结果
func parseFactor() -> num
{
if not Parser.hasMore() : err "Unexpected end of expression"
set tok: str = Parser.peek()
if tok == "+"
{
Parser.advance()
return parseFactor()
}
if tok == "-"
{
Parser.advance()
set val_neg: num = parseFactor()
return 0 - val_neg
}
if tok == "("
{
Parser.advance()
set inner: num = parseExpr()
if not Parser.hasMore() : err "Missing closing parenthesis"
if Parser.peek() ~= ")" : err "Missing closing parenthesis"
Parser.advance()
return inner
}
set tok_val: str = Parser.peek()
Parser.advance()
return %num%tok_val
}
# Description: 解析项:处理 * /
# Return: 数值结果
func parseTerm() -> num
{
set value: num = parseFactor()
while Parser.hasMore()
{
set op: str = Parser.peek()
if op == "*"
{
Parser.advance()
set value = value * parseFactor()
}
elif op == "/"
{
Parser.advance()
set rhs: num = parseFactor()
if rhs == 0 : err "Divide by zero"
set value = value / rhs
}
else
{
break
}
}
return value
}
# Description: 解析表达式:处理 + -
# Return: 数值结果
func parseExpr() -> num
{
set value: num = parseTerm()
while Parser.hasMore()
{
set op: str = Parser.peek()
if op == "+"
{
Parser.advance()
set value = value + parseTerm()
}
elif op == "-"
{
Parser.advance()
set value = value - parseTerm()
}
else
{
break
}
}
return value
}
; ==============================
; 求值封装
; ==============================
# Description: 分词、解析并输出结果
# Param [expr]: 表达式字符串
proc evalExpression(expr: str)
{
Parser.init(tokenize(expr))
set result: num = parseExpr()
if Parser.g_pos < Parser.g_tok_len : err "Unexpected token after valid expression"
set result_text: str = %str%result
echo `= {result_text}`
}
; ==============================
; 主流程:交互式 REPL
; ==============================
# Description: 交互式四则计算器主入口
proc main()
{
Cmd.setTitle("Interactive Calculator")
echo "Interactive Calculator (type q to quit)"
echo "Supports + - * /, parentheses, unary +/-"
loop
{
set expr: str = getin "calc>>"
if expr == "q" : break
if expr == "" : continue
evalExpression(expr)
}
echo "Bye."
}
main()
# Description: 学生成绩管理系统(使用 SimpKv 持久化,分号分隔列表)
# Author: ChatGPT
# Date: 2026-04-01
# File: StudentGradeSystem.bs
# Version: 1.0.0
lib SimpKv
lib Cmd
lib* Iterable ; 解包 Iterable:arrIndexOf()、strContain() 等直接可用
; ================================
; 全局配置(单行 group)
; ================================
group Config { let STORE_PREFIX: str = "student:"; let LIST_KEY: str = "students"; let SEP: str = ";" }
# Description: 构建学生存储键
# Param [name]: 学生名
# Return: 存储键
func Config.buildKey(name: str) -> str
{
return Config.STORE_PREFIX .. name
}
; ================================
; 字符串工具 group
; ================================
group StrUtils
{
; 通用字符串辅助方法
}
# Description: 按单字符分隔符切分字符串
# Param [src]: 原串
# Param [sep]: 单字符分隔符
# Return: 切分结果数组
func StrUtils.split(src: str, sep: str) -> arr<str>
{
set result: arr<str> = []
set n: num = @src
set start: num = 0
set i: num = 0
while i < n
{
set i1: num = i + 1
if src[i:i1] == sep
{
result^ src[start:i]
set start = i + 1
}
set i = i + 1
}
result^ src[start:n]
return result
}
# Description: 用分隔符拼接字符串数组
# Param [items]: 字符串数组
# Param [sep]: 分隔符
# Return: 拼接后的字符串
func StrUtils.join(items: arr<str>, sep: str) -> str
{
set total: num = @items
if total == 0 : return ""
set acc: str = items[0]
set idx: num = 1
while idx < total
{
set acc = acc .. sep .. items[idx]
set idx = idx + 1
}
return acc
}
; ================================
; 校验工具 group
; ================================
group Validator
{
; 输入合法性校验
}
# Description: 判断字符串是否非空
# Param [v]: 待检测字符串
# Return: 非空返回 True
func Validator.isNonEmpty(v: str) -> bool
{
return ?v
}
# Description: 检查名称是否合法(非空且不含 ';' 或 '=')
# Param [name]: 待检测名称
# Return: 合法返回 True
func Validator.isValidName(name: str) -> bool
{
if not ?name : return False
iterate name => ch
{
if ch == ";" or ch == "=" : return False
}
return True
}
# Description: 检查成绩文本格式是否合法(0-100,至多两位小数)
# Param [txt]: 成绩原始输入
# Return: 合法返回 True
func Validator.isValidScore(txt: str) -> bool
{
if not ?txt : return False
set dot_count: num = 0
set digit_count: num = 0
set dec_len: num = 0
set after_dot: bool = False
iterate txt => ch
{
if ch == "."
{
if dot_count >= 1 : return False
set dot_count = dot_count + 1
set after_dot = True
}
elif ch >= "0" and ch <= "9"
{
set digit_count = digit_count + 1
if after_dot
{
set dec_len = dec_len + 1
if dec_len > 2 : return False
}
}
else
{
return False
}
}
if digit_count == 0 : return False
if dot_count == 1 and dec_len == 0 : return False
set val: num = %num%txt
if val < 0 or val > 100 : return False
return True
}
; ================================
; 持久化操作 group
; ================================
group StudentStore
{
; 封装 SimpKv 的学生 CRUD
}
# Description: 判断学生是否存在
# Param [name]: 学生名
# Return: 存在返回 True
func StudentStore.exists(name: str) -> bool
{
set val: str? = SimpKv.get(Config.buildKey(name))
return ?val and val ~= ""
}
# Description: 读取学生科目-成绩对数组(["Math=95", ...])
# Param [name]: 学生名
# Return: 对数组,无记录则空数组
func StudentStore.loadPairs(name: str) -> arr<str>
{
set raw: str? = SimpKv.get(Config.buildKey(name))
if not ?raw or raw == "" : return []
set all: arr<str> = StrUtils.split(raw, Config.SEP)
set cleaned: arr<str> = []
iterate all => p
{
if ?p : cleaned^ p
}
return cleaned
}
# Description: 保存科目-成绩对数组
# Param [name]: 学生名
# Param [pairs]: 对数组
proc StudentStore.savePairs(name: str, pairs: arr<str>)
{
set data: str = ""
if @pairs > 0 : set data = StrUtils.join(pairs, Config.SEP)
SimpKv.set(Config.buildKey(name), data)
}
# Description: 将学生名追加到全局名单(幂等)
# Param [name]: 学生名
proc StudentStore.upsertList(name: str)
{
set raw: str? = SimpKv.get(Config.LIST_KEY)
if not ?raw or raw == ""
{
SimpKv.set(Config.LIST_KEY, name)
return
}
set names: arr<str> = StrUtils.split(raw, Config.SEP)
set found: bool = False
iterate names => nm
{
if nm == name
{
set found = True
break
}
}
if not found
{
names^ name
SimpKv.set(Config.LIST_KEY, StrUtils.join(names, Config.SEP))
}
}
# Description: 从全局名单中删除学生名
# Param [name]: 学生名
proc StudentStore.removeFromList(name: str)
{
set raw: str? = SimpKv.get(Config.LIST_KEY)
if not ?raw or raw == "" : return
set names: arr<str> = StrUtils.split(raw, Config.SEP)
set new_names: arr<str> = []
iterate names => nm
{
if nm ~= name and ?nm : new_names^ nm
}
set updated: str = ""
if @new_names > 0 : set updated = StrUtils.join(new_names, Config.SEP)
SimpKv.set(Config.LIST_KEY, updated)
}
# Description: 删除学生记录(置空)
# Param [name]: 学生名
proc StudentStore.delete(name: str)
{
SimpKv.set(Config.buildKey(name), "")
}
; ================================
; 业务逻辑 group
; ================================
group GradeManager
{
; 成绩的增删查逻辑
}
# Description: 从 "Sub=Score" 对中提取科目名
# Param [p]: 成绩对字符串
# Return: 科目名
func GradeManager.subject(p: str) -> str
{
set parts: arr<str> = StrUtils.split(p, "=")
return parts[0]
}
# Description: 从 "Sub=Score" 对中提取分数文本
# Param [p]: 成绩对字符串
# Return: 分数文本
func GradeManager.score(p: str) -> str
{
set parts: arr<str> = StrUtils.split(p, "=")
return parts[1]
}
# Description: 设置或更新某科目成绩
# Param [name]: 学生名
# Param [subject]: 科目名
# Param [score_txt]: 成绩文本
proc GradeManager.setScore(name: str, subject: str, score_txt: str)
{
if not Validator.isValidScore(score_txt) : err "成绩格式非法"
set pairs: arr<str> = StudentStore.loadPairs(name)
set updated: arr<str> = []
set replaced: bool = False
iterate pairs => p
{
if GradeManager.subject(p) == subject
{
updated^ subject .. "=" .. score_txt
set replaced = True
}
else
{
updated^ p
}
}
if not replaced : updated^ subject .. "=" .. score_txt
StudentStore.savePairs(name, updated)
StudentStore.upsertList(name)
}
# Description: 输出某学生的全部成绩及平均分
# Param [name]: 学生名
proc GradeManager.printReport(name: str)
{
if not StudentStore.exists(name)
{
echo "学生不存在"
return
}
set pairs: arr<str> = StudentStore.loadPairs(name)
set total: num = @pairs
echo `学生: {name}`
if total == 0
{
echo "无成绩记录"
return
}
set sum_score: num = 0
iterate pairs => p
{
set sub: str = GradeManager.subject(p)
set val_txt: str = GradeManager.score(p)
set val_num: num = %num%val_txt
echo ` {sub}: {val_txt}`
set sum_score = sum_score + val_num
}
set avg: num = sum_score / %num%total
set avg_txt: str = %str%avg
echo `平均分: {avg_txt}`
}
; ================================
; 菜单交互过程
; ================================
# Description: 添加学生
proc doAddStudent()
{
set name: str = getin "学生姓名>>"
if not Validator.isValidName(name) : err "姓名不能为空且不含 ';' 或 '='"
if not StudentStore.exists(name)
{
StudentStore.savePairs(name, [])
StudentStore.upsertList(name)
echo "已创建学生"
}
else
{
echo "学生已存在(未作更改)"
}
}
# Description: 录入或更新成绩
proc doInputScore()
{
set name: str = getin "学生姓名>>"
if not Validator.isValidName(name) : err "姓名不合法"
if not StudentStore.exists(name)
{
echo "学生不存在,自动创建空记录"
StudentStore.savePairs(name, [])
StudentStore.upsertList(name)
}
set subject: str = getin "科目>>"
if not Validator.isValidName(subject) : err "科目不合法"
set score_txt: str = getin "成绩(0-100,至多两位小数)>>"
GradeManager.setScore(name, subject, score_txt)
echo "已保存成绩"
}
# Description: 查询学生成绩
proc doQueryStudent()
{
set name: str = getin "学生姓名>>"
GradeManager.printReport(name)
}
# Description: 列出所有学生
proc doListStudents()
{
set raw: str? = SimpKv.get(Config.LIST_KEY)
if not ?raw or raw == ""
{
echo "当前无学生记录"
return
}
set names: arr<str> = StrUtils.split(raw, Config.SEP)
set shown: bool = False
iterate names => nm
{
if ?nm
{
echo nm
set shown = True
}
}
if not shown : echo "当前无学生记录"
}
# Description: 删除学生
proc doDeleteStudent()
{
set name: str = getin "学生姓名>>"
if not StudentStore.exists(name)
{
echo "学生不存在"
return
}
StudentStore.removeFromList(name)
StudentStore.delete(name)
echo "已删除"
}
# Description: 显示菜单
proc showMenu()
{
echo ""
echo "==== 学生成绩管理系统 ===="
echo "1) 添加学生"
echo "2) 录入/更新成绩"
echo "3) 查询学生成绩"
echo "4) 列出所有学生"
echo "5) 删除学生"
echo "6) 退出"
}
# Description: 主流程
proc main()
{
Cmd.setTitle("学生成绩管理系统")
loop
{
showMenu()
set choice: str = getin "选择>>"
if choice == "6" : break
condition choice
{
"1": doAddStudent()
"2": doInputScore()
"3": doQueryStudent()
"4": doListStudents()
"5": doDeleteStudent()
/: echo "无效选项"
}
}
echo "已退出"
}
main()
# Description: 演示 batScript 与原生 BAT 内嵌结合(runraw 与 $|)
# Author: ChatGPT
# Date: 2026-04-01
# File: NativeBridge.bs
# Version: 1.0.0
lib* Windows ; 解包 Windows:version()、getEnv()、setEnv() 直接可用
lib Admin
lib Cmd
; 状态组(单行 group)
group BridgeState { set counter: num = 42; set admin_checked: bool = False }
# Description: 检查管理员状态并输出
proc BridgeState.checkAdmin()
{
set BridgeState.admin_checked = Admin.isAdmin()
set admin_txt: str = %str%BridgeState.admin_checked
echo `isAdmin = {admin_txt}`
}
# Description: 演示 batScript 与原生 BAT 互操作
proc showNativeInterop()
{
echo "=== batScript 部分 ==="
; 通过 lib* Windows 直接调用 version()
set os_ver: str = version()
echo `OS version = {os_ver}`
; 通过 lib* Windows 直接调用 getEnv()
set userprofile: str = getEnv("USERPROFILE")
echo `USERPROFILE = {userprofile}`
; 管理员状态检查
BridgeState.checkAdmin()
; 取原始变量名用于 runraw 展开
set counter_raw: str = $|BridgeState.counter
set counter_txt: str = %str%BridgeState.counter
echo `counter (batScript) = {counter_txt}`
; 单行 runraw 示例
runraw: "echo [BAT] single-line runraw reached."
; 块状 runraw:混合字符串与原始变量名展开
runraw
{
"@echo off",
"echo --- Native BAT block start ---",
"echo Working dir (BAT):",
"cd",
"echo.",
"echo 通过原生 BAT 展开 batScript 变量:",
"echo counter = %", counter_raw, "%",
"echo.",
"echo 当前目录列表:",
"dir /b",
"echo --- Native BAT block end ---"
}
}
# Description: 主入口
proc main()
{
Cmd.setTitle("NativeBridge Demo")
showNativeInterop()
echo "Back in batScript after runraw."
}
main()
# Description: batScript 其它语法综合示例(覆盖此前未重点展示的特性)
# Author: ChatGPT
# Date: 2026-04-01
# File: OtherSyntaxDemo.bs
# Version: 1.0.0
lib* Math ; PI、abs()、sqrt()、floor()、ceil()、round()、pow() 等直接可用
lib* Windows ; version()、getEnv()、setEnv() 直接可用
lib* Iterable ; arrIndexOf()、strContain() 直接可用
lib Admin
lib Cmd
; =====================================================
; 应用信息(单行 group)
; =====================================================
group AppInfo { let APP_NAME: str = "OtherSyntaxDemo"; let APP_VER: str = "1.0.0" }
; =====================================================
; 数学工具 group(使用 lib* Math 的解包符号)
; =====================================================
group MathUtils
{
; 几何计算,依赖 lib* Math 展开的 PI
}
# Description: 计算圆面积(使用解包后的 PI)
# Param [r]: 半径
# Return: 面积
func MathUtils.circleArea(r: num) -> num
{
return PI * r * r
}
# Description: 计算圆周长
# Param [r]: 半径
# Return: 周长
func MathUtils.circlePerim(r: num) -> num
{
return 2 * PI * r
}
# Description: 将角度转为弧度
# Param [deg]: 角度值
# Return: 弧度值
func MathUtils.toRad(deg: num) -> num
{
return deg * PI / 180
}
; =====================================================
; 输出格式工具 group
; =====================================================
group Fmt
{
; 格式化辅助
}
# Description: 输出分节标题
# Param [title]: 标题
proc Fmt.section(title: str)
{
echo ""
echo "----"
echo title
echo "----"
}
# Description: 输出类型字符串
# Param [label]: 标签
# Param [val_type]: 类型串
proc Fmt.showType(label: str, val_type: str)
{
echo `type({label}) = {val_type}`
}
; =====================================================
; 约束函数(用于 match 绑定)
; =====================================================
# Description: 检查数值是否非负
# Param [v]: 数值
# Return: 非负返回 True
func isNonNeg(v: num) -> bool
{
return v >= 0
}
# Description: 检查字符串长度是否不超过上限
# Param [v]: 字符串
# Param [limit]: 长度上限
# Return: 合规返回 True
func isWithinLen(v: str, limit: num) -> bool
{
if not ?v : return False
return @v <= limit
}
; =====================================================
; 主流程
; =====================================================
# Description: 主流程,覆盖各类语法特性
proc main()
{
Cmd.setTitle(`{AppInfo.APP_NAME} v{AppInfo.APP_VER}`)
; ---------- conf 配置信号 ----------
if conf d : echo "Debug flag /d detected"
; ---------- lib* Math 解包使用 ----------
Fmt.section("lib* Math 解包直接使用")
set r: num = 5
set area: num = MathUtils.circleArea(r)
set perim: num = MathUtils.circlePerim(r)
set rad: num = MathUtils.toRad(90)
set root2: num = sqrt(2) ; sqrt 来自 lib* Math
set floored: num = floor(3.7) ; floor 来自 lib* Math
set area_txt: str = %str%area
set perim_txt: str = %str%perim
set rad_txt: str = %str%rad
set root2_txt: str = %str%root2
set floored_txt: str = %str%floored
echo `circleArea(5) = {area_txt}`
echo `circlePerim(5) = {perim_txt}`
echo `toRad(90) = {rad_txt}`
echo `sqrt(2) = {root2_txt}`
echo `floor(3.7) = {floored_txt}`
echo `PI = {%str%PI}`
; ---------- lib* Windows 解包使用 ----------
Fmt.section("lib* Windows 解包直接使用")
set os_ver: str = version() ; Windows.version(),已解包
set home: str = getEnv("USERPROFILE")
echo `OS version = {os_ver}`
echo `USERPROFILE = {home}`
; ---------- lib* Iterable 解包使用 ----------
Fmt.section("lib* Iterable 解包直接使用")
set fruits: arr<str> = ["apple", "banana", "cherry"]
set idx_banana: num = arrIndexOf(fruits, "banana") ; 已解包
set has_cherry: bool = strContain("I like cherry pie", "cherry")
set idx_txt: str = %str%idx_banana
set has_txt: str = %str%has_cherry
echo `indexOf banana = {idx_txt}`
echo `has cherry = {has_txt}`
; ---------- lib Admin ----------
Fmt.section("lib Admin(命名空间保留)")
set is_adm: bool = Admin.isAdmin()
set is_adm_txt: str = %str%is_adm
echo `isAdmin = {is_adm_txt}`
if not is_adm : echo "Running without admin privileges."
; ---------- match 约束绑定 ----------
Fmt.section("match 约束绑定")
set balance: num = 0 match isNonNeg(balance)
set dep_raw: str = getin "Deposit (>=0)>>"
set dep: num = %num%dep_raw
set balance = balance + dep
set bal_txt: str = %str%balance
echo `balance = {bal_txt}`
set tag: str = "hello" match isWithinLen(tag, 8)
echo `tag = {tag}`
; ---------- 可空类型与 Nil/Inf ----------
Fmt.section("可空类型与 Nil / Inf")
set maybe: num? = Nil
if ?maybe
{
set maybe_txt: str = %str%maybe
echo `maybe = {maybe_txt}`
}
else
{
echo "maybe is Nil, defaulting to 99"
set maybe = 99
set maybe_txt: str = %str%maybe
echo `maybe = {maybe_txt}`
}
set inf_val: num = Inf
Fmt.showType("inf_val", &inf_val)
if inf_val == Inf : echo "Inf comparison OK"
; ---------- 数组追加/删除/切片/负索引 ----------
Fmt.section("数组追加 / 删除 / 切片 / 负索引")
set nums: arr<num> = [1, 2, 3, 4]
nums^ 5 ; 追加 5 → [1,2,3,4,5]
^nums 1 ; 删除下标 1 → [1,3,4,5]
^nums ; 删除末尾 → [1,3,4]
set last: num = nums[-1]
set last_txt: str = %str%last
echo `last = {last_txt}`
set sliced: arr<num> = nums[0:2]
set slice_len_txt: str = %str%@sliced
echo `slice len = {slice_len_txt}`
; ---------- 字符串切片与反转 ----------
Fmt.section("字符串切片与反转")
set word: str = "batScript"
set part: str = word[3:9]
set word_rev: str = !word
echo `part = {part}`
echo `word_rev = {word_rev}`
; ---------- pair 取位与对调 ----------
Fmt.section("pair 取位与对调")
set p: pair<num, str> = (7, "ok")
set fst: num = ^p
set snd: str = p^
set fst_txt: str = %str%fst
echo `first = {fst_txt}`
echo `second = {snd}`
set swapped: pair<str, num> = !p
set sw_fst: str = ^swapped
set sw_snd: num = swapped^
set sw_snd_txt: str = %str%sw_snd
echo `swapped.first = {sw_fst}`
echo `swapped.second = {sw_snd_txt}`
; ---------- 类型字符串 & 与原始变量名 $| ----------
Fmt.section("& 类型字符串 与 $| 原始变量名")
Fmt.showType("balance", &balance)
set raw_name: str = $|balance
echo `raw var name = {raw_name}`
; ---------- condition 正确用法 ----------
Fmt.section("condition")
set op: str = getin "Choose op (+/-/*)>>"
condition op
{
"+": echo "add"
"-": echo "sub"
"*": echo "mul"
/: echo "other"
}
; ---------- loop => 计次索引 ----------
Fmt.section("loop => 计次索引")
set tick: num = 0
loop => i
{
set tick = tick + 1
set i_txt: str = %str%i
echo `loop i = {i_txt}`
if tick >= 3 : break
}
echo ""
echo "Demo finished."
}
main()