OPA 文档模型

OPA将从外部加载的数据成为基本文档(base documents),有规则产生的值成为虚拟文档(virtual documents),此处"虚拟"的意思表示文档由策略进行了计算,且不是外部加载的。Rego中可以使用名为data的全局变量访问这两种数据。

异步加载的基本文档可以通过data全局变量进行访问。另一方面,如果软件需要查询OPA来获取策略决策时,也可以将基础文档同步推入或拉入OPA,此时需要通过input全局变量来引用同步推送的基本文档。同步加载的数据保存在data之外,防止命名冲突。

逻辑与

{
"servers": [
{"id": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
{"id": "db", "protocols": ["mysql"], "ports": ["p3"]},
{"id": "cache", "protocols": ["memcache"], "ports": ["p3"]},
{"id": "ci", "protocols": ["http"], "ports": ["p1", "p2"]},
{"id": "busybox", "protocols": ["telnet"], "ports": ["p1"]}
],
"networks": [
{"id": "net1", "public": false},
{"id": "net2", "public": false},
{"id": "net3", "public": true},
{"id": "net4", "public": true}
],
"ports": [
{"id": "p1", "network": "net1"},
{"id": "p2", "network": "net3"},
{"id": "p3", "network": "net2"}
]
}

OPA使用;来表示逻辑AND

input.servers[0].id == "app"; input.servers[0].protocols[0] == "https"

$ true

也可以使用多行来忽略;

input.servers[0].id == "app"
input.servers[0].protocols[0] == "https" $ rue

如果引用的内容不存在或匹配失败,则返回的结果为undefined

s := input.servers[0]
s.id == "app1" $undefined decision
s := input.servers[1110]

s.id == "app1"
$ undefined decision

变量

OPA的变量一旦赋值之后就是不可变的:

s := input.servers[0]
s := input.servers[1] $ 1 error occurred: 2:1: rego_compile_error: var s assigned above

迭代

找出连接到public网络的portsid,下面使用some关键字定义循环的变量,使用id变量提取符合要求的portsid,最后一行input.networks[j].public也可以写为input.networks[j].public==true,同时注意条件之间是逻辑与的关系

some i, j
id := input.ports[i].id
input.ports[i].network == input.networks[j].id
input.networks[j].public $ +---+------+---+
| i | id | j |
+---+------+---+
| 1 | "p2" | 2 |
+---+------+---+

可以使用下划线_(通配符)进行遍历,使用下划线来表示实例的单独变量。如使用如下方式找出input.servers中协议为httpid

s := input.servers[_]
id := s.id
s.protocols[_] == "http" $ true

规则(Rules)

使用规则可以重用判定逻辑,可以看作是一种函数实现。规则可以是"完整(complete)"或"部分(partial)"的。

完整规则

每个规则都包含一个head和一个body,如下headany_public_networks = truebodynet := input.networks[_]; net.public。如果忽略= <value>部分(= <value>用于使用右值赋予左边的变量,如apps_by_hostname[hostname] = app,key为hostname,value为app),则默认为true

package example.rules

any_public_networks = true {  # is true if...
net := input.networks[_] # some network exists and..
net.public # it is public.
}

可以为规则定义默认值,这样在结果返回"undefined decision"时,会将any_public_networks置为false

package example.rules

default any_public_networks = false
any_public_networks = true { # is true if...
net := input.no_exist_networks[_] # some network exists and..
net.public # it is public.
} any_public_networks $ false

可以使用如下方式进行访问:

any_public_networks

$ true

也可以使用全局变量data进行访问,访问方式为data.<package-path>.<rule-name>

data.example.rules.any_public_networks

$ true

可以使用如下方式定义常量

package example.constants

pi := 3.14

部分规则

headpublic_network[net.id]bodynet := input.networks[_]; net.public,可以看作是带返回值的函数

package example.rules

public_network[net.id] {      # net.id is in the public_network set if...
net := input.networks[_] # some network exists and...
net.public # it is public.
}

可以遍历返回值

public_network[_]

$ +-------------------+
| public_network[_] |
+-------------------+
| "net3" |
| "net4" |
+-------------------+

逻辑或

完整规则的逻辑或

使用逻辑或时,要求多条规则的名称相同。如下用于校验servers是否暴露了telnetssh协议:

package example.logical_or

default shell_accessible = false

shell_accessible = true {
input.servers[_].protocols[_] == "telnet"
} shell_accessible = true {
input.servers[_].protocols[_] == "ssh"
}
部分规则的逻辑或

如下规则用于找出协议为telnetssh的server的id

package example.logical_or

shell_accessible[server.id] {
server := input.servers[_]
server.protocols[_] == "telnet"
} shell_accessible[server.id] {
server := input.servers[_]
server.protocols[_] == "ssh"
}

rego语法

标量

rego支持字符串、数字、布尔和null

greeting   := "Hello"
max_height := 42
pi := 3.14159
allowed := true
location := null

字符串

支持两种类型的字符串:双引号包围的字符串和原始字符串,后者一般用于正则表达式。

复合值

定义了数值集合

cube := {"width": 3, "height": 4, "depth":true}
cube.depth

$ true

对象

可以看作golang的map

ips_by_port := {
80: ["1.1.1.1", "1.1.1.2"],
443: ["2.2.2.1"],
}
ips_by_port[80]

$ [
"1.1.1.1",
"1.1.1.2"
]

数组

数组使用下标进行索引

s1 := [1, 2, 3]
s2 := [2, 1, 3]
s1[1]
$ 2
s1 == s2
$ false

集合

它是唯一值的无序集合。它没有key,且无法使用下标进行索引。注意在解析为JSON格式时,集合体现为数组格式。

s1 := {1, 2, 3}
s2 := {2, 1, 3}
s1 == s2
$ true

变量

变量位于规则的head和body,规则head中的变量可以认为是输入或输出,如果提供了确定的值,则认为是输入,否则认为是输出。例如:

sites := [
{"name": "prod"},
{"name": "smoke1"},
{"name": "dev"}
] q[name] { name := sites[_].name }

如下x并没有绑定到某个值,则返回所有xq[x]的值

q[x]
$ +----------+----------+
| x | q[x] |
+----------+----------+
| "dev" | "dev" |
| "prod" | "prod" |
| "smoke1" | "smoke1" |
+----------+----------+

如下"dev"是一个确定的值,作为输入,用于判断name中是否存在该值

q["dev"]
$ "dev"

引用

有两种方式访问嵌套文档:点访问方式和方括号访问方式,如下:

sites[0].servers[1].hostname
sites[0]["servers"][1]["hostname"]

变量键

引用可以使用变量作为键,这种方式用于选择所有元素的值

sites[i].servers[j].hostname

$ +---+---+------------------------------+
| i | j | sites[i].servers[j].hostname |
+---+---+------------------------------+
| 0 | 0 | "hydrogen" |
| 0 | 1 | "helium" |
| 0 | 2 | "lithium" |
| 1 | 0 | "beryllium" |
| 1 | 1 | "boron" |
| 1 | 2 | "carbon" |
| 2 | 0 | "nitrogen" |
| 2 | 1 | "oxygen" |
+---+---+------------------------------+

如果迭代时不需要用到变量,则可以使用下划线_

sites[_].servers[_].hostname
$ +------------------------------+
| sites[_].servers[_].hostname |
+------------------------------+
| "hydrogen" |
| "helium" |
| "lithium" |
| "beryllium" |
| "boron" |
| "carbon" |
| "nitrogen" |
| "oxygen" |
+------------------------------+

复合键

s := {[1, 2], [1, 4], [2, 6]}
s[[1, 2]]
$ [
1,
2
]
s[[1, x]]
$ +---+-----------+
| x | s[[1, x]] |
+---+-----------+
| 2 | [1,2] |
| 4 | [1,4] |
+---+-----------+

多表达式

规则通常是多表达式的,包含到documents的引用。下面定义了一个数组,每个数组包含一个服务的应用名称和主机名称

apps_and_hostnames[[name, hostname]] {
some i, j, k
name := apps[i].name
server := apps[i].servers[_]
sites[j].servers[k].name == server
hostname := sites[j].servers[k].hostname
}
apps_and_hostnames[x]
$ +----------------------+-----------------------+
| x | apps_and_hostnames[x] |
+----------------------+-----------------------+
| ["mongodb","oxygen"] | ["mongodb","oxygen"] |
| ["mysql","carbon"] | ["mysql","carbon"] |
| ["mysql","lithium"] | ["mysql","lithium"] |
| ["web","beryllium"] | ["web","beryllium"] |
| ["web","boron"] | ["web","boron"] |
| ["web","helium"] | ["web","helium"] |
| ["web","hydrogen"] | ["web","hydrogen"] |
| ["web","nitrogen"] | ["web","nitrogen"] |
+----------------------+-----------------------+

推导式

与规则类似,推导式有一个head和一个body。

region := "west"
names := [name | sites[i].region == region; name := sites[i].name]
$ +-----------------+--------+
| names | region |
+-----------------+--------+
| ["smoke","dev"] | "west" |
+-----------------+--------+

这与python中的推导式类似

# Python equivalent of Rego comprehension shown above.
names = [site.name for site in sites if site.region == "west"]

数组推导式

格式如下:

[ <term> | <body> ]
app_to_hostnames[app_name] = hostnames {
app := apps[_]
app_name := app.name
hostnames := [hostname | name := app.servers[_]
s := sites[_].servers[_]
s.name == name
hostname := s.hostname]
}
app_to_hostnames[app]
$ +-----------+------------------------------------------------------+
| app | app_to_hostnames[app] |
+-----------+------------------------------------------------------+
| "mongodb" | ["oxygen"] |
| "mysql" | ["lithium","carbon"] |
| "web" | ["hydrogen","helium","beryllium","boron","nitrogen"] |
+-----------+------------------------------------------------------+

对象推导式

格式如下:

{ <key>: <term> | <body> }

注意key不能有冲突

app_to_hostnames := {app.name: hostnames |
app := apps[_]
hostnames := [hostname |
name := app.servers[_]
s := sites[_].servers[_]
s.name == name
hostname := s.hostname]
}
app_to_hostnames[app]
$ +-----------+------------------------------------------------------+
| app | app_to_hostnames[app] |
+-----------+------------------------------------------------------+
| "mongodb" | ["oxygen"] |
| "mysql" | ["lithium","carbon"] |
| "web" | ["hydrogen","helium","beryllium","boron","nitrogen"] |
+-----------+------------------------------------------------------+

集合推导式

格式如下:

{ <term> | <body> }
a := [1, 2, 3, 4, 3, 4, 3, 4, 5]
b := {x | x = a[_]}
$ +---------------------+-------------+
| a | b |
+---------------------+-------------+
| [1,2,3,4,3,4,3,4,5] | [1,2,3,4,5] |
+---------------------+-------------+

规则

集合

返回结果是一个集合

hostnames[name] { name := sites[_].servers[_].hostname }
hostnames[name]
$ +-------------+-----------------+
| name | hostnames[name] |
+-------------+-----------------+
| "beryllium" | "beryllium" |
| "boron" | "boron" |
| "carbon" | "carbon" |
+-------------+-----------------+
对象

返回结果是一个可检索的对象

apps_by_hostname[hostname] = app {
some i
server := sites[_].servers[_]
hostname := server.hostname
apps[i].servers[_] == server.name
app := apps[i].name
}
apps_by_hostname["helium"]
$ "web"
增量定义

增量定义实际就是逻辑或

如下,将serverscontainers 数据抽象为 instances:

instances[instance] {
server := sites[_].servers[_]
instance := {"address": server.hostname, "name": server.name}
} instances[instance] {
container := containers[_]
instance := {"address": container.ipaddress, "name": container.name}
}
完整定义

除了使用部分规则定义集合和对象,还可以使用完整规则,完整规则忽略了head中的key,通常用于表示常量

pi := 3.14159

完整定义一次性赋予一个值,如下将32和4赋值给max_memory就会发生错误

# Power users get 32GB memory.
max_memory = 32 # Restricted users get 4GB memory.
max_memory = 4 $ module.rego:8: eval_conflict_error: complete rules must not produce multiple outputs

使用:=时,每个包中只能声明一个相同名称的完整定义:

package example

pi := 3.14

# some other rules...

pi := 3.14156   # Redeclaration error because 'pi' already declared above.
函数

Rego支持自定义函数,这些函数可以与内置函数一样调用。函数可以有任意多个输入,但只能有一个输出

trim_and_split(s) = x {
t := trim(s, " ")
x := split(t, ".")
}
trim_and_split(" foo.bar ")
$ [
"foo",
"bar"
]

一个函数可以定义多次,用于实现通过条件来选择所要执行的函数:

q(1, x) = y {
y := x
}
q(2, x) = y {
y := x*4
}
q(1, 2)
$ 2
q(2, 2)
$ 8

但在调用时需要注意,入参不能匹配多个函数

r(1, x) = y {
y := x
} r(x, 2) = y {
y := x*4
}
r(1, 2)
$ module.rego:3: eval_conflict_error: functions must not produce multiple outputs for same inputs

注意,如果无法匹配到函数,则结果是未定义的:

s(x, 2) = y {
y := x * 4
}
s(5, 3)
$ undefined decision

否定

t {
greeting := "hello"
not greeting == "goodbye"
}
t
$ true

下面用于分别找出在和不在prod环境的app:

prod_servers[name] {
site := sites[_]
site.name == "prod"
name := site.servers[_].name
} apps_in_prod[name] {
app := apps[_]
server := app.servers[_]
prod_servers[server] #过滤出在prod的app,行与行之间是与的关系,如果不存在则不会执行下一个语句,即name不会被赋值
name := app.name
} apps_not_in_prod[name] {
name := apps[_].name
not apps_in_prod[name]
}

全量(FOR ALL)

Rego没有直接的方式来表示全量("FOR ALL")。例如需要找出名称非"bitcoin-miner"的app时,使用如下方式是错误的,无论apps中是否存在名为"bitcoin-miner"的app,最终都会返回true

no_bitcoin_miners {
app := apps[_]
app.name != "bitcoin-miner" # THIS IS NOT CORRECT.
}

可以使用如下方式来实现上述目的:

no_bitcoin_miners_using_negation {
not any_bitcoin_miners
} any_bitcoin_miners {
some i
app := apps[i]
app.name == "bitcoin-miner"
}

此外还可以使用推导式实现:

no_bitcoin_miners_using_comprehension {
bitcoin_miners := {app | app := apps[_]; app.name == "bitcoin-miner"}
count(bitcoin_miners) == 0
}

模块

在rego中,策略被定义在模块中,一个模块需要包含:

注释

使用#进行注释

package

包可以将一个或多个模块中的规则打包到特定的命名空间中。

import

模块中可以使用datainput引用文本

如在 kubernetes.admission中定义了一个规则 deny:

package kubernetes.admission

deny[msg] {
input.request.kind.kind == "Pod"
some i
image := input.request.object.spec.containers[i].image
not startswith(image, "hooli.com/")
msg := sprintf("image '%v' comes from untrusted registry", [image])
}

在另一个包中可以通过如下方式引用deny规则:

{
"user": "alice",
"action": "read",
"object": "id123",
"type": "dog"
}
package app.rbac
import data.kubernetes.admission deny[input.user]

some关键字

With 关键字

with关键字允许查询以编程方式指定嵌套在input 文档data 文档下的值。with关键字充当表达式的修饰符。一个表达式可以有零或多个with修饰符。

格式如下,必须是对input文档或data文档中的值的引用。当应用于data文档时,不能尝试部分定义虚拟文档(例如一个给定的虚拟文档路径为data.foo.bar,如果策略试图替换data.foo.bar.baz,那么编译器将产生错误)。

<expr> with <target-1> as <value-1> [with <target-2> as <value-2> [...]]

举例如下:

allow with input as {"user": "charlie", "method": "GET"} with data.roles as {"dev": ["charlie"]}

with关键字仅影响连接表达符,后续表达式将看到未修改的值。下面是一种例外(input.foo=1,input.bar=2),outer中的输入在middle中进行了计算

inner := [x, y] {
x := input.foo
y := input.bar
} middle := [a, b] {
a := inner with input.foo as 100
b := input
} outer := result {
result := middle with input as {"foo": 200, "bar": 300} #middle中修改了a
}
{
"inner": [
1,
2
],
"middle": [
[
100,
2
],
{
"bar": 2,
"foo": 1
}
],
"outer": [
[
100,
300
],
{
"bar": 300,
"foo": 200
}
]
}

Default 关键字

default关键字允许策略为具有完整定义的规则生成的文档定义默认值。格式如下:

default <name> = <term>

用法如下,如果没有default,则会返回undefined

default allow = false

allow {
input.user == "bodddb"
input.method == "GEdddT"
} allow {
input.user == "aliddce"
}

Else 关键字

与编程语言中的else类似

authorize = "allow" {
input.user == "superuser" # allow 'superuser' to perform any operation.
} else = "deny" {
input.path[0] == "admin" # disallow 'admin' operations...
input.source_network == "external" # from external networks.
} # ... more rules

操作符

成员和迭代:in

需要import future.keywords。成员操作符in用于检查一个元素是否存在于array, set, 或 object中,返回truefalse

import future.keywords.in

p = [x, y, z] {
x := 3 in [1, 2, 3] # array
y := 3 in {1, 2, 3} # set
z := 3 in {"foo": 1, "bar": 3} # object
}
{
"p": [
true,
true,
true
]
}

当在in操作符左侧提供两个参数,且右侧为object或array,则第一个参数作为key(object)或index(array):

import future.keywords.in

p := [ x, y ] {
x := "foo", "bar" in {"foo": "bar"} # key, val with object
y := 2, "baz" in ["foo", "bar", "baz"] # key, val with array
}
{
"p": [
true,
true
]
}

注意在列表(如集合或数组以及函数参数)上下文中需要使用圆括号来让两侧参数一一对应

import future.keywords.in

p := x {
x := { 0, 2 in [2] } #这是一个集合,表示0和2 in [2]
}
q := x {
x := { (0, 2 in [2]) }#这是一个集合,但计算的是0, 2 in [2]
}
w := x {
x := g((0, 2 in [2]))#g(x)只有一个参数,需要使用圆括号括起来
}
z := x {
x := f(0, 2 in [2])#f(x)有两个参数,第一个参数是0,第二个参数是2 in [2]
} f(x, y) = sprintf("two function arguments: %v, %v", [x, y])
g(x) = sprintf("one function argument: %v", [x])

not结合使用,可以很方便地断言一个元素是否是数组的成员:

import future.keywords.in

deny {
not "admin" in input.user.roles
} test_deny {
deny with input.user.roles as ["operator", "user"]
}
{
"test_deny": true
}

使用some,可以根据不同的类型引入新的变量

import future.keywords.in

p[x] {
some x in ["a", "r", "r", "a", "y"]
} q[x] {
some x in {"s", "e", "t"}
} r[x] {
some x in {"foo": "bar", "baz": "quz"}
}
{
"p": [
"a",
"r",
"y"
],
"q": [
"e",
"s",
"t"
],
"r": [
"bar",
"quz"
]
}

使用两个参数可以检索object的关键字和array的索引

import future.keywords.in

p[x] {
some x, "r" in ["a", "r", "r", "a", "y"] # key variable, value constant
} q[x] = y {
some x, y in ["a", "r", "r", "a", "y"] # both variables
} r[y] = x {
some x, y in {"foo": "bar", "baz": "quz"}
}
{
"p": [
1,
2
],
"q": {
"0": "a",
"1": "r",
"2": "r",
"3": "a",
"4": "y"
},
"r": {
"bar": "foo",
"quz": "baz"
}
}

some变量的任何参数都可以是复合的非基础值:

import future.keywords.in

p[x] = y {
some x, {"foo": y} in [{"foo": 100}, {"bar": 200}]#x为key为foo的数组索引,y为key为foo的值
}
p[x] = y {
some {"bar": x}, {"foo": y} in {{"bar": "b"}: {"foo": "f"}} # x为bar的值,y为foo的值
}
{
"p": {
"0": 100,
"b": "f"
}
}

等式:赋值,比较和联合

Rego支持三种等式:赋值(:=),比较()和联合(=)。建议使用赋值(:=)和比较()。

赋值 :=

可以使用一种简单的解构形式将数组中的值解包并将其分配给变量

address := ["3 Abbey Road", "NW8 9AY", "London", "England"]

in_london {
[_, _, city, country] := address
city == "London"
country == "England"
}
{
"address": [
"3 Abbey Road",
"NW8 9AY",
"London",
"England"
],
"in_london": true
}
比较 ==
联合 =

Rego会将比较为真的值赋于变量。联合可以赋予变量使表达式为true的值。

sites[i].servers[j].name = apps[k].servers[m]
+---+---+---+---+
| i | j | k | m |
+---+---+---+---+
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 |
| 0 | 2 | 1 | 0 |
| 1 | 0 | 0 | 2 |
| 1 | 1 | 0 | 3 |
| 1 | 2 | 1 | 1 |
| 2 | 0 | 0 | 4 |
| 2 | 1 | 2 | 0 |
+---+---+---+---+

比较表达式

a  ==  b  #  `a` is equal to `b`.
a != b # `a` is not equal to `b`.
a < b # `a` is less than `b`.
a <= b # `a` is less than or equal to `b`.
a > b # `a` is greater than `b`.
a >= b # `a` is greater than or equal to `b`.

内置函数

内置函数的格式如下:

<name>(<arg-1>, <arg-2>, ..., <arg-n>)

错误

默认情况下,遇到运行时错误的内置函数调用会将结果设为undefined (通常可以被视为false),且不会停止策略计算。这种方式可以保证在使用调用内置函数时,输入无效参数不会导致整个策略停止计算。

策略相关

Assignment and Equality

# assign variable x to value of field foo.bar.baz in input
x := input.foo.bar.baz # check if variable x has same value as variable y
x == y # check if variable x is a set containing "foo" and "bar"
x == {"foo", "bar"} # OR {"foo", "bar"} == x

Lookup

Arrays
# lookup value at index 0
val := arr[0] # check if value at index 0 is "foo"
"foo" == arr[0] # find all indices i that have value "foo"
"foo" == arr[i] # lookup last value
val := arr[count(arr)-1] # with `import future.keywords.in`
some 0, val in arr # lookup value at index 0
0, "foo" in arr # check if value at index 0 is "foo"
some i, "foo" in arr # find all indices i that have value "foo"
Objects
# lookup value for key "foo"
val := obj["foo"] # check if value for key "foo" is "bar"
"bar" == obj["foo"] # OR "bar" == obj.foo # check if key "foo" exists and is not false
obj.foo # check if key assigned to variable k exists
k := "foo"
obj[k] # check if path foo.bar.baz exists and is not false
obj.foo.bar.baz # check if path foo.bar.baz, foo.bar, or foo does not exist or is false
not obj.foo.bar.baz # with `import future.keywords.in`
o := {"foo": false}
# check if value exists: the expression will be true
false in o
# check if value for key "foo" is false
"foo", false in o
Sets
# check if "foo" belongs to the set
a_set["foo"] # check if "foo" DOES NOT belong to the set
not a_set["foo"] # check if the array ["a", "b", "c"] belongs to the set
a_set[["a", "b", "c"]] # find all arrays of the form [x, "b", z] in the set
a_set[[x, "b", z]] # with `import future.keywords.in`
"foo" in a_set
not "foo" in a_set
some ["a", "b", "c"] in a_set
some [x, "b", z] in a_set

Iteration

Arrays
# iterate over indices i
arr[i] # iterate over values
val := arr[_] # iterate over index/value pairs
val := arr[i] # with `import future.keywords.in`
some val in arr # iterate over values
some i, _ in arr # iterate over indices
some i, val in arr # iterate over index/value pairs
Objects
# iterate over keys
obj[key] # iterate over values
val := obj[_] # iterate over key/value pairs
val := obj[key] # with `import future.keywords.in`
some val in obj # iterate over values
some key, _ in obj # iterate over keys
some key, val in obj # key/value pairs
Sets
# iterate over values
set[val] # with `import future.keywords.in`
some val in set
Advanced
# nested: find key k whose bar.baz array index i is 7
foo[k].bar.baz[i] == 7 # simultaneous: find keys in objects foo and bar with same value
foo[k1] == bar[k2] # simultaneous self: find 2 keys in object foo with same value
foo[k1] == foo[k2]; k1 != k2 # multiple conditions: k has same value in both conditions
foo[k].bar.baz[i] == 7; foo[k].qux > 3

For All

# assert no values in set match predicate
count({x | set[x]; f(x)}) == 0 # assert all values in set make function f true
count({x | set[x]; f(x)}) == count(set) # assert no values in set make function f true (using negation and helper rule)
not any_match # assert all values in set make function f true (using negation and helper rule)
not any_not_match
any_match {
set[x]
f(x)
} any_not_match {
set[x]
not f(x)
}

Rules

In the examples below ... represents one or more conditions.

Constants
a = {1, 2, 3}
b = {4, 5, 6}
c = a | b
Conditionals (Boolean)
# p is true if ...
p = true { ... } # OR p { ... }
Conditionals
default a = 1
a = 5 { ... }
a = 100 { ... }
Incremental
# a_set will contain values of x and values of y
a_set[x] { ... }
a_set[y] { ... } # a_map will contain key->value pairs x->y and w->z
a_map[x] = y { ... }
a_map[w] = z { ... }
Ordered (Else)
default a = 1
a = 5 { ... }
else = 10 { ... }
Functions (Boolean)
f(x, y) {
...
} # OR f(x, y) = true {
...
}
Functions (Conditionals)
f(x) = "A" { x >= 90 }
f(x) = "B" { x >= 80; x < 90 }
f(x) = "C" { x >= 70; x < 80 }

最新文章

  1. DMZ区
  2. MVC导出数据到EXCEL新方法:将视图或分部视图转换为HTML后再直接返回FileResult
  3. [改善Java代码]优先使用整型池
  4. 深度观察:腾讯收购大众点评背景下的O2O大格局
  5. bzoj1684 [Usaco2005 Oct]Close Encounter
  6. [Unity]蓝鸥Untiy-RPC-Sample
  7. VBA编程的工程性规划
  8. JeeSite数据分页与翻页
  9. 微信小程序地图控件篇 ---自定义图标被地图覆盖的问题
  10. hiho 1097 最小生成树一&#183;Prim算法 (最小生成树)
  11. 关于element-ui表格table设置header-cell-class-name样式不起作用的原因分析
  12. PTA-栈(括弧匹配)
  13. docker部署springboot项目
  14. ASP.NET技术总结
  15. windows下如何通过git bash获取gitlab ssh公钥
  16. JS中模态窗口(showModalDialog)的详细使用
  17. mysql INSERT的几个语法 IGNORE|REPLACE|LOW_PRIORITY | DELAYED
  18. 有关 WCF 的一些错误处理
  19. php可选缓存APC
  20. Spring DBCP用xml和properties2种格式配置DataSource

热门文章

  1. MySQL 尽量避免使用 TIMESTAMP
  2. matplotlib 进阶之Constrained Layout Guide
  3. Java Web程序设计笔记 • 【第3章 JSP内置对象】
  4. VMware 创建的虚拟机,Xshell无法进行连接
  5. 编写java程序压缩Linux本地目录
  6. SpringMVC拦截器的应用
  7. JSR-303规范
  8. Spring学习:简单实现一个依赖注入和循环依赖的解决
  9. 【PTA】6-1 计算两个复数之积 (10 分)
  10. 如何管理leader对你的能力预期?