跳转到内容

语法与值

每次调用都是括号包裹的表达式。内层调用先求值,再作为参数传入:

(str "hello, " (str "a " "friend"))
(list one "two words" #true)

在值的位置,裸词表示文本:123、true、false 和 nil 都没有特殊的字面量含义。 双引号字符串支持 $name 和 $(...) 插值,以及 \"、\\、\n、\r、\t 和 \$ 转义。 单引号字符串完全保留原文,不插值也不处理转义。${name} 保持为普通文本。 读取变量值时使用 $name;绑定和赋值目标使用裸名字,包括函数参数、循环绑定和环境变量设置。

reader 读取文本、标量字面值、变量引用、插值字符串和表达式列表。求值可以返回文本、数值、布尔值、列表、映射、nil 或原生命令状态。它们是不同的类型;裸词 true 和 nil 本身仍然只是文本。

(eq true true)
(not #false)
(list a "b c" #true)

交互返回值依次为 #true、#true 和 ("a" "b c" #true)。文本以引号与转义形式显示;nil 不显示。文件和 -c 脚本不会自动打印返回值。

(map name parsh) 构造映射,显示为 (map "name" "parsh")。 映射使用文本键并保留插入顺序,构造、读取与修改方式见映射。

使用 # 构造有类型的标量:#1 是 int,#1.0 和 #1e3 是 float, #true、#false 是 bool,#nil 是 nil。普通的 1、false、nil 仍是文本。例如:

(let config {retries #3 timeout #1.5 enabled #false})
(list (typeof #1) (typeof 1) (eq #1 1))

列表显示为 ("int" "text" #false)。数字和布尔结果显示时带 #,包括集合中的元素; 集合中的 nil 显示为 #nil,顶层 nil 结果继续静默。 str、字符串插值、外部参数和环境变量使用不带前缀的文本:(str #1 #false) 返回 "1false"。

所有以 # 开头的裸词都已保留,#flase、#123abc 和单独的 # 均为读取错误。 文本需要加引号,例如 "#fff";abc#1 仍是普通文本。源码最开头的 shebang 保持有效。 结构化注释中不校验 # 字面值,但继续检查字符串结构和括号配对。 字面值必须连续书写。数字沿用正负号、十进制、小数和指数语法,使用 i64 整数与有限 f64; 整数溢出不会改为浮点数。布尔和 nil 名称仅接受完整小写写法,不提供 #t、#f 别名。

提示符和脚本顶层可以直接书写字面值,脚本不会自动打印其结果。 字面值是源码语法:引号文本、变量内容和命令输出不会重新解析,#$name 不是转换操作。 动态转换使用 int、float、bool。

调用头接受文本命令名、函数值和外部 command 引用。(#1) 以及数值变量作为命令名都会报错; 需要文本命令名时显式使用 str。#false 是普通假值,(false) 保持外部命令的失败行为。 数字和 nil 不会隐式变成条件。

[...] 是 (list ...) 的简写,{...} 是 (map ...) 的简写:

[]
[one "two words" #true]
{}
{name parsh items [one two] nested {enabled #true}}
(let items [a (; 忽略这段注释) b])

元素以空白分隔。映射使用交替的键和值,沿用 map 的文本键和重复键规则。 元素、键和值从左到右各求值一次。集合可以嵌套,并用于任何允许调用表达式的位置。 交互返回值沿用现有格式:("one" "two words" #true) 和 (map ...)。

三种括号 ()、[]、{} 都必须配对,包括结构化注释内部。 只有 (; ...) 表示注释;[; a] 包含两个文本值。 字符串外的方括号和花括号即使没有相邻空白也会分隔表达式。 包含它们的字面文本和 glob 模式需要加引号,例如 (glob 'file[12].txt')。 字符串内的方括号和花括号保持为普通文本,${name} 仍然是字面文本。

提示符中可以直接输入或粘贴集合。按 Enter 时,如果补入一个匹配的 ] 或 } 就能使整个输入完整,会自动补入并提交;未完成的嵌套结构会继续换行。 灰色闭括号在提交前只参与显示。

(let name parsh)
(str "hello, $name")
(str "$(get name)_suffix")
(str "sum=$(+ #1 #2)")
(str "nested=$(str "a" 'b')")
(str "cwd=$(pwd)")
(str '$name $(+ 1 2)\n')
(str "\$name \$(+ 1 2)")

$name 读取最长的合法变量名。变量名后紧接 _suffix 等文本时,使用 $(get name) 明确边界。 ${name} 没有特殊含义;不构成变量名或 $( 开头的 $ 保持为文本。

$(...) 中的括号就是普通调用表达式。$(name) 调用名为 name 的零参数函数或命令; $(get name) 读取变量。支持所有现有表达式,包括嵌套调用、控制流及结构化注释。 内层字符串按正常 parsh 语法书写,不需要额外为外层转义。pwd 示例需要命令执行后端。

各片段从左到右各求值一次。每个括号片段都有自己的词法作用域,let 会把绑定写入外围表达式作用域; 其他副作用与错误规则与普通调用相同。 标量按 str 的规则转换:数值、布尔值转文本,nil 转空文本,列表和映射报错。 外部命令捕获 UTF-8 标准输出并移除末尾 LF 换行符,标准错误保留原来的输出目标。 错误会停止当前批次。整个结果保持为一个参数,插入的值不会再次解析或插值。

普通单引号保留直到下一个单引号之间的全部内容,包括反斜杠、美元符和换行。 需要单引号字符时使用双引号。相邻的引号字符串和裸词表达式不会自动拼接。 要把字面 $name 或 $(...) 传给 sh -c 等其他程序,使用单引号或以 \$ 转义美元符。 文本显示也会转义美元符,避免复制显示的字符串时触发插值。

三引号允许在内容中直接放入单个或两个同类引号。 """...""" 支持与普通双引号相同的插值和转义,'''...''' 完全保留原文:

(str """hello, "$name"
sum=$(+ #1 #2)""")
(str '''literal '$name'
$(pwd)\n''')

所有换行、缩进及首尾空白都会保留,包括起始分隔符后紧接的换行。 只有连续三个同类引号才结束字符串。三重双引号中的 \" 仍会转义引号, 因此 \""" 可以插入三个字面双引号。连续六个同类引号表示空字符串。 相邻字符串仍然是独立表达式。

123 是文本,#123 才是整数。算术运算符 +、-、*、/ 和数值比较 =、!=、<、<=、>、>= 只接受运行时 int 或 float,允许两种数值混合。 运算符不会解析文本:(+ 1 #2) 报类型错误,(+ (int 1) #2) 返回 #3。 环境变量、脚本参数和捕获的命令输出应先用 int 或 float 显式转换。

表达式 交互返回值
(+ #123 #2) #125
(+ (float "1.5") #2) #3.5
(/ #5 #2) #2.5
(/ #8 #2) #4.0
(str 00123) "00123"
(eq (+ #1 #2) 3) #false
(eq #3 #3.0) #false
(= (+ #1 #2) #3) #true
(= #001 #1.0) #true
(!= #1 #1.5) #true
(< #2 #10) #true
(<= #3 #3.0) #true
(> #10 #2) #true
(>= #2.5 #3) #false

字面值支持 #+001、#-12、#.5、#1.、#2.5e1 等写法。 整数使用 i64,带小数点或指数时使用 f64;前导零按十进制处理,不表示八进制。 整数溢出直接报错,不会改为浮点数。运行时文本由 int、float 去掉首尾空白后完整解析, 运算符不再承担解析职责。布尔值、nil、列表和映射不能作为数值使用。

参数从左到右各求值一次,算术运算左结合。+、* 接受任意数量的参数, (+) 返回整数 0,(*) 返回整数 1。 -、/ 至少接受一个参数;(- #5) 返回 -5,(/ #4) 返回 0.25。 整数加减乘检查溢出,每步运算遇到浮点数时使用 f64;除法始终返回浮点数。 后面的浮点参数不能避免前面的整数运算溢出。

eq 按类型和值比较,包括列表和映射中的元素;映射的插入顺序不影响比较。数值比较 = 判断相等,!= 判断不相等; <、<=、>、>= 分别判断小于、小于等于、大于、大于等于。 每个数值比较运算符恰好接受两个 int/float 参数,返回布尔值。 整数与浮点数按数值比较,混合比较时仍保留整数精度, 例如 (> #9007199254740993 #9007199254740992.0) 返回 true。 浮点比较是精确比较,不引入容差:由于 f64 的二进制舍入误差, (= (+ #0.1 #0.2) #0.3) 返回 false。涉及浮点数的算术也可能损失大整数精度。

(let count (+ #1 #2))
(if (>= $count #3) (str "at least three") (str fewer))

数值显示带 #,浮点数保留小数点或指数,包括 #4.0 和 #-0.0; str、外部命令参数和环境变量使用不带 # 的数值文本。捕获命令输出或导出数值会重新得到文本。 数值不会隐式转换为布尔值;在条件中使用数值比较。 非法操作数、除零、整数溢出和非有限浮点数会在出错的操作数处报告语言错误,退出码为 2。

(; 这是一个完整的注释表达式。
(; 注释可以嵌套。 )
(no-such-command $undefined)
)
(echo (; 这个注释不会产生参数。 ) hello)
(echo ; this;is;text)

首个裸词为 ; 的列表会被 reader 丢弃,无论它出现在什么位置。 注释内容仍遵循字符串与括号配平规则,但不会求值,也不会检查名称是否已定义。 其他位置的 ; 都是普通文本;没有行注释。带引号的 ";" 不是注释标记。

列表、字符串、类型判断与补充数值函数见基础库。