Projects STRLCPY CVS Files
🤬
main
ROOT /
VDSL_CN.md
528 lines | UTF-8 | 13 KB

VDSL语法

VDSL的语法设计为Go开发人员所熟悉,同时更简单、更精简。

您可以在CVE PoC IDE中测试VDSL代码

值和值类型

在VDSL中,一切都是一个值,并且所有值都与一个类型相关联。

11 + 22               // int 值
"aa" + `bb`     // string 值
-1.22 + 3e9          // float 值
true || false         // bool 值
'七' > '7'             // char 值
[33, false, "xx"]     // array 值
{a: true, b: "oo"}  // map 值
func() { /*...*/ }    // function 值

以下是VDSL中所有可用值类型的列表。

VDSL 类型描述go中的等效类型
int有符号64位整数值int64
float64位浮点值float64
bool布尔值bool
charunicode字符rune
string unicode字符串string
bytes 字节数组[]byte
error错误-
time时间值time.Time
array值数组 (可变 )[]any
const array常量 数组-
map具有字符串键的值映射 (可变)map[string]any
const map常量 映射-
nilnil-
function函数-
user-defined用户定义类型-

Error 值

在VDSL中,可以使用“error”类型的值来表示错误。一个错误值是使用“error”表达式创建的,并且它必须有一个底层值可以使用.value访问错误值的基础值选择器。

err1 := error("oops")    // error with string value
err2 := error(1+2+3)     // error with int value
if is_error(err1) {      // 'is_error' builtin function
  err_val := err1.value  // get underlying value
}

Const 值

在VDSL中,基本上所有的值(除了数组和映射)都是常量。

s := "12345"
s[1] = 'b'    // illegal: String is const

a := [1, 2, 3]
a[1] = "two"  // ok: a is now [1, "two", 3]

可以使用“const”表达式将数组或映射值设为const。

b := const([1, 2, 3])
b[1] = "foo"  // illegal: 'b' references to an const array.

请注意,将新值重新分配给变量与值不变性不冲突。

s := "abc"
s = "foo"                  // ok
a := const([1, 2, 3])
a = false                  // ok

请注意,如果复制(使用“copy”内置函数)常量值将返回一个“可变”副本。此外,不变性不适用于数组或映射值的各个元素,除非它们是显式生成的常量。

a := const({b: 4, c: [1, 2, 3]})
a.b = 5        // illegal
a.c[1] = 5     // ok: because 'a.c' is not const

a = const({b: 4, c: const([1, 2, 3])})
a.c[1] = 5     // illegal

Nil 值

在VDSL中,“nil”值可用于表示意外的或不存在的值:

  • 一个函数,它不返回或明确返回nil值,都被认为返回nil值。
  • 如果键或索引不存在,则复合值类型的索引器或选择器可能返回nil
  • 如果转换失败,没有默认值的类型转换内置函数将返回“nil”。
a := func() { b := 4 }()    // a == nil
b := [1, 2, 3][10]          // b == nil
c := {a: "foo"}["b"]        // c == nil
d := int("foo")             // d == nil

Array 值

在VDSL中,数组是任何类型的值的有序列表。可以使用索引器“[]”访问数组的元素。

[1, 2, 3][0]       // == 1
[1, 2, 3][2]       // == 3
[1, 2, 3][3]       // == nil

["foo", "bar", [1, 2, 3]]   // ok: array with an array element

Map 值

在VDSL中,map是一组键值对,其中key是字符串,value是任何值类型。可以使用索引器“[]”或选择器“”访问映射的值操作员。

m := { a: 1, b: false, c: "foo" }
m["b"]                                // == false
m.c                                   // == "foo"
m.x                                   // == nil

{a: [1,2,3], b: {c: "foo", d: "bar"}} // ok: map with an array element and a map element

Function 值

在VDSL中,函数是一个具有多个函数参数和一个返回值的可调用值。就像任何其他值一样,函数可以传递到另一个函数中,也可以从该函数返回。

my_func := func(arg1, arg2) {
  return arg1 + arg2
}

adder := func(base) {
  return func(x) { return base + x }  // capturing 'base'
}
add5 := adder(5)
nine := add5(4)    // == 9

与Go不同,VDSL没有声明。因此,以下代码是非法的:

func my_func(arg1, arg2) {  // illegal
  return arg1 + arg2
}

VDSL还支持可变函数/闭包:

variadic := func (a, b, ...c) {
  return [a, b, c]
}
variadic(1, 2, 3, 4) // [1, 2, [3, 4]]

variadicClosure := func(a) {
  return func(b, ...c) {
    return [a, b, c]
  }
}
variadicClosure(1)(2, 3, 4) // [1, 2, [3, 4]]

只有最后一个参数可以是可变的。以下代码也是非法的:

// illegal, because a is variadic and is not the last parameter
illegal := func(a..., b) { /*... */ }

调用函数时,传递参数的数量必须与该函数定义相匹配。

f := func(a, b) {}
f(1, 2, 3) // Runtime Error: wrong number of arguments: want=2, got=3

像Go一样,您可以使用省略号“…”要传递数组类型值作为其最后一个参数:

f1 := func(a, b, c) { return a + b + c }
f1([1, 2, 3]...)    // => 6
f1(1, [2, 3]...)    // => 6
f1(1, 2, [3]...)    // => 6
f1([1, 2]...)       // Runtime Error: wrong number of arguments: want=3, got=2

f2 := func(a, ...b) {}
f2(1)               // valid; a = 1, b = []
f2(1, 2)            // valid; a = 1, b = [2]
f2(1, 2, 3)         // valid; a = 1, b = [2, 3]
f2([1, 2, 3]...)    // valid; a = 1, b = [2, 3]

变量和范围

可以使用赋值运算符“:=”和“=”将值赋值给变量。

  • :=运算符在作用域中定义一个新变量并赋值。
  • =运算符为作用域中的现有变量分配一个新值。

变量在全局范围(在函数外部定义)或本地范围(在功能内部定义)中定义。

a := "foo"      // define 'a' in global scope

func() {        // function scope A
  b := 52       // define 'b' in function scope A

  func() {      // function scope B
    c := 19.84  // define 'c' in function scope B

    a = "bee"   // ok: assign new value to 'a' from global scope
    b = 20      // ok: assign new value to 'b' from function scope A

    b := true   // ok: define new 'b' in function scope B
                //     (shadowing 'b' from function scope A)
  }

  a = "bar"     // ok: assigne new value to 'a' from global scope
  b = 10        // ok: assigne new value to 'b'
  a := -100     // ok: define new 'a' in function scope A
                //     (shadowing 'a' from global scope)

  c = -9.1      // illegal: 'c' is not defined
  b := [1, 2]   // illegal: 'b' is already defined in the same scope
}

b = 25          // illegal: 'b' is not defined
a := {d: 2}     // illegal: 'a' is already defined in the same scope

与Go不同,可以为变量指定不同类型的值。

a := 123        // assigned    'int'
a = "123"       // re-assigned 'string'
a = [1, 2, 3]   // re-assigned 'array'

类型转换

虽然VDSL中没有直接指定类型,但可以使用类型转换在值类型之间进行转换。

s1 := string(1984)    // "1984"
i2 := int("-999")     // -999
f3 := float(-51)      // -51.0
b4 := bool(1)         // true
c5 := char("X")       // 'X'

运算符

一元运算符

运算符用法类型
+等同 0 + xint, float
-等同 0 - xint, float
!逻辑非all types*
^按位异或int

在VDSL中,所有值都可以是真值也可以是假值

二元运算符

运算符用法类型
==检查两个值是否相等all types
!=检查两个值是否不相等all types
&&逻辑 AND 运算符all types
\|\|逻辑 OR 运算符all types
+相加/字符串连接int, float, string, char, time, array
-相减int, float, char, time
*相乘int, float
/相除int, float
&按位与运算符int
\|按位或运算符int
^按位异或运算符int
&^位清除(AND NOT)int
<<左移运算符int
>>右移运算符int
<检查左边值是否小于右边值int, float, char, time, string
<=检查左边值是否小于等于右边值int, float, char, time, string
>检查左边值是否大于右边值int, float, char, time, string
>=检查左边值是否大于等于右边值int, float, char, time, string

三元运算符

VDSL有一个三元条件运算符(条件表达式)?(true表达式):(false表达式)

a := true ? 1 : -1    // a == 1

min := func(a, b) {
  return a < b ? a : b
}
b := min(5, 10)      // b == 5

赋值和增量运算符

运算符用法
+=(lhs) = (lhs) + (rhs)
-=(lhs) = (lhs) - (rhs)
*=(lhs) = (lhs) * (rhs)
/=(lhs) = (lhs) / (rhs)
%=(lhs) = (lhs) % (rhs)
&=(lhs) = (lhs) & (rhs)
\|=(lhs) = (lhs) \| (rhs)
&^=(lhs) = (lhs) &^ (rhs)
^=(lhs) = (lhs) ^ (rhs)
<<=(lhs) = (lhs) << (rhs)
>>=(lhs) = (lhs) >> (rhs)
++(lhs) = (lhs) + 1
--(lhs) = (lhs) - 1

操作符优先级

一元运算符具有最高优先级,三元运算符具有最低优先级。二进制运算符有五个优先级。

乘法运算符绑定最强,其次是加法运算符,比较运算符,“&&”(逻辑AND),最后是“||”(逻辑OR):

优先级操作符
5* / % << >> & &^
4+ - \| ^
3== != < <= > >=
2&&
1\|\|

与Go一样,“++”和“--”运算符形成语句,而不是表达式,它们位于运算符层次结构之外。

选择器和索引器

可以使用选择器(.)和索引器([])运算符来读取或写入复合类型(数组、映射、字符串、字节)的元素。

["one", "two", "three"][1]  // == "two"

m := {
  a: 1,
  b: [2, 3, 4],
  c: func() { return 10 }
}
m.a              // == 1
m["b"][1]        // == 3
m.c()            // == 10
m.x = 5          // add 'x' to map 'm'
m["b"][5]        // == nil
m["b"][5].d      // == nil
m.b[5] = 0       // == nil
m.x.y.z          // == nil

与Go一样,可以对数组、字符串、字节等序列值类型使用切片运算符“[:]”。

a := [1, 2, 3, 4, 5][1:3]    // == [2, 3]
b := [1, 2, 3, 4, 5][3:]     // == [4, 5]
c := [1, 2, 3, 4, 5][:3]     // == [1, 2, 3]
d := "hello world"[2:10]     // == "llo worl"
c := [1, 2, 3, 4, 5][-1:10]  // == [1, 2, 3, 4, 5]

注意:关键字不能用作选择器

a := {in: true} // Parse Error: expected map key, found 'in'
a.func = ""     // Parse Error: expected selector, found 'func'

使用双引号和索引器将关键字与映射一起使用。

a := {"in": true}
a["func"] = ""

语句

If 语句

“If”语句与Go非常相似。

if a < 0 {
  // execute if 'a' is negative
} else if a == 0 {
  // execute if 'a' is zero
} else {
  // execute if 'a' is positive
}

与Go一样,条件表达式前面可能有一个简单的语句,该语句在计算表达式之前执行。

if a := foo(); a < 0 {
  // execute if 'a' is negative
}

For 语句

“For”语句与Go非常相似。

// for (init); (condition); (post) {}
for a:=0; a<10; a++ {
  // ...
}

// for (condition) {}
for a < 10 {
  // ...
}

// for {}
for {
  // ...
}

For-In 语句

VDSL中新增了“for in”语句。它类似于Go的“for range”语句。

“For In”语句可以迭代任何可迭代的值类型(数组、映射、字节、字符串、nil)。

for v in [1, 2, 3] {          // array: element
  // 'v' is value
}
for i, v in [1, 2, 3] {       // array: index and element
  // 'i' is index
  // 'v' is value
}
for k, v in {k1: 1, k2: 2} {  // map: key and value
  // 'k' is key
  // 'v' is value
}

模块

模块是VDSL中的基本编译单元。一个模块可以使用“import”表达式导入另一个模块。

主模块:

sum := import("./sum")  // load module from a local file
fmt.print(sum(10))      // module function

“sum.dsl”文件中的另一个模块:

base := 5

export func(x) {
  return x + base
}

默认情况下,“import”将模块文件缺少的扩展名解决为".dsl"^note!!missing!!。 因此,“sum:=import("./sum")”相当于“sum:=import("./sum.dsl")”。

在VDSL中,模块与函数非常相似。

  • import 表达式加载模块代码并像函数一样执行它。
  • 模块应使用“export”语句返回一个值。
    • 模块可以返回“导出”任何类型的值:int、map、function等。
    • 模块中的export类似于函数中的return:它停止执行并导入代码返回一个值。
    • export的值总是常量。
    • 如果模块没有任何“export”语句,则“import”表达式只返回“nil”(就像没有“return”的函数一样。)
    • 请注意,如果代码作为主模块执行,则“export”语句将被完全忽略,并且不会进行求值。

此外,您还可以使用“import”表达式来加载标准库。

math := import("math")
a := math.abs(-19.84)  // == 19.84

Comments

与Go一样,VDSL支持行注释(//…)和块注释(/* ... */).

/*
  multi-line block comments
*/

a := 5    // line comments

与Go的区别

与Go不同,VDSL没有以下功能:

  • Declarations
  • Imaginary 值
  • Structs
  • Pointers
  • Channels
  • Goroutines
  • Tuple 赋值
  • Variable 参数
  • Switch 语句
  • Goto 语句
  • Defer 语句
  • Panic
  • Type 断言
Please wait...
Page is in error, reload to recover