@libdbm/libcel-ts 1.0.2-rc.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, gtnicol
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
11
+
12
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
15
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
20
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,288 @@
1
+ # libcel-ts - Common Expression Language for TypeScript
2
+
3
+ A complete TypeScript implementation of Google's [Common Expression Language (CEL)](https://github.com/google/cel-spec) specification, ported from the Java implementation.
4
+
5
+ ## Overview
6
+
7
+ CEL is a non-Turing complete expression language designed for simplicity, speed, and safety. It's commonly used for evaluating user-provided expressions in a secure sandbox environment.
8
+
9
+ ## Features
10
+
11
+ - **Complete CEL Implementation**: All CEL operators, functions, and macros
12
+ - **Type Safe**: Leverages TypeScript's type system with strict typing
13
+ - **High Performance**: Hand-written recursive descent parser with AST compilation
14
+ - **Extensible**: Easy to add custom functions
15
+ - **Well Tested**: 100+ comprehensive tests ensuring functional equivalence
16
+ - **Zero External Dependencies**: Pure TypeScript implementation (except dev dependencies)
17
+ - **Vite-Powered**: Modern tooling with fast builds and excellent DX
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @libdbm/libcel-ts
23
+ # or
24
+ pnpm add @libdbm/libcel-ts
25
+ # or
26
+ yarn add @libdbm/libcel-ts
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Basic Usage
32
+
33
+ ```typescript
34
+ import { CEL } from '@libdbm/libcel-ts';
35
+
36
+ const cel = new CEL();
37
+
38
+ // Simple expression evaluation
39
+ console.log(cel.eval('2 + 3 * 4', {})); // 14
40
+
41
+ // Using variables
42
+ const vars = { name: 'Alice', age: 30 };
43
+ console.log(cel.eval('name + " is " + string(age) + " years old"', vars));
44
+ // Output: Alice is 30 years old
45
+
46
+ // Boolean logic
47
+ console.log(cel.eval('age >= 18 && age < 65', vars)); // true
48
+ ```
49
+
50
+ ### Compiling and Reusing Programs
51
+
52
+ For better performance when evaluating the same expression multiple times:
53
+
54
+ ```typescript
55
+ const cel = new CEL();
56
+ const program = cel.compile('price * quantity * (1 - discount)');
57
+
58
+ // Reuse with different variables
59
+ const result1 = program.evaluate({ price: 10, quantity: 5, discount: 0.1 });
60
+ const result2 = program.evaluate({ price: 20, quantity: 3, discount: 0.2 });
61
+ ```
62
+
63
+ ### Working with Complex Data
64
+
65
+ ```typescript
66
+ const cel = new CEL();
67
+ const data = {
68
+ user: {
69
+ name: 'Alice',
70
+ roles: ['admin', 'user'],
71
+ metadata: { active: true },
72
+ },
73
+ permissions: ['read', 'write', 'delete'],
74
+ };
75
+
76
+ // Check complex conditions
77
+ const canDelete = cel.eval('"admin" in user.roles && "delete" in permissions', data);
78
+ // true
79
+
80
+ // Use macro functions
81
+ const users = [
82
+ { name: 'Alice', active: true },
83
+ { name: 'Bob', active: false },
84
+ { name: 'Charlie', active: true },
85
+ ];
86
+
87
+ const activeNames = cel.eval('users.filter(u, u.active).map(u, u.name)', { users });
88
+ // ['Alice', 'Charlie']
89
+ ```
90
+
91
+ ### Custom Functions
92
+
93
+ Extend the standard library with custom functions:
94
+
95
+ ```typescript
96
+ import { CEL, StandardFunctions } from '@libdbm/libcel-ts';
97
+
98
+ class CustomFunctions extends StandardFunctions {
99
+ callFunction(name: string, args: any[]): any {
100
+ if (name === 'reverse') {
101
+ return (args[0] as string).split('').reverse().join('');
102
+ }
103
+ return super.callFunction(name, args);
104
+ }
105
+ }
106
+
107
+ const cel = new CEL(new CustomFunctions());
108
+ console.log(cel.eval("reverse('hello')", {})); // "olleh"
109
+ ```
110
+
111
+ ## Supported Features
112
+
113
+ ### Literals
114
+
115
+ - Null: `null`
116
+ - Booleans: `true`, `false`
117
+ - Integers: `42`, `-7`, `0xFF` (hexadecimal)
118
+ - Unsigned: `42u`, `0xFFu`
119
+ - Doubles: `3.14`, `6.022e23`
120
+ - Strings: `"hello"`, `'world'`, `r"raw\nstring"`, `"""multi-line"""`
121
+ - Bytes: `b"data"`
122
+ - Lists: `[1, 2, 3]`
123
+ - Maps: `{"key": "value"}`
124
+
125
+ ### Operators
126
+
127
+ - **Arithmetic**: `+`, `-`, `*`, `/`, `%`
128
+ - **Comparison**: `<`, `<=`, `>`, `>=`, `==`, `!=`
129
+ - **Logical**: `&&`, `||`, `!`
130
+ - **Conditional**: `condition ? trueValue : falseValue`
131
+ - **Membership**: `in` (for lists, maps, strings)
132
+
133
+ ### Functions
134
+
135
+ - **Type conversions**: `int()`, `double()`, `string()`, `bool()`
136
+ - **Type checking**: `type()`
137
+ - **Collections**: `size()`, `has()`
138
+ - **String methods**: `contains()`, `startsWith()`, `endsWith()`, `toLowerCase()`, `toUpperCase()`, `trim()`, `replace()`, `split()`
139
+ - **Regex**: `matches()`
140
+ - **Math**: `max()`, `min()`
141
+
142
+ ### Macro Functions
143
+
144
+ ```typescript
145
+ // map - Transform each element
146
+ cel.eval('[1, 2, 3].map(x, x * 2)', {}); // [2, 4, 6]
147
+
148
+ // filter - Keep elements matching condition
149
+ cel.eval('[1, 2, 3, 4].filter(x, x % 2 == 0)', {}); // [2, 4]
150
+
151
+ // exists - Check if any element matches
152
+ cel.eval('[1, 2, 3].exists(x, x > 2)', {}); // true
153
+
154
+ // all - Check if all elements match
155
+ cel.eval('[1, 2, 3].all(x, x > 0)', {}); // true
156
+
157
+ // existsOne - Check if exactly one element matches
158
+ cel.eval('[1, 2, 3].existsOne(x, x == 2)', {}); // true
159
+ ```
160
+
161
+ ## Building
162
+
163
+ ```bash
164
+ # Install dependencies
165
+ npm install
166
+
167
+ # Run tests
168
+ npm test
169
+
170
+ # Run tests with coverage
171
+ npm run test:coverage
172
+
173
+ # Build the library
174
+ npm run build
175
+
176
+ # Type check
177
+ npm run typecheck
178
+
179
+ # Lint and format
180
+ npm run lint
181
+ npm run format
182
+ ```
183
+
184
+ ## Testing
185
+
186
+ The project includes comprehensive test coverage:
187
+
188
+ - 32 parser tests
189
+ - 21 interpreter tests
190
+ - 50 integration tests
191
+ - All tests from the Java implementation ported and passing
192
+
193
+ ```bash
194
+ # Run all tests
195
+ npm test
196
+
197
+ # Run tests in watch mode
198
+ npm run test:watch
199
+
200
+ # Generate coverage report
201
+ npm run test:coverage
202
+ ```
203
+
204
+ ## Architecture
205
+
206
+ - **expression.ts**: Abstract Syntax Tree (AST) with visitor pattern
207
+ - **parser.ts**: Hand-written recursive descent parser with integrated lexer
208
+ - **interpreter.ts**: AST evaluator using Visitor pattern
209
+ - **functions.ts**: Extensible function library
210
+ - **cel.ts**: Main API entry point
211
+ - **program.ts**: Compiled, reusable programs
212
+
213
+ ## Functional Equivalence
214
+
215
+ This TypeScript implementation is functionally equivalent to the [Java libcel](https://github.com/libdbm/libcel-java) implementation:
216
+
217
+ - Same AST structure and expression types
218
+ - Identical parsing rules and operator precedence
219
+ - Same evaluation semantics
220
+ - Equivalent macro function behavior
221
+ - Compatible error handling
222
+
223
+ All tests from the Java version have been ported to ensure equivalence.
224
+
225
+ ## Requirements
226
+
227
+ - Node.js 18+ or modern browser
228
+ - TypeScript 5.0+ (for development)
229
+
230
+ ## API Documentation
231
+
232
+ ### CEL Class
233
+
234
+ ```typescript
235
+ class CEL {
236
+ constructor(functions?: Functions | null);
237
+ compile(expression: string): Program;
238
+ eval(expression: string, variables?: Record<string, any>): any;
239
+
240
+ static compile(expression: string, functions: Functions): Program;
241
+ static eval(expression: string, functions: Functions, variables: Record<string, any>): any;
242
+ }
243
+ ```
244
+
245
+ ### Program Class
246
+
247
+ ```typescript
248
+ class Program {
249
+ evaluate(variables?: Record<string, any>): any;
250
+ }
251
+ ```
252
+
253
+ ### Functions Interface
254
+
255
+ ```typescript
256
+ interface Functions {
257
+ callFunction(name: string, args: any[]): any;
258
+ callMethod(target: any, method: string, args: any[]): any;
259
+ }
260
+ ```
261
+
262
+ ## Examples
263
+
264
+ See the [examples](./examples) directory for more detailed examples:
265
+
266
+ - [quickstart.ts](./examples/quickstart.ts) - Comprehensive usage examples
267
+ - [parser-example.ts](./examples/parser-example.ts) - Parser API demonstration
268
+ - [interpreter-example.ts](./examples/interpreter-example.ts) - Interpreter API demonstration
269
+
270
+ ## License
271
+
272
+ BSD 3-Clause License - see [LICENSE](./LICENSE) file for details.
273
+
274
+ ## Acknowledgments
275
+
276
+ - Based on the [Common Expression Language](https://github.com/google/cel-spec) specification by Google
277
+ - Ported from the [Java libcel](https://github.com/libdbm/libcel-java) implementation
278
+ - Original [Dart libcel](https://pub.dev/packages/libcel) implementation
279
+
280
+ ## Contributing
281
+
282
+ Contributions are welcome! Please feel free to submit issues or pull requests.
283
+
284
+ ## Links
285
+
286
+ - [CEL Specification](https://github.com/google/cel-spec)
287
+ - [Java Implementation](https://github.com/libdbm/libcel-java)
288
+ - [Dart Implementation](https://pub.dev/packages/libcel)
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class p extends Error{constructor(t,e,i){super(`${t} at ${e}:${i}`),this.line=e,this.column=i,this.name="ParseError"}}var s=(r=>(r.NULL="NULL",r.TRUE="TRUE",r.FALSE="FALSE",r.INT="INT",r.UINT="UINT",r.DOUBLE="DOUBLE",r.STRING="STRING",r.BYTES="BYTES",r.IDENTIFIER="IDENTIFIER",r.PLUS="PLUS",r.MINUS="MINUS",r.STAR="STAR",r.SLASH="SLASH",r.PERCENT="PERCENT",r.EQ="EQ",r.NE="NE",r.LT="LT",r.LE="LE",r.GT="GT",r.GE="GE",r.LOGICAL_AND="LOGICAL_AND",r.LOGICAL_OR="LOGICAL_OR",r.BANG="BANG",r.IN="IN",r.LPAREN="LPAREN",r.RPAREN="RPAREN",r.LBRACKET="LBRACKET",r.RBRACKET="RBRACKET",r.LBRACE="LBRACE",r.RBRACE="RBRACE",r.DOT="DOT",r.COMMA="COMMA",r.COLON="COLON",r.QUESTION="QUESTION",r.EOF="EOF",r))(s||{});class h{constructor(t,e,i,n){this.type=t,this.value=e,this.line=i,this.column=n}toString(){return`${this.type}(${this.value})`}}class H{constructor(t){this.input=t,this.position=0,this.line=1,this.column=1,this.lookahead=[]}next(){return this.lookahead.length>0?this.lookahead.shift():this.token()}peek(t){for(;this.lookahead.length<t;)this.lookahead.push(this.token());return this.lookahead[t-1]}step(){if(this.position>=this.input.length)return;const t=this.input[this.position];this.position++,t==="\r"?(this.position<this.input.length&&this.input[this.position]===`
2
+ `&&this.position++,this.line++,this.column=1):t===`
3
+ `?(this.line++,this.column=1):this.column++}forward(t){for(let e=0;e<t;e++)this.step()}token(){if(this.whitespace(),this.position>=this.input.length)return new h("EOF","",this.line,this.column);const t=this.position,e=this.line,i=this.column,n=this.input[this.position];switch(n){case"(":return this.step(),new h("LPAREN","(",e,i);case")":return this.step(),new h("RPAREN",")",e,i);case"[":return this.step(),new h("LBRACKET","[",e,i);case"]":return this.step(),new h("RBRACKET","]",e,i);case"{":return this.step(),new h("LBRACE","{",e,i);case"}":return this.step(),new h("RBRACE","}",e,i);case",":return this.step(),new h("COMMA",",",e,i);case".":return this.step(),new h("DOT",".",e,i);case":":return this.step(),new h("COLON",":",e,i);case"?":return this.step(),new h("QUESTION","?",e,i);case"+":return this.step(),new h("PLUS","+",e,i);case"*":return this.step(),new h("STAR","*",e,i);case"/":return this.step(),new h("SLASH","/",e,i);case"%":return this.step(),new h("PERCENT","%",e,i)}if(n==="&"&&this.peekChar()==="&")return this.forward(2),new h("LOGICAL_AND","&&",e,i);if(n==="|"&&this.peekChar()==="|")return this.forward(2),new h("LOGICAL_OR","||",e,i);if(n==="="&&this.peekChar()==="=")return this.forward(2),new h("EQ","==",e,i);if(n==="!"&&this.peekChar()==="=")return this.forward(2),new h("NE","!=",e,i);if(n==="<"&&this.peekChar()==="=")return this.forward(2),new h("LE","<=",e,i);if(n===">"&&this.peekChar()==="=")return this.forward(2),new h("GE",">=",e,i);if(n==="<")return this.step(),new h("LT","<",e,i);if(n===">")return this.step(),new h("GT",">",e,i);if(n==="!")return this.step(),new h("BANG","!",e,i);if(n==="-")return this.step(),new h("MINUS","-",e,i);if(n==='"'||n==="'")return this.string(e,i,t);if((n==="r"||n==="R")&&this.position+1<this.input.length){const o=this.input[this.position+1];if(o==='"'||o==="'")return this.string(e,i,t)}if((n==="b"||n==="B")&&this.position+1<this.input.length){const o=this.input[this.position+1];if(o==='"'||o==="'")return this.bytes(e,i,t)}if(this.isDigit(n))return this.number(e,i,t);if(this.isLetter(n)||n==="_")return this.identifier(e,i,t);throw new p(`Unexpected character: ${n}`,e,i)}string(t,e,i){let n=!1;if((this.input[this.position]==="r"||this.input[this.position]==="R")&&(n=!0,this.step()),this.position+2<this.input.length){const l=this.input.substring(this.position,this.position+3);if(l==='"""'||l==="'''"){const c=this.input[this.position];for(this.forward(3);this.position+2<this.input.length;){if(this.input[this.position]===c&&this.input[this.position+1]===c&&this.input[this.position+2]===c)return this.forward(3),new h("STRING",this.input.substring(i,this.position),t,e);this.step()}throw new p("Unterminated triple-quoted string",t,e)}}const o=this.input[this.position];for(this.step();this.position<this.input.length;){const l=this.input[this.position];if(l===o)return this.step(),new h("STRING",this.input.substring(i,this.position),t,e);l==="\\"&&!n&&this.position+1<this.input.length?this.forward(2):this.step()}throw new p("Unterminated string",t,e)}bytes(t,e,i){this.step();const n=this.input[this.position];for(this.step();this.position<this.input.length;){const o=this.input[this.position];if(o===n)return this.step(),new h("BYTES",this.input.substring(i,this.position),t,e);o==="\\"&&this.position+1<this.input.length?this.forward(2):this.step()}throw new p("Unterminated bytes literal",t,e)}number(t,e,i){if(this.input[this.position]==="0"&&this.position+1<this.input.length){const l=this.input[this.position+1];if(l==="x"||l==="X"){for(this.forward(2);this.position<this.input.length&&this.isHexDigit(this.input[this.position]);)this.step();if(this.position<this.input.length){const c=this.input[this.position];if(c==="u"||c==="U")return this.step(),new h("UINT",this.input.substring(i,this.position),t,e)}return new h("INT",this.input.substring(i,this.position),t,e)}}for(;this.position<this.input.length&&this.isDigit(this.input[this.position]);)this.step();let n=!1;if(this.position<this.input.length&&this.input[this.position]===".")for(n=!0,this.step();this.position<this.input.length&&this.isDigit(this.input[this.position]);)this.step();if(this.position<this.input.length){const l=this.input[this.position];if(l==="e"||l==="E"){if(n=!0,this.step(),this.position<this.input.length){const c=this.input[this.position];(c==="+"||c==="-")&&this.step()}for(;this.position<this.input.length&&this.isDigit(this.input[this.position]);)this.step()}}if(!n&&this.position<this.input.length){const l=this.input[this.position];if(l==="u"||l==="U")return this.step(),new h("UINT",this.input.substring(i,this.position),t,e)}const o=n?"DOUBLE":"INT";return new h(o,this.input.substring(i,this.position),t,e)}identifier(t,e,i){for(;this.position<this.input.length;){const l=this.input[this.position];if(!this.isLetterOrDigit(l)&&l!=="_")break;this.step()}const n=this.input.substring(i,this.position),o=this.typeOf(n);return new h(o,n,t,e)}typeOf(t){switch(t){case"null":return"NULL";case"true":return"TRUE";case"false":return"FALSE";case"in":return"IN";default:return"IDENTIFIER"}}whitespace(){for(;this.position<this.input.length;){const t=this.input[this.position];if(t!==" "&&t!==" "&&t!==`
4
+ `&&t!=="\r")break;this.step()}}peekChar(){const t=this.position+1;return t>=this.input.length?"\0":this.input[t]}isHexDigit(t){return t>="0"&&t<="9"||t>="a"&&t<="f"||t>="A"&&t<="F"}isDigit(t){return t>="0"&&t<="9"}isLetter(t){return t>="a"&&t<="z"||t>="A"&&t<="Z"}isLetterOrDigit(t){return this.isLetter(t)||this.isDigit(t)}}var u=(r=>(r.ADD="ADD",r.SUBTRACT="SUBTRACT",r.MULTIPLY="MULTIPLY",r.DIVIDE="DIVIDE",r.MODULO="MODULO",r.EQUAL="EQUAL",r.NOT_EQUAL="NOT_EQUAL",r.LESS="LESS",r.LESS_EQUAL="LESS_EQUAL",r.GREATER="GREATER",r.GREATER_EQUAL="GREATER_EQUAL",r.IN="IN",r.LOGICAL_AND="LOGICAL_AND",r.LOGICAL_OR="LOGICAL_OR",r))(u||{}),A=(r=>(r.NOT="NOT",r.NEGATE="NEGATE",r))(A||{}),w=(r=>(r.NULL_VALUE="NULL_VALUE",r.BOOL="BOOL",r.INT="INT",r.UINT="UINT",r.DOUBLE="DOUBLE",r.STRING="STRING",r.BYTES="BYTES",r))(w||{});class d{constructor(t,e){this.value=t,this.literalType=e}accept(t){return t.visitLiteral(this)}}class S{constructor(t){this.name=t}accept(t){return t.visitIdentifier(this)}}class R{constructor(t,e,i=!1){this.operand=t,this.field=e,this.isTest=i}accept(t){return t.visitSelect(this)}}class D{constructor(t,e){this.operand=t,this.index=e}accept(t){return t.visitIndex(this)}}class y{constructor(t,e,i,n=!1){this.target=t,this.functionName=e,this.args=i,this.isMacro=n}accept(t){return t.visitCall(this)}}class T{constructor(t){this.elements=t}accept(t){return t.visitList(this)}}class B{constructor(t,e){this.key=t,this.value=e}}class b{constructor(t){this.entries=t}accept(t){return t.visitMap(this)}}class x{constructor(t,e){this.field=t,this.value=e}}class m{constructor(t,e){this.typeName=t,this.fields=e}accept(t){return t.visitStruct(this)}}class V{constructor(t,e,i,n,o,l,c){this.variable=t,this.range=e,this.accumulator=i,this.initializer=n,this.condition=o,this.step=l,this.result=c}accept(t){return t.visitComprehension(this)}}class N{constructor(t,e){this.op=t,this.operand=e}accept(t){return t.visitUnary(this)}}class L{constructor(t,e,i){this.op=t,this.left=e,this.right=i}accept(t){return t.visitBinary(this)}}class G{constructor(t,e,i){this.condition=t,this.thenExpr=e,this.otherwiseExpr=i}accept(t){return t.visitConditional(this)}}const X=new Set(["map","filter","all","exists","existsOne"]);class ${constructor(t){this.lexer=new H(t),this.current=this.lexer.next()}parse(){const t=this.parseExpr();if(this.current.type!==s.EOF)throw new p(`Unexpected token after expression: ${this.current.value}`,this.current.line,this.current.column);return t}parseExpr(){const t=this.parseConditionalOr();if(this.match(s.QUESTION)){const e=this.parseConditionalOr();this.expect(s.COLON);const i=this.parseExpr();return new G(t,e,i)}return t}parseConditionalOr(){let t=this.parseConditionalAnd();for(;this.match(s.LOGICAL_OR);){const e=this.parseConditionalAnd();t=new L(u.LOGICAL_OR,t,e)}return t}parseConditionalAnd(){let t=this.parseRelation();for(;this.match(s.LOGICAL_AND);){const e=this.parseRelation();t=new L(u.LOGICAL_AND,t,e)}return t}parseRelation(){let t=this.parseAddition();for(;this.isRelationalOp(this.current.type);){const e=this.current.type;this.advance();const i=this.parseAddition();t=new L(this.toBinaryOp(e),t,i)}return t}parseAddition(){let t=this.parseMultiplication();for(;this.current.type===s.PLUS||this.current.type===s.MINUS;){const e=this.current.type;this.advance();const i=this.parseMultiplication();t=new L(e===s.PLUS?u.ADD:u.SUBTRACT,t,i)}return t}parseMultiplication(){let t=this.parseUnary();for(;this.current.type===s.STAR||this.current.type===s.SLASH||this.current.type===s.PERCENT;){const e=this.current.type;this.advance();const i=this.parseUnary(),n=e===s.STAR?u.MULTIPLY:e===s.SLASH?u.DIVIDE:u.MODULO;t=new L(n,t,i)}return t}parseUnary(){return this.current.type===s.BANG?(this.advance(),new N(A.NOT,this.parseUnary())):this.current.type===s.MINUS?(this.advance(),new N(A.NEGATE,this.parseUnary())):this.parseMember()}parseMember(){let t=this.parsePrimary();for(;;)if(this.current.type===s.DOT){this.advance();const e=this.expectIdentifier();if(this.current.type===s.LPAREN){this.advance();const i=this.parseExprList();this.expect(s.RPAREN);const n=X.has(e);t=new y(t,e,i,n)}else t=new R(t,e)}else if(this.current.type===s.LBRACKET){this.advance();const e=this.parseExpr();this.expect(s.RBRACKET),t=new D(t,e)}else break;return t}parsePrimary(){if(this.isLiteralToken(this.current.type))return this.parseLiteral();if(this.current.type===s.LBRACKET)return this.parseListLiteral();if(this.current.type===s.LBRACE)return this.parseMapOrStructLiteral(null);if(this.current.type===s.LPAREN){this.advance();const t=this.parseExpr();return this.expect(s.RPAREN),t}if(this.current.type===s.DOT){this.advance();const t=this.expectIdentifier();if(this.current.type===s.LPAREN){this.advance();const e=this.parseExprList();return this.expect(s.RPAREN),new y(null,t,e)}return new R(null,t)}if(this.current.type===s.IDENTIFIER){const t=this.current.value;if(this.advance(),this.current.type===s.LPAREN){this.advance();const e=this.parseExprList();return this.expect(s.RPAREN),new y(null,t,e)}if(this.current.type===s.DOT&&this.isQualifiedStructLiteral()){const e=this.parseQualifiedIdent(t);return this.parseMapOrStructLiteral(e)}return this.current.type===s.LBRACE?this.parseMapOrStructLiteral(t):new S(t)}throw new p(`Unexpected token: ${this.current.value}`,this.current.line,this.current.column)}parseListLiteral(){this.expect(s.LBRACKET);const t=[];return this.current.type!==s.RBRACKET&&(t.push(...this.parseExprList()),this.current.type===s.COMMA&&this.advance()),this.expect(s.RBRACKET),new T(t)}parseMapOrStructLiteral(t){if(this.expect(s.LBRACE),this.current.type===s.RBRACE)return this.advance(),t!==null?new m(t,[]):new b([]);if(this.current.type===s.IDENTIFIER&&this.peekAhead(1).type===s.COLON||t!==null){const i=this.parseFieldInits();return this.current.type===s.COMMA&&this.advance(),this.expect(s.RBRACE),new m(t,i)}else{const i=this.parseMapInits();return this.current.type===s.COMMA&&this.advance(),this.expect(s.RBRACE),new b(i)}}parseExprList(){const t=[],e=this.current.type;if(e===s.RPAREN||e===s.RBRACKET)return t;for(t.push(this.parseExpr());this.current.type===s.COMMA&&(this.advance(),!(this.current.type===s.RPAREN||this.current.type===s.RBRACKET));)t.push(this.parseExpr());return t}parseMapInits(){const t=[];for(t.push(this.parseMapInit());this.current.type===s.COMMA&&(this.advance(),this.current.type!==s.RBRACE);)t.push(this.parseMapInit());return t}parseMapInit(){const t=this.parseExpr();this.expect(s.COLON);const e=this.parseExpr();return new B(t,e)}parseFieldInits(){const t=[];for(t.push(this.parseFieldInit());this.current.type===s.COMMA&&(this.advance(),this.current.type!==s.RBRACE);)t.push(this.parseFieldInit());return t}parseFieldInit(){const t=this.expectIdentifier();this.expect(s.COLON);const e=this.parseExpr();return new x(t,e)}parseQualifiedIdent(t){let e=t;for(;this.current.type===s.DOT;)this.advance(),e+=".",e+=this.expectIdentifier();return e}parseLiteral(){const t=this.current;switch(this.advance(),t.type){case s.NULL:return new d(null,w.NULL_VALUE);case s.TRUE:return new d(!0,w.BOOL);case s.FALSE:return new d(!1,w.BOOL);case s.INT:return new d(this.parseIntLiteral(t.value),w.INT);case s.UINT:return new d(this.parseUintLiteral(t.value),w.UINT);case s.DOUBLE:return new d(parseFloat(t.value),w.DOUBLE);case s.STRING:return new d(this.parseStringLiteral(t.value),w.STRING);case s.BYTES:return new d(this.parseBytesLiteral(t.value),w.BYTES);default:throw new p(`Not a literal: ${t.value}`,t.line,t.column)}}parseIntLiteral(t){return t.startsWith("-0x")||t.startsWith("-0X")?-parseInt(t.substring(3),16):t.startsWith("0x")||t.startsWith("0X")?parseInt(t.substring(2),16):parseInt(t,10)}parseUintLiteral(t){const e=t.substring(0,t.length-1);return e.startsWith("0x")||e.startsWith("0X")?parseInt(e.substring(2),16):parseInt(e,10)}parseStringLiteral(t){const e=t.startsWith("r")||t.startsWith("R");let i;return e&&(t.substring(1).startsWith('"""')||t.substring(1).startsWith("'''"))?i=t.substring(4,t.length-3):t.startsWith('"""')||t.startsWith("'''")?(i=t.substring(3,t.length-3),i=this.unescapeString(i)):e?i=t.substring(2,t.length-1):(i=t.substring(1,t.length-1),i=this.unescapeString(i)),i}parseBytesLiteral(t){const e=t.substring(2,t.length-1);return this.unescapeString(e)}unescapeString(t){let e="",i=0;for(;i<t.length;)if(t[i]==="\\"&&i+1<t.length){const n=t[i+1];switch(n){case"\\":e+="\\",i+=2;break;case'"':e+='"',i+=2;break;case"'":e+="'",i+=2;break;case"`":e+="`",i+=2;break;case"?":e+="?",i+=2;break;case"a":e+="\x07",i+=2;break;case"b":e+="\b",i+=2;break;case"f":e+="\f",i+=2;break;case"n":e+=`
5
+ `,i+=2;break;case"r":e+="\r",i+=2;break;case"t":e+=" ",i+=2;break;case"v":e+="\v",i+=2;break;case"x":if(i+3<t.length){const o=t.substring(i+2,i+4);e+=String.fromCharCode(parseInt(o,16)),i+=4}else e+=t[i],i++;break;case"u":if(i+5<t.length){const o=t.substring(i+2,i+6);e+=String.fromCharCode(parseInt(o,16)),i+=6}else e+=t[i],i++;break;case"U":if(i+9<t.length){const o=t.substring(i+2,i+10),l=parseInt(o,16);e+=String.fromCodePoint(l),i+=10}else e+=t[i],i++;break;default:if(i+3<t.length&&n>="0"&&n<="3"&&t[i+2]>="0"&&t[i+2]<="7"&&t[i+3]>="0"&&t[i+3]<="7"){const o=t.substring(i+1,i+4);e+=String.fromCharCode(parseInt(o,8)),i+=4}else e+=t[i],i++}}else e+=t[i],i++;return e}isLiteralToken(t){return t===s.NULL||t===s.TRUE||t===s.FALSE||t===s.INT||t===s.UINT||t===s.DOUBLE||t===s.STRING||t===s.BYTES}isRelationalOp(t){return t===s.LT||t===s.LE||t===s.GT||t===s.GE||t===s.EQ||t===s.NE||t===s.IN}toBinaryOp(t){switch(t){case s.LT:return u.LESS;case s.LE:return u.LESS_EQUAL;case s.GT:return u.GREATER;case s.GE:return u.GREATER_EQUAL;case s.EQ:return u.EQUAL;case s.NE:return u.NOT_EQUAL;case s.IN:return u.IN;default:throw new p(`Unknown relational operator: ${t}`,this.current.line,this.current.column)}}match(t){return this.current.type===t?(this.advance(),!0):!1}expect(t){if(this.current.type!==t)throw new p(`Expected ${t} but found ${this.current.type}`,this.current.line,this.current.column);this.advance()}expectIdentifier(){if(this.current.type!==s.IDENTIFIER)throw new p(`Expected identifier but found ${this.current.value}`,this.current.line,this.current.column);const t=this.current.value;return this.advance(),t}advance(){this.current=this.lexer.next()}peekAhead(t){return this.lexer.peek(t)}isQualifiedStructLiteral(){let t=1,e=this.peekAhead(t);if(e.type!==s.IDENTIFIER)return!1;for(t++,e=this.peekAhead(t);e.type===s.DOT;){if(t++,e=this.peekAhead(t),e.type!==s.IDENTIFIER)return!1;t++,e=this.peekAhead(t)}return e.type===s.LBRACE}}class a extends Error{constructor(t){super(t),this.name="EvaluationError"}}function C(r){if(r==null)return 0;if(typeof r=="string"||Array.isArray(r))return r.length;if(typeof r=="object"&&r!==null)return Object.keys(r).length;throw new Error(`size() not supported for type: ${typeof r}`)}function O(r){if(typeof r=="number")return Math.trunc(r);if(typeof r=="string"){const t=parseInt(r,10);if(isNaN(t))throw new Error(`Cannot convert to int: ${r}`);return t}if(typeof r=="boolean")return r?1:0;throw new Error(`Cannot convert to int: ${r}`)}function P(r){const t=O(r);if(t<0)throw new Error(`Cannot convert negative value to uint: ${r}`);return t}function _(r){if(typeof r=="number")return r;if(typeof r=="string"){const t=parseFloat(r);if(isNaN(t))throw new Error(`Cannot convert to double: ${r}`);return t}throw new Error(`Cannot convert to double: ${r}`)}function k(r){return r==null?"null":String(r)}function U(r){return typeof r=="boolean"?r:typeof r=="number"?r!==0:typeof r=="string"||Array.isArray(r)?r.length>0:typeof r=="object"&&r!==null?Object.keys(r).length>0:r!=null}function F(r){return r==null?"null":typeof r=="boolean"?"bool":typeof r=="number"?Number.isInteger(r)?"int":"double":typeof r=="string"?"string":Array.isArray(r)?"list":typeof r=="object"?"map":"unknown"}function Q(r,t){return typeof r=="object"&&r!==null&&typeof t=="string"?t in r:!1}function q(r,t){try{return new RegExp(t).test(r)}catch{throw new Error(`Invalid regex pattern: ${t}`)}}function W(r){if(r.length===0)throw new Error("max() requires at least one argument");let t=r[0];for(let e=1;e<r.length;e++)E(r[e],t)>0&&(t=r[e]);return t}function j(r){if(r.length===0)throw new Error("min() requires at least one argument");let t=r[0];for(let e=1;e<r.length;e++)E(r[e],t)<0&&(t=r[e]);return t}function E(r,t){if(r===null&&t===null)return 0;if(r===null)return-1;if(t===null)return 1;if(typeof r=="number"&&typeof t=="number")return r-t;if(typeof r=="string"&&typeof t=="string")return r.localeCompare(t);if(typeof r=="boolean"&&typeof t=="boolean")return r===t?0:r?1:-1;if(Array.isArray(r)&&Array.isArray(t)){const e=Math.min(r.length,t.length);for(let i=0;i<e;i++){const n=E(r[i],t[i]);if(n!==0)return n}return r.length-t.length}throw new Error(`Cannot compare types: ${typeof r} and ${typeof t}`)}function g(r,t){if(r==null||t===null||t===void 0)return r===t;if(Array.isArray(r)&&Array.isArray(t)){if(r.length!==t.length)return!1;for(let e=0;e<r.length;e++)if(!g(r[e],t[e]))return!1;return!0}if(typeof r=="object"&&typeof t=="object"&&!Array.isArray(r)&&!Array.isArray(t)){const e=Object.keys(r),i=Object.keys(t);if(e.length!==i.length)return!1;for(const n of e)if(!(n in t)||!g(r[n],t[n]))return!1;return!0}return r===t}function K(r,t){for(const e of r)if(g(e,t))return!0;return!1}const Z=Object.freeze(Object.defineProperty({__proto__:null,asBool:U,asDouble:_,asInt:O,asString:k,asUInt:P,compare:E,containsInArray:K,deepEquals:g,has:Q,matches:q,max:W,min:j,sizeOf:C,typeOf:F},Symbol.toStringTag,{value:"Module"}));class v{callFunction(t,e){switch(t){case"size":return C(e[0]);case"int":return O(e[0]);case"uint":return P(e[0]);case"double":return _(e[0]);case"string":return k(e[0]);case"bool":return U(e[0]);case"type":return F(e[0]);case"has":if(e.length!==2)throw new Error("has() requires 2 arguments");return Q(e[0],e[1]);case"matches":if(e.length!==2)throw new Error("matches() requires 2 arguments");return q(e[0],e[1]);case"max":return W(e);case"min":return j(e);default:throw new Error(`Unknown function: ${t}`)}}callMethod(t,e,i){if(t==null)throw new Error("Cannot call method on null");switch(e){case"contains":if(typeof t=="string"&&i.length===1&&typeof i[0]=="string")return t.includes(i[0]);if(Array.isArray(t)&&i.length===1)return t.includes(i[0]);throw new Error("Invalid arguments for contains()");case"startsWith":if(typeof t=="string"&&i.length===1&&typeof i[0]=="string")return t.startsWith(i[0]);throw new Error("startsWith() requires string target and argument");case"endsWith":if(typeof t=="string"&&i.length===1&&typeof i[0]=="string")return t.endsWith(i[0]);throw new Error("endsWith() requires string target and argument");case"toLowerCase":if(typeof t=="string"&&i.length===0)return t.toLowerCase();throw new Error("toLowerCase() requires string target");case"toUpperCase":if(typeof t=="string"&&i.length===0)return t.toUpperCase();throw new Error("toUpperCase() requires string target");case"trim":if(typeof t=="string"&&i.length===0)return t.trim();throw new Error("trim() requires string target");case"replace":if(typeof t=="string"&&i.length===2&&typeof i[0]=="string"&&typeof i[1]=="string")return t.replaceAll(i[0],i[1]);throw new Error("replace() requires string target and 2 string arguments");case"split":if(typeof t=="string"&&i.length===1&&typeof i[0]=="string")return t.split(i[0]);throw new Error("split() requires string target and separator");case"size":return C(t);case"map":case"filter":case"all":case"exists":case"existsOne":throw new a(`Macro function ${e} was not properly handled by the interpreter`);default:return this.callNativeMethod(t,e,i)}}callNativeMethod(t,e,i){if(typeof t[e]=="function")try{return t[e](...i)}catch(n){throw new a(`Invocation of method '${e}' failed: ${n instanceof Error?n.message:String(n)}`)}throw new Error(`No such method '${e}' on type ${typeof t} with ${i.length} argument(s)`)}}class z{constructor(t,e){this.variables=t??{},this.functions=e??new v}evaluate(t){return t.accept(this)}visitLiteral(t){return t.value}visitIdentifier(t){if(!(t.name in this.variables))throw new a(`Undefined variable: ${t.name}`);return this.variables[t.name]}visitSelect(t){const e=t.operand!==null?this.evaluate(t.operand):this.variables;if(e==null){if(t.isTest)return!1;throw new a(`Cannot select field ${t.field} from null`)}if(typeof e=="object"){if(t.isTest)return t.field in e;if(!(t.field in e))throw new a(`Field ${t.field} not found`);return e[t.field]}throw new a("Cannot select field from non-object type")}visitCall(t){if(t.isMacro&&t.target!==null){const i=this.evaluate(t.target);if(t.args.length===0)throw new a(`Macro ${t.functionName} requires arguments`);const n=t.args[0];if(!(n instanceof S))throw new a(`First argument to macro ${t.functionName} must be a variable name`);const o=n.name;if(t.args.length<2)throw new a(`Macro ${t.functionName} requires an expression argument`);const l=t.args[1];return this.evaluateMacro(i,t.functionName,o,l)}const e=[];for(const i of t.args)e.push(this.evaluate(i));if(t.target!==null){const i=this.evaluate(t.target);return this.functions.callMethod(i,t.functionName,e)}else return this.functions.callFunction(t.functionName,e)}evaluateMacro(t,e,i,n){if(!Array.isArray(t))throw new a(`Macro ${e} requires a list target`);const o=this.variables[i],l=i in this.variables;try{switch(e){case"map":{const c=[];for(const f of t)this.variables[i]=f,c.push(this.evaluate(n));return c}case"filter":{const c=[];for(const f of t)this.variables[i]=f,this.evaluate(n)===!0&&c.push(f);return c}case"all":{for(const c of t)if(this.variables[i]=c,this.evaluate(n)!==!0)return!1;return!0}case"exists":{for(const c of t)if(this.variables[i]=c,this.evaluate(n)===!0)return!0;return!1}case"existsOne":{let c=0;for(const f of t)if(this.variables[i]=f,this.evaluate(n)===!0&&(c++,c>1))return!1;return c===1}default:throw new a(`Unknown macro function: ${e}`)}}finally{l?this.variables[i]=o:delete this.variables[i]}}visitList(t){const e=[];for(const i of t.elements)e.push(this.evaluate(i));return e}visitMap(t){const e={};for(const i of t.entries){const n=this.evaluate(i.key),o=this.evaluate(i.value);e[n]=o}return e}visitStruct(t){const e={};for(const i of t.fields)e[i.field]=this.evaluate(i.value);return e}visitComprehension(t){const e=this.evaluate(t.range);if(!Array.isArray(e))throw new a("Comprehension range must be a list");const i=this.variables[t.variable],n=this.variables[t.accumulator],o=t.variable in this.variables,l=t.accumulator in this.variables;try{let c=this.evaluate(t.initializer);this.variables[t.accumulator]=c;for(const f of e)this.variables[t.variable]=f,this.evaluate(t.condition)===!0&&(c=this.evaluate(t.step),this.variables[t.accumulator]=c);return this.evaluate(t.result)}finally{o?this.variables[t.variable]=i:delete this.variables[t.variable],l?this.variables[t.accumulator]=n:delete this.variables[t.accumulator]}}visitUnary(t){const e=this.evaluate(t.operand);switch(t.op){case A.NOT:if(typeof e!="boolean")throw new a("NOT operator requires boolean operand");return!e;case A.NEGATE:if(typeof e!="number")throw new a("Negation requires numeric operand");return-e;default:throw new a(`Unknown unary operator: ${t.op}`)}}visitBinary(t){if(t.op===u.LOGICAL_AND)return this.evaluate(t.left)!==!0?!1:this.evaluate(t.right)===!0;if(t.op===u.LOGICAL_OR)return this.evaluate(t.left)===!0?!0:this.evaluate(t.right)===!0;const e=this.evaluate(t.left),i=this.evaluate(t.right);switch(t.op){case u.ADD:if(typeof e=="string"||typeof i=="string")return String(e)+String(i);if(Array.isArray(e)&&Array.isArray(i))return[...e,...i];if(typeof e=="number"&&typeof i=="number")return e+i;throw new a("Invalid operands for addition");case u.SUBTRACT:if(typeof e=="number"&&typeof i=="number")return e-i;throw new a("Subtraction requires numeric operands");case u.MULTIPLY:if(typeof e=="number"&&typeof i=="number")return e*i;if(typeof e=="string"&&typeof i=="number")return e.repeat(i);if(Array.isArray(e)&&typeof i=="number"){const n=[];for(let o=0;o<i;o++)n.push(...e);return n}throw new a("Invalid operands for multiplication");case u.DIVIDE:if(typeof e=="number"&&typeof i=="number"){if(i===0)throw new a("Division by zero");return e/i}throw new a("Division requires numeric operands");case u.MODULO:if(typeof e=="number"&&typeof i=="number"){if(i===0)throw new a("Modulo by zero");return e%i}throw new a("Modulo requires integer operands");case u.EQUAL:return g(e,i);case u.NOT_EQUAL:return!g(e,i);case u.LESS:return E(e,i)<0;case u.LESS_EQUAL:return E(e,i)<=0;case u.GREATER:return E(e,i)>0;case u.GREATER_EQUAL:return E(e,i)>=0;case u.IN:if(Array.isArray(i))return K(i,e);if(typeof i=="object"&&i!==null)return e in i;if(typeof i=="string"&&typeof e=="string")return i.includes(e);throw new a("IN operator requires list, map, or string on right side");default:throw new a(`Unknown binary operator: ${t.op}`)}}visitConditional(t){return this.evaluate(t.condition)===!0?this.evaluate(t.thenExpr):this.evaluate(t.otherwiseExpr)}visitIndex(t){const e=this.evaluate(t.operand),i=this.evaluate(t.index);if(e==null)throw new a("Cannot index null value");if(Array.isArray(e)){if(typeof i!="number")throw new a("List index must be an integer");const n=Math.trunc(i);if(n<0||n>=e.length)throw new a(`List index out of bounds: ${n}`);return e[n]}else if(typeof e=="object"){if(!(i in e))throw new a(`Map key not found: ${i}`);return e[i]}else if(typeof e=="string"){if(typeof i!="number")throw new a("String index must be an integer");const n=Math.trunc(i);if(n<0||n>=e.length)throw new a(`String index out of bounds: ${n}`);return e[n]}throw new a(`Cannot index type: ${typeof e}`)}}class Y{constructor(t,e){this.ast=t,this.functions=e}evaluate(t={}){return new z({...t},this.functions).evaluate(this.ast)}}class I{constructor(t){this.functions=t??new v}static compile(t,e){const i=new $(t);return new Y(i.parse(),e)}static eval(t,e,i){return this.compile(t,e).evaluate(i)}compile(t){return I.compile(t,this.functions)}eval(t,e={}){return I.eval(t,this.functions,e)}}exports.Binary=L;exports.BinaryOp=u;exports.CEL=I;exports.Call=y;exports.Comprehension=V;exports.Conditional=G;exports.EvaluationError=a;exports.FieldInitializer=x;exports.Identifier=S;exports.Index=D;exports.Interpreter=z;exports.ListExpression=T;exports.Literal=d;exports.LiteralType=w;exports.MapEntry=B;exports.MapExpression=b;exports.ParseError=p;exports.Parser=$;exports.Program=Y;exports.Select=R;exports.StandardFunctions=v;exports.Struct=m;exports.Unary=N;exports.UnaryOp=A;exports.Utilities=Z;exports.asBool=U;
6
+ //# sourceMappingURL=index.cjs.map