@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 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+ between_tod_only
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.5",
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.19",
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.1.0",
27
+ "@types/node": "^25.3.0",
28
28
  "browserify": "^17.0.1",
29
29
  "chai": "^4",
30
30
  "gulp": "^5.0.1",
@@ -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"]]}]
@@ -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