@halleyassist/rule-templater 0.0.5 → 0.0.7
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 +22 -0
- package/dist/rule-templater.browser.js +37 -2
- package/index.d.ts +6 -0
- package/package.json +3 -3
- package/src/RuleTemplate.ebnf.js +1 -1
- package/src/RuleTemplate.production.ebnf.js +1 -1
- package/src/RuleTemplater.js +33 -0
- package/src/RuleTemplater.production.js +33 -0
package/README.md
CHANGED
|
@@ -31,6 +31,11 @@ console.log(variables);
|
|
|
31
31
|
// { name: 'THRESHOLD', filters: [], positions: [{ start: 36, end: 48 }] }
|
|
32
32
|
// ]
|
|
33
33
|
|
|
34
|
+
// Extract function calls from the template
|
|
35
|
+
const functions = parsed.extractFunctions();
|
|
36
|
+
console.log(functions);
|
|
37
|
+
// ['EventIs', 'Value']
|
|
38
|
+
|
|
34
39
|
// Validate that variables are provided correctly
|
|
35
40
|
const validation = parsed.validate({
|
|
36
41
|
EVENT_TYPE: { value: 'sensor-update', type: 'string' },
|
|
@@ -160,6 +165,23 @@ Extracts all variables from the template using the AST.
|
|
|
160
165
|
|
|
161
166
|
Note: If a variable appears multiple times in the template, all occurrences will be recorded in the `positions` array.
|
|
162
167
|
|
|
168
|
+
### `ruleTemplate.extractFunctions()`
|
|
169
|
+
|
|
170
|
+
Extracts all function calls from the template using the AST.
|
|
171
|
+
|
|
172
|
+
**Returns:** Array of unique function names (sorted alphabetically) used in the template.
|
|
173
|
+
|
|
174
|
+
**Example:**
|
|
175
|
+
```javascript
|
|
176
|
+
const template = 'EventIs("test") && Value() > 10 && TimeLastTrueCheck("last_check") < 60';
|
|
177
|
+
const parsed = RuleTemplate.parse(template);
|
|
178
|
+
const functions = parsed.extractFunctions();
|
|
179
|
+
console.log(functions);
|
|
180
|
+
// ['EventIs', 'TimeLastTrueCheck', 'Value']
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This is useful for comparing against a hub's list of available functions to ensure all functions used in the template are supported.
|
|
184
|
+
|
|
163
185
|
### `ruleTemplate.validate(variables)`
|
|
164
186
|
|
|
165
187
|
Validates that all required variables are provided and have valid types.
|
|
@@ -78,6 +78,8 @@ dow_range ||= "ON" WS+ dow_range_inner (WS+ "TO" WS+ dow_range_inner)
|
|
|
78
78
|
|
|
79
79
|
between_time_only ||= "BETWEEN" WS+ between_number_time
|
|
80
80
|
between_tod_only ||= "BETWEEN" WS+ between_tod
|
|
81
|
+
between_time_only_atom ::= between_time_only
|
|
82
|
+
between_tod_only_atom ::= between_tod_only
|
|
81
83
|
|
|
82
84
|
AND ||= (WS* "&&" WS*) | (WS+ "AND" WS+)
|
|
83
85
|
OR ||= (WS* "||" WS*) | (WS+ "OR" WS+)
|
|
@@ -104,7 +106,7 @@ number_time ::= number WS+ unit
|
|
|
104
106
|
number_tod ::= ([0-9]+) ":" ([0-9]+)
|
|
105
107
|
|
|
106
108
|
time_period_ago ||= number_time_atom (WS+ number_time_atom)* WS+ AGO
|
|
107
|
-
time_period_ago_between ||= number_time_atom (WS+ number_time_atom)* WS+ AGO WS+
|
|
109
|
+
time_period_ago_between ||= number_time_atom (WS+ number_time_atom)* WS+ AGO WS+ (between_time_only_atom | between_tod_only_atom)
|
|
108
110
|
time_period_const ||= "today" | time_period_ago
|
|
109
111
|
time_period ::= time_period_ago_between | time_period_const | between_tod_only | between_time_only
|
|
110
112
|
|
|
@@ -2032,7 +2034,7 @@ Object.defineProperty(exports, "ParsingError", { enumerable: true, get: function
|
|
|
2032
2034
|
exports.Grammars = require("./Grammars");
|
|
2033
2035
|
|
|
2034
2036
|
},{"./Grammars":5,"./Parser":6,"./ParsingError":7,"./TokenError":9}],11:[function(require,module,exports){
|
|
2035
|
-
module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]}]
|
|
2037
|
+
module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value","WS+","unit"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]}]
|
|
2036
2038
|
},{}],12:[function(require,module,exports){
|
|
2037
2039
|
module.exports = require('./RuleTemplater.production.js');
|
|
2038
2040
|
},{"./RuleTemplater.production.js":13}],13:[function(require,module,exports){
|
|
@@ -2165,6 +2167,39 @@ class RuleTemplate {
|
|
|
2165
2167
|
return variables;
|
|
2166
2168
|
}
|
|
2167
2169
|
|
|
2170
|
+
/**
|
|
2171
|
+
* Extract function calls from the template using the AST
|
|
2172
|
+
* @returns {Array} Array of unique function names used in the template
|
|
2173
|
+
*/
|
|
2174
|
+
extractFunctions(){
|
|
2175
|
+
const functions = new Set();
|
|
2176
|
+
|
|
2177
|
+
const traverse = (node) => {
|
|
2178
|
+
if (!node) return;
|
|
2179
|
+
|
|
2180
|
+
// Check if this is a function call node
|
|
2181
|
+
if (node.type === 'fcall') {
|
|
2182
|
+
// Find the function name in children
|
|
2183
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
2184
|
+
if (fnameNode && fnameNode.text) {
|
|
2185
|
+
functions.add(fnameNode.text.trim());
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// Traverse children
|
|
2190
|
+
if (node.children) {
|
|
2191
|
+
for (const child of node.children) {
|
|
2192
|
+
traverse(child);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
};
|
|
2196
|
+
|
|
2197
|
+
traverse(this.ast);
|
|
2198
|
+
|
|
2199
|
+
// Convert set to sorted array for consistent output
|
|
2200
|
+
return Array.from(functions).sort();
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2168
2203
|
/**
|
|
2169
2204
|
* Extract variable name and filters from a template_value AST node
|
|
2170
2205
|
* @private
|
package/index.d.ts
CHANGED
|
@@ -68,6 +68,12 @@ export default class RuleTemplate {
|
|
|
68
68
|
*/
|
|
69
69
|
extractVariables(): VariableInfo[];
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Extract function calls from the template using the AST
|
|
73
|
+
* @returns Array of unique function names used in the template
|
|
74
|
+
*/
|
|
75
|
+
extractFunctions(): string[];
|
|
76
|
+
|
|
71
77
|
/**
|
|
72
78
|
* Validate variable types against the AST
|
|
73
79
|
* @param variables Object mapping variable names to {value, type} objects
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@halleyassist/rule-templater",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "The grammar for HalleyAssist rules",
|
|
5
5
|
"main": "src/RuleTemplater.production.js",
|
|
6
6
|
"browser": "./dist/rule-templater.browser.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/HalleyAssist/rule-templater#readme",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@halleyassist/rule-parser": "^1.0.
|
|
23
|
+
"@halleyassist/rule-parser": "^1.0.21",
|
|
24
24
|
"ebnf": "git+https://github.com/HalleyAssist/node-ebnf.git"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^25.
|
|
27
|
+
"@types/node": "^25.3.0",
|
|
28
28
|
"browserify": "^17.0.1",
|
|
29
29
|
"chai": "^4",
|
|
30
30
|
"gulp": "^5.0.1",
|
package/src/RuleTemplate.ebnf.js
CHANGED
|
@@ -24,7 +24,7 @@ const grammar = `
|
|
|
24
24
|
template_filter_arg ::= value | template_value
|
|
25
25
|
|
|
26
26
|
number_atom ::= number | template_value
|
|
27
|
-
number_time_atom ::= number_time | template_value
|
|
27
|
+
number_time_atom ::= number_time | template_value WS+ unit | template_value
|
|
28
28
|
tod_atom ::= number_tod | template_value
|
|
29
29
|
dow_atom ::= dow | template_value
|
|
30
30
|
between_time_only_atom ::= between_time_only | template_value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]}]
|
|
1
|
+
module.exports=[{"name":"TEMPLATE_BEGIN","bnf":[["\"${\""]]},{"name":"TEMPLATE_END","bnf":[["\"}\""]]},{"name":"PIPE","bnf":[["\"|\""]]},{"name":"%IDENT[2]","bnf":[[/[A-Za-z0-9_]/]]},{"name":"IDENT","bnf":[[/[A-Za-z_]/,"%IDENT[2]*"]]},{"name":"DOT","bnf":[["\".\""]]},{"name":"template_value","bnf":[["TEMPLATE_BEGIN","WS*","template_expr","WS*","TEMPLATE_END"]]},{"name":"%template_expr[2]","bnf":[["WS*","template_pipe","WS*","template_filter_call"]],"fragment":true},{"name":"template_expr","bnf":[["template_path","%template_expr[2]*"]]},{"name":"template_pipe","bnf":[["PIPE"]]},{"name":"%template_path[2]","bnf":[["WS*","DOT","WS*","IDENT"]],"fragment":true},{"name":"template_path","bnf":[["IDENT","%template_path[2]*"]]},{"name":"%template_filter_call[2]","bnf":[["WS*","BEGIN_ARGUMENT","WS*","template_filter_args?","WS*","END_ARGUMENT"]],"fragment":true},{"name":"template_filter_call","bnf":[["template_filter_name","%template_filter_call[2]?"]]},{"name":"template_filter_name","bnf":[["IDENT"]]},{"name":"%template_filter_args[2]","bnf":[["WS*","\",\"","WS*","template_filter_arg"]],"fragment":true},{"name":"template_filter_args","bnf":[["template_filter_arg","%template_filter_args[2]*"]]},{"name":"template_filter_arg","bnf":[["value"],["template_value"]]},{"name":"number_atom","bnf":[["number"],["template_value"]]},{"name":"number_time_atom","bnf":[["number_time"],["template_value","WS+","unit"],["template_value"]]},{"name":"tod_atom","bnf":[["number_tod"],["template_value"]]},{"name":"dow_atom","bnf":[["dow"],["template_value"]]},{"name":"between_time_only_atom","bnf":[["between_time_only"],["template_value"]]},{"name":"between_tod_only_atom","bnf":[["between_tod_only"],["template_value"]]}]
|
package/src/RuleTemplater.js
CHANGED
|
@@ -127,6 +127,39 @@ class RuleTemplate {
|
|
|
127
127
|
return variables;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Extract function calls from the template using the AST
|
|
132
|
+
* @returns {Array} Array of unique function names used in the template
|
|
133
|
+
*/
|
|
134
|
+
extractFunctions(){
|
|
135
|
+
const functions = new Set();
|
|
136
|
+
|
|
137
|
+
const traverse = (node) => {
|
|
138
|
+
if (!node) return;
|
|
139
|
+
|
|
140
|
+
// Check if this is a function call node
|
|
141
|
+
if (node.type === 'fcall') {
|
|
142
|
+
// Find the function name in children
|
|
143
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
144
|
+
if (fnameNode && fnameNode.text) {
|
|
145
|
+
functions.add(fnameNode.text.trim());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Traverse children
|
|
150
|
+
if (node.children) {
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
traverse(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
traverse(this.ast);
|
|
158
|
+
|
|
159
|
+
// Convert set to sorted array for consistent output
|
|
160
|
+
return Array.from(functions).sort();
|
|
161
|
+
}
|
|
162
|
+
|
|
130
163
|
/**
|
|
131
164
|
* Extract variable name and filters from a template_value AST node
|
|
132
165
|
* @private
|
|
@@ -127,6 +127,39 @@ class RuleTemplate {
|
|
|
127
127
|
return variables;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Extract function calls from the template using the AST
|
|
132
|
+
* @returns {Array} Array of unique function names used in the template
|
|
133
|
+
*/
|
|
134
|
+
extractFunctions(){
|
|
135
|
+
const functions = new Set();
|
|
136
|
+
|
|
137
|
+
const traverse = (node) => {
|
|
138
|
+
if (!node) return;
|
|
139
|
+
|
|
140
|
+
// Check if this is a function call node
|
|
141
|
+
if (node.type === 'fcall') {
|
|
142
|
+
// Find the function name in children
|
|
143
|
+
const fnameNode = node.children?.find(c => c.type === 'fname');
|
|
144
|
+
if (fnameNode && fnameNode.text) {
|
|
145
|
+
functions.add(fnameNode.text.trim());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Traverse children
|
|
150
|
+
if (node.children) {
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
traverse(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
traverse(this.ast);
|
|
158
|
+
|
|
159
|
+
// Convert set to sorted array for consistent output
|
|
160
|
+
return Array.from(functions).sort();
|
|
161
|
+
}
|
|
162
|
+
|
|
130
163
|
/**
|
|
131
164
|
* Extract variable name and filters from a template_value AST node
|
|
132
165
|
* @private
|