@@ -21,7 +21,7 @@ GödelScript 编译器主要应用场景为:
2121
22221 . 面向用户编写简单或复杂查询,提供更便捷的写法,提高编写查询的效率;
23232 . 提供严格类型检查与类型推导,给予更智能的代码修改提示;
24- 3 . 提供严格的 [ ungrounded] ( #ungrounded-error ) 检测,避免容易触发的 Soufflé Ungrounded Error;
24+ 3 . 提供严格的 [ ungrounded(未赋值/未绑定) ] ( #ungrounded-error-未赋值未绑定错误 ) 检测,避免触发 Soufflé Ungrounded Error;
25254 . Language Server 以及 IDE Extension 支持。
2626
2727### 基本程序构成
@@ -105,11 +105,9 @@ GödelScript 采用类 C 语言的注释方式。
105105
106106#### ` main ` 函数
107107
108- GödelScript 查询脚本可以包含` main ` 函数,该函数无返回值。
108+ GödelScript 查询脚本可以包含` main ` 函数,该函数无返回值。在不实现 ` main ` 函数,且没有写 query 声明的情况下,程序不会输出。
109109
110- 在` main ` 中可以多次使用` output(...) ` 来表明要输出多个查询结果。
111-
112- ` main ` 函数只允许使用` output ` ,其他语句会导致编译错误。
110+ 更多详细内容请看 [ ` main ` 函数] ( #gödelscript-main-函数 ) 。
113111
114112``` rust
115113fn main () {
@@ -118,11 +116,9 @@ fn main() {
118116}
119117```
120118
121- 在不实现` main ` 函数,且没有写 query 声明的情况下,程序不会输出。
122-
123119### 基础类型和编译器内建函数
124120
125- GödelScript 包含基础类型` int``string ` ,` bool ` 属于基础类型,但是不能作为值存储。
121+ GödelScript 包含基础类型` int ` ` string ` ,` bool ` 属于基础类型,但是不能作为值存储。
126122
127123#### ` int ` 类型 native 函数
128124
@@ -204,7 +200,7 @@ GödelScript 包含基础类型`int``string`,`bool`属于基础类型,但是
204200| to<T > | (self) -> T | 转换到其他类型的 schema,采用 duck type 检测。 |
205201| is<T > | (self) -> bool | 判断是否可以是其他类型的 schema,采用 duck type 检测。如果自身 schema 有主键,则底层只会通过主键判断是否可以是其他类型。 |
206202| key_eq | (self, T) -> bool | 检查两个 schema 实例的主键是否相等。 |
207- | key_neq | (self, T) -> bool | 检查两个 schema 实例的主键是否 ** 不 ** 相等 。 |
203+ | key_neq | (self, T) -> bool | 检查两个 schema 实例的主键是否不等 。 |
208204
209205schema native 函数实例:
210206
@@ -232,42 +228,101 @@ fn convert() -> *ElementParent {
232228
233229### 函数
234230
235- #### `main` 函数
231+ #### G ödelScript `main` 函数
236232
237- 在上文中已经提及,该函数是 G ödelScript 中唯一不需要声明返回值的函数 。
233+ `main`函数是 G ödelScript 中唯一不声明返回值的函数。`main`函数只允许使用`output`,其他语句会导致编译错误;多次使用`output( ... )`可以输出多个查询结果,查询结果会分表显示,表名即为`output`中调用的查询函数的函数名 。
238234
239235#### 查询函数
240236
241237查询函数的返回值类型推荐为`bool `,需要输出查询结果时,需要使用`output()`函数。
242238
239+ 在`output()`中调用的查询函数不再是常规思路中的用传参调用函数。参数列表在此时会变化为输出表的表结构,下面是两个查询函数的应用实例:
240+
241+ 1. 单表`output`
242+
243+ 单表`output`特指在`main`函数中,只使用一次`output`来输出。
244+
245+ ```rust
246+ fn example(a: int, b: string) -> bool {... }
247+
248+ fn main() {
249+ output(example()) // 此时参数列表变为输出表结构,不需要传参
250+ }
251+ ```
252+
253+ 对应的输出表结构为:
254+
255+ ```json
256+ [
257+ {"a": 0, "b": "xxx"},
258+ {"a": 1, "b": "xxx"}
259+ ]
260+ ```
261+
262+ 2. 多表`output`
263+
264+ 多表`output`是指在`main`函数中,使用多次`output`来输出。在这种情况下,输出数据会附带对应的表名。
265+
266+ ```rust
267+ fn example0(a: int, b: string) -> bool {... }
268+ fn example1(a: string, b: int) -> bool {... }
269+
270+ fn main() {
271+ output(example0())
272+ output(example1())
273+ }
274+ ```
275+
276+ 对应的输出表结构为:
277+
278+ ```json
279+ {
280+ "example0": [
281+ {"a": 0, "b": "xxx"},
282+ {"a": 1, "b": "xxx"}
283+ ],
284+ "example1": [
285+ {"a": "xxx", "b": 0},
286+ {"a": "xxx", "b": 1}
287+ ]
288+ }
289+ ```
290+
291+ 下面是一个比较详细的例子,在这个例子中,我们直接构造了两组数据并输出。在下列代码中,需要注意的是:
292+
293+ 1. G ödelScript 中,布尔值可以使用`true`和`false`关键字。
294+
295+ 2. `= `符号在 G ödelScript 中是比较特殊的符号,不能用常规的编程语言的思路来理解。G ödelScript 是一种 Datalog 语言。在这里,`= `符号同时具备两种语义,一个是 __赋值__ 一个是 __判等__。详情可看[`= `运算符](#赋值和判等运算符)。
296+
297+ 3. 在这个例子的条件语句中,`a`和`b`均使用了`= `的赋值语义,因为`int`和`string`类型参数在函数体中被认为是`ungrounded(未赋值/ 未绑定)`,必须要被赋值才能使用。
298+
299+ 4. `= `赋值语句的返回值是`true`。
300+
243301```rust
244- fn myQuery(a: int, b: string) -> bool {
245- if (a = 1 && b = "hello") {
302+ fn example(a: int, b: string) -> bool {
303+ // = 符号同时具有赋值和比较的功能,取决于此时的左值是否已经“被赋值”
304+ // 这里的 a 和 b 所用的 = 符号均是赋值语义
305+ if (a = 1 && b = "1") {
306+ // GödelScript 使用关键字 true 和 false 来表示布尔值
246307 return true
247308 }
248- if (... ) {
249- ...
309+ if (a = 2 && b = "2" ) {
310+ return true
250311 }
251- ...
252312}
253313
254314fn main() {
255- output(myQuery ()) // 这里无需填写传入参数
315+ output(example ())
256316}
257317```
258318
259- `output()`会根据该函数的参数列表格式来输出表结构。所以在`main`中使用时,`output()`并不需要你提供这个函数的传入参数。对应的输出表结构大致如下 :
319+ 预期的输出结果应该为 :
260320
261- | a | b |
262- | --- | --- |
263- | 1 | "hello" |
264- | ... | ... |
265-
266- G ödelScript 使用`true`和`false`关键字来代表返回的`bool `类型结果:
267-
268- ```rust
269- return true
270- return false
321+ ```json
322+ [
323+ {"a": 1, "b": "1"},
324+ {"a": 2, "b": "2"}
325+ ]
271326```
272327
273328#### 普通函数
@@ -352,11 +407,70 @@ if (f.getName().contains("util") || f.getName().contains("com")) {
352407
353408条件可以使用这些逻辑运算符进行连接:`! `取反,`|| `或,`&& `与。
354409
355- 条件中的比较运算符:`>`大于,`<`小于,`>= `大于等于,`<= `小于等于,`= `等于 ,`!= `不等于。
410+ 条件中的比较运算符:`>`大于,`<`小于,`>= `大于等于,`<= `小于等于,`= `等于或者赋值 ,`!= `不等于。
356411
357412常规算术运算可以使用如下运算符:`+ `加法,`- `减法/ 取负,`* `乘法,`/ `除法。
358413
359- ** 注意:比较运算符中的** `* *= ** `** 在左侧变量没有被绑定数据时,会执行绑定操作并返回** `* * true** `** ,类似于赋值操作。**
414+ ##### 赋值和判等运算符`= `
415+
416+ `= `符号在 G ödelScript 中具有两种不同的语义:赋值和判等,具体的语义需要分情况进行讨论:
417+
418+ 1. 赋值
419+
420+ 赋值一般出现在`int` `string`这类基础类型的变量参数上,这类变量作为函数的参数出现时,一般被认为是未赋值的。而具有这类变量的函数被调用时,传入的参数,实际上是作为筛选条件存在。
421+
422+ ```rust
423+ fn example(a: int) -> bool {
424+ // 这里比较反直觉,在过程式语言中,这里通常会被认为是判断 a == 1
425+ // 但是在 datalog 方言中,datalog 的每个函数实际上都是在算一个中间表 (view)
426+ // 所以这个函数本质上是生成了一个 view,数据为 [{"a": 1}]
427+ return a = 1 // assign a = 1
428+ }
429+
430+ fn test() -> bool {
431+ // 这里看似是在通过传参让 a = 2,实际上并不是
432+ // example() 自己会返回 view: [{"a": 1}]
433+ // 然后通过 a = 2 来约束结果,可以看到,我们这里没有拿到任何结果
434+ // 所以返回了 false
435+ return example(2) // false
436+ }
437+ ```
438+
439+ 2. 判等
440+
441+ 对于 schema 类型来说,任何一个 schema 背后都有一个全集,所以参数列表中的 schema 类型一般被认为是已经被赋值的。对于已经赋值的变量来说,`= `就是判等操作。
442+
443+ ```rust
444+ // 声明 schema
445+ schema A {... }
446+
447+ // 实现 schema 的成员函数
448+ impl A {
449+ // 这里定义了 schema A 的全集
450+ @ data_constraint
451+ pub fn __all__() -> * A {... }
452+ }
453+
454+ fn example(a: A ) -> bool {
455+ for (temp in A :: __all__()) {
456+ if (a = temp) {
457+ return true
458+ }
459+ }
460+ }
461+ ```
462+
463+ 同样,对于中间声明的有初始值的`int`或者`string`,`= `也是判等操作。
464+
465+ ```rust
466+ fn example() -> bool {
467+ let (a = 1) { // assign a = 1
468+ if (a = 1) { // compare a = 1
469+ return true
470+ }
471+ }
472+ }
473+ ```
360474
361475#### match 语句
362476
@@ -944,9 +1058,9 @@ fn class_method(className: string, methodName: string, methodSignature: string)
9441058}
9451059```
9461060
947- ### Ungrounded Error
1061+ ### Ungrounded Error : 未赋值 / 未绑定错误
9481062
949- G ödelScript 会将未与集合绑定的符号判定为 `ungrounded`。基本判定规则为:
1063+ G ödelScript 会将未与数据绑定的符号判定为 `ungrounded(未赋值 / 未绑定) `。基本判定规则为:
9501064
9511065- 未初始化的/ 未被使用的/ 未与集合绑定的符号
9521066 - 未被绑定的`int``string`参数
@@ -2086,9 +2200,9 @@ e, p 的笛卡尔积就变成了 e, i 的笛卡尔积,从运算的层面来看
20862200### 不要滥用`@ inline`/ 必须用`@ inline`的优化策略
20872201
20882202inline 函数的底层机制是在** 调用处展开** ,如果该函数不存在大量的 schema 传参,并且在很多位置都被调用,inline 可能会导致** 编译结果膨胀且重复计算次数指数级增加** ,有时反而不利于减少运行时间。
2089- 如果存在必须要使用 inline 的情况 (比如规避 ungrounded),但是使用之后反而出现运行速度变慢的情况,可以采取将内嵌语句拆分为 predicate 的方式来避免展开导致的编译结果膨胀。
2203+ 如果存在必须要使用 inline 的情况 (比如规避` ungrounded` ),但是使用之后反而出现运行速度变慢的情况,可以采取将内嵌语句拆分为 predicate 的方式来避免展开导致的编译结果膨胀。
20902204
2091- 下面的例子中,`getValueByAttributeNameByDefaultValue`为了避免`attributeName`被识别为 ungrounded 所以标注 inline,后续在 if 分支中添加了一个条件语句,但是导致了执行时间从 3 秒变成 35 秒:
2205+ 下面的例子中,`getValueByAttributeNameByDefaultValue`为了避免`attributeName`被识别为` ungrounded` 所以标注` inline` ,后续在 if 分支中添加了一个条件语句,但是导致了执行时间从 3 秒变成 35 秒:
20922206
20932207```rust
20942208impl XmlElementBase {
0 commit comments