Skip to content

Latest commit

 

History

History
1533 lines (1274 loc) · 39.1 KB

File metadata and controls

1533 lines (1274 loc) · 39.1 KB

batScript

English

batScript是一个强类型, 结构化的转译语言, 以.bs作为后缀名. 就和JavaScript转译为TypeScript一样, batScript转译.bs.bat, 为陈旧简陋的.bat脚本带来了:

  • 合理的命名规范和使用: 批处理原生的语法简陋怪异, batScript提供贴近正常语言的关键字和标识符设计.
  • 类型系统: 提供三种单元类型, 包括两种基本类型(num, bool)和一种组合类型(pair), 以及两种可迭代类型(str, arr), 变量区分大小写.
  • 正常的控制流: if-else-elif基本选择分支, while, loop基本循环分支, 以及延展的condition状态机选择分支和iterate可迭代体循环分支, 及loop, repeat, circle语法糖. 控制流关键字均无需括号包裹条件.
  • 函数和过程. 分别通过funcproc声明, 强类型, 支持递归.
  • 现代的运算符. 主要运算符符合现代语言习惯, 以及和BAT契合的特别运算符, 如取原始运算符$|.
  • 基本的包管理. pkg导入包, lib导入库, lib*解包库至当前作用域, 以及bspm包管理器.
  • 结构化的命名空间. group声明数据组, 支持单行简写及func/proc方法绑定语法.
  • 脚本特制的语法, 如conf配置, match约束等.
  • 其它语法扩展. 包括最基础的异常err, 输入输出echogetin等, 原生BAT运行runraw, 成组group等.

这里开始阅读完整文档.

速览

编译器命令

bsc -Version :: 输出版本
bsc -Compile input_file -Output output_file :: 执行编译
bsc -Check -File check_file :: 检查代码

示例:

bsc -Compile MyScript.bs -Output MyScript.bat

包管理器命令

bspm /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后, PIabs()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: 解包导入,PIabs()等直接可用
  • group MyGroup { let X: num = 1 }: 单行组声明
  • func MyGroup.myFunc(a: num) -> num / proc MyGroup.myProc(): 组方法绑定

编码规范

  • 文件: 大驼峰.bs
  • 变量: 小写+下划线
  • 常量: 大写+下划线
  • 函数及过程: 小驼峰
  • 组名: 大驼峰
  • 组方法名: 小驼峰
  • 配置名: 每个词以大写开头, 使用下划线连接
  • AllMan风格括号
  • 一切文件, 组, 函数和过程编写文档注释

示例程序

HelloWorld

最基本的示范, 演示输出与控制台初始化.

# 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()

和原生BAT的内嵌结合

# 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()