Lumesh 语法手册1 (基础语法)
一、变量与类型
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. 数据类型
基本类型
类型 | 示例 |
---|---|
变量 | x , $a |
整数 | 42 , -3 |
浮点数 | 3.14 , -0.5 , 10% |
字符串 | "Hello\n" , 'raw' |
布尔值 | True , False |
列表(数组) | [1, "a", True] |
映射(字典) | {name: "Alice", age: 30} |
区间 | 1..8 , 1..<10 |
regex | r'^\w+ |
时间 | t'2025-5-20' |
文件大小 | 4K ,5T |
空值 | None |
复杂类型
类型 | 示例 |
---|---|
函数 | 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的文件
日期时间类型
例如:Fs.parse('2025-5-20')
日期时间类型可参与运算。
如:
Fs.parse('2025-5-20') > Fs.parse('2025-1-20') # 返回True |
具体操作请参考内置函数的time
模块
百分数
书写为百分数,会自动识别为浮点数
print 37% 2% + 3 |
百分号紧跟数字后为百分数,数字后空格+%则为模运算
二、运算符
1. 运算符分类与优先级
优先级从高到低(数值越大优先级越高)
优先级 | 运算符/结构 | 示例/说明 |
---|---|---|
13 | 括号 () |
(a + b) * c |
12 | 单目运算符 ! , - , __.. |
!flag , -5 |
11 | 幂运算 ^ |
2 ^ 3 |
10 | 乘除模 * , / , % , _*.. |
a * b % c |
9 | 加减 + , - ,_+.. |
a + b - c |
8 | 比较 == , != , > 等 |
`a > b |
7 | 逻辑与 && |
cond1 && cond2 |
6 | 逻辑或 ` | |
5 | 条件运算符 ? : |
cond ? t : f |
4 | Lambda 表达式 | x -> x+1 |
3 | 错误处理 ?: ?. |
3 / 0 ?: 1 |
2 | 管道 ` | ` |
重定向 << >> >! |
date >> /tmp/day.txt |
|
1 | 赋值 = , := ? |
x = 5 , let y := 10 |
管道和重定向同级,逻辑运算比管道和重定向优先级更高。
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--
,a++
自定义操作符
自定义操作符是以
_
开头的符号,只能包含符号,不能包含数字或字母。自定义单目运算符 以
__
开头,如__+
, 优先级同单目运算符。仅能用于后缀运算。自定义+级运算符 以
_+
开头,如_+%
,优先级同+
-
自定义*级运算符 以
_*
开头,如_*-
,优先级同*
/
let __++ = x -> x + 1;
3 __++
边缘情况:
x++y
非法,x+++y
合法。
3. 特别的操作符
==
带类型相等比较.~=
值相等比较.~~
字符串正则匹配.~:
是否包含(可用于字符串,数组,区间,字典(检测是否包含指定key)).!~~
字符串正则不匹配.!~:
不包含.
举例:
5 == "5" # 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 |
边缘情况:
- 除以零会报错。
5. 隐式类型转换
数字之间的运算,自动向高精度类型转换。
不同类型数据相加,总是尝试自动转为第一个数据的类型。
# 非严格模式 |
三、区间、列表与映射
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}
# 允许简写:
let a = b =3,
{a, b}
{
a,
b,
} # 允许末尾逗号,允许多行书写
{a, } # 单个键值的逗号不能省略字典索引
# 基础访问
mydict["name"] # → "Alice"
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中操作文件名是最常见的操作 |
四、语句
语句块
用{}
表示。一般用于流程控制语句。语句组(子命令捕获)
用括号表示子命令,子命令不新建进程,不隔离变量作用域。
echo (len [5,6])
语句
用;
或enter
分割
换行符:
;
或回车。续行符:使用
\
+ 换行符跨行书写。let long_expr = 3 \
+ 5 # 等价于 "3 + 5"
let long_str = "Hello
World" # 等价于 "Hello\n World"注意:引号内的内容无须续行符。
- 注释
注释以#
开头
五、控制结构
条件语句
If 条件
支持嵌套:
if cond1 { ... } else if cond2 { ... } else { ... }
不使用 then
关键字,代码块用 {}
包裹。
if True {1} else {if False {2} else {3}} |
Match 语句
替代bash的switch语句。
支持在一个分支中匹配多个选项,多个选项之间用,
分隔。
支持正则匹配,正则需要用’’ 或 “” 包裹。
支持字面量直接匹配。并且不会被解读为变量。
为提高匹配效率,建议只有正则才写入引号内;普通字符串直接以字面量书写
let fruit = "apple" |
循环语句
repeat 循环
repeat 10 {a += 1} # 输出[1,2...10] |
For 循环
for i in 0..5 { # 输出 0,1,2,3,4 |
支持*扩展
While 循环
let count = 0 |
Loop 循环
let count = 0 |
语句表达式
控制语句也可以作为表达式使用:
let a = if b>0 {5} else {-5}
条件表达式
a = c>0 ? t : f
支持嵌套。
关于函数、命令、管道、错误处理等内容,请继续阅读:
关于函数库等,请阅读: