@halleyassist/rule-templater 0.0.4 → 0.0.6

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
@@ -27,10 +27,15 @@ const parsed = RuleTemplate.parse(template);
27
27
  const variables = parsed.extractVariables();
28
28
  console.log(variables);
29
29
  // [
30
- // { name: 'EVENT_TYPE', filters: [] },
31
- // { name: 'THRESHOLD', filters: [] }
30
+ // { name: 'EVENT_TYPE', filters: [], positions: [{ start: 8, end: 21 }] },
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' },
@@ -57,8 +62,8 @@ const parsed = RuleTemplate.parse(template);
57
62
  // Extract variables
58
63
  const variables = parsed.extractVariables();
59
64
  // [
60
- // { name: 'ACTION', filters: [] },
61
- // { name: 'TIME', filters: [] }
65
+ // { name: 'ACTION', filters: [], positions: [{ start: 48, end: 57 }] },
66
+ // { name: 'TIME', filters: [], positions: [{ start: 142, end: 149 }] }
62
67
  // ]
63
68
 
64
69
  // Prepare with values
@@ -79,7 +84,7 @@ const template = 'EventIs(${EVENT_TYPE|upper})';
79
84
 
80
85
  const parsed = RuleTemplate.parse(template);
81
86
  const variables = parsed.extractVariables();
82
- // [{ name: 'EVENT_TYPE', filters: ['upper'] }]
87
+ // [{ name: 'EVENT_TYPE', filters: ['upper'], positions: [{ start: 8, end: 27 }] }]
83
88
 
84
89
  // Prepare with filters applied
85
90
  const prepared = parsed.prepare({
@@ -154,6 +159,28 @@ Extracts all variables from the template using the AST.
154
159
  **Returns:** Array of objects with:
155
160
  - `name` (string): The variable name
156
161
  - `filters` (array): Array of filter names applied to the variable
162
+ - `positions` (array): Array of position objects, each with:
163
+ - `start` (number): Zero-based start index of the variable in the template string
164
+ - `end` (number): Zero-based end index of the variable in the template string
165
+
166
+ Note: If a variable appears multiple times in the template, all occurrences will be recorded in the `positions` array.
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.
157
184
 
158
185
  ### `ruleTemplate.validate(variables)`
159
186
 
@@ -2113,11 +2113,11 @@ class RuleTemplate {
2113
2113
 
2114
2114
  /**
2115
2115
  * Extract variables from the template using the AST
2116
- * @returns {Array} Array of {name, filters: []} objects
2116
+ * @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
2117
2117
  */
2118
2118
  extractVariables(){
2119
2119
  const variables = [];
2120
- const seen = new Set();
2120
+ const variableMap = new Map();
2121
2121
 
2122
2122
  const traverse = (node) => {
2123
2123
  if (!node) return;
@@ -2126,9 +2126,24 @@ class RuleTemplate {
2126
2126
  if (node.type === 'template_value') {
2127
2127
  // Extract the variable information
2128
2128
  const varInfo = this._extractVariableFromNode(node);
2129
- if (varInfo && !seen.has(varInfo.name)) {
2130
- seen.add(varInfo.name);
2131
- variables.push(varInfo);
2129
+ if (varInfo) {
2130
+ // Add position to existing variable or create new entry
2131
+ if (variableMap.has(varInfo.name)) {
2132
+ const existing = variableMap.get(varInfo.name);
2133
+ existing.positions.push({
2134
+ start: varInfo.start,
2135
+ end: varInfo.end
2136
+ });
2137
+ } else {
2138
+ variableMap.set(varInfo.name, {
2139
+ name: varInfo.name,
2140
+ filters: varInfo.filters,
2141
+ positions: [{
2142
+ start: varInfo.start,
2143
+ end: varInfo.end
2144
+ }]
2145
+ });
2146
+ }
2132
2147
  }
2133
2148
  }
2134
2149
 
@@ -2141,9 +2156,48 @@ class RuleTemplate {
2141
2156
  };
2142
2157
 
2143
2158
  traverse(this.ast);
2159
+
2160
+ // Convert map to array
2161
+ for (const variable of variableMap.values()) {
2162
+ variables.push(variable);
2163
+ }
2164
+
2144
2165
  return variables;
2145
2166
  }
2146
2167
 
2168
+ /**
2169
+ * Extract function calls from the template using the AST
2170
+ * @returns {Array} Array of unique function names used in the template
2171
+ */
2172
+ extractFunctions(){
2173
+ const functions = new Set();
2174
+
2175
+ const traverse = (node) => {
2176
+ if (!node) return;
2177
+
2178
+ // Check if this is a function call node
2179
+ if (node.type === 'fcall') {
2180
+ // Find the function name in children
2181
+ const fnameNode = node.children?.find(c => c.type === 'fname');
2182
+ if (fnameNode && fnameNode.text) {
2183
+ functions.add(fnameNode.text.trim());
2184
+ }
2185
+ }
2186
+
2187
+ // Traverse children
2188
+ if (node.children) {
2189
+ for (const child of node.children) {
2190
+ traverse(child);
2191
+ }
2192
+ }
2193
+ };
2194
+
2195
+ traverse(this.ast);
2196
+
2197
+ // Convert set to sorted array for consistent output
2198
+ return Array.from(functions).sort();
2199
+ }
2200
+
2147
2201
  /**
2148
2202
  * Extract variable name and filters from a template_value AST node
2149
2203
  * @private
@@ -2172,7 +2226,11 @@ class RuleTemplate {
2172
2226
  }
2173
2227
  }
2174
2228
 
2175
- return { name, filters };
2229
+ // Extract position information from the node
2230
+ const start = node.start;
2231
+ const end = node.end;
2232
+
2233
+ return { name, filters, start, end };
2176
2234
  }
2177
2235
 
2178
2236
  /**
package/index.d.ts CHANGED
@@ -1,6 +1,12 @@
1
+ export interface VariablePosition {
2
+ start: number;
3
+ end: number;
4
+ }
5
+
1
6
  export interface VariableInfo {
2
7
  name: string;
3
8
  filters: string[];
9
+ positions: VariablePosition[];
4
10
  }
5
11
 
6
12
  export interface VariableValue {
@@ -62,6 +68,12 @@ export default class RuleTemplate {
62
68
  */
63
69
  extractVariables(): VariableInfo[];
64
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
+
65
77
  /**
66
78
  * Validate variable types against the AST
67
79
  * @param variables Object mapping variable names to {value, type} objects
@@ -89,3 +101,24 @@ export default class RuleTemplate {
89
101
  export const ParserRules: any[];
90
102
  export const VariableTypes: string[];
91
103
  export const TemplateFilters: TemplateFiltersType;
104
+
105
+ export interface IParsingErrorPosition {
106
+ offset: number;
107
+ line: number;
108
+ column: number;
109
+ }
110
+
111
+ export interface IFailureTreeNode {
112
+ name: string;
113
+ expected?: string | RegExp;
114
+ children?: IFailureTreeNode[];
115
+ }
116
+
117
+ export class ParsingError extends Error {
118
+ readonly position: IParsingErrorPosition;
119
+ readonly expected: string[];
120
+ readonly found: string;
121
+ readonly failureTree?: IFailureTreeNode[];
122
+ constructor(message: string, position: IParsingErrorPosition, expected: string[], found: string, failureTree?: IFailureTreeNode[]);
123
+ toString(): string;
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halleyassist/rule-templater",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "The grammar for HalleyAssist rules",
5
5
  "main": "src/RuleTemplater.production.js",
6
6
  "browser": "./dist/rule-templater.browser.js",
@@ -75,11 +75,11 @@ class RuleTemplate {
75
75
 
76
76
  /**
77
77
  * Extract variables from the template using the AST
78
- * @returns {Array} Array of {name, filters: []} objects
78
+ * @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
79
79
  */
80
80
  extractVariables(){
81
81
  const variables = [];
82
- const seen = new Set();
82
+ const variableMap = new Map();
83
83
 
84
84
  const traverse = (node) => {
85
85
  if (!node) return;
@@ -88,9 +88,24 @@ class RuleTemplate {
88
88
  if (node.type === 'template_value') {
89
89
  // Extract the variable information
90
90
  const varInfo = this._extractVariableFromNode(node);
91
- if (varInfo && !seen.has(varInfo.name)) {
92
- seen.add(varInfo.name);
93
- variables.push(varInfo);
91
+ if (varInfo) {
92
+ // Add position to existing variable or create new entry
93
+ if (variableMap.has(varInfo.name)) {
94
+ const existing = variableMap.get(varInfo.name);
95
+ existing.positions.push({
96
+ start: varInfo.start,
97
+ end: varInfo.end
98
+ });
99
+ } else {
100
+ variableMap.set(varInfo.name, {
101
+ name: varInfo.name,
102
+ filters: varInfo.filters,
103
+ positions: [{
104
+ start: varInfo.start,
105
+ end: varInfo.end
106
+ }]
107
+ });
108
+ }
94
109
  }
95
110
  }
96
111
 
@@ -103,9 +118,48 @@ class RuleTemplate {
103
118
  };
104
119
 
105
120
  traverse(this.ast);
121
+
122
+ // Convert map to array
123
+ for (const variable of variableMap.values()) {
124
+ variables.push(variable);
125
+ }
126
+
106
127
  return variables;
107
128
  }
108
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
+
109
163
  /**
110
164
  * Extract variable name and filters from a template_value AST node
111
165
  * @private
@@ -134,7 +188,11 @@ class RuleTemplate {
134
188
  }
135
189
  }
136
190
 
137
- return { name, filters };
191
+ // Extract position information from the node
192
+ const start = node.start;
193
+ const end = node.end;
194
+
195
+ return { name, filters, start, end };
138
196
  }
139
197
 
140
198
  /**
@@ -75,11 +75,11 @@ class RuleTemplate {
75
75
 
76
76
  /**
77
77
  * Extract variables from the template using the AST
78
- * @returns {Array} Array of {name, filters: []} objects
78
+ * @returns {Array} Array of {name, filters: [], positions: [{start, end}]} objects
79
79
  */
80
80
  extractVariables(){
81
81
  const variables = [];
82
- const seen = new Set();
82
+ const variableMap = new Map();
83
83
 
84
84
  const traverse = (node) => {
85
85
  if (!node) return;
@@ -88,9 +88,24 @@ class RuleTemplate {
88
88
  if (node.type === 'template_value') {
89
89
  // Extract the variable information
90
90
  const varInfo = this._extractVariableFromNode(node);
91
- if (varInfo && !seen.has(varInfo.name)) {
92
- seen.add(varInfo.name);
93
- variables.push(varInfo);
91
+ if (varInfo) {
92
+ // Add position to existing variable or create new entry
93
+ if (variableMap.has(varInfo.name)) {
94
+ const existing = variableMap.get(varInfo.name);
95
+ existing.positions.push({
96
+ start: varInfo.start,
97
+ end: varInfo.end
98
+ });
99
+ } else {
100
+ variableMap.set(varInfo.name, {
101
+ name: varInfo.name,
102
+ filters: varInfo.filters,
103
+ positions: [{
104
+ start: varInfo.start,
105
+ end: varInfo.end
106
+ }]
107
+ });
108
+ }
94
109
  }
95
110
  }
96
111
 
@@ -103,9 +118,48 @@ class RuleTemplate {
103
118
  };
104
119
 
105
120
  traverse(this.ast);
121
+
122
+ // Convert map to array
123
+ for (const variable of variableMap.values()) {
124
+ variables.push(variable);
125
+ }
126
+
106
127
  return variables;
107
128
  }
108
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
+
109
163
  /**
110
164
  * Extract variable name and filters from a template_value AST node
111
165
  * @private
@@ -134,7 +188,11 @@ class RuleTemplate {
134
188
  }
135
189
  }
136
190
 
137
- return { name, filters };
191
+ // Extract position information from the node
192
+ const start = node.start;
193
+ const end = node.end;
194
+
195
+ return { name, filters, start, end };
138
196
  }
139
197
 
140
198
  /**