Lumesh 语法手册

wiki syntax

Lumesh Shell 语法手册

一、变量与类型

1. 变量声明与赋值

  • 声明变量:使用 let 关键字,支持多变量声明。类型根据赋值自动分配。

    let x = 10                 # 单变量
    let a, b = 1, "hello" # 多变量分别赋值(右侧表达式数量必须匹配)
    let a, b, c = 1 # 多变量统一赋值
    let a = b = 1 # 多变量连续赋值
  • 赋值操作符

    • =:普通赋值。
    • :=:延迟赋值(将表达式作为引用存储)。
    x := 2 + 3  # 延迟求值,存储表达式而非结果
    echo x # 输出表达式: 2+3
    eval x # 输出结果: 5
  • 删除变量:使用 del

    del x
  • 使用变量:可选$,直接使用 echo a x

严格模式需要$: echo $x

  • 查看变量类型:
    let a = 10
    type a # Integer
    type a == "Integer" # True

边缘情况

  • 多变量声明时,右侧表达式数量必须与左侧变量数量一致或统一一个值,否则抛出 SyntaxError

2. 数据类型

基本类型

类型 示例
整数 42, -3
浮点数 3.14, -0.5, 10%
字符串 "Hello\n", 'raw'
布尔值 True, False
列表(数组) [1, "a", True]
映射(字典) {name: "Alice", age: 30}
区间 1..8, 1..10
时间 time.parse('2025-5-20')
文件大小 4K,5T
空值 None

复杂类型

类型 示例
变量 x
函数 fn add(x,y){return x+y}
lambda let add = (x,y) -> x + y
内置库 math.floor

作用域规则

  • lambda, function函数 将创建子环境作用域。
  • 子环境继承父环境变量,不修改父作用域。

字符串

  • 单引号为原始字符串。

  • 双引号支持 文本转义(如 \n),unicode转义(如\u{2614}),ansi转义(如\033[34m)。

  • 点引号为字符串模板,支持$var变量替换和 ${var}子进程语句执行捕获,支持ansi转义字符。

    print "Hello\nworld!\u{2614}"
    Hello #输出两行, \n被转义为换行符
    world!☔ #unicode转义,\u{2614}是雨伞字符

    let str2 = 'Hello\nworld!'
    Hello\nworld! #输出一行,包括\n的原始形式

    let a = [1,2,5]
    print `a is $a, and a[0] is ${a[0]}`

    print "\033[31mRed msg\033[m" #输出红色的Red msg

    点引号内的变量替换比子进程语句捕获效率高,非必要情况建议使用前者

  • 边缘情况

    单引号字符串中的 \ 按字面处理(如 'Line\n' 输出 Line\n)。
    未定义的转义序列会报错,如:

    echo "Hello\_"

    [PARSE FAILED] syntax error: invalid string escape sequence `\_`
    |
    1 | echo "Hello\_"
    |

文件大小类型:

以整数紧跟单位构成,支持如下单位:
"B" "K" "M" "G" "T" "P"

lumesh将自动识别单位并以人类可读模式输出,例如:

print 3050M 1038B 3000G

# 输出
2.98G 1K 2.93T

文件大小类型可参与运算。
如:

1M > 30K      # 返回 True
fs.ls -l | where(size>20K) # 筛选大于20k的文件

日期时间类型

例如:time.parse('2025-5-20')

日期时间类型可参与运算。
如:

time.parse('2025-5-20') > time.parse('2025-1-20')  # 返回True

具体操作请参考内置函数的time模块

百分数

书写为百分数,会自动识别为浮点数

print 37% 2% + 3
# 输出
0.37 3.02

百分号紧跟数字后为百分数,数字后空格+%则为模运算


二、运算符

1. 运算符分类与优先级

优先级从高到低(数值越小优先级越高)

优先级 运算符/结构 示例/说明
1 括号 () (a + b) * c
2 函数调用、列表 [] func(arg)[1, 2]
3 单目运算符 !-, __.. !flag-5
4 幂运算 ^ 2 ^ 3
5 乘除模 */%, _*.. a * b % c
6 加减 +- ,_+.. a + b - c
7 比较 ==!=> `a > b
8 逻辑与 && cond1 && cond2
9 逻辑或 `
10 条件运算符 ? : cond ? t : f
11 赋值 =:= ? x = 5let y := 10
12 管道 ` `
12 重定向 << >> >>! date >> /tmp/day.txt

管道和重定向同级,逻辑运算比管道和重定向优先级更高。

2. 空格规则

运算符类型 是否需要空格 示例
常规运算符 两侧应空格 a + b, x <= 10
非严格模式,部分符号两侧可以不空格 a+b, x<=10, a=5
-/应空格 b-3为字符串,3- b为减法
自定义运算符 必须用下划线开头且两侧空格 x _*+ y, a _?= b
后缀运算符,必须用双下划线开头且空格 x __*+
前缀运算符 前面为空格或开始,后面无空格 !x, -7
中缀运算符 前后均无空格 dict.key, 1..9
后缀运算符 前后均无空格 func(a), array[i], 10M
未来可能废弃,不推荐使用 a++, a--

a++ 推荐用 a += 1
因为 – 在shell中通常用于参数传递。所以不建议使用a–

自定义操作符

  • 自定义操作符是以 _开头的符号,只能包含符号,不能包含数字或字母。

  • 自定义单目运算符 以 __开头,如__+, 优先级同单目运算符。仅能用于后缀运算。

  • 自定义+级运算符 以 _+开头,如_+%,优先级同+ -

  • 自定义*级运算符 以 _*开头,如_*-,优先级同* /

    let __++ = x -> x + 1;
    3 __++

边缘情况

  • x++y 非法, x+++y合法。

3. 特别的操作符

  • == 带类型相等比较.
  • ~= 值相等比较.
  • ~~ 字符串正则匹配.
  • ~: 是否包含(可用于字符串,数组,区间,字典(检测是否包含指定key)).

举例:

5 == "5"        # False
5 ~= "5" # True
"abc" ~: "a" # True
0..8 ~: 8 # False
0..8 ~: 8 # True
"abc" ~~ '\d' # False

4. 重载的运算符

加法 +

数字

  • 数字 + 数字 = 数字以高精度相加

    1 + 2.5           # → 3.5
  • 数字 + 字符串 = 以数值相加

    1 + "2.5"           # → 3.5
  • 数字 + 列表 = 一起求和

    1 + [2.5,3]           # → 6.5

字符串

  • 字符串 + 字符串 = 字符串连接

    "1" + 2.5           # → "12.5"
  • 字符串 + 列表 = 一起串联

    "1" + [2,3]           # → "123"

区间

  • 区间 + 整数 = 区间扩充(正数从尾部,负数从头部)
    0..8 + 2           # → 0..10
    0..8 + (-2) # → 2..8

列表

  • 列表 + 列表 = 列表合并

    [1,2] + [3,4,5]           # → [1,2,3,4,5]
  • 列表 + 其他 = 向列表插入值

    [1] + 2.5           # → [1,2.5]

映射

  • 映射 + 映射 = 映射合并

    {a:b} + {c:d}           # → {a:b,c:d}
  • 映射 + 其他 = 向映射插入值

    {a:b} + c           # → {a:b,c:c}

字节

  • 字节 + 字节 = 字节追加
  • 字节 + 字符串 = 字节追加

其他情况抛出异常


减法 -

数字

  • 数字 - 数字 = 数字以高精度相减

    1 - 2.5           # → -1.5
  • 数字 - 字符串 = 以数值相减

    1 - "2.5"           # → -1.5

字符串

  • 字符串 - 字符串 = 字符串移除(首次出现)

    "i am lume" - "a"           # → "i m lume"
  • 字符串 - 浮点数 = 字符串移除(首次出现)

    "i am lume v4.2" - 4.2           # → "i am lume v"
  • 字符串 - 整数 = 字符串移除(正数从尾部,负数从头部)

    "98101" - 1           # → "9801"
    "98101" - (-2) # → "101"

    超出长度则返回空字符串

区间

  • 区间 - 整数 = 区间缩放(正数从尾部,负数从头部)
    0..8 - 2           # → 0..6
    0..8 - (-2) # → -2..8

列表

  • 列表 - 列表 = 列表差集

    [1,2,3,4,5] - [3,4,5]           # → [1,2]
  • 列表 - 其他 = 列表移除

    [1,2,3] - 2           # → [1,3]

映射

  • 映射 - 映射 = 映射差集

    {a:b,c:d} - {c:d}           # → {a:b}
  • 映射 - 其他 = 映射键移除

    {a:b,c:d} - c           # → {a:b}

其他情况抛出异常


乘法 *

数字

  • 数字 * 数字 = 数字相乘
    2 * 2.5           # → 5
  • 数字 * 字符串 = 以数值相乘
    2 * "2.5"           # → 5

字符串

  • 字符串 * 整数 = 字符串重复

    "2" * 5           # → "22222"
  • 列表 * 列表 = 矩阵乘法

    [[1,2,3],[4,5,6]] * [[7,8],[9,10],[11,12]]

    # → 返回
    +----------+
    | C0 C1 |
    +==========+
    | 58 64 |
    | 139 154 |
    +----------+

    矩阵乘法要求维度正确
    如内部有缺失元素,计算时会以0填充

  • 列表 * 数字 = 每个元素乘法

    [1,2,3] * 2.5           # → [2.5,5,7.5]

除法 /

数字

  • 数字 / 数字 = 数字相除
    5 / 2             # → 2
    5 / 2.0 # → 2.5
  • 数字 / 字符串 = 以数值相除
    5 / "2"               # → 2
    5.0 / "2.5" # → 2

列表

  • 列表 / 数字 = 每个元素除法
    [2,4,6] / 2          # → [1,2,3]

其他 += -= *= /=

只适用于数字运算。

对于未初始化的,自动初始化为0

和四则运算不同的是,对于非数字会返回None,而不是抛出异常。
如:

a += 1               # → 1
a += [1] # None

边缘情况

  • 除以零会报错。

5. 隐式类型转换

数字之间的运算,自动向高精度类型转换。
不同类型数据相加,总是尝试自动转为第一个数据的类型。

# 非严格模式
3 + "5" # → 8(自动转为整数)
"10" + 2 # → "102" (自动转为字符串)
"10" * 2 # → "1010" (字符串重复)
[1,2,3] + 5 # → [1,2,3,5]
[2,3] - 2 # → [3]
0..8 + 3 # → 0..11
{name:wang, age:18} - name # → {age:18}

三、区间、列表与映射

1. 区间

  • 区间表达式
    区间使用 ..<(左闭右开) 或 ..(为闭区间),左右不能有空格。
    支持变量扩展。

    0..<10        #不包含10
    0..10 #包含10
    a..b

    区间表达式支持步进::step

    0..6:2     # 表示步进为2: [0,2,4,6]

    可用于循环、包含检测、数组创建等。

    let r = 0..8

    for i in r {...} # 比直接在数组上循环效率更高
    r ~: 5 # 检测是否包含元素
    list.from(r) # 转为数组

2. 列表(数组)

  • 列表用[ ]表示。内部元素是有序的。

  • 也可以用......<直接从区间创建

    0...5               # 输出[0,1,2,3,4]
    # 等同于
    list.from(0..5)

两个点.. ..<创建区间,三个点... ...<创建数组

  • 索引用.[i]表示。

    let arr = [10, "a", True]
  • 索引和切片

    # 基础索引

    arr.1
    arr[0] # → 10

    # 切片操作
    arr[1:3] # → ["a", True](左闭右开)
    arr[::2] # → [10, True](步长2)
    arr[-1:] # → True(切片支持负数索引)
  • 复杂嵌套

    # 复杂嵌套
    [1,24,5,[5,6,8]][3][1] # 显示6
    # # 修改元素
    # arr[2] = 3.14 # → [10, "a", 3.14]
  • 高级操作
    参考 list 模块。

边缘情况

  • 数组索引如果超出边界,会触发out of bounds错误
  • 数组切片支持负数,表示从后往前数的index
  • 如果对不能索引的对象进行索引,会触发如下错误:
    [ERROR] type error: expected indexable type (list/dict/string), found symbol

3. 映射(字典)

  • 映射用{}表示

    let mydict = {name: "Alice", age: 30}
  • 字典索引

    # 基础访问

    mydict["name"] # → "Alice"
    mydict.name # → "Alice"(简写形式)

    # 动态键支持
    let key = "ag" + "e"
    dict[key] # → 30

    # 嵌套访问
    let data = {user: mydict}
    data.user.age # → 100
  • 高级操作
    参考 map 模块。

边缘情况

场景 行为
访问不存在的数组索引 触发[ERROR] key x not found in map错误
对非字典对象进行索引 触发 [ERROR] not valid index option 错误
对未定义的符号进行索引 返回字符串,因为shell中操作文件名是最常见的操作
对未定义的符号进行索引 返回字符串,因为shell中操作文件名是最常见的操作

四、语句

  1. 语句块
    {} 表示。一般用于流程控制语句。

  2. 语句组(子命令捕获)
    用括号表示子命令,子命令不新建进程,不隔离变量作用域。
    echo (len [5,6])

  3. 语句
    ;enter 分割

  • 换行符;或回车。

  • 续行符:使用 \ + 换行符跨行书写。

    let long_expr = 3 \
    + 5 # 等价于 "3 + 5"

    let long_str = "Hello
    World" # 等价于 "Hello\n World"

    注意:引号内的内容无须续行符

  1. 注释
    注释以#开头

五、控制结构

条件语句

If 条件

支持嵌套:

  `if cond1 { ... } else if cond2 { ... } else { ... }`

不使用 then 关键字,代码块用 {} 包裹。

  
if True {1} else {if False {2} else {3}}

if x > 10 {
print("Large")
} else if x == 10 {
print("Equal")
} else {
print("Small")
}

Match 语句

替代bash的switch语句。

  
let x = "a"
match x {
"b" => echo "is letter",
_ => echo "is others"
}

循环语句

repeat 循环

  
repeat 10 {a += 1}   # 输出[1,2...10]

For 循环

  
for i in 0..5 {    # 输出 0,1,2,3,4
print(i)
}

for i in [1,5,8] { print i }

for i in *.md { #支持*扩展
fs.cp i /tmp/
}

支持*扩展

While 循环

  
let count = 0
while count < 3 {
print(count)
count = count + 1
}

Loop 循环

  
let count = 0
let result = loop { #语句也可以作为表达式使用
print(count)
count = count + 1
if count > 3 {
break count
}
}
print result

语句表达式

  • 控制语句也可以作为表达式使用:

    let a = if b>0 {5} else {-5}
  • 条件表达式

    a = c>0 ? t : f

    支持嵌套。


六、函数

fn函数定义

  • 使用 fn 定义,支持默认参数,支持剩余参数收集
    fn add(a,b,c=10,*d) {
    return a + b + c + len(c)
    }

    # 等价于:
    fn add(a,b,c=10,*d) {
    a + b + c + len(c)
    }

    echo add(2, 3) # 输出 5
    echo add(2,3,4, 5) # 输出 10

lambda表达式

  • 使用 -> 定义。

    let add = (x,y) -> x + y
  • lambda 与 普通函数的区别:

    • lambda 不支持默认参数和return语句,不支持剩余参数收集。
    • lambda 支持部分参数应用,返回后续lambda
  • 相同的是:
    lambda和函数都 继承当前环境变量,在隔离的环境中运行,不会污染当前环境。