Scriban
是一个快速、强大、安全且轻量级的模板引擎,同时兼容liquid
语法规则。
项目地址:https://github.com/scriban/scriban
这个文档是语言语法的中文翻译
原文地址:https://github.com/scriban/scriban/blob/master/doc/language.md
翻译人:Frank.Cui
Email:frank@mondol.info
本文档描述了Scriban
语言的语法(在{{
和 }}
内)
在纯脚本环境中,语言的规则是相同的。
注意:本文档不描述
liquid
语言,如有需求可查看它的官方[liquid website](https://shopify.github.io/liquid/)
块
模板中有3种类型的文本块
- 代码块:包含
Scriban
代码语句 - 文本块:按原样输出的普通文本
- 转义块:可以转义代码块的文本块
代码块
由 {{
和 }}
括起来的文本是 Scriban
的代码块,将由 Scriban
模板引擎进行执行。
代码中块能包含:
- 单行表达式语句:
{{ name }}
- 或多行语句:
{{
if !name
name = "default"
end
name
}}
- 或由分号(
;
)分隔的多条语句,某些用例中格式更紧凑:
{{if !name; name = "default"; end; name }}
在代码块中,空白字符对解析没有影响,但每个语句后面的行尾字符除外。 唯一的例外是使用空格来区分数组索引器和数组初始值设定项。
此外,当语句是表达式(但不是赋值表达式)时,表达式的结果将显示在模板的输出中:
{{
x = 5 # 变量赋值,不会输出任何内容
x # 这个表达式输出5
x + 1 # 这个表达式输出6
}}
56
请注意,在上面的示例中,5 和 6 之间没有换行,因为我们位于代码块内。
但您仍然可以在代码块内使用n
来主动换行,或者可以使用混合代码和文本块:
{{ x = 5 }}
{{ x }}
{{ x + 1 }}
5
6
文本块
否则,任何文本都将被视为文本块并不加修改地输出。
Hello this is {{ name }}, welcome to scriban!
______________ _____________________
文本块 文本块
转义块
任何代码和文本块都可以通过用 {%{
和 }%}
括起来来转义,以生成文本块。
例如下面的转义:
输入:
{%{Hello this is {{ name }}}%}
输出:Hello this is {{ name }}
如果要转义转义块,可以增加起始块和结束块中的 %
数量:
输入:
{%%{This is an escaped block: }%} here}%%}
输出:This is an escaped block: }%} here
这允许有效地嵌套逃生块并且仍然能够逃脱它们。
例如:{%%%%{ will require an ending }%%%%}
空白控制
默认情况下,代码/转义块之前或之后的任何空格(包括Tab、换行符)都会按原样复制到输出。
Scriban 提供了两种控制空白的模式:
使用字符-
{{-
或 -}}
表示贪婪模式
会删除所有空白字符(包括换行符)。
例如:删除左侧的空白
This is a text
This is a text
例如:删除右侧的空白
This is a text:
This is a text
例如:删除左、右的空白
This is a text:
This is a text
使用字符~
{{~
或 ~}}
表示非贪婪模式
会删除所有空白字符(换行符除外)。
当您只想在一行上使用 scriban 语句,但希望该行完全被使用时,此模式非常方便,从输出中删除,但保持该行前后的空格完好无损。
在下面的示例中,我们想要完全删除行 {{~ for products in products ~}}
和{{~ end ~}}
,但我们想要保留开头
使用贪婪模式(
{{-
或 -}}
)会删除所有空格和行,并将结果放在一行上。
{{~ for product in products ~}}
- {{ product.name }}
{{~ end ~}}
- Orange
- Banana
- Apple
组合控制
-
~
和-
也可以与转义块一起使用- 例如:
{%%{~``~}%%}
或{%%{-``-}%%}
- 例如:
-
~
和-
也可以混用- 例如:
{{- name ~}}
或{{~ name -}}
- 例如:
- 也可以与无控制符混用
- 例如:
{{ name ~}}
或{{~ name }}
- 例如:
注释
在代码块内,Scriban
支持单行注释 #
和多行注释 ##
{{ name # 这是单行注释 }}
{{ ## 这是
多行
注释 ## }}
正如您所看到到的,单行和多行注释都可以通过代码块退出标记(}}
)来关闭。
数据类型
字符串
Scriban 支持3种类型的字符串
常规字符串
用双引号"..."
或单引号'...'
括起来的常规字符串。常规字符串支持多行并将支持以下转义字符:
-
'
单引号 -
"
双引号 -
\
反斜杠 -
n
换行 -
r
回行 -
t
tab -
b
退格键 -
f
换页 -
uxxxx
其中 xxxx 是 unicode 十六进制代码编号0000
到ffff
-
x00-xFF
范围从0x00
到0xFF
的十六进制
原始字符串
用反引号...
括起来的字符串,会原样输出,不会处理转义,例如可用于正则表达式场景:
{{ "this is a text" | regex.split `s+` }}
[this, is, a, text]
内插字符串
以 $
开头并用双引号 $"..."
或单引号 $'...'
括起来的内插字符串。
内插字符串中可以使用变量、表达式。
{{ $"this is an interpolated string with an expression {1 + 2} and a substring {"Hello"}" }}
this is an interpolated string with an expression 3 and a substring Hello
数值
与Javascript中的数值是一样的,例如:{{ 100 }}
整数
- 10进制整数:
100
,1e3
- 16进制整数:
0x1ef
- 无符号整数:
0x80000000u
浮点数
-
100.0
,1.0e3
,1.0e-3
- 32位:
100.0f
- 64位:
100.0d
- 128高精度:
100.0m
布尔
布尔值 {{ true }}
或{{ false }}
{{ true }}
{{ false }}
true
false
null
与Javascript里的null
是一样的,例如:{{ null }}
当解析为字符串输出时,null值将输出空字符串:
{{ null }}
变量
Scriban 支持全局变量和局部变量的概念。
全局变量
全局变量以字母或下划线 _ 开头,后面跟着字母 A-Z a-z、数字 0-9、下划线 _,例如:{{ name }}
以下是有效的变量名称:
var服务器托管网
var9
_var
局部变量
局部变量是以 $
开头的标识符(例如:{{ $name }}
),局部变量只能在同一页面或函数体内访问。
单独的特殊局部变量 $
是一个数组,其中包含传递给当前函数或页页的参数。
特殊局部变量 $0
$1
...
$n
是 $[0]
、$[1]
...
$[n]
的简写。例如,使用 $0
返回当前函数或页面的第一个参数。
特殊变量this
this
变量使您可以访问当前对象绑定,您可以在其中访问当前范围的所有局部变量。
因此以下变量访问是等效的:
{{
a = 5
a # output 5
this.a = 6
a # output 6
this["a"] = 7
a # output 7
}}
567
在 with
语句的情况下, this
运算符引用传递给 with
的对象:
{{
a = {x: 1, y: 2}
with a
b = this
end
b.x
}}
1
特殊变量empty
empty
变量代表一个空对象,主要用于与 Liquid
模板兼容。它提供了一种将对象与空变量进行比较以确定它是否为空的方法:
{{
a = {}
b = [1, 2]~}}
{{a == empty}}
{{b == empty}}
true
false
对象
Scriban 支持类似 javascript 的对象 {...}
。
对象可以初始化为空:
{{ myobject = {} }}
一个对象可以用一些成员来初始化:
{{ myobject = { member1: "yes", member2: "no" } }}
或使用 json 语法:
{{ myobject = { "member1": "yes", "member2": "no" } }}
也可以通过多行初始化一个对象:
{{
myobject = {
member1: "yes",
member2: "no"
}
}}
可以使用点符号或方括号符号来访问对象的成员:
{{ myobject.member1 }}
等同于 {{ myobject["member1"] }}
您可以通过可选成员运算符 ?
访问链中的可选成员(而不是常规成员运算符.
)
{{ myobject.member1?.submember1?.submember2 ?? "nothing" }}
将返回"nothing"
因为member1
不包含submember1
/submember2
如果该对象是纯Scriban对象(使用 {...}
创建或由运行时实例化为 ScriptObject),您还可以通过简单的赋值向其添加成员:
{{
myobject = {}
myobject.member3 = "may be"
myobject.member3
}}
may be
特殊属性empty?
任何对象、数组都可以使用属性.empty?
检查它是否为空:
{{
a = {}
b = [1, 2]~}}
{{a.empty?}}
{{b.empty?}}
true
false
数组
数组可以初始化为空:
{{ myarray = [] }}
可以使用一些数据项来初始化数组:
{{ myarray = [1, 2, 3, 4] }}
也可以通过多行初始化一个数组:
{{
myarray = [
1,
2,
3,
4,
]
}}
数组的项可以通过索引访问(从0开始):
{{ myarray[0] }}
如果数组是纯Scriban数组(使用 [...]
创建或由运行时实例化为 ScriptArray),您还可以通过简单的赋值向其中添加数组项,该赋值将根据索引自动扩展数组 :
{{
myarray = []
myarray[0] = 1
myarray[1] = 2
myarray[2] = 3
myarray[3] = 4
}}
您还可以使用数组内置对象来操作数组。
注意
虽然在 Scriban 中解析时空格字符大多不相关,但在某些情况下空格有助于消除数组索引器和数组初始值设定项之间的歧义。
例如,如果在 [ 之前发现空格,并且前面的表达式是变量路径表达式(见下文),则以下表达式 […] 将被视为数组初始值设定项而不是数组索引器:
{{
myfunction [1] # myfunction 后面有一个空格。
# 它将导致调用 myfunction 并将数组作为参数传递
myvariable[1] # 没有空格,这是访问
# 访问myvariable数组元素
}}
具有属性的数组
数组还可以包含附加属性:
{{
a = [5, 6, 7]
a.x = "yes"
a.x + a[0]
}}
yes5
特殊属性size
数组有一个 size
属性,可用于查询数组中元素的数量:
{{
a = [1, 2, 3]
a.size
}}
3
函数
Scriban 允许定义四种不同类型的函数
- 简单函数
- 带参数的函数
- 匿名函数
- 内联函数
简单函数
下面声明了一个函数 sub
,它接受两个参数 a
和 b
,并从 a
中减去 b
的值:
{{func sub
ret $0 - $1
end}}
所有参数都传递给特殊变量 $ ,该变量将包含直接参数列表和命名参数:
-
$0
或$[0]
将访问第1个参数 -
$1
或$[1]
将访问第2个参数 -
$[-1]
将访问最后1个参数 -
$.named
将访问命名参数named
然后可以使用该函数:
{{sub 5 1}}
{{5 | sub 1}}
4
4
从上面的示例中可以注意到,使用管道(|
)时,管道的结果被传递给为管道接收者的第一个参数。
请注意,函数也可以混合代码与文本编写:
{{func inc}}
这是带有以下参数的文本 {{ $0 + 1 }}
{{end}}
注意
在简单函数中设置变量(例如 a = 10)将导致在全局级别定义它,而不是在函数级别。
带参数的函数
通过在函数内引入新的变量范围来解决此行为。
带参数的函数
它类似于简单函数,但它们是用括号声明的,同时还支持声明不同类型的参数(普通、可选、变量)。
与简单函数
的另一个区别是它们需要函数调用和参数来匹配预期的函数参数。
一个标准的带参数函数
{{func sub(x,y)
ret x - y
end}}
{{sub 5 1}}
{{5 | sub 1}}
4
4
具有普通参数和具有默认值的可选参数的函数:
{{func sub_opt(x, y, z = 1, w = 2)
ret x - y - z - w
end}}
{{sub_opt 5 1}}
{{5 | sub_opt 1}}
1
1
这里我们覆盖 z
的值并将其设置为 0 而不是默认的 1:
{{sub_opt 5 1 0 }}
{{5 | sub_opt 1 0}}
2
2
具有普通参数和具有默认值的可选参数的函数:
{{func sub_variable(x, y...)
ret x - (y[0] ?? 0) - (y[1] ?? 0)
end}}
{{sub_variable 5 1 -1}
{{5 | sub_variable 1 -1}}
5
5
注意:特殊变量 $
仍然可以在参数函数中访问,并表示参数的直接列表。 在上面的例子中 $ = [5, [1, -1]]
匿名函数
匿名函数类似于简单函数,但可以在表达式中使用(例如作为函数调用的最后一个参数)
{{ sub = do; ret $0 - $1; end; 1 | sub 3 }}
-2
它可以非常方便地构建自定义块函数:
{{ func launch; ret $0 1 2; end
launch do
ret $0 + $1
end
}}
3
内联函数
对于简单函数,像数学函数一样定义简单函数是很方便的:
{{ sub(x,y) = x - y }}
内联函数与参数函数类似,但它们只支持普通参数。 它们不支持可选或可变参数。
函数指针
因为函数也是对象,所以可以使用别名@
运算符将它们存储到对象的属性中:
{{
myobject.myinc = @inc # 使用 @ 别名运算符允许引用函数而不执行它
x = 1 | myobject.myinc # x = x + 1
}}
函数别名运算符 @
允许将一个函数作为参数传递给另一个函数,从而实现强大的函数组合。
函数调用
可以通过传递由空格分隔的参数来调用函数,例如:
{{ myfunction arg1 "arg2" (1+5) }}
管道操作符 |
还可以用于将表达式的结果通过管道传递给函数的第1个参数,例如:
{{ date.parse '2016/01/05' | date.to_string '%g' }}
将输出:06 Jan 2016
请注意,当函数接收管道调用的结果(例如上例中的 date.to_string )时,它会作为调用的第一个参数传递。
管道对于空白字符是非常贪婪的。 这允许它们跨多行链接:
{{-
"text" |
string.append "END" |
string.prepend "START"
-}}
STARTtextEND
指定参数名调用
调用时还可以指定参数名,这对于可选参数非常有用
{{func sub_opt(x, y, z = 1, w = 2)
ret x - y - z - w
end}}
{{ sub_opt 5 1 w: 3 }}
{{ 5 | sub_opt 1 w: 3 }}
0
0
请注意,一旦命名了参数,后面的参数就必须全部命名。
在使用 func 声明的自定义函数中,命名参数可以通过变量参数变量 $ 访问,但作为属性(而不是作为默认数组参数的一部分):
{{
func my_processor
"Argument count:" + $.count
"Argument options:" + $["options"]
for $x in $
"arg[" + $x + "]: " + $x
end
end
my_processor "Hello" "World" count: 15 options: "optimized"
}}
Argument count: 15
Argument options: optimized
arg[0]: Hello
arg[1]: World
语句
每个语句必须由代码块 }}
或代码块中的 EOL(End Of Line)
终止,或者用分号来分隔代码块中单行上的多个语句。
表达式
Scriban 支持传统的一元和二元表达式。
变量路径表达式
变量路径表达式包含变量的路径:
- 简单的变量访问:
{{ name }}
表示访问顶级变量name
- 数组访问:
{{ myarray[1] }}
表示访问顶级数组变量的第1个元素 - 成员访问:
{{ myobject.member1.myarray[2] }}
表示访问顶级对象myobject
的member1属性下的myarray属性的第3个数组元素
请注意,变量路径可以访问简单变量,也可以当做无参数函数调用。
赋值表达式
可以将值分配给顶级变量或对象/数组的成员:
-
{{ name = "foo" }}
表示将字符串"foo"
赋值给了name
变量 {{ myobject.member1.myarray[0] = "foo" }}
赋值表达式必须是顶级表达式语句,并且不能在子表达式中使用
嵌套表达式
由 (
和 )
括起来的表达式表示嵌套表达式,例如:
{{ name = ('foo' + 'bar') }}
算术表达式
数字支持以下二元运算符:
操作 | 描述 |
---|---|
+ |
2个数字相加,例如:1 + 2
|
- |
左边数字减右边,例如:2 - 1
|
* |
2个数字相乘,例如:2 * 2
|
/ |
左边除以右边,例如:2 / 1
|
// |
左边除以右边并四舍五入为整数 |
% |
左边除以右边取余数 |
如果 left
或 right
是浮点数,而另一个是整数,则运算结果将为浮点数。
字符串表达式
字符串支持以下二元运算符:
操作 | 描述 |
---|---|
'left' + |
拼接2个字符串,例如:"ab" + "c" 结果为"abc"
|
'left' * |
将左边的字符串重复right 次,例如:'a' * 5 结果为aaaaa
|
字符串与数字的左右顺序是可以交换的,例如:5 * 'a'
|
二元运算中只要有一个字符串,其他部分就会自动转换为字符串。
以下代码将转换为字符串:
-
null -> ""
例如:"aaaa" + null -> "aaaa"
0 -> "0"
1.0 -> "1.0"
true -> "true"
false -> "false"
条件表达式
布尔表达式通过比较左右值来生成布尔值。
操作 | 描述 |
---|---|
== |
左右是否相等? |
!= |
左右是否不等? |
> |
左边是否大于右边? |
>= |
左边是否大于等于右边? |
|
左边是否小于右边? |
|
左边是否小于等于右边? |
以上表达式支持数字、字符串、日期时间
您可以将条件表达式与 &&
(与运算符)||
(或运算符)结合起来用。
与 javascript 不同,它总是返回布尔值,并且从不返回 或
操作 | 描述 |
---|---|
&& |
左边为真 并且 右边为真 |
|| |
左边为真 或者 右边为真 |
三元条件表达式
同时支持Javascript一样的三元表达式:条件 ? 条件为真时的取值 : 条件为假时的取值
{{
gender = name == "frank" ? "男" : "女"
gender
}}
男
一元表达式
操作 | 描述 |
---|---|
! |
布尔值取反。例如:if !page
|
+ |
算数正数表达式。例如:+1.5
|
- |
算数负数表达式 |
^ |
展开传递给函数调用参数的数组(请参阅函数调用) |
@ |
如果是函数调用则将计算的表达式结果的函数指针 |
++ |
变量自增,与Javascript中的等效 |
-- |
变量自减,与Javascript中的等效 |
++ |
变量自减,与Javascript中的等效 |
-- |
变量自减,与Javascript中的等效 |
注意:对于自增和自减运算符,操作数必须是变量、属性或索引器
范围表达式
它们是提供迭代器的特殊二进制表达式(通常与 for 语句一起使用)。
其中 left
与right
表达式必须在运行时解析为整数。
操作 | 描述 |
---|---|
left..right |
返回一个迭代器,内容范围为left 到 right
|
步长为1,结果内容包含 right
|
|
例如:1..5 返回的是[1, 2, 3, 4, 5]
|
|
left.. |
返回一个迭代器,内容范围为left 到 right
|
步长为1,结果内容不包含 right
|
|
例如:1..返回的是
|
null
合并运算符:??
, ?!
# 常规写法
name2 = name != null ? name : "未知"
# 优雅写法
name2 = name ?? "未知"
# 常规写法
name2 = name == null ? name : "未知"
# 优雅写法
name2 = name ?! "未知"
if
, else
, else if
一般语法是:
{{
if
...
else if
...
else
...
end
}}
if
语句必须以 end
结束,或者后跟 else
或 else if
语句。
else
或 else if
语句后面必须跟有 else
、else if
或以 end
语句结束。
对于 if
或 else if
后面的表达式,其计算结果必需为布尔值。
真与假
默认情况下,当计算为布尔值时,只有 null
和 false
被视为 false
当用于if
判断时,以下值也可以隐式转换为布尔值:
0 -> true
-
1 -> true
或任意非0的值 **null -> false**
**false -> false**
非null的对象 -> true
-
"" -> true
空字符串返回true
"foo" -> true
case
与when
这相当于 C# 中的 switch
语句,这是一个选择语句,它根据值匹配从候选列表中选择要执行的单个 switch 部分。
-
case
要判断的变量或表达式 -
when
允许与指定表达式和case表达式匹配-
when
也可以与用,
或||
分隔的多个值一起使用
-
- 最后的
else
可以用作默认处理情况,以防没有匹配的情况。
{{
x = 5
case x
when 1, 2, 3
"Value is 1 or 2 or 3"
when 5
"Value is 5"
else
"Value is " + x
end
}}
Value is 5
循环
for in ... end
{{for in }}
...
{{end}}
表达式可以是数组或范围迭代器:
- 在数组上循环:
{{ for page in pages }} 这是页面 {{ page.title }}{{ end }}
- 在迭代器上循环:
{{ for x in 1..n }} 这是循环体 [{{x}}]{{ end }}
for
循环(以及下面的 tablerow
语句)支持附加参数offset
, limit
与 reversed
,它们也可以一起使用:
offset
参数
允许在指定的从零开始的索引处开始循环迭代:
{{~ for $i in (4..9) offset:2 ~}}
{{ $i }}
{{~ end ~}}
6
7
8
9
limit
参数
限制迭代次数
{{~ for $i in (4..9) limit:2 ~}}
{{ $i }}
{{~ end ~}}
4
5
reversed
参数
倒序迭代
{{~ for $i in (1..3) reversed ~}}
{{ $i }}
{{~ end ~}}
3
2
1
特殊循环变量
以下变量可在 for
块中访问:
名称 | 描述 |
---|---|
{{for.index}} |
The current index
|
of the for loop | |
{{for.rindex}} |
The current index
|
of the for loop starting from the end of the list | |
{{for.first}} |
A boolean indicating whether this is the first step in the loop |
{{for.last}} |
A boolean indicating whether this is the last step in the loop |
{{for.even}} |
A boolean indicating whether this is an even row in the loop |
{{for.odd}} |
A boolean indicating whether this is an odd row in the loop |
{{for.changed}} |
A boolean indicating whether a current value of this iteration changed from previous step |
while ... end
{{while }}
...
{{end}}
当表达式结果为true时则循环
特殊循环变量
以下变量可在 while
块中访问:
名称 | 描述 |
---|---|
{{while.index}} |
The current index
|
of the while loop | |
{{while.first}} |
A boolean indicating whether this is the first step in the loop |
{{while.even}} |
A boolean indicating whether this is an even row in the loop |
{{while.odd}} |
A boolean indicating whether this is an odd row in the loop |
tablerow in ... end
此函数生成与 HTML 表兼容的 HTML 行。 必须包含在开始
和结束
HTML 标记中。
This服务器托管网 statement is mainly for compatibility reason with the liquid tablerow
tag.
It uses similar syntax to a for
statement (supporting the same parameters).
{{tablerow in }}
...
{{end}}
{{~ tablerow $p in products | array.sort "title" -}}
{{ $p.title -}}
{{ end ~}}
Apple
Banana
Computer
Mobile Phone
Orange
Sofa
Table
The cols
parameter
Defines the number of columns to output:
{{~ tablerow $p in (products | array.sort "title") limit: 4 cols: 2 -}}
{{ $p.title -}}
{{ end ~}}
Apple
Banana
Computer
Mobile Phone
break
与 continue
break
语句允许提前退出循环
{{ for i in 1..5
if i > 2
break
end
end }}
continue
语句允许跳过循环的其余部分并继续下一步
{{ for i in 1..5
if i == 2
continue
end
}}
[{{i}}]] step
{{ end }}
[1] step
[3] step
[4] step
[5] step
capture ... end
capture ... end
语句允许将模板输出捕获到变量:
例如下面的代码:
{{ capture myvariable }}
This is the result of a capture {{ date.now }}
{{ end }}
将设置myvariable = "This is the result of a capture 06 Jan 2016n"
readonly
readonly
语句阻止变量进行后续赋值:
{{ x = 1 }}
{{ readonly x }}
{{ x = 2 }}
import
import
语句允许将对象的成员作为当前绑定的变量导入:
{{
myobject = { member1: "yes" }
import myobject
member1 # 将打印“yes”字符串到输出
}}
请注意,只读变量不会被覆盖。
with ... end
The with ... end
statement will open a new object context with the passed variable, all assignment will result in setting the members of the passed object.
myobject = {}
with myobject
member1 = "yes"
end
wrap ... end
将语句块传递给函数,该函数将能够使用特殊变量 $$
对其进行计算
{{
func wrapped
for $i in 1.. This is inside the wrap!rn"
end
}}
1 -> This is inside the wrap!
2 -> This is inside the wrap!
3 -> This is inside the wrap!
4 -> This is inside the wrap!
请注意,在 with
块外部声明的变量可以在内部访问。
include arg1?...argn?
include
is not a statement but rather a function that allows you to parse and render a specified template. To use this function, a delegate to a template loader must be setup on the [TemplateOptions.TemplateLoader](runtime.md#include-and-itemplateloader)
property passed to the Template.Parse
method.
include 'myinclude.html'
x = include 'myinclude.html'
x + " modified"
assuming that myinclude.html
is
{{ y = y + 1 ~}}
This is a string with the value {{ y }}
will output:
This is a string with the value 1
This is a string with the value 2 modified
ret ?
ret
语句用于提前退出顶级/包含页面或函数。
This is a text
{{~ ret ~}}
This text will not appear
This is a text
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net