@cnrs/hel 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -9
- package/package.json +2 -2
- package/src/ast2str.js +11 -8
- package/src/index.js +19 -0
- package/src/str2ast.js +2 -1
package/README.md
CHANGED
|
@@ -2,25 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
<a href='https://datasphere.readthedocs.io/projects/hel/'><img src='https://gitlab.huma-num.fr/datasphere/doc/assets/-/raw/main/banners/HEL.png' width='100%'></a>
|
|
4
4
|
|
|
5
|
-
](https://www.gnu.org/licenses/agpl-3.0.html)
|
|
6
|
+
[](https://www.repostatus.org/#wip)
|
|
7
|
+
[](https://gitlab.huma-num.fr/datasphere/hel/js/pipelines/latest)
|
|
8
|
+
[](https://datasphere.gitpages.huma-num.fr/hel/js/coverage/)
|
|
9
|
+
[](https://www.npmjs.com/package/@cnrs/hel)
|
|
10
|
+
[](https://datasphere.gitpages.huma-num.fr/hel/js/doc/)
|
|
11
|
+
[](https://doi.org/10.5281/zenodo.18413436)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
## What is this?
|
|
16
16
|
|
|
17
|
-
**Hel**
|
|
17
|
+
**Hel** makes easier to query databases in a generic way.
|
|
18
|
+
In practice, **Hel** is both the specification of a very simple query language, as long as a JavaScript module that allows you to:
|
|
19
|
+
|
|
20
|
+
- convert a query string to the corresponding [AST (Abstract Syntax Tree)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ;
|
|
21
|
+
- convert a query string to the corresponding filtering function, to then use it for example with [Heimdall](https://datasphere.readthedocs.io/projects/heimdall/) ;
|
|
22
|
+
- convert an AST to the corresponding query string.
|
|
23
|
+
|
|
24
|
+
For example, combined with [Heimdall.js](https://gitlab.huma-num.fr/datasphere/heimdall/js), **Hel.js** allows you to build web applications that query [Hera](https://datasphere.readthedocs.io/projects/hera/) databases by abstracting both the data retrieval and the data querying details.
|
|
25
|
+
… Which is what [Hecate](https://datasphere.readthedocs.io/projects/hera/) does, by the way.
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
## Why should I use it?
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
Well really y̴̖̿̓ö̶͕͕͝ǘ̶͖͍ ̵̺̫́s̵̼͝h̷͓͂ò̸̠ü̸͍̈́ḽ̷̋͊ḏ̷̽ṅ̷̘'̷̺́̈́ṭ̶̬́͋,̸͔̥̾ ̴̪̌ô̶̤̇r̴̳̅̀ ̶͚̫͒̕H̶̹̿͜e̵̛̞̒l̵̖͗ ̵̪͓͘m̷̰̦̾a̵̠̱̽y̴͙̝͑ ̷͈̙͌d̵̯͝ř̷̟̓ͅa̷̟͖͗i̸͔̮̎n̶̗̠͂͋ ̶̢̦̿̕y̶̖̼̎o̵̖͉͐̎ú̴̧̕r̶͙̳͆ ̸̟͈͋s̶̰̫̿̀o̵̲͕̐ṳ̸̠̋͠l̷͔̫͘ ̴̦̇̐ ̶̧̤̑̂̃͐ä̵̡̙́̀n̵̞͓̬̅d̷̛͕̞̈̂͘ ̸̧͖̘̘̀t̵͖̓̿̆͂ą̵̳̘͉̿͂̈̎k̴̳̘̖̉͆͠ě̴̠̫ ̴̛̞̏̈́̕i̷̬̜̻͗́͒̽͜t̸̘̻̓ ̴̮̎̇̓a̶̦̪̰͈̍̍͝l̶͕̣̙̆̓ő̴̭͊͌͑ǹ̵̡͖̼̗̚g̷͖̙̤̐̂ ̷̝̞̣͑͜ẗ̸̯́h̶̙̒̊e̶̟͓̖͆̄ͅ ̴̣̰͋t̸̙̭́ẅ̷̨̹́ͅẻ̵̗͍̑l̶̪̏͊͌v̴̼̣̾͑͜͠é̷͇͈̥̣́̇ ̸͈̤̱̗͌̃́r̶̬̼͓̕͝ͅi̷̪̎v̵͍͎̦͂͊̓e̴̗̠͖̍̅͝r̸̡̩̘͝s̷̢̝̞͆ ̸̮̒̎̏̕a̴̳̖̔́̕ḷ̷̨͇͙͌͂̓l̴͖̺̤͓͌̓ ̶̢̩̑̃̅t̷̤͙̙̅̓h̸̢̙̰̋ḙ̶͎̰̀ ̵̢̪̿̍w̶͈͚̺̋̃á̵͈̑̅y̷͇̍ ̷̧̠͍̈́̾͑͝t̴̨̨͕̪̎͋̂ȏ̸̳ͅ ̴̡͓̀̔̓̐t̵͖̖̤̽h̴̞͑́ȇ̵̹̰̫̞ ̸̧̻̻̻̌͗H̷̪̜̐́a̴͎̜͖̞͒̋̉̃l̵͖̀̿́͂ḽ̸͛͌ş̶̜̤͐͌ ̴̧̜̞̊̂o̴̥͚̩̐͜f̶̤̦́̔ ̵̳̰̈́͌ṫ̶̡̩͈h̸̳̳̯̏̈́́͒ḙ̶̣̈͊̎̕ ̴̢͓͇̬̍̽D̷̩̋̈́͝͝e̶̲̥̲̐̀͠a̶̪̐d̷͙̉͑ ̸̣̪̥͠f̷̨͈̰̎̿̓ͅỏ̶͓̬͉̘̀͝r̸̫͍̘̪̍͑̋̕ ̸̱̤̎a̵̺̻̿́ṅ̸̢̡̮̹͘̕ ̴̳̻̮̣̊̍̔e̷͚̠̜̠̽t̵̮͚͈̙̋ę̶̭͔͕͛r̶̼̿̋̕n̷̞̼͈̻̒ḭ̴͚̔͛͊͊ͅṱ̴̣͆͘y̸̤̝̘̲͝ ̴̧͓̩̈́̍͐̀ö̵́́̽͠ͅf̴̼͎̣̂̔̔ ̷̨̝͋̕s̸̫͐͝u̴̦͇̙̤̓f̷̥̙̆f̴̡̲̠̽́̎ě̶̡͍̀r̸̛̹͎̭͔̽i̷̖͕͋̚n̴͓̊́͜ģ̶͉̭̈́̅̌̇.̶̝̓̕ ̵̝̣̞͋̋̽̚
|
|
32
|
+
Anyways, what was I saying? Oh well, it's up to you I guess. ¯\\_(ツ)_/¯
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
|
|
@@ -33,6 +42,32 @@ pnpm install @cnrs/hel
|
|
|
33
42
|
```
|
|
34
43
|
You can add it as a dependency to your `package.json` file, as usual.
|
|
35
44
|
|
|
45
|
+
Once installed, you can start using it with a few simple calls:
|
|
46
|
+
```
|
|
47
|
+
import { * as hel } from '@cnrs/hel';
|
|
48
|
+
|
|
49
|
+
const query = '!(type != "cat") & (subtype == "bobcat" | danger > "high")';
|
|
50
|
+
const filter = hel.fromStringToFilter(query); // filter function
|
|
51
|
+
const tree = hel.fromStringToTree(query); // Abstract Syntax Tree (AST)
|
|
52
|
+
const equivalent_query = hel.fromTreeToString(tree); // equivalent to query
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
The equivalences between the HEL query language syntax and the corresponding [AST (Abstract Syntax Tree)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) JavaScript nodes :
|
|
57
|
+
|
|
58
|
+
| Name | Syntax | AST node | Atom? | Notes |
|
|
59
|
+
| --------- | ------------- | -------------------- | ----- | ------------------------------------------ |
|
|
60
|
+
| Entity |`eid VALUE` |`{ eid: 'VALUE'}` | No | An item of entity ID `VALUE`:String. |
|
|
61
|
+
| Attribute |`aid VALUE` |`{ aid: 'VALUE'}` | Yes | A metadata of attribute ID `VALUE`:String. |
|
|
62
|
+
| Property |`pid VALUE` |`{ pid: 'VALUE'}` | Yes | A metadata of property ID `VALUE`:String. |
|
|
63
|
+
| Number |`VALUE` |`VALUE` | Yes | A litteral numeric equals to `VALUE`. |
|
|
64
|
+
| String |`"VALUE"` |`"VALUE"` | Yes | A litteral string equals to `VALUE`. |
|
|
65
|
+
| Relation |`ATOM OP ATOM` |`{ left: ATOM, operator: OP, right: ATOM}`| No | A relation between a left and a right `ATOM`, using a binary relationnal operator `OP`. |
|
|
66
|
+
| And |`N¹ & N² & …` |`{ and: [ N¹, N², …]}`| No | True if all nodes `N¹`,`N²`,… are true. |
|
|
67
|
+
| Or |`N¹ \| N² \| …`|`{ or: [ N¹, N², …]}` | No | True if any node `N¹`,`N²`,… is true. |
|
|
68
|
+
| Not |`!( N )` |`{ not: N }` | No | The negated value of node `N`. |
|
|
69
|
+
| Parentheses |`( N )` |`N` | No | Used for priority between And and Or. |
|
|
70
|
+
|
|
36
71
|
|
|
37
72
|
|
|
38
73
|
## Is it documented?
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"https://medium.com/sapioit/why-having-3-numbers-in-the-version-name-is-bad-92fc1f6bc73c",
|
|
29
29
|
"https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e"
|
|
30
30
|
],
|
|
31
|
-
"version": "0.
|
|
31
|
+
"version": "0.5.0",
|
|
32
32
|
"keywords": [
|
|
33
33
|
"hera",
|
|
34
34
|
"hecate",
|
|
@@ -144,4 +144,4 @@
|
|
|
144
144
|
"lint": "exec eslint \"src/**/*.js\"",
|
|
145
145
|
"doc:html": "./node_modules/jsdoc/jsdoc.js -c .jsdoc.conf.json"
|
|
146
146
|
}
|
|
147
|
-
}
|
|
147
|
+
}
|
package/src/ast2str.js
CHANGED
|
@@ -16,7 +16,7 @@ function visit(ast, parentheses=false) {
|
|
|
16
16
|
return visit_(ast, parentheses);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
throw new Error(`${
|
|
19
|
+
throw new Error(`${JSON.stringify(ast)} is invalid`);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function property(ast) {
|
|
@@ -76,12 +76,15 @@ function atom(ast) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function isPropertyOrAttribute(ast) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
if ('pid' in ast) {
|
|
80
|
+
return property(ast);
|
|
81
|
+
} else
|
|
82
|
+
if ('aid' in ast) {
|
|
83
|
+
return attribute(ast);
|
|
84
|
+
} else {
|
|
85
|
+
const dump = JSON.stringify(ast);
|
|
86
|
+
throw new Error(`${dump} is invalid (expected key: 'aid' or 'pid')`);
|
|
87
|
+
}
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
|
|
@@ -101,5 +104,5 @@ function validate(ast, keys) {
|
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
export function toString(ast) {
|
|
104
|
-
return visit(
|
|
107
|
+
return visit(ast);
|
|
105
108
|
}
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toString as fromTreeToString } from './ast2str.js';
|
|
2
|
+
import { toAst as fromStringToTree } from './str2ast.js';
|
|
3
|
+
import { toFunction as fromStringToFilter } from './str2fun.js';
|
|
4
|
+
|
|
5
|
+
import { operators as ops } from './tokens.js';
|
|
6
|
+
|
|
7
|
+
const operators = { };
|
|
8
|
+
for (const [key, token] of Object.entries(ops)) {
|
|
9
|
+
// convert pattern to string and remove surrounding slashes '/'
|
|
10
|
+
const operator = token.PATTERN.toString();
|
|
11
|
+
operators[key] = operator.substring(1, operator.length-1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
fromTreeToString,
|
|
16
|
+
fromStringToTree,
|
|
17
|
+
fromStringToFilter,
|
|
18
|
+
operators,
|
|
19
|
+
};
|
package/src/str2ast.js
CHANGED
|
@@ -71,7 +71,8 @@ class StringToAstVisitor extends CstVisitor {
|
|
|
71
71
|
if (context.attribute) return this.visit(context.attribute);
|
|
72
72
|
if (context.property) return this.visit(context.property);
|
|
73
73
|
if (context.Integer) return parseInt(context.Integer[0].image);
|
|
74
|
-
|
|
74
|
+
const value = context.String[0].image;
|
|
75
|
+
return value.substring(1, value.length-1);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
relationalOperator(context) {
|