├── .gitignore ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── package-lock.json ├── package.json ├── src ├── BuiltInFunctions.ts ├── CelValue.ts ├── Context.ts ├── ErrorCollector.ts ├── Evaluator.ts ├── Runtime.ts ├── TypeChecker.ts ├── Visitor.ts ├── generated │ ├── CEL.interp │ ├── CEL.tokens │ ├── CELLexer.interp │ ├── CELLexer.tokens │ ├── CELLexer.ts │ ├── CELListener.ts │ ├── CELParser.ts │ └── CELVisitor.ts ├── grammar │ └── CEL.g4 └── index.ts ├── tests ├── TypeChecker.test.ts ├── context.test.ts ├── evaluation.test.ts ├── functions.test.ts └── parsing.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp_cel_spec 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | .nyc_output 18 | coverage.* 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | typings/ 39 | lib/*.js 40 | /lib/ 41 | test/*.js 42 | *.map 43 | 44 | *~ 45 | .idea 46 | *.iml 47 | *#* 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cel-javascript 2 | 3 | ## This project is not ready for production yet. 4 | 5 | Parser and evaluator for CEL in JavaScript using ANTLR4 6 | This library provides parser and evaluator for Common Expression Language (CEL) expressions in Javascript and Typescript projects. CEL is a language developed by Google that allows for safe and fast evaluation of expressions in a wide variety of applications, such as policy engines, rule engines, and more. 7 | 8 | ## Features 9 | 10 | - Parse and evaluate CEL expressions directly within TypeScript projects. 11 | - Support for common arithmetic operations, logical operations, and comparisons. 12 | - Extensible design for adding custom functions and variables. 13 | - Error handling during parsing with custom error listeners. 14 | - Context-based evaluation to support dynamic expression evaluation. 15 | - DataType checker based on the values and existing context 16 | 17 | 18 | ## Installation 19 | 20 | Install the library via npm: 21 | 22 | ```bash 23 | npm install @gresb/cel-javascript 24 | ``` 25 | 26 | ## Usage 27 | ### Basic Example 28 | Here is a simple example of how to use the CEL Interpreter: 29 | 30 | ``` 31 | import { Runtime } from '@gresb/cel-javascript'; 32 | 33 | // Define a CEL expression to evaluate 34 | const celExpression = '1 + 2 * 3'; 35 | 36 | // Create an instance of Runtime with the CEL expression 37 | const runtime = new Runtime(celExpression); 38 | 39 | // Evaluate the expression within a given context 40 | const context = {}; // Replace with your actual context if needed 41 | const result = runtime.evaluate(context); 42 | 43 | console.log(`Result of '${celExpression}':`, result); // Output: Result of '1 + 2 * 3': 7 44 | 45 | ``` 46 | 47 | ### Parsing Validation 48 | You can validate if a CEL expression can be parsed without evaluating it: 49 | 50 | ``` 51 | import { Runtime } from '@gresb/cel-javascript'; 52 | 53 | const celExpression = '1 + 2 * 3'; 54 | const canParse = Runtime.canParse(celExpression); 55 | 56 | console.log(`Can parse '${celExpression}':`, canParse); 57 | 58 | ``` 59 | 60 | ### Error Handling During Parsing 61 | When parsing a CEL expression, you can catch errors and handle them accordingly: 62 | 63 | ``` 64 | import { Runtime } from '@gresb/cel-javascript'; 65 | 66 | const celExpression = 'a + b'; 67 | const typesContext = { a: 'int', b: 'string' }; // Define the expected types of variables 68 | 69 | const typeCheckResult = Runtime.typeCheck(celExpression, typesContext); 70 | 71 | if (typeCheckResult.success) { 72 | console.log('Type checking passed.'); 73 | } else { 74 | console.error('Type checking failed:', typeCheckResult.error); 75 | } 76 | // Output: Type checking failed: Operator '+' requires numeric or string operands, but got 'int' and 'string' 77 | 78 | ``` 79 | 80 | ### Typechecking Validation 81 | 82 | You can perform type checking to ensure that the expression is type-safe within a given context: 83 | 84 | ``` 85 | import { Runtime } from '@gresb/cel-javascript'; 86 | 87 | const celExpression = 'a + b'; 88 | const typesContext = { a: 'int', b: 'string' }; // Define the expected types of variables 89 | 90 | const typeCheckResult = Runtime.typeCheck(celExpression, typesContext); 91 | 92 | if (typeCheckResult.success) { 93 | console.log('Type checking passed.'); 94 | } else { 95 | console.error('Type checking failed:', typeCheckResult.error); 96 | } 97 | // Output: Type checking failed: Operator '+' requires numeric or string operands, but got 'int' and 'string' 98 | 99 | ``` 100 | 101 | *Note:* The typeCheck method checks if the expression is valid with the provided variable types without actually evaluating it. 102 | 103 | ### Evaluating Expressions 104 | Evaluating an expression will automatically perform parsing and type checking before execution. If the expression fails to parse or does not pass type checking, an error will be thrown. 105 | 106 | ``` 107 | import { Runtime } from '@gresb/cel-javascript'; 108 | 109 | const celExpression = 'a * (b + c)'; 110 | const context = { a: 2, b: 3, c: 4 }; // Provide the variables and their values 111 | 112 | const runtime = new Runtime(celExpression); 113 | 114 | try { 115 | const result = runtime.evaluate(context); 116 | console.log(`Result of '${celExpression}':`, result); // Output: Result of 'a * (b + c)': 14 117 | } catch (error) { 118 | console.error('Evaluation failed:', error.message); 119 | } 120 | 121 | ``` 122 | 123 | ### Contextual Evaluation with Variables 124 | 125 | You can pass variables to the expression through the context: 126 | 127 | 128 | ``` 129 | import { Runtime } from '@gresb/cel-javascript'; 130 | 131 | const celExpression = 'user.age >= 18'; 132 | const context = { 133 | user: { 134 | name: 'Alice', 135 | age: 20, 136 | }, 137 | }; 138 | 139 | const runtime = new Runtime(celExpression); 140 | 141 | const result = runtime.evaluate(context); 142 | 143 | console.log(`Is user adult?`, result); // Output: Is user adult? true 144 | 145 | ``` 146 | 147 | ### Built-in Functions 148 | 149 | The interpreter supports several built-in functions. Here's an example using the contains function: 150 | 151 | ``` 152 | import { Runtime } from '@gresb/cel-javascript'; 153 | 154 | const celExpression = 'contains(message, "world")'; 155 | const context = { 156 | message: 'Hello, world!', 157 | }; 158 | 159 | const runtime = new Runtime(celExpression); 160 | 161 | const result = runtime.evaluate(context); 162 | 163 | console.log(`Does message contain 'world'?`, result); // Output: Does message contain 'world'? true 164 | 165 | ``` 166 | 167 | Here is a list of supported functions: 168 | 169 | **Arithmetic Functions** 170 | - min(a, b, ...): Returns the minimum of the arguments. 171 | - max(a, b, ...): Returns the maximum of the arguments. 172 | - abs(x): Returns the absolute value of a number. 173 | - ceil(x): Rounds a number up to the nearest integer. 174 | - floor(x): Rounds a number down to the nearest integer. 175 | - round(x): Rounds a number to the nearest integer. 176 | 177 | **String Functions** 178 | - contains(string, substring): Checks if a string contains a specified substring. 179 | - endsWith(string, suffix): Checks if a string ends with a specified suffix. 180 | - indexOf(string, substring): Returns the index of a substring in a string. 181 | - length(string): Returns the length of a string. 182 | - lower(string): Converts a string to lowercase. 183 | - replace(string, search, replacement): Replaces occurrences of a substring within a string. 184 | - split(string, delimiter): Splits a string by a specified delimiter. 185 | - startsWith(string, prefix): Checks if a string starts with a specified prefix. 186 | - upper(string): Converts a string to uppercase. 187 | 188 | **List and Map Functions** 189 | - size(list|map): Returns the number of elements in a list or entries in a map. 190 | - has(list|map, key): Checks if a list or map contains a specified key. 191 | 192 | **Type Conversion Functions** 193 | - int(value): Converts a value to an integer. 194 | - uint(value): Converts a value to an unsigned integer. 195 | - double(value): Converts a value to a floating-point number. 196 | - string(value): Converts a value to a string. 197 | - bool(value): Converts a value to a boolean. 198 | 199 | **Null Handling Functions** 200 | - exists(value): Checks if a value is not null. 201 | - existsOne(list): Checks if at least one element in a list is not null. 202 | 203 | **Date and Time Functions** 204 | - timestamp(string): Converts a string to a timestamp. 205 | - duration(int): Returns a duration from an integer. 206 | - time(year, month, day, hour, minute, second, millisecond): Creates a timestamp. 207 | - date(year, month, day): Creates a date. 208 | - getFullYear(timestamp): Extracts the year from a timestamp. 209 | - getMonth(timestamp): Extracts the month from a timestamp. 210 | - getDate(timestamp): Extracts the day from a timestamp. 211 | - getHours(timestamp): Extracts the hour from a timestamp. 212 | - getMinutes(timestamp): Extracts the minutes from a timestamp. 213 | - getSeconds(timestamp): Extracts the seconds from a timestamp. 214 | 215 | 216 | ## Deployment 217 | 218 | To deploy the package to npm, follow these steps: 219 | 220 | *Update the Version:* Increment the version number in your package.json file according to semantic versioning. 221 | 222 | ``` 223 | { 224 | "name": "@gresb/cel-javascript", 225 | "version": "1.0.1", // Update this version 226 | ... 227 | } 228 | ``` 229 | 230 | **Authenticate with npm:** Log in to your npm account using your email address. 231 | 232 | 233 | ``` 234 | npm login -e EMAIL_ADDRESS 235 | ``` 236 | **Build the Package:** Ensure your package is built and ready for publishing. 237 | 238 | 239 | ``` 240 | npm run build 241 | ``` 242 | **Publish the Package:** Publish the package to npm registry with public access. 243 | 244 | ``` 245 | npm publish --access public 246 | ``` 247 | 248 | Note: Ensure that you have the necessary permissions to publish the package and that all changes are committed before publishing. 249 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-parts": { 20 | "inputs": { 21 | "nixpkgs-lib": "nixpkgs-lib" 22 | }, 23 | "locked": { 24 | "lastModified": 1722555600, 25 | "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", 26 | "owner": "hercules-ci", 27 | "repo": "flake-parts", 28 | "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "hercules-ci", 33 | "repo": "flake-parts", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1709126324, 43 | "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "d465f4819400de7c8d874d50b982301f28a84605", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixdoc": { 56 | "inputs": { 57 | "flake-compat": "flake-compat", 58 | "flake-utils": "flake-utils", 59 | "nixpkgs": "nixpkgs" 60 | }, 61 | "locked": { 62 | "lastModified": 1717797326, 63 | "narHash": "sha256-9gBrFudzn75rzx7bTPgr+zzUpX2cLHOmE12xFtoH1eA=", 64 | "owner": "nix-community", 65 | "repo": "nixdoc", 66 | "rev": "37121757bf509829f25b11976defc53b150b1ca8", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "nix-community", 71 | "repo": "nixdoc", 72 | "type": "github" 73 | } 74 | }, 75 | "nixpkgs": { 76 | "locked": { 77 | "lastModified": 1709780214, 78 | "narHash": "sha256-p4iDKdveHMhfGAlpxmkCtfQO3WRzmlD11aIcThwPqhk=", 79 | "owner": "NixOS", 80 | "repo": "nixpkgs", 81 | "rev": "f945939fd679284d736112d3d5410eb867f3b31c", 82 | "type": "github" 83 | }, 84 | "original": { 85 | "owner": "NixOS", 86 | "ref": "nixpkgs-unstable", 87 | "repo": "nixpkgs", 88 | "type": "github" 89 | } 90 | }, 91 | "nixpkgs-lib": { 92 | "locked": { 93 | "lastModified": 1722555339, 94 | "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", 95 | "type": "tarball", 96 | "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" 97 | }, 98 | "original": { 99 | "type": "tarball", 100 | "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" 101 | } 102 | }, 103 | "nixpkgs_2": { 104 | "locked": { 105 | "lastModified": 1722421184, 106 | "narHash": "sha256-/DJBI6trCeVnasdjUo9pbnodCLZcFqnVZiLUfqLH4jA=", 107 | "owner": "nixos", 108 | "repo": "nixpkgs", 109 | "rev": "9f918d616c5321ad374ae6cb5ea89c9e04bf3e58", 110 | "type": "github" 111 | }, 112 | "original": { 113 | "owner": "nixos", 114 | "ref": "nixos-unstable", 115 | "repo": "nixpkgs", 116 | "type": "github" 117 | } 118 | }, 119 | "root": { 120 | "inputs": { 121 | "flake-parts": "flake-parts", 122 | "nixdoc": "nixdoc", 123 | "nixpkgs": "nixpkgs_2" 124 | } 125 | }, 126 | "systems": { 127 | "locked": { 128 | "lastModified": 1681028828, 129 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 130 | "owner": "nix-systems", 131 | "repo": "default", 132 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 133 | "type": "github" 134 | }, 135 | "original": { 136 | "owner": "nix-systems", 137 | "repo": "default", 138 | "type": "github" 139 | } 140 | } 141 | }, 142 | "root": "root", 143 | "version": 7 144 | } 145 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "TypeScript CEL"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | flake-parts.url = "github:hercules-ci/flake-parts"; 7 | nixdoc.url = "github:nix-community/nixdoc"; 8 | }; 9 | 10 | outputs = { self, nixpkgs, flake-parts, ... }@inputs: 11 | flake-parts.lib.mkFlake { inherit inputs; } 12 | { 13 | systems = [ 14 | "x86_64-linux" 15 | ]; 16 | 17 | flake = { }; 18 | 19 | perSystem = { config, self', inputs', pkgs, system, ... }: 20 | let 21 | 22 | typescript = pkgs.nodePackages.typescript; 23 | 24 | antlr4 = pkgs.antlr4; 25 | 26 | 27 | in 28 | { 29 | devShells.default = pkgs.mkShell { 30 | nativeBuildInputs = [ 31 | antlr4 32 | typescript 33 | pkgs.nodejs 34 | pkgs.tree 35 | ]; 36 | 37 | shellHook = '' 38 | export PATH=$PATH:${typescript}/bin 39 | export PATH=$PATH:$(pwd)/node_modules/.bin 40 | ''; 41 | }; 42 | 43 | packages = { 44 | default = typescript; 45 | }; 46 | }; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gresb/cel-javascript", 3 | "version": "0.0.20", 4 | "description": "Parser and evaluator for Google's Common Expression Language (CEL) in JavaScript using ANTLR4", 5 | "main": "src/index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "build": "tsc", 9 | "test": "vitest" 10 | }, 11 | "author": "yottanami", 12 | "license": "unlicense", 13 | "dependencies": { 14 | "antlr4": "^4.13.2" 15 | }, 16 | "devDependencies": { 17 | "@types/jest": "^27.0.3", 18 | "jest": "^27.2.5", 19 | "ts-node": "^10.9.2", 20 | "typescript": "^4.4.4", 21 | "vitest": "^2.1.2" 22 | }, 23 | "directories": { 24 | "test": "tests" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/GRESB/cel-javascript.git" 29 | }, 30 | "keywords": [ 31 | "cel", 32 | "javascript", 33 | "typescript", 34 | "common", 35 | "expression", 36 | "language" 37 | ], 38 | "bugs": { 39 | "url": "https://github.com/GRESB/cel-javascript/issues" 40 | }, 41 | "homepage": "https://github.com/GRESB/cel-javascript#readme" 42 | } 43 | -------------------------------------------------------------------------------- /src/BuiltInFunctions.ts: -------------------------------------------------------------------------------- 1 | // BuiltInFunctions.ts 2 | 3 | export const builtInFunctions = { 4 | // Arithmetic Functions 5 | min: (...args: number[]) => Math.min(...args), 6 | max: (...args: number[]) => Math.max(...args), 7 | abs: (value: number) => Math.abs(value), 8 | ceil: (value: number) => Math.ceil(value), 9 | floor: (value: number) => Math.floor(value), 10 | round: (value: number) => Math.round(value), 11 | 12 | // String Functions 13 | contains: (str: string, substr: string) => str.includes(substr), 14 | endsWith: (str: string, substr: string) => str.endsWith(substr), 15 | indexOf: (str: string, substr: string) => str.indexOf(substr), 16 | length: (str: string) => str.length, 17 | lower: (str: string) => str.toLowerCase(), 18 | replace: (str: string, substr: string, newSubstr: string) => str.replace(substr, newSubstr), 19 | split: (str: string, separator: string) => str.split(separator), 20 | startsWith: (str: string, substr: string) => str.startsWith(substr), 21 | upper: (str: string) => str.toUpperCase(), 22 | 23 | // List Functions 24 | size: (value: any) => { 25 | if (typeof value === 'string' || Array.isArray(value)) { 26 | return value.length; 27 | } else if (value && typeof value === 'object') { 28 | return Object.keys(value).length; 29 | } else { 30 | throw new Error('size() function requires a string, array, or object'); 31 | } 32 | }, 33 | 34 | // Type Conversion Functions 35 | int: (value: any) => parseInt(value, 10), 36 | uint: (value: any) => Math.max(0, parseInt(value, 10)), 37 | double: (value: any) => parseFloat(value), 38 | string: (value: any) => String(value), 39 | bool: (value: any) => Boolean(value), 40 | 41 | // Null Handling Functions 42 | exists: (value: any) => value !== null && value !== undefined, 43 | existsOne: (list: any[]) => list.filter(item => item !== null && item !== undefined).length === 1, 44 | 45 | // Date/Time Functions 46 | matches: (value: string, regex: string) => { 47 | const re = new RegExp(regex); 48 | return re.test(value); 49 | }, 50 | timestamp: (value: string) => { 51 | const date = new Date(value); 52 | if (isNaN(date.getTime())) { 53 | throw new Error(`Invalid timestamp: ${value}`); 54 | } 55 | return date; 56 | }, 57 | type: (value: any) => { 58 | if (value === null) return 'null'; 59 | switch (typeof value) { 60 | case 'boolean': 61 | return 'bool'; 62 | case 'number': 63 | return Number.isInteger(value) ? 'int' : 'float'; 64 | case 'string': 65 | return 'string'; 66 | case 'object': 67 | if (Array.isArray(value)) return 'list'; 68 | else return 'object'; 69 | default: 70 | return typeof value; 71 | } 72 | }, 73 | duration: (seconds: number) => `${seconds}s`, 74 | time: (year: number, month: number, day: number, hour: number, minute: number, second: number, millisecond: number) => { 75 | return new Date(Date.UTC(year, month - 1, day, hour, minute, second, millisecond)).toISOString(); 76 | }, 77 | date: (year: number, month: number, day: number) => { 78 | return new Date(Date.UTC(year, month - 1, day)).toISOString().split('T')[0]; 79 | }, 80 | getFullYear: (date: Date) => date.getUTCFullYear(), 81 | getMonth: (date: Date) => date.getUTCMonth(), 82 | getDate: (date: Date) => date.getUTCDate(), 83 | getHours: (date: Date) => date.getUTCHours(), 84 | getMinutes: (date: Date) => date.getUTCMinutes(), 85 | getSeconds: (date: Date) => date.getUTCSeconds(), 86 | }; 87 | -------------------------------------------------------------------------------- /src/CelValue.ts: -------------------------------------------------------------------------------- 1 | export type CelValue = string | number | boolean | CelObject | CelList | null ; 2 | 3 | export interface CelObject { 4 | [key: string]: CelValue; 5 | } 6 | 7 | export type CelList = CelValue[]; 8 | -------------------------------------------------------------------------------- /src/Context.ts: -------------------------------------------------------------------------------- 1 | export default class Context { 2 | private variables: { [key: string]: any }; 3 | private types: { [key: string]: any }; 4 | 5 | constructor(variables: { [key: string]: any } = {}, types: { [key: string]: any } = {}) { 6 | this.variables = variables; 7 | this.types = types; 8 | } 9 | 10 | getVariable(name: string) { 11 | const parts = name.split('.'); 12 | let current = this.variables; 13 | for (const part of parts) { 14 | if (current && typeof current === 'object' && part in current) { 15 | current = current[part]; 16 | } else { 17 | return undefined; 18 | } 19 | } 20 | return current; 21 | } 22 | 23 | getType(name: string) { 24 | const parts = name.split('.'); 25 | let current = this.types; 26 | for (const part of parts) { 27 | if (current && typeof current === 'object' && part in current) { 28 | current = current[part]; 29 | } else { 30 | return undefined; 31 | } 32 | } 33 | return current; 34 | } 35 | 36 | setVariable(name: string, value: any) { 37 | this.variables[name] = value; 38 | } 39 | 40 | setType(name: string, type: any) { 41 | this.types[name] = type; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ErrorCollector.ts: -------------------------------------------------------------------------------- 1 | export class ErrorCollector { 2 | public errors: Array<{ 3 | line: number; 4 | column: number; 5 | message: string; 6 | offendingSymbol: any; 7 | }> = []; 8 | 9 | syntaxError( 10 | recognizer: any, 11 | offendingSymbol: any, 12 | line: number, 13 | column: number, 14 | msg: string, 15 | e: any 16 | ): void { 17 | this.errors.push({ 18 | line, 19 | column, 20 | message: msg, 21 | offendingSymbol, 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Evaluator.ts: -------------------------------------------------------------------------------- 1 | import CELVisitor from './generated/CELVisitor'; 2 | import { builtInFunctions } from './BuiltInFunctions'; 3 | import Context from './Context'; 4 | 5 | export class Evaluator extends CELVisitor { 6 | private context: Context; 7 | 8 | constructor(context: Context = new Context({})) { 9 | super(); 10 | this.context = context; 11 | } 12 | 13 | setContext(context: Context) { 14 | this.context = context; 15 | } 16 | 17 | visitStart = (ctx: any) => { 18 | return this.visit(ctx.expr()); 19 | }; 20 | 21 | visitExpr = (ctx: any) => { 22 | return this.visit(ctx.getChild(0)); 23 | }; 24 | 25 | visitConditionalOr = (ctx: any) => { 26 | if (ctx.children.length === 1) { 27 | return this.visit(ctx.getChild(0)); 28 | } else if (ctx.children.length === 5 && ctx.getChild(1).getText() === '?') { 29 | const condition = this.visit(ctx.getChild(0)); 30 | return condition ? this.visit(ctx.getChild(2)) : this.visit(ctx.getChild(4)); 31 | } else { 32 | let result = this.visit(ctx.getChild(0)); 33 | for (let i = 1; i < ctx.children.length; i += 2) { 34 | const right = this.visit(ctx.getChild(i + 1)); 35 | result = result || right; 36 | if (result) break; 37 | } 38 | return result; 39 | } 40 | }; 41 | 42 | visitConditionalAnd = (ctx: any) => { 43 | if (ctx.children.length === 1) { 44 | return this.visit(ctx.getChild(0)); 45 | } else { 46 | let result = this.visit(ctx.getChild(0)); 47 | for (let i = 1; i < ctx.children.length; i += 2) { 48 | const right = this.visit(ctx.getChild(i + 1)); 49 | result = result && right; 50 | if (!result) break; 51 | } 52 | return result; 53 | } 54 | }; 55 | 56 | visitRelationOp = (ctx: any) => { 57 | const left = this.visit(ctx.getChild(0)); 58 | const operator = ctx.getChild(1).getText(); 59 | const right = this.visit(ctx.getChild(2)); 60 | switch (operator) { 61 | case '==': 62 | return left === right; 63 | case '!=': 64 | return left !== right; 65 | case '<': 66 | return left < right; 67 | case '<=': 68 | return left <= right; 69 | case '>': 70 | return left > right; 71 | case '>=': 72 | return left >= right; 73 | default: 74 | throw new Error(`Unknown operator: ${operator}`); 75 | } 76 | }; 77 | 78 | visitRelationCalc = (ctx: any) => { 79 | return this.visit(ctx.getChild(0)); 80 | }; 81 | 82 | visitCalcAddSub = (ctx: any) => { 83 | if (ctx.children.length === 1) { 84 | return this.visit(ctx.getChild(0)); 85 | } else { 86 | let result = this.visit(ctx.getChild(0)); 87 | for (let i = 1; i < ctx.children.length; i += 2) { 88 | const operator = ctx.getChild(i).getText(); 89 | const right = this.visit(ctx.getChild(i + 1)); 90 | switch (operator) { 91 | case '+': 92 | result = result + right; 93 | break; 94 | case '-': 95 | result = result - right; 96 | break; 97 | default: 98 | throw new Error(`Unknown operator: ${operator}`); 99 | } 100 | } 101 | return result; 102 | } 103 | }; 104 | 105 | visitCalcMulDiv = (ctx: any) => { 106 | if (ctx.children.length === 1) { 107 | return this.visit(ctx.getChild(0)); 108 | } else { 109 | let result = this.visit(ctx.getChild(0)); 110 | for (let i = 1; i < ctx.children.length; i += 2) { 111 | const operator = ctx.getChild(i).getText(); 112 | const right = this.visit(ctx.getChild(i + 1)); 113 | switch (operator) { 114 | case '*': 115 | result = result * right; 116 | break; 117 | case '/': 118 | result = result / right; 119 | break; 120 | case '%': 121 | result = result % right; 122 | break; 123 | default: 124 | throw new Error(`Unknown operator: ${operator}`); 125 | } 126 | } 127 | return result; 128 | } 129 | }; 130 | 131 | visitCalcUnary = (ctx: any) => { 132 | if (ctx.children.length === 1) { 133 | return this.visit(ctx.getChild(0)); 134 | } else { 135 | const operator = ctx.getChild(0).getText(); 136 | const operand = this.visit(ctx.getChild(1)); 137 | switch (operator) { 138 | case '!': 139 | return !operand; 140 | case '-': 141 | return -operand; 142 | default: 143 | throw new Error(`Unknown unary operator: ${operator}`); 144 | } 145 | } 146 | }; 147 | 148 | visitLogicalNot = (ctx: any) => { 149 | const value = this.visit(ctx.getChild(1)); 150 | return !value; 151 | }; 152 | 153 | visitNegate = (ctx: any) => { 154 | const value = this.visit(ctx.getChild(1)); 155 | return -value; 156 | }; 157 | 158 | visitMemberExpr = (ctx: any) => { 159 | return this.visit(ctx.getChild(0)); 160 | }; 161 | 162 | visitSelectOrCall = (ctx: any) => { 163 | if (ctx.children.length === 1) { 164 | return this.visit(ctx.getChild(0)); 165 | } 166 | 167 | const object = this.visit(ctx.getChild(0)); 168 | const member = ctx.getChild(2).getText(); 169 | 170 | if (ctx.children.length === 3) { 171 | if (object && typeof object === 'object' && member in object) { 172 | return object[member]; 173 | } else { 174 | throw new Error(`Property '${member}' does not exist on object`); 175 | } 176 | } else if (ctx.children.length >= 4 && ctx.getChild(3).getText() === '(') { 177 | let args = []; 178 | if (ctx.children.length === 5) { 179 | args = this.visitExprList(ctx.getChild(4)); 180 | } 181 | 182 | if (builtInFunctions.hasOwnProperty(member)) { 183 | try { 184 | // @ts-ignore 185 | return builtInFunctions[member](object, args); 186 | 187 | } catch (error) { 188 | throw new Error(`Error in method '${member}': ${(error as Error).message}`); 189 | } 190 | } else { 191 | throw new Error(`Method '${member}' is not defined`); 192 | } 193 | } else { 194 | throw new Error('Invalid member expression'); 195 | } 196 | }; 197 | 198 | visitPrimaryExpr = (ctx: any) => { 199 | if (ctx.children.length === 1) { 200 | return this.visit(ctx.getChild(0)); 201 | } else if (ctx.children.length === 3 && ctx.getChild(0).getText() === '(') { 202 | return this.visit(ctx.getChild(1)); 203 | } else { 204 | throw new Error('Invalid primary expression'); 205 | } 206 | }; 207 | 208 | visitIdentOrGlobalCall = (ctx: any) => { 209 | const id = ctx.getChild(0).getText(); 210 | 211 | if (ctx.children.length === 1) { 212 | const variableValue = this.context.getVariable(id); 213 | if (variableValue !== undefined) { 214 | return variableValue; 215 | } 216 | throw new Error(`Variable '${id}' is not defined`); 217 | } else if (ctx.children.length >= 3 && ctx.getChild(1).getText() === '(') { 218 | let args = []; 219 | if (ctx.children.length === 4) { 220 | args = this.visitExprList(ctx.getChild(2)); 221 | } 222 | 223 | if (builtInFunctions.hasOwnProperty(id)) { 224 | try { 225 | // @ts-ignore 226 | return builtInFunctions[id](...args); 227 | } catch (error) { 228 | throw new Error(`Error in function '${id}': ${(error as Error).message}`); 229 | } 230 | } else { 231 | throw new Error(`Function '${id}' is not defined`); 232 | } 233 | } else { 234 | throw new Error('Invalid function call'); 235 | } 236 | }; 237 | 238 | visitNested = (ctx: any) => { 239 | return this.visit(ctx.expr()); 240 | }; 241 | 242 | visitExprList = (ctx: any) => { 243 | const exprs = []; 244 | for (let i = 0; i < ctx.children.length; i += 2) { 245 | exprs.push(this.visit(ctx.getChild(i))); 246 | } 247 | return exprs; 248 | }; 249 | 250 | visitConstantLiteral = (ctx: any) => { 251 | return this.visit(ctx.getChild(0)); 252 | }; 253 | 254 | visitInt = (ctx: any) => { 255 | const text = ctx.getText(); 256 | return parseInt(text, 10); 257 | }; 258 | 259 | visitUint = (ctx: any) => { 260 | const text = ctx.getText(); 261 | return parseInt(text, 10); 262 | }; 263 | 264 | visitDouble = (ctx: any) => { 265 | const text = ctx.getText(); 266 | return parseFloat(text); 267 | }; 268 | 269 | visitString = (ctx: any) => { 270 | let text = ctx.getText(); 271 | text = text.substring(1, text.length - 1).replace(/\\'/g, "'"); 272 | return text; 273 | }; 274 | 275 | visitBoolTrue = (ctx: any) => { 276 | return true; 277 | }; 278 | 279 | visitBoolFalse = (ctx: any) => { 280 | return false; 281 | }; 282 | 283 | visitNull = (ctx: any) => { 284 | return null; 285 | }; 286 | 287 | visitIdent = (ctx: any) => { 288 | const id = ctx.getText(); 289 | const variableValue = this.context.getVariable(id); 290 | if (variableValue !== undefined) { 291 | return variableValue; 292 | } 293 | throw new Error(`Variable '${id}' is not defined`); 294 | }; 295 | 296 | visitConditionalExpr = (ctx: any) => { 297 | if (ctx.children.length === 1) { 298 | return this.visit(ctx.getChild(0)); 299 | } else { 300 | const condition = this.visit(ctx.getChild(0)); 301 | return condition ? this.visit(ctx.getChild(2)) : this.visit(ctx.getChild(4)); 302 | } 303 | }; 304 | } 305 | 306 | export default Evaluator; 307 | -------------------------------------------------------------------------------- /src/Runtime.ts: -------------------------------------------------------------------------------- 1 | import antlr4 from 'antlr4'; 2 | import CELLexer from './generated/CELLexer.js'; 3 | import CELParser from './generated/CELParser.js'; 4 | import Evaluator from './Evaluator'; 5 | import Context from './Context'; 6 | import { ErrorCollector } from './ErrorCollector'; 7 | import TypeChecker from './TypeChecker'; 8 | 9 | export class Runtime { 10 | private ast: any = null; 11 | private celExpression: string; 12 | private errors: Array<{ 13 | line: number; 14 | column: number; 15 | message: string; 16 | offendingSymbol: any; 17 | }> = []; 18 | 19 | constructor(celExpression: string) { 20 | //const chars = new antlr4.InputStream(celExpression); 21 | // const lexer = new CELLexer(chars); 22 | this.celExpression = celExpression; 23 | const chars = antlr4.CharStreams.fromString(celExpression); 24 | const lexer = new CELLexer(chars); 25 | const tokens = new antlr4.CommonTokenStream(lexer); 26 | const parser = new CELParser(tokens); 27 | parser.buildParseTrees = true; 28 | 29 | parser.removeErrorListeners(); 30 | const errorCollector = new ErrorCollector(); 31 | parser.addErrorListener(errorCollector); 32 | 33 | try { 34 | this.ast = parser.start(); 35 | } catch (error) { 36 | this.ast = null; 37 | } 38 | 39 | if (errorCollector.errors.length > 0) { 40 | this.errors = errorCollector.errors; 41 | this.ast = null; 42 | } 43 | } 44 | 45 | static canParse(celExpression: string): boolean { 46 | const runtime: Runtime = new Runtime(celExpression); 47 | return runtime.ast !== null; 48 | } 49 | 50 | static parseString(celExpression: string): { success: boolean; error?: string } { 51 | const runtime: Runtime = new Runtime(celExpression); 52 | if (runtime.ast !== null) { 53 | return { success: true }; 54 | } else { 55 | return { 56 | success: false, 57 | error: runtime.errors[0]?.message || 'Parsing failed with errors', 58 | }; 59 | } 60 | } 61 | 62 | static typeCheck(expression: string, context?: any, types?: any): { success: boolean; error?: string } { 63 | const runtime: Runtime = new Runtime(expression); 64 | if (runtime.ast !== null) { 65 | try { 66 | let contextObj: Context; 67 | if (context instanceof Context) { 68 | contextObj = context; 69 | } else { 70 | contextObj = new Context(context || {}, types || {}); 71 | } 72 | const typeChecker: TypeChecker = new TypeChecker(contextObj); 73 | typeChecker.visit(runtime.ast); 74 | return { success: true }; 75 | } catch (error) { 76 | return { success: false, error: (error as Error).message }; 77 | } 78 | } else { 79 | return { success: false, error: 'Parsing failed with errors' }; 80 | } 81 | } 82 | 83 | 84 | 85 | evaluate(context: any, types?: any) { 86 | if (!this.ast) { 87 | throw new Error('AST is not available. Parsing might have failed.'); 88 | } 89 | const contextObj: Context = new Context(context, types); 90 | const typeCheckResult = Runtime.typeCheck(this.celExpression, contextObj); 91 | if(typeCheckResult.success) { 92 | const evaluator: Evaluator = new Evaluator(contextObj); 93 | return evaluator.visit(this.ast); 94 | } else { 95 | throw new Error(typeCheckResult.error); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/TypeChecker.ts: -------------------------------------------------------------------------------- 1 | import CELVisitor from './generated/CELVisitor'; 2 | import Context from './Context'; 3 | 4 | type FunctionSignature = { args: (string | string[])[]; returnType: string; varArgs?: boolean }; 5 | 6 | class TypeChecker extends CELVisitor { 7 | private context: Context; 8 | 9 | private functionSignatures: { [key: string]: FunctionSignature } = { 10 | min: { args: ['int'], varArgs: true, returnType: 'int' }, 11 | max: { args: ['int'], varArgs: true, returnType: 'int' }, 12 | abs: { args: ['int'], returnType: 'int' }, 13 | ceil: { args: ['float'], returnType: 'int' }, 14 | floor: { args: ['float'], returnType: 'int' }, 15 | round: { args: ['float'], returnType: 'int' }, 16 | contains: { args: ['string', 'string'], returnType: 'bool' }, 17 | endsWith: { args: ['string', 'string'], returnType: 'bool' }, 18 | indexOf: { args: ['string', 'string'], returnType: 'int' }, 19 | length: { args: ['string'], returnType: 'int' }, 20 | lower: { args: ['string'], returnType: 'string' }, 21 | replace: { args: ['string', 'string', 'string'], returnType: 'string' }, 22 | split: { args: ['string', 'string'], returnType: 'list' }, 23 | startsWith: { args: ['string', 'string'], returnType: 'bool' }, 24 | upper: { args: ['string'], returnType: 'string' }, 25 | size: { args: [['string', 'list', 'map']], returnType: 'int' }, 26 | int: { args: ['any'], returnType: 'int' }, 27 | uint: { args: ['any'], returnType: 'int' }, 28 | double: { args: ['any'], returnType: 'float' }, 29 | string: { args: ['any'], returnType: 'string' }, 30 | bool: { args: ['any'], returnType: 'bool' }, 31 | exists: { args: ['any'], returnType: 'bool' }, 32 | existsOne: { args: ['list'], returnType: 'bool' }, 33 | matches: { args: ['string', 'string'], returnType: 'bool' }, 34 | timestamp: { args: ['string'], returnType: 'timestamp' }, 35 | type: { args: ['any'], returnType: 'string' }, 36 | duration: { args: ['int'], returnType: 'duration' }, 37 | time: { args: ['int', 'int', 'int', 'int', 'int', 'int', 'int'], returnType: 'timestamp' }, 38 | date: { args: ['int', 'int', 'int'], returnType: 'date' }, 39 | getFullYear: { args: ['timestamp'], returnType: 'int' }, 40 | getMonth: { args: ['timestamp'], returnType: 'int' }, 41 | getDate: { args: ['timestamp'], returnType: 'int' }, 42 | getHours: { args: ['timestamp'], returnType: 'int' }, 43 | getMinutes: { args: ['timestamp'], returnType: 'int' }, 44 | getSeconds: { args: ['timestamp'], returnType: 'int' }, 45 | has: { args: ['list', 'any'], returnType: 'bool' } 46 | }; 47 | 48 | constructor(context: Context) { 49 | super(); 50 | if (!(context instanceof Context)) { 51 | throw new Error('TypeChecker requires a Context object'); 52 | } 53 | this.context = context; 54 | } 55 | 56 | visit = (ctx: any): any => { 57 | return super.visit(ctx); 58 | } 59 | 60 | getRuleName = (ctx: any): string => { 61 | return ctx.constructor.name.replace('Context', ''); 62 | } 63 | 64 | visitStart = (ctx: any): any => { 65 | return this.visit(ctx.expr()); 66 | } 67 | 68 | visitExpr = (ctx: any): any => { 69 | return this.visit(ctx.getChild(0)); 70 | }; 71 | 72 | visitSelectOrCall = (ctx: any): string => { 73 | const objectType = this.visit(ctx.getChild(0)); 74 | const memberName = ctx.getChild(2).getText(); 75 | 76 | if (objectType && typeof objectType === 'object' && memberName in objectType) { 77 | return objectType[memberName]; 78 | } else { 79 | 80 | const objectName = ctx.getChild(0).getText(); 81 | const objectValue = this.context.getVariable(objectName); 82 | if (objectValue && typeof objectValue === 'object' && memberName in objectValue) { 83 | const memberValue = objectValue[memberName]; 84 | const memberType = getType(memberValue); 85 | return memberType; 86 | } else { 87 | throw new Error(`Cannot access property '${memberName}' on object '${objectName}'`); 88 | } 89 | } 90 | }; 91 | 92 | 93 | resolveObjectValue = (node: any): any => { 94 | const ruleName = node.constructor.name; 95 | 96 | if (ruleName === 'IdentOrGlobalCallContext') { 97 | const id = node.getText(); 98 | const variableValue = this.context.getVariable(id); 99 | if (variableValue === undefined) { 100 | throw new Error(`Variable '${id}' is not defined`); 101 | } 102 | return variableValue; 103 | } else if (ruleName === 'SelectOrCallContext') { 104 | const objectValue = this.resolveObjectValue(node.getChild(0)); 105 | const memberName = node.getChild(2).getText(); 106 | if (objectValue && typeof objectValue === 'object' && memberName in objectValue) { 107 | return objectValue[memberName]; 108 | } else { 109 | throw new Error(`Property '${memberName}' does not exist on object`); 110 | } 111 | } else { 112 | throw new Error(`Cannot resolve value of node type '${ruleName}'`); 113 | } 114 | }; 115 | 116 | 117 | visitIdentOrGlobalCall = (ctx: any): any => { 118 | const id = ctx.getChild(0).getText(); 119 | 120 | if (ctx.children.length === 1) { 121 | let varType = this.context.getType(id); 122 | if (varType === undefined) { 123 | const variableValue = this.context.getVariable(id); 124 | if (variableValue !== undefined) { 125 | varType = getType(variableValue) as any; 126 | this.context.setType(id, varType); 127 | } else { 128 | throw new Error(`Variable '${id}' is not defined`); 129 | } 130 | } 131 | return varType; 132 | } else if (ctx.getChildCount() >= 3 && ctx.getChild(1).getText() === '(') { 133 | const args: any[] = this.visit(ctx.exprList()); 134 | const flattenedArgs = args.filter(value => value !== undefined && value !== null && value !== ''); 135 | 136 | const signature = this.functionSignatures[id]; 137 | 138 | if (!signature) { 139 | throw new Error(`Function '${id}' is not defined`); 140 | } 141 | 142 | if (signature.varArgs && flattenedArgs.length < signature.args.length) { 143 | throw new Error(`Function '${id}' expects at least ${signature.args.length} arguments, but got ${flattenedArgs.length}`); 144 | } else if (!signature.varArgs && flattenedArgs.length !== signature.args.length) { 145 | throw new Error(`Function '${id}' expects ${signature.args.length} arguments, but got ${flattenedArgs.length}`); 146 | } 147 | 148 | for (let i = 0; i < flattenedArgs.length; i++) { 149 | const expectedTypes = signature.args[signature.varArgs ? 0 : i] || 'any'; 150 | const actualType = flattenedArgs[i]; 151 | let expectedTypeArray = Array.isArray(expectedTypes) ? expectedTypes : [expectedTypes]; 152 | let typeMatched = false; 153 | 154 | for (const expectedType of expectedTypeArray) { 155 | if (expectedType === 'any' || expectedType === actualType) { 156 | typeMatched = true; 157 | break; 158 | } 159 | 160 | if (expectedType.startsWith('list<') && actualType.startsWith('list<')) { 161 | const expectedElemType = expectedType.slice(5, -1); 162 | const actualElemType = actualType.slice(5, -1); 163 | if (expectedElemType === 'any' || expectedElemType === actualElemType) { 164 | typeMatched = true; 165 | break; 166 | } 167 | } 168 | } 169 | 170 | if (!typeMatched) { 171 | throw new Error(`Argument ${i + 1} of function '${id}' expects type '${expectedTypes}', but got '${actualType}'`); 172 | } 173 | } 174 | 175 | return signature.returnType; 176 | } else { 177 | throw new Error('Invalid identifier or function call'); 178 | } 179 | } 180 | 181 | visitCalcAddSub = (ctx: any): string => { 182 | let leftType = this.visit(ctx.getChild(0)); 183 | leftType = normalizeType(leftType); 184 | 185 | for (let i = 1; i < ctx.getChildCount(); i += 2) { 186 | const operator = ctx.getChild(i).getText(); 187 | let rightType = this.visit(ctx.getChild(i + 1)); 188 | rightType = normalizeType(rightType); 189 | 190 | const operators: string[] = ['+', '-']; 191 | const possibleTypesAdd: string[] = ['int', 'float', 'string']; 192 | const possibleTypesSub: string[] = ['int', 'float']; 193 | if(!operators.includes(operator)) { 194 | throw new Error(`Unknown operator '${operator}'`); 195 | } 196 | if((leftType !== rightType) || (operator === '+' && !possibleTypesAdd.includes(leftType)) || (operator === '-' && !possibleTypesSub.includes(leftType))) { 197 | throw new Error(`Operator '${operator}' requires matching types, but got '${leftType}' and '${rightType}'`); 198 | } 199 | } 200 | 201 | return leftType; 202 | }; 203 | 204 | visitCalcMulDiv = (ctx: any): string => { 205 | let leftType = this.visit(ctx.getChild(0)); 206 | 207 | for (let i = 1; i < ctx.getChildCount(); i += 2) { 208 | const operator = ctx.getChild(i).getText(); 209 | let rightType = this.visit(ctx.getChild(i + 1)); 210 | 211 | leftType = normalizeType(leftType); 212 | rightType = normalizeType(rightType); 213 | 214 | const operators: string[] = ['*', '/', '%']; 215 | const possibleCalcMulDivTypes: string[] = ['int', 'float']; 216 | if(!operators.includes(operator)) { 217 | throw new Error(`Unknown operator '${operator}'`); 218 | } 219 | if((leftType !== rightType) || (!possibleCalcMulDivTypes.includes(leftType))) { 220 | throw new Error(`Operator '${operator}' requires matching numeric operands, but got '${leftType}' and '${rightType}'`); 221 | } 222 | } 223 | 224 | return leftType; 225 | }; 226 | 227 | visitLogicalNot = (ctx: any): string => { 228 | let exprType = this.visit(ctx.getChild(1)); 229 | exprType = normalizeType(exprType); 230 | if (exprType !== 'bool') { 231 | throw new Error(`Logical '!' requires boolean operand, but got '${exprType}'`); 232 | } 233 | return 'bool'; 234 | } 235 | 236 | visitConditionalAnd = (ctx: any): string => { 237 | let resultType = this.visit(ctx.getChild(0)); 238 | resultType = normalizeType(resultType); 239 | for (let i = 1; i < ctx.getChildCount(); i += 2) { 240 | let nextExprType = this.visit(ctx.getChild(i + 1)); 241 | nextExprType = normalizeType(nextExprType); 242 | if (resultType !== 'bool' || nextExprType !== 'bool') { 243 | throw new Error(`Logical '&&' requires boolean operands, but got '${resultType}' and '${nextExprType}'`); 244 | } 245 | 246 | resultType = 'bool'; 247 | } 248 | 249 | return resultType; 250 | } 251 | 252 | visitConditionalOr = (ctx: any): string => { 253 | let resultType = this.visit(ctx.getChild(0)); 254 | resultType = normalizeType(resultType); 255 | for (let i = 1; i < ctx.getChildCount(); i += 2) { 256 | let nextExprType = this.visit(ctx.getChild(i + 1)); 257 | nextExprType = normalizeType(nextExprType); 258 | if (resultType !== 'bool' || nextExprType !== 'bool') { 259 | throw new Error(`Logical '||' requires boolean operands, but got '${resultType}' and '${nextExprType}'`); 260 | } 261 | 262 | resultType = 'bool'; 263 | } 264 | 265 | return resultType; 266 | } 267 | 268 | visitPrimaryExpr = (ctx: any): string => { 269 | if (ctx.getChildCount() === 1) { 270 | return this.visit(ctx.getChild(0)); 271 | } else if (ctx.getChildCount() === 3 && ctx.getChild(0).getText() === '(') { 272 | return this.visit(ctx.getChild(1)); 273 | } else { 274 | throw new Error('Invalid primary expression'); 275 | } 276 | } 277 | 278 | visitConstantLiteral = (ctx: any): string => { 279 | return this.visit(ctx.getChild(0)); 280 | }; 281 | 282 | visitInt = (ctx: any): string => { 283 | return 'int'; 284 | }; 285 | 286 | visitDouble = (ctx: any): string => { 287 | return 'float'; 288 | }; 289 | 290 | visitString = (ctx: any): string => { 291 | return 'string'; 292 | }; 293 | 294 | visitBoolTrue = (ctx: any): string => { 295 | return 'bool'; 296 | }; 297 | 298 | visitBoolFalse = (ctx: any): string => { 299 | return 'bool'; 300 | }; 301 | 302 | visitNull = (ctx: any): string => { 303 | return 'null'; 304 | }; 305 | 306 | 307 | visitRelationOp = (ctx: any): string => { 308 | const leftType = this.visit(ctx.getChild(0)); 309 | const operator = ctx.getChild(1).getText(); 310 | const rightType = this.visit(ctx.getChild(2)); 311 | 312 | const normalizedLeftType: string = normalizeType(leftType); 313 | const normalizedRightType: string = normalizeType(rightType); 314 | 315 | if (operator === '==' || operator === '!=') { 316 | if (normalizedLeftType !== normalizedRightType) { 317 | throw new Error(`Mismatching types: Cannot compare '${normalizedLeftType}' and '${normalizedRightType}' with '${operator}'`); 318 | } 319 | } else if (['<', '<=', '>', '>='].includes(operator)) { 320 | if (normalizedLeftType !== normalizedRightType) { 321 | throw new Error(`Mismatching types: Cannot compare '${normalizedLeftType}' and '${normalizedRightType}' with '${operator}'`); 322 | } 323 | if(!(normalizedLeftType === 'int' || normalizedLeftType === 'float')) { 324 | throw new Error(`Operator '${operator}' requires numeric operands, but got '${normalizedLeftType}' and '${normalizedRightType}'`); 325 | } 326 | } else if (operator === 'in') { 327 | } else { 328 | throw new Error(`Unknown operator '${operator}'`); 329 | } 330 | 331 | return 'bool'; 332 | }; 333 | 334 | visitExprList = (ctx: any): string[] => { 335 | const types = []; 336 | for (let i = 0; i < ctx.getChildCount(); i += 2) { // assuming commas are every second child 337 | const type = this.visit(ctx.getChild(i)); 338 | types.push(type); 339 | } 340 | return types; 341 | }; 342 | 343 | 344 | 345 | } 346 | 347 | const getType = (value: any): string => { 348 | if (value === null) { 349 | return 'null'; 350 | } 351 | 352 | if (Array.isArray(value)) { 353 | const elementTypes = [...new Set(value.map(getType))]; 354 | if (elementTypes.length === 1) { 355 | return `list<${elementTypes[0]}>`; 356 | } else { 357 | return 'list'; 358 | } 359 | } 360 | 361 | if (typeof value === 'number') { 362 | return Number.isInteger(value) ? 'int' : 'float'; 363 | } 364 | 365 | if (typeof value === 'boolean') { 366 | return 'bool'; 367 | } 368 | 369 | if (typeof value === 'string') { 370 | return 'string'; 371 | } 372 | 373 | if (value instanceof Date) { 374 | return 'timestamp'; 375 | } 376 | 377 | if (typeof value === 'object') { 378 | return 'map'; 379 | } 380 | 381 | throw new Error(`Unsupported type: ${typeof value}`); 382 | }; 383 | 384 | 385 | const normalizeType = (input: any): string => { 386 | if (typeof input === 'string') { 387 | return input.trim(); 388 | } else if (Array.isArray(input)) { 389 | const flatArray = input.flat(Infinity) 390 | .filter(value => value !== undefined && value !== null && value !== ''); 391 | const uniqueTypes = [...new Set(flatArray)]; 392 | if (uniqueTypes.length === 1) { 393 | return uniqueTypes[0]; 394 | } else if (uniqueTypes.length === 0) { 395 | return 'unknown'; 396 | } else { 397 | return 'unknown'; 398 | } 399 | } else { 400 | throw new Error(`Unsupported input type: ${typeof input}`); 401 | } 402 | }; 403 | 404 | export default TypeChecker; 405 | -------------------------------------------------------------------------------- /src/Visitor.ts: -------------------------------------------------------------------------------- 1 | // Placeholder for generated visitor interface 2 | export interface Visitor { 3 | visit(node: any): Result; 4 | visitChildren(ctx: any): Result; 5 | visitTerminal(node: any): Result; 6 | visitErrorNode(node: any): Result; 7 | } 8 | -------------------------------------------------------------------------------- /src/generated/CEL.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '==' 4 | '!=' 5 | 'in' 6 | '<' 7 | '<=' 8 | '>=' 9 | '>' 10 | '&&' 11 | '||' 12 | '[' 13 | ']' 14 | '{' 15 | '}' 16 | '(' 17 | ')' 18 | '.' 19 | ',' 20 | '-' 21 | '!' 22 | '?' 23 | ':' 24 | '+' 25 | '*' 26 | '/' 27 | '%' 28 | 'true' 29 | 'false' 30 | 'null' 31 | null 32 | null 33 | null 34 | null 35 | null 36 | null 37 | null 38 | null 39 | 40 | token symbolic names: 41 | null 42 | EQUALS 43 | NOT_EQUALS 44 | IN 45 | LESS 46 | LESS_EQUALS 47 | GREATER_EQUALS 48 | GREATER 49 | LOGICAL_AND 50 | LOGICAL_OR 51 | LBRACKET 52 | RPRACKET 53 | LBRACE 54 | RBRACE 55 | LPAREN 56 | RPAREN 57 | DOT 58 | COMMA 59 | MINUS 60 | EXCLAM 61 | QUESTIONMARK 62 | COLON 63 | PLUS 64 | STAR 65 | SLASH 66 | PERCENT 67 | TRUE 68 | FALSE 69 | NULL 70 | WHITESPACE 71 | COMMENT 72 | NUM_FLOAT 73 | NUM_INT 74 | NUM_UINT 75 | STRING 76 | BYTES 77 | IDENTIFIER 78 | 79 | rule names: 80 | start 81 | expr 82 | conditionalOr 83 | conditionalAnd 84 | relation 85 | calc 86 | unary 87 | member 88 | primary 89 | exprList 90 | fieldInitializerList 91 | mapInitializerList 92 | literal 93 | 94 | 95 | atn: 96 | [4, 1, 36, 209, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 36, 8, 1, 1, 2, 1, 2, 1, 2, 5, 2, 41, 8, 2, 10, 2, 12, 2, 44, 9, 2, 1, 3, 1, 3, 1, 3, 5, 3, 49, 8, 3, 10, 3, 12, 3, 52, 9, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 60, 8, 4, 10, 4, 12, 4, 63, 9, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 74, 8, 5, 10, 5, 12, 5, 77, 9, 5, 1, 6, 1, 6, 4, 6, 81, 8, 6, 11, 6, 12, 6, 82, 1, 6, 1, 6, 4, 6, 87, 8, 6, 11, 6, 12, 6, 88, 1, 6, 3, 6, 92, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 102, 8, 7, 1, 7, 3, 7, 105, 8, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 115, 8, 7, 1, 7, 3, 7, 118, 8, 7, 1, 7, 5, 7, 121, 8, 7, 10, 7, 12, 7, 124, 9, 7, 1, 8, 3, 8, 127, 8, 8, 1, 8, 1, 8, 1, 8, 3, 8, 132, 8, 8, 1, 8, 3, 8, 135, 8, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 143, 8, 8, 1, 8, 3, 8, 146, 8, 8, 1, 8, 1, 8, 1, 8, 3, 8, 151, 8, 8, 1, 8, 3, 8, 154, 8, 8, 1, 8, 1, 8, 3, 8, 158, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 163, 8, 9, 10, 9, 12, 9, 166, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 175, 8, 10, 10, 10, 12, 10, 178, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 188, 8, 11, 10, 11, 12, 11, 191, 9, 11, 1, 12, 3, 12, 194, 8, 12, 1, 12, 1, 12, 1, 12, 3, 12, 199, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 207, 8, 12, 1, 12, 0, 3, 8, 10, 14, 13, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 0, 3, 1, 0, 1, 7, 1, 0, 23, 25, 2, 0, 18, 18, 22, 22, 235, 0, 26, 1, 0, 0, 0, 2, 29, 1, 0, 0, 0, 4, 37, 1, 0, 0, 0, 6, 45, 1, 0, 0, 0, 8, 53, 1, 0, 0, 0, 10, 64, 1, 0, 0, 0, 12, 91, 1, 0, 0, 0, 14, 93, 1, 0, 0, 0, 16, 157, 1, 0, 0, 0, 18, 159, 1, 0, 0, 0, 20, 167, 1, 0, 0, 0, 22, 179, 1, 0, 0, 0, 24, 206, 1, 0, 0, 0, 26, 27, 3, 2, 1, 0, 27, 28, 5, 0, 0, 1, 28, 1, 1, 0, 0, 0, 29, 35, 3, 4, 2, 0, 30, 31, 5, 20, 0, 0, 31, 32, 3, 4, 2, 0, 32, 33, 5, 21, 0, 0, 33, 34, 3, 2, 1, 0, 34, 36, 1, 0, 0, 0, 35, 30, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 3, 1, 0, 0, 0, 37, 42, 3, 6, 3, 0, 38, 39, 5, 9, 0, 0, 39, 41, 3, 6, 3, 0, 40, 38, 1, 0, 0, 0, 41, 44, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 5, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 45, 50, 3, 8, 4, 0, 46, 47, 5, 8, 0, 0, 47, 49, 3, 8, 4, 0, 48, 46, 1, 0, 0, 0, 49, 52, 1, 0, 0, 0, 50, 48, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 7, 1, 0, 0, 0, 52, 50, 1, 0, 0, 0, 53, 54, 6, 4, -1, 0, 54, 55, 3, 10, 5, 0, 55, 61, 1, 0, 0, 0, 56, 57, 10, 1, 0, 0, 57, 58, 7, 0, 0, 0, 58, 60, 3, 8, 4, 2, 59, 56, 1, 0, 0, 0, 60, 63, 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 9, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 64, 65, 6, 5, -1, 0, 65, 66, 3, 12, 6, 0, 66, 75, 1, 0, 0, 0, 67, 68, 10, 2, 0, 0, 68, 69, 7, 1, 0, 0, 69, 74, 3, 10, 5, 3, 70, 71, 10, 1, 0, 0, 71, 72, 7, 2, 0, 0, 72, 74, 3, 10, 5, 2, 73, 67, 1, 0, 0, 0, 73, 70, 1, 0, 0, 0, 74, 77, 1, 0, 0, 0, 75, 73, 1, 0, 0, 0, 75, 76, 1, 0, 0, 0, 76, 11, 1, 0, 0, 0, 77, 75, 1, 0, 0, 0, 78, 92, 3, 14, 7, 0, 79, 81, 5, 19, 0, 0, 80, 79, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 92, 3, 14, 7, 0, 85, 87, 5, 18, 0, 0, 86, 85, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 86, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 92, 3, 14, 7, 0, 91, 78, 1, 0, 0, 0, 91, 80, 1, 0, 0, 0, 91, 86, 1, 0, 0, 0, 92, 13, 1, 0, 0, 0, 93, 94, 6, 7, -1, 0, 94, 95, 3, 16, 8, 0, 95, 122, 1, 0, 0, 0, 96, 97, 10, 3, 0, 0, 97, 98, 5, 16, 0, 0, 98, 104, 5, 36, 0, 0, 99, 101, 5, 14, 0, 0, 100, 102, 3, 18, 9, 0, 101, 100, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 5, 15, 0, 0, 104, 99, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 121, 1, 0, 0, 0, 106, 107, 10, 2, 0, 0, 107, 108, 5, 10, 0, 0, 108, 109, 3, 2, 1, 0, 109, 110, 5, 11, 0, 0, 110, 121, 1, 0, 0, 0, 111, 112, 10, 1, 0, 0, 112, 114, 5, 12, 0, 0, 113, 115, 3, 20, 10, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 118, 5, 17, 0, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 121, 5, 13, 0, 0, 120, 96, 1, 0, 0, 0, 120, 106, 1, 0, 0, 0, 120, 111, 1, 0, 0, 0, 121, 124, 1, 0, 0, 0, 122, 120, 1, 0, 0, 0, 122, 123, 1, 0, 0, 0, 123, 15, 1, 0, 0, 0, 124, 122, 1, 0, 0, 0, 125, 127, 5, 16, 0, 0, 126, 125, 1, 0, 0, 0, 126, 127, 1, 0, 0, 0, 127, 128, 1, 0, 0, 0, 128, 134, 5, 36, 0, 0, 129, 131, 5, 14, 0, 0, 130, 132, 3, 18, 9, 0, 131, 130, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 135, 5, 15, 0, 0, 134, 129, 1, 0, 0, 0, 134, 135, 1, 0, 0, 0, 135, 158, 1, 0, 0, 0, 136, 137, 5, 14, 0, 0, 137, 138, 3, 2, 1, 0, 138, 139, 5, 15, 0, 0, 139, 158, 1, 0, 0, 0, 140, 142, 5, 10, 0, 0, 141, 143, 3, 18, 9, 0, 142, 141, 1, 0, 0, 0, 142, 143, 1, 0, 0, 0, 143, 145, 1, 0, 0, 0, 144, 146, 5, 17, 0, 0, 145, 144, 1, 0, 0, 0, 145, 146, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 158, 5, 11, 0, 0, 148, 150, 5, 12, 0, 0, 149, 151, 3, 22, 11, 0, 150, 149, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 151, 153, 1, 0, 0, 0, 152, 154, 5, 17, 0, 0, 153, 152, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 158, 5, 13, 0, 0, 156, 158, 3, 24, 12, 0, 157, 126, 1, 0, 0, 0, 157, 136, 1, 0, 0, 0, 157, 140, 1, 0, 0, 0, 157, 148, 1, 0, 0, 0, 157, 156, 1, 0, 0, 0, 158, 17, 1, 0, 0, 0, 159, 164, 3, 2, 1, 0, 160, 161, 5, 17, 0, 0, 161, 163, 3, 2, 1, 0, 162, 160, 1, 0, 0, 0, 163, 166, 1, 0, 0, 0, 164, 162, 1, 0, 0, 0, 164, 165, 1, 0, 0, 0, 165, 19, 1, 0, 0, 0, 166, 164, 1, 0, 0, 0, 167, 168, 5, 36, 0, 0, 168, 169, 5, 21, 0, 0, 169, 176, 3, 2, 1, 0, 170, 171, 5, 17, 0, 0, 171, 172, 5, 36, 0, 0, 172, 173, 5, 21, 0, 0, 173, 175, 3, 2, 1, 0, 174, 170, 1, 0, 0, 0, 175, 178, 1, 0, 0, 0, 176, 174, 1, 0, 0, 0, 176, 177, 1, 0, 0, 0, 177, 21, 1, 0, 0, 0, 178, 176, 1, 0, 0, 0, 179, 180, 3, 2, 1, 0, 180, 181, 5, 21, 0, 0, 181, 189, 3, 2, 1, 0, 182, 183, 5, 17, 0, 0, 183, 184, 3, 2, 1, 0, 184, 185, 5, 21, 0, 0, 185, 186, 3, 2, 1, 0, 186, 188, 1, 0, 0, 0, 187, 182, 1, 0, 0, 0, 188, 191, 1, 0, 0, 0, 189, 187, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 23, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 192, 194, 5, 18, 0, 0, 193, 192, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 195, 1, 0, 0, 0, 195, 207, 5, 32, 0, 0, 196, 207, 5, 33, 0, 0, 197, 199, 5, 18, 0, 0, 198, 197, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 200, 1, 0, 0, 0, 200, 207, 5, 31, 0, 0, 201, 207, 5, 34, 0, 0, 202, 207, 5, 35, 0, 0, 203, 207, 5, 26, 0, 0, 204, 207, 5, 27, 0, 0, 205, 207, 5, 28, 0, 0, 206, 193, 1, 0, 0, 0, 206, 196, 1, 0, 0, 0, 206, 198, 1, 0, 0, 0, 206, 201, 1, 0, 0, 0, 206, 202, 1, 0, 0, 0, 206, 203, 1, 0, 0, 0, 206, 204, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 25, 1, 0, 0, 0, 29, 35, 42, 50, 61, 73, 75, 82, 88, 91, 101, 104, 114, 117, 120, 122, 126, 131, 134, 142, 145, 150, 153, 157, 164, 176, 189, 193, 198, 206] -------------------------------------------------------------------------------- /src/generated/CEL.tokens: -------------------------------------------------------------------------------- 1 | EQUALS=1 2 | NOT_EQUALS=2 3 | IN=3 4 | LESS=4 5 | LESS_EQUALS=5 6 | GREATER_EQUALS=6 7 | GREATER=7 8 | LOGICAL_AND=8 9 | LOGICAL_OR=9 10 | LBRACKET=10 11 | RPRACKET=11 12 | LBRACE=12 13 | RBRACE=13 14 | LPAREN=14 15 | RPAREN=15 16 | DOT=16 17 | COMMA=17 18 | MINUS=18 19 | EXCLAM=19 20 | QUESTIONMARK=20 21 | COLON=21 22 | PLUS=22 23 | STAR=23 24 | SLASH=24 25 | PERCENT=25 26 | TRUE=26 27 | FALSE=27 28 | NULL=28 29 | WHITESPACE=29 30 | COMMENT=30 31 | NUM_FLOAT=31 32 | NUM_INT=32 33 | NUM_UINT=33 34 | STRING=34 35 | BYTES=35 36 | IDENTIFIER=36 37 | '=='=1 38 | '!='=2 39 | 'in'=3 40 | '<'=4 41 | '<='=5 42 | '>='=6 43 | '>'=7 44 | '&&'=8 45 | '||'=9 46 | '['=10 47 | ']'=11 48 | '{'=12 49 | '}'=13 50 | '('=14 51 | ')'=15 52 | '.'=16 53 | ','=17 54 | '-'=18 55 | '!'=19 56 | '?'=20 57 | ':'=21 58 | '+'=22 59 | '*'=23 60 | '/'=24 61 | '%'=25 62 | 'true'=26 63 | 'false'=27 64 | 'null'=28 65 | -------------------------------------------------------------------------------- /src/generated/CELLexer.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '==' 4 | '!=' 5 | 'in' 6 | '<' 7 | '<=' 8 | '>=' 9 | '>' 10 | '&&' 11 | '||' 12 | '[' 13 | ']' 14 | '{' 15 | '}' 16 | '(' 17 | ')' 18 | '.' 19 | ',' 20 | '-' 21 | '!' 22 | '?' 23 | ':' 24 | '+' 25 | '*' 26 | '/' 27 | '%' 28 | 'true' 29 | 'false' 30 | 'null' 31 | null 32 | null 33 | null 34 | null 35 | null 36 | null 37 | null 38 | null 39 | 40 | token symbolic names: 41 | null 42 | EQUALS 43 | NOT_EQUALS 44 | IN 45 | LESS 46 | LESS_EQUALS 47 | GREATER_EQUALS 48 | GREATER 49 | LOGICAL_AND 50 | LOGICAL_OR 51 | LBRACKET 52 | RPRACKET 53 | LBRACE 54 | RBRACE 55 | LPAREN 56 | RPAREN 57 | DOT 58 | COMMA 59 | MINUS 60 | EXCLAM 61 | QUESTIONMARK 62 | COLON 63 | PLUS 64 | STAR 65 | SLASH 66 | PERCENT 67 | TRUE 68 | FALSE 69 | NULL 70 | WHITESPACE 71 | COMMENT 72 | NUM_FLOAT 73 | NUM_INT 74 | NUM_UINT 75 | STRING 76 | BYTES 77 | IDENTIFIER 78 | 79 | rule names: 80 | EQUALS 81 | NOT_EQUALS 82 | IN 83 | LESS 84 | LESS_EQUALS 85 | GREATER_EQUALS 86 | GREATER 87 | LOGICAL_AND 88 | LOGICAL_OR 89 | LBRACKET 90 | RPRACKET 91 | LBRACE 92 | RBRACE 93 | LPAREN 94 | RPAREN 95 | DOT 96 | COMMA 97 | MINUS 98 | EXCLAM 99 | QUESTIONMARK 100 | COLON 101 | PLUS 102 | STAR 103 | SLASH 104 | PERCENT 105 | TRUE 106 | FALSE 107 | NULL 108 | BACKSLASH 109 | LETTER 110 | DIGIT 111 | EXPONENT 112 | HEXDIGIT 113 | RAW 114 | ESC_SEQ 115 | ESC_CHAR_SEQ 116 | ESC_OCT_SEQ 117 | ESC_BYTE_SEQ 118 | ESC_UNI_SEQ 119 | WHITESPACE 120 | COMMENT 121 | NUM_FLOAT 122 | NUM_INT 123 | NUM_UINT 124 | STRING 125 | BYTES 126 | IDENTIFIER 127 | 128 | channel names: 129 | DEFAULT_TOKEN_CHANNEL 130 | HIDDEN 131 | 132 | mode names: 133 | DEFAULT_MODE 134 | 135 | atn: 136 | [4, 0, 36, 423, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 177, 8, 31, 1, 31, 4, 31, 180, 8, 31, 11, 31, 12, 31, 181, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 192, 8, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 225, 8, 38, 1, 39, 4, 39, 228, 8, 39, 11, 39, 12, 39, 229, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 238, 8, 40, 10, 40, 12, 40, 241, 9, 40, 1, 40, 1, 40, 1, 41, 4, 41, 246, 8, 41, 11, 41, 12, 41, 247, 1, 41, 1, 41, 4, 41, 252, 8, 41, 11, 41, 12, 41, 253, 1, 41, 3, 41, 257, 8, 41, 1, 41, 4, 41, 260, 8, 41, 11, 41, 12, 41, 261, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 268, 8, 41, 11, 41, 12, 41, 269, 1, 41, 3, 41, 273, 8, 41, 3, 41, 275, 8, 41, 1, 42, 4, 42, 278, 8, 42, 11, 42, 12, 42, 279, 1, 42, 1, 42, 1, 42, 1, 42, 4, 42, 286, 8, 42, 11, 42, 12, 42, 287, 3, 42, 290, 8, 42, 1, 43, 4, 43, 293, 8, 43, 11, 43, 12, 43, 294, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 4, 43, 303, 8, 43, 11, 43, 12, 43, 304, 1, 43, 1, 43, 3, 43, 309, 8, 43, 1, 44, 1, 44, 1, 44, 5, 44, 314, 8, 44, 10, 44, 12, 44, 317, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 323, 8, 44, 10, 44, 12, 44, 326, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 335, 8, 44, 10, 44, 12, 44, 338, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 349, 8, 44, 10, 44, 12, 44, 352, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 360, 8, 44, 10, 44, 12, 44, 363, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 370, 8, 44, 10, 44, 12, 44, 373, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 383, 8, 44, 10, 44, 12, 44, 386, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 398, 8, 44, 10, 44, 12, 44, 401, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 407, 8, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 3, 46, 414, 8, 46, 1, 46, 1, 46, 1, 46, 5, 46, 419, 8, 46, 10, 46, 12, 46, 422, 9, 46, 4, 336, 350, 384, 399, 0, 47, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 0, 61, 0, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 29, 81, 30, 83, 31, 85, 32, 87, 33, 89, 34, 91, 35, 93, 36, 1, 0, 16, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 82, 82, 114, 114, 10, 0, 34, 34, 39, 39, 63, 63, 92, 92, 96, 98, 102, 102, 110, 110, 114, 114, 116, 116, 118, 118, 2, 0, 88, 88, 120, 120, 3, 0, 9, 10, 12, 13, 32, 32, 1, 0, 10, 10, 2, 0, 85, 85, 117, 117, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 1, 0, 92, 92, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 66, 66, 98, 98, 456, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 3, 98, 1, 0, 0, 0, 5, 101, 1, 0, 0, 0, 7, 104, 1, 0, 0, 0, 9, 106, 1, 0, 0, 0, 11, 109, 1, 0, 0, 0, 13, 112, 1, 0, 0, 0, 15, 114, 1, 0, 0, 0, 17, 117, 1, 0, 0, 0, 19, 120, 1, 0, 0, 0, 21, 122, 1, 0, 0, 0, 23, 124, 1, 0, 0, 0, 25, 126, 1, 0, 0, 0, 27, 128, 1, 0, 0, 0, 29, 130, 1, 0, 0, 0, 31, 132, 1, 0, 0, 0, 33, 134, 1, 0, 0, 0, 35, 136, 1, 0, 0, 0, 37, 138, 1, 0, 0, 0, 39, 140, 1, 0, 0, 0, 41, 142, 1, 0, 0, 0, 43, 144, 1, 0, 0, 0, 45, 146, 1, 0, 0, 0, 47, 148, 1, 0, 0, 0, 49, 150, 1, 0, 0, 0, 51, 152, 1, 0, 0, 0, 53, 157, 1, 0, 0, 0, 55, 163, 1, 0, 0, 0, 57, 168, 1, 0, 0, 0, 59, 170, 1, 0, 0, 0, 61, 172, 1, 0, 0, 0, 63, 174, 1, 0, 0, 0, 65, 183, 1, 0, 0, 0, 67, 185, 1, 0, 0, 0, 69, 191, 1, 0, 0, 0, 71, 193, 1, 0, 0, 0, 73, 196, 1, 0, 0, 0, 75, 201, 1, 0, 0, 0, 77, 224, 1, 0, 0, 0, 79, 227, 1, 0, 0, 0, 81, 233, 1, 0, 0, 0, 83, 274, 1, 0, 0, 0, 85, 289, 1, 0, 0, 0, 87, 308, 1, 0, 0, 0, 89, 406, 1, 0, 0, 0, 91, 408, 1, 0, 0, 0, 93, 413, 1, 0, 0, 0, 95, 96, 5, 61, 0, 0, 96, 97, 5, 61, 0, 0, 97, 2, 1, 0, 0, 0, 98, 99, 5, 33, 0, 0, 99, 100, 5, 61, 0, 0, 100, 4, 1, 0, 0, 0, 101, 102, 5, 105, 0, 0, 102, 103, 5, 110, 0, 0, 103, 6, 1, 0, 0, 0, 104, 105, 5, 60, 0, 0, 105, 8, 1, 0, 0, 0, 106, 107, 5, 60, 0, 0, 107, 108, 5, 61, 0, 0, 108, 10, 1, 0, 0, 0, 109, 110, 5, 62, 0, 0, 110, 111, 5, 61, 0, 0, 111, 12, 1, 0, 0, 0, 112, 113, 5, 62, 0, 0, 113, 14, 1, 0, 0, 0, 114, 115, 5, 38, 0, 0, 115, 116, 5, 38, 0, 0, 116, 16, 1, 0, 0, 0, 117, 118, 5, 124, 0, 0, 118, 119, 5, 124, 0, 0, 119, 18, 1, 0, 0, 0, 120, 121, 5, 91, 0, 0, 121, 20, 1, 0, 0, 0, 122, 123, 5, 93, 0, 0, 123, 22, 1, 0, 0, 0, 124, 125, 5, 123, 0, 0, 125, 24, 1, 0, 0, 0, 126, 127, 5, 125, 0, 0, 127, 26, 1, 0, 0, 0, 128, 129, 5, 40, 0, 0, 129, 28, 1, 0, 0, 0, 130, 131, 5, 41, 0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 5, 46, 0, 0, 133, 32, 1, 0, 0, 0, 134, 135, 5, 44, 0, 0, 135, 34, 1, 0, 0, 0, 136, 137, 5, 45, 0, 0, 137, 36, 1, 0, 0, 0, 138, 139, 5, 33, 0, 0, 139, 38, 1, 0, 0, 0, 140, 141, 5, 63, 0, 0, 141, 40, 1, 0, 0, 0, 142, 143, 5, 58, 0, 0, 143, 42, 1, 0, 0, 0, 144, 145, 5, 43, 0, 0, 145, 44, 1, 0, 0, 0, 146, 147, 5, 42, 0, 0, 147, 46, 1, 0, 0, 0, 148, 149, 5, 47, 0, 0, 149, 48, 1, 0, 0, 0, 150, 151, 5, 37, 0, 0, 151, 50, 1, 0, 0, 0, 152, 153, 5, 116, 0, 0, 153, 154, 5, 114, 0, 0, 154, 155, 5, 117, 0, 0, 155, 156, 5, 101, 0, 0, 156, 52, 1, 0, 0, 0, 157, 158, 5, 102, 0, 0, 158, 159, 5, 97, 0, 0, 159, 160, 5, 108, 0, 0, 160, 161, 5, 115, 0, 0, 161, 162, 5, 101, 0, 0, 162, 54, 1, 0, 0, 0, 163, 164, 5, 110, 0, 0, 164, 165, 5, 117, 0, 0, 165, 166, 5, 108, 0, 0, 166, 167, 5, 108, 0, 0, 167, 56, 1, 0, 0, 0, 168, 169, 5, 92, 0, 0, 169, 58, 1, 0, 0, 0, 170, 171, 7, 0, 0, 0, 171, 60, 1, 0, 0, 0, 172, 173, 2, 48, 57, 0, 173, 62, 1, 0, 0, 0, 174, 176, 7, 1, 0, 0, 175, 177, 7, 2, 0, 0, 176, 175, 1, 0, 0, 0, 176, 177, 1, 0, 0, 0, 177, 179, 1, 0, 0, 0, 178, 180, 3, 61, 30, 0, 179, 178, 1, 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 64, 1, 0, 0, 0, 183, 184, 7, 3, 0, 0, 184, 66, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186, 68, 1, 0, 0, 0, 187, 192, 3, 71, 35, 0, 188, 192, 3, 75, 37, 0, 189, 192, 3, 77, 38, 0, 190, 192, 3, 73, 36, 0, 191, 187, 1, 0, 0, 0, 191, 188, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 191, 190, 1, 0, 0, 0, 192, 70, 1, 0, 0, 0, 193, 194, 3, 57, 28, 0, 194, 195, 7, 5, 0, 0, 195, 72, 1, 0, 0, 0, 196, 197, 3, 57, 28, 0, 197, 198, 2, 48, 51, 0, 198, 199, 2, 48, 55, 0, 199, 200, 2, 48, 55, 0, 200, 74, 1, 0, 0, 0, 201, 202, 3, 57, 28, 0, 202, 203, 7, 6, 0, 0, 203, 204, 3, 65, 32, 0, 204, 205, 3, 65, 32, 0, 205, 76, 1, 0, 0, 0, 206, 207, 3, 57, 28, 0, 207, 208, 5, 117, 0, 0, 208, 209, 3, 65, 32, 0, 209, 210, 3, 65, 32, 0, 210, 211, 3, 65, 32, 0, 211, 212, 3, 65, 32, 0, 212, 225, 1, 0, 0, 0, 213, 214, 3, 57, 28, 0, 214, 215, 5, 85, 0, 0, 215, 216, 3, 65, 32, 0, 216, 217, 3, 65, 32, 0, 217, 218, 3, 65, 32, 0, 218, 219, 3, 65, 32, 0, 219, 220, 3, 65, 32, 0, 220, 221, 3, 65, 32, 0, 221, 222, 3, 65, 32, 0, 222, 223, 3, 65, 32, 0, 223, 225, 1, 0, 0, 0, 224, 206, 1, 0, 0, 0, 224, 213, 1, 0, 0, 0, 225, 78, 1, 0, 0, 0, 226, 228, 7, 7, 0, 0, 227, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 231, 1, 0, 0, 0, 231, 232, 6, 39, 0, 0, 232, 80, 1, 0, 0, 0, 233, 234, 5, 47, 0, 0, 234, 235, 5, 47, 0, 0, 235, 239, 1, 0, 0, 0, 236, 238, 8, 8, 0, 0, 237, 236, 1, 0, 0, 0, 238, 241, 1, 0, 0, 0, 239, 237, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 242, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 243, 6, 40, 0, 0, 243, 82, 1, 0, 0, 0, 244, 246, 3, 61, 30, 0, 245, 244, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 251, 5, 46, 0, 0, 250, 252, 3, 61, 30, 0, 251, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 256, 1, 0, 0, 0, 255, 257, 3, 63, 31, 0, 256, 255, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 275, 1, 0, 0, 0, 258, 260, 3, 61, 30, 0, 259, 258, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 264, 3, 63, 31, 0, 264, 275, 1, 0, 0, 0, 265, 267, 5, 46, 0, 0, 266, 268, 3, 61, 30, 0, 267, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 272, 1, 0, 0, 0, 271, 273, 3, 63, 31, 0, 272, 271, 1, 0, 0, 0, 272, 273, 1, 0, 0, 0, 273, 275, 1, 0, 0, 0, 274, 245, 1, 0, 0, 0, 274, 259, 1, 0, 0, 0, 274, 265, 1, 0, 0, 0, 275, 84, 1, 0, 0, 0, 276, 278, 3, 61, 30, 0, 277, 276, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 277, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 290, 1, 0, 0, 0, 281, 282, 5, 48, 0, 0, 282, 283, 5, 120, 0, 0, 283, 285, 1, 0, 0, 0, 284, 286, 3, 65, 32, 0, 285, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 290, 1, 0, 0, 0, 289, 277, 1, 0, 0, 0, 289, 281, 1, 0, 0, 0, 290, 86, 1, 0, 0, 0, 291, 293, 3, 61, 30, 0, 292, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 297, 7, 9, 0, 0, 297, 309, 1, 0, 0, 0, 298, 299, 5, 48, 0, 0, 299, 300, 5, 120, 0, 0, 300, 302, 1, 0, 0, 0, 301, 303, 3, 65, 32, 0, 302, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 302, 1, 0, 0, 0, 304, 305, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 7, 9, 0, 0, 307, 309, 1, 0, 0, 0, 308, 292, 1, 0, 0, 0, 308, 298, 1, 0, 0, 0, 309, 88, 1, 0, 0, 0, 310, 315, 5, 34, 0, 0, 311, 314, 3, 69, 34, 0, 312, 314, 8, 10, 0, 0, 313, 311, 1, 0, 0, 0, 313, 312, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 318, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 318, 407, 5, 34, 0, 0, 319, 324, 5, 39, 0, 0, 320, 323, 3, 69, 34, 0, 321, 323, 8, 11, 0, 0, 322, 320, 1, 0, 0, 0, 322, 321, 1, 0, 0, 0, 323, 326, 1, 0, 0, 0, 324, 322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 327, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 327, 407, 5, 39, 0, 0, 328, 329, 5, 34, 0, 0, 329, 330, 5, 34, 0, 0, 330, 331, 5, 34, 0, 0, 331, 336, 1, 0, 0, 0, 332, 335, 3, 69, 34, 0, 333, 335, 8, 12, 0, 0, 334, 332, 1, 0, 0, 0, 334, 333, 1, 0, 0, 0, 335, 338, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 336, 334, 1, 0, 0, 0, 337, 339, 1, 0, 0, 0, 338, 336, 1, 0, 0, 0, 339, 340, 5, 34, 0, 0, 340, 341, 5, 34, 0, 0, 341, 407, 5, 34, 0, 0, 342, 343, 5, 39, 0, 0, 343, 344, 5, 39, 0, 0, 344, 345, 5, 39, 0, 0, 345, 350, 1, 0, 0, 0, 346, 349, 3, 69, 34, 0, 347, 349, 8, 12, 0, 0, 348, 346, 1, 0, 0, 0, 348, 347, 1, 0, 0, 0, 349, 352, 1, 0, 0, 0, 350, 351, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 351, 353, 1, 0, 0, 0, 352, 350, 1, 0, 0, 0, 353, 354, 5, 39, 0, 0, 354, 355, 5, 39, 0, 0, 355, 407, 5, 39, 0, 0, 356, 357, 3, 67, 33, 0, 357, 361, 5, 34, 0, 0, 358, 360, 8, 13, 0, 0, 359, 358, 1, 0, 0, 0, 360, 363, 1, 0, 0, 0, 361, 359, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 364, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 364, 365, 5, 34, 0, 0, 365, 407, 1, 0, 0, 0, 366, 367, 3, 67, 33, 0, 367, 371, 5, 39, 0, 0, 368, 370, 8, 14, 0, 0, 369, 368, 1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 374, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 39, 0, 0, 375, 407, 1, 0, 0, 0, 376, 377, 3, 67, 33, 0, 377, 378, 5, 34, 0, 0, 378, 379, 5, 34, 0, 0, 379, 380, 5, 34, 0, 0, 380, 384, 1, 0, 0, 0, 381, 383, 9, 0, 0, 0, 382, 381, 1, 0, 0, 0, 383, 386, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 384, 382, 1, 0, 0, 0, 385, 387, 1, 0, 0, 0, 386, 384, 1, 0, 0, 0, 387, 388, 5, 34, 0, 0, 388, 389, 5, 34, 0, 0, 389, 390, 5, 34, 0, 0, 390, 407, 1, 0, 0, 0, 391, 392, 3, 67, 33, 0, 392, 393, 5, 39, 0, 0, 393, 394, 5, 39, 0, 0, 394, 395, 5, 39, 0, 0, 395, 399, 1, 0, 0, 0, 396, 398, 9, 0, 0, 0, 397, 396, 1, 0, 0, 0, 398, 401, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 399, 397, 1, 0, 0, 0, 400, 402, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 402, 403, 5, 39, 0, 0, 403, 404, 5, 39, 0, 0, 404, 405, 5, 39, 0, 0, 405, 407, 1, 0, 0, 0, 406, 310, 1, 0, 0, 0, 406, 319, 1, 0, 0, 0, 406, 328, 1, 0, 0, 0, 406, 342, 1, 0, 0, 0, 406, 356, 1, 0, 0, 0, 406, 366, 1, 0, 0, 0, 406, 376, 1, 0, 0, 0, 406, 391, 1, 0, 0, 0, 407, 90, 1, 0, 0, 0, 408, 409, 7, 15, 0, 0, 409, 410, 3, 89, 44, 0, 410, 92, 1, 0, 0, 0, 411, 414, 3, 59, 29, 0, 412, 414, 5, 95, 0, 0, 413, 411, 1, 0, 0, 0, 413, 412, 1, 0, 0, 0, 414, 420, 1, 0, 0, 0, 415, 419, 3, 59, 29, 0, 416, 419, 3, 61, 30, 0, 417, 419, 5, 95, 0, 0, 418, 415, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 418, 417, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421, 1, 0, 0, 0, 421, 94, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 36, 0, 176, 181, 191, 224, 229, 239, 247, 253, 256, 261, 269, 272, 274, 279, 287, 289, 294, 304, 308, 313, 315, 322, 324, 334, 336, 348, 350, 361, 371, 384, 399, 406, 413, 418, 420, 1, 0, 1, 0] -------------------------------------------------------------------------------- /src/generated/CELLexer.tokens: -------------------------------------------------------------------------------- 1 | EQUALS=1 2 | NOT_EQUALS=2 3 | IN=3 4 | LESS=4 5 | LESS_EQUALS=5 6 | GREATER_EQUALS=6 7 | GREATER=7 8 | LOGICAL_AND=8 9 | LOGICAL_OR=9 10 | LBRACKET=10 11 | RPRACKET=11 12 | LBRACE=12 13 | RBRACE=13 14 | LPAREN=14 15 | RPAREN=15 16 | DOT=16 17 | COMMA=17 18 | MINUS=18 19 | EXCLAM=19 20 | QUESTIONMARK=20 21 | COLON=21 22 | PLUS=22 23 | STAR=23 24 | SLASH=24 25 | PERCENT=25 26 | TRUE=26 27 | FALSE=27 28 | NULL=28 29 | WHITESPACE=29 30 | COMMENT=30 31 | NUM_FLOAT=31 32 | NUM_INT=32 33 | NUM_UINT=33 34 | STRING=34 35 | BYTES=35 36 | IDENTIFIER=36 37 | '=='=1 38 | '!='=2 39 | 'in'=3 40 | '<'=4 41 | '<='=5 42 | '>='=6 43 | '>'=7 44 | '&&'=8 45 | '||'=9 46 | '['=10 47 | ']'=11 48 | '{'=12 49 | '}'=13 50 | '('=14 51 | ')'=15 52 | '.'=16 53 | ','=17 54 | '-'=18 55 | '!'=19 56 | '?'=20 57 | ':'=21 58 | '+'=22 59 | '*'=23 60 | '/'=24 61 | '%'=25 62 | 'true'=26 63 | 'false'=27 64 | 'null'=28 65 | -------------------------------------------------------------------------------- /src/generated/CELLexer.ts: -------------------------------------------------------------------------------- 1 | // Generated from src/grammar/CEL.g4 by ANTLR 4.13.2 2 | // noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols 3 | import { 4 | ATN, 5 | ATNDeserializer, 6 | CharStream, 7 | DecisionState, DFA, 8 | Lexer, 9 | LexerATNSimulator, 10 | RuleContext, 11 | PredictionContextCache, 12 | Token 13 | } from "antlr4"; 14 | export default class CELLexer extends Lexer { 15 | public static readonly EQUALS = 1; 16 | public static readonly NOT_EQUALS = 2; 17 | public static readonly IN = 3; 18 | public static readonly LESS = 4; 19 | public static readonly LESS_EQUALS = 5; 20 | public static readonly GREATER_EQUALS = 6; 21 | public static readonly GREATER = 7; 22 | public static readonly LOGICAL_AND = 8; 23 | public static readonly LOGICAL_OR = 9; 24 | public static readonly LBRACKET = 10; 25 | public static readonly RPRACKET = 11; 26 | public static readonly LBRACE = 12; 27 | public static readonly RBRACE = 13; 28 | public static readonly LPAREN = 14; 29 | public static readonly RPAREN = 15; 30 | public static readonly DOT = 16; 31 | public static readonly COMMA = 17; 32 | public static readonly MINUS = 18; 33 | public static readonly EXCLAM = 19; 34 | public static readonly QUESTIONMARK = 20; 35 | public static readonly COLON = 21; 36 | public static readonly PLUS = 22; 37 | public static readonly STAR = 23; 38 | public static readonly SLASH = 24; 39 | public static readonly PERCENT = 25; 40 | public static readonly TRUE = 26; 41 | public static readonly FALSE = 27; 42 | public static readonly NULL = 28; 43 | public static readonly WHITESPACE = 29; 44 | public static readonly COMMENT = 30; 45 | public static readonly NUM_FLOAT = 31; 46 | public static readonly NUM_INT = 32; 47 | public static readonly NUM_UINT = 33; 48 | public static readonly STRING = 34; 49 | public static readonly BYTES = 35; 50 | public static readonly IDENTIFIER = 36; 51 | public static readonly EOF = Token.EOF; 52 | 53 | public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; 54 | public static readonly literalNames: (string | null)[] = [ null, "'=='", 55 | "'!='", "'in'", 56 | "'<'", "'<='", 57 | "'>='", "'>'", 58 | "'&&'", "'||'", 59 | "'['", "']'", 60 | "'{'", "'}'", 61 | "'('", "')'", 62 | "'.'", "','", 63 | "'-'", "'!'", 64 | "'?'", "':'", 65 | "'+'", "'*'", 66 | "'/'", "'%'", 67 | "'true'", "'false'", 68 | "'null'" ]; 69 | public static readonly symbolicNames: (string | null)[] = [ null, "EQUALS", 70 | "NOT_EQUALS", 71 | "IN", "LESS", 72 | "LESS_EQUALS", 73 | "GREATER_EQUALS", 74 | "GREATER", 75 | "LOGICAL_AND", 76 | "LOGICAL_OR", 77 | "LBRACKET", 78 | "RPRACKET", 79 | "LBRACE", "RBRACE", 80 | "LPAREN", "RPAREN", 81 | "DOT", "COMMA", 82 | "MINUS", "EXCLAM", 83 | "QUESTIONMARK", 84 | "COLON", "PLUS", 85 | "STAR", "SLASH", 86 | "PERCENT", 87 | "TRUE", "FALSE", 88 | "NULL", "WHITESPACE", 89 | "COMMENT", 90 | "NUM_FLOAT", 91 | "NUM_INT", 92 | "NUM_UINT", 93 | "STRING", "BYTES", 94 | "IDENTIFIER" ]; 95 | public static readonly modeNames: string[] = [ "DEFAULT_MODE", ]; 96 | 97 | public static readonly ruleNames: string[] = [ 98 | "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS", 99 | "GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE", 100 | "RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK", 101 | "COLON", "PLUS", "STAR", "SLASH", "PERCENT", "TRUE", "FALSE", "NULL", 102 | "BACKSLASH", "LETTER", "DIGIT", "EXPONENT", "HEXDIGIT", "RAW", "ESC_SEQ", 103 | "ESC_CHAR_SEQ", "ESC_OCT_SEQ", "ESC_BYTE_SEQ", "ESC_UNI_SEQ", "WHITESPACE", 104 | "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING", "BYTES", "IDENTIFIER", 105 | ]; 106 | 107 | 108 | constructor(input: CharStream) { 109 | super(input); 110 | this._interp = new LexerATNSimulator(this, CELLexer._ATN, CELLexer.DecisionsToDFA, new PredictionContextCache()); 111 | } 112 | 113 | public get grammarFileName(): string { return "CEL.g4"; } 114 | 115 | public get literalNames(): (string | null)[] { return CELLexer.literalNames; } 116 | public get symbolicNames(): (string | null)[] { return CELLexer.symbolicNames; } 117 | public get ruleNames(): string[] { return CELLexer.ruleNames; } 118 | 119 | public get serializedATN(): number[] { return CELLexer._serializedATN; } 120 | 121 | public get channelNames(): string[] { return CELLexer.channelNames; } 122 | 123 | public get modeNames(): string[] { return CELLexer.modeNames; } 124 | 125 | public static readonly _serializedATN: number[] = [4,0,36,423,6,-1,2,0, 126 | 7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9, 127 | 7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7, 128 | 16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23, 129 | 2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2, 130 | 31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38, 131 | 7,38,2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7, 132 | 45,2,46,7,46,1,0,1,0,1,0,1,1,1,1,1,1,1,2,1,2,1,2,1,3,1,3,1,4,1,4,1,4,1, 133 | 5,1,5,1,5,1,6,1,6,1,7,1,7,1,7,1,8,1,8,1,8,1,9,1,9,1,10,1,10,1,11,1,11,1, 134 | 12,1,12,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,1,18,1,19, 135 | 1,19,1,20,1,20,1,21,1,21,1,22,1,22,1,23,1,23,1,24,1,24,1,25,1,25,1,25,1, 136 | 25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,28,1,28, 137 | 1,29,1,29,1,30,1,30,1,31,1,31,3,31,177,8,31,1,31,4,31,180,8,31,11,31,12, 138 | 31,181,1,32,1,32,1,33,1,33,1,34,1,34,1,34,1,34,3,34,192,8,34,1,35,1,35, 139 | 1,35,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1, 140 | 38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38, 141 | 3,38,225,8,38,1,39,4,39,228,8,39,11,39,12,39,229,1,39,1,39,1,40,1,40,1, 142 | 40,1,40,5,40,238,8,40,10,40,12,40,241,9,40,1,40,1,40,1,41,4,41,246,8,41, 143 | 11,41,12,41,247,1,41,1,41,4,41,252,8,41,11,41,12,41,253,1,41,3,41,257,8, 144 | 41,1,41,4,41,260,8,41,11,41,12,41,261,1,41,1,41,1,41,1,41,4,41,268,8,41, 145 | 11,41,12,41,269,1,41,3,41,273,8,41,3,41,275,8,41,1,42,4,42,278,8,42,11, 146 | 42,12,42,279,1,42,1,42,1,42,1,42,4,42,286,8,42,11,42,12,42,287,3,42,290, 147 | 8,42,1,43,4,43,293,8,43,11,43,12,43,294,1,43,1,43,1,43,1,43,1,43,1,43,4, 148 | 43,303,8,43,11,43,12,43,304,1,43,1,43,3,43,309,8,43,1,44,1,44,1,44,5,44, 149 | 314,8,44,10,44,12,44,317,9,44,1,44,1,44,1,44,1,44,5,44,323,8,44,10,44,12, 150 | 44,326,9,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,5,44,335,8,44,10,44,12,44, 151 | 338,9,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,5,44,349,8,44,10, 152 | 44,12,44,352,9,44,1,44,1,44,1,44,1,44,1,44,1,44,5,44,360,8,44,10,44,12, 153 | 44,363,9,44,1,44,1,44,1,44,1,44,1,44,5,44,370,8,44,10,44,12,44,373,9,44, 154 | 1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,5,44,383,8,44,10,44,12,44,386,9, 155 | 44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,5,44,398,8,44,10,44, 156 | 12,44,401,9,44,1,44,1,44,1,44,1,44,3,44,407,8,44,1,45,1,45,1,45,1,46,1, 157 | 46,3,46,414,8,46,1,46,1,46,1,46,5,46,419,8,46,10,46,12,46,422,9,46,4,336, 158 | 350,384,399,0,47,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23, 159 | 12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47, 160 | 24,49,25,51,26,53,27,55,28,57,0,59,0,61,0,63,0,65,0,67,0,69,0,71,0,73,0, 161 | 75,0,77,0,79,29,81,30,83,31,85,32,87,33,89,34,91,35,93,36,1,0,16,2,0,65, 162 | 90,97,122,2,0,69,69,101,101,2,0,43,43,45,45,3,0,48,57,65,70,97,102,2,0, 163 | 82,82,114,114,10,0,34,34,39,39,63,63,92,92,96,98,102,102,110,110,114,114, 164 | 116,116,118,118,2,0,88,88,120,120,3,0,9,10,12,13,32,32,1,0,10,10,2,0,85, 165 | 85,117,117,4,0,10,10,13,13,34,34,92,92,4,0,10,10,13,13,39,39,92,92,1,0, 166 | 92,92,3,0,10,10,13,13,34,34,3,0,10,10,13,13,39,39,2,0,66,66,98,98,456,0, 167 | 1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0, 168 | 0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1, 169 | 0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0, 170 | 0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1, 171 | 0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0, 172 | 0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1, 173 | 0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,1,95,1,0,0,0,3,98,1,0,0,0,5,101,1,0,0,0, 174 | 7,104,1,0,0,0,9,106,1,0,0,0,11,109,1,0,0,0,13,112,1,0,0,0,15,114,1,0,0, 175 | 0,17,117,1,0,0,0,19,120,1,0,0,0,21,122,1,0,0,0,23,124,1,0,0,0,25,126,1, 176 | 0,0,0,27,128,1,0,0,0,29,130,1,0,0,0,31,132,1,0,0,0,33,134,1,0,0,0,35,136, 177 | 1,0,0,0,37,138,1,0,0,0,39,140,1,0,0,0,41,142,1,0,0,0,43,144,1,0,0,0,45, 178 | 146,1,0,0,0,47,148,1,0,0,0,49,150,1,0,0,0,51,152,1,0,0,0,53,157,1,0,0,0, 179 | 55,163,1,0,0,0,57,168,1,0,0,0,59,170,1,0,0,0,61,172,1,0,0,0,63,174,1,0, 180 | 0,0,65,183,1,0,0,0,67,185,1,0,0,0,69,191,1,0,0,0,71,193,1,0,0,0,73,196, 181 | 1,0,0,0,75,201,1,0,0,0,77,224,1,0,0,0,79,227,1,0,0,0,81,233,1,0,0,0,83, 182 | 274,1,0,0,0,85,289,1,0,0,0,87,308,1,0,0,0,89,406,1,0,0,0,91,408,1,0,0,0, 183 | 93,413,1,0,0,0,95,96,5,61,0,0,96,97,5,61,0,0,97,2,1,0,0,0,98,99,5,33,0, 184 | 0,99,100,5,61,0,0,100,4,1,0,0,0,101,102,5,105,0,0,102,103,5,110,0,0,103, 185 | 6,1,0,0,0,104,105,5,60,0,0,105,8,1,0,0,0,106,107,5,60,0,0,107,108,5,61, 186 | 0,0,108,10,1,0,0,0,109,110,5,62,0,0,110,111,5,61,0,0,111,12,1,0,0,0,112, 187 | 113,5,62,0,0,113,14,1,0,0,0,114,115,5,38,0,0,115,116,5,38,0,0,116,16,1, 188 | 0,0,0,117,118,5,124,0,0,118,119,5,124,0,0,119,18,1,0,0,0,120,121,5,91,0, 189 | 0,121,20,1,0,0,0,122,123,5,93,0,0,123,22,1,0,0,0,124,125,5,123,0,0,125, 190 | 24,1,0,0,0,126,127,5,125,0,0,127,26,1,0,0,0,128,129,5,40,0,0,129,28,1,0, 191 | 0,0,130,131,5,41,0,0,131,30,1,0,0,0,132,133,5,46,0,0,133,32,1,0,0,0,134, 192 | 135,5,44,0,0,135,34,1,0,0,0,136,137,5,45,0,0,137,36,1,0,0,0,138,139,5,33, 193 | 0,0,139,38,1,0,0,0,140,141,5,63,0,0,141,40,1,0,0,0,142,143,5,58,0,0,143, 194 | 42,1,0,0,0,144,145,5,43,0,0,145,44,1,0,0,0,146,147,5,42,0,0,147,46,1,0, 195 | 0,0,148,149,5,47,0,0,149,48,1,0,0,0,150,151,5,37,0,0,151,50,1,0,0,0,152, 196 | 153,5,116,0,0,153,154,5,114,0,0,154,155,5,117,0,0,155,156,5,101,0,0,156, 197 | 52,1,0,0,0,157,158,5,102,0,0,158,159,5,97,0,0,159,160,5,108,0,0,160,161, 198 | 5,115,0,0,161,162,5,101,0,0,162,54,1,0,0,0,163,164,5,110,0,0,164,165,5, 199 | 117,0,0,165,166,5,108,0,0,166,167,5,108,0,0,167,56,1,0,0,0,168,169,5,92, 200 | 0,0,169,58,1,0,0,0,170,171,7,0,0,0,171,60,1,0,0,0,172,173,2,48,57,0,173, 201 | 62,1,0,0,0,174,176,7,1,0,0,175,177,7,2,0,0,176,175,1,0,0,0,176,177,1,0, 202 | 0,0,177,179,1,0,0,0,178,180,3,61,30,0,179,178,1,0,0,0,180,181,1,0,0,0,181, 203 | 179,1,0,0,0,181,182,1,0,0,0,182,64,1,0,0,0,183,184,7,3,0,0,184,66,1,0,0, 204 | 0,185,186,7,4,0,0,186,68,1,0,0,0,187,192,3,71,35,0,188,192,3,75,37,0,189, 205 | 192,3,77,38,0,190,192,3,73,36,0,191,187,1,0,0,0,191,188,1,0,0,0,191,189, 206 | 1,0,0,0,191,190,1,0,0,0,192,70,1,0,0,0,193,194,3,57,28,0,194,195,7,5,0, 207 | 0,195,72,1,0,0,0,196,197,3,57,28,0,197,198,2,48,51,0,198,199,2,48,55,0, 208 | 199,200,2,48,55,0,200,74,1,0,0,0,201,202,3,57,28,0,202,203,7,6,0,0,203, 209 | 204,3,65,32,0,204,205,3,65,32,0,205,76,1,0,0,0,206,207,3,57,28,0,207,208, 210 | 5,117,0,0,208,209,3,65,32,0,209,210,3,65,32,0,210,211,3,65,32,0,211,212, 211 | 3,65,32,0,212,225,1,0,0,0,213,214,3,57,28,0,214,215,5,85,0,0,215,216,3, 212 | 65,32,0,216,217,3,65,32,0,217,218,3,65,32,0,218,219,3,65,32,0,219,220,3, 213 | 65,32,0,220,221,3,65,32,0,221,222,3,65,32,0,222,223,3,65,32,0,223,225,1, 214 | 0,0,0,224,206,1,0,0,0,224,213,1,0,0,0,225,78,1,0,0,0,226,228,7,7,0,0,227, 215 | 226,1,0,0,0,228,229,1,0,0,0,229,227,1,0,0,0,229,230,1,0,0,0,230,231,1,0, 216 | 0,0,231,232,6,39,0,0,232,80,1,0,0,0,233,234,5,47,0,0,234,235,5,47,0,0,235, 217 | 239,1,0,0,0,236,238,8,8,0,0,237,236,1,0,0,0,238,241,1,0,0,0,239,237,1,0, 218 | 0,0,239,240,1,0,0,0,240,242,1,0,0,0,241,239,1,0,0,0,242,243,6,40,0,0,243, 219 | 82,1,0,0,0,244,246,3,61,30,0,245,244,1,0,0,0,246,247,1,0,0,0,247,245,1, 220 | 0,0,0,247,248,1,0,0,0,248,249,1,0,0,0,249,251,5,46,0,0,250,252,3,61,30, 221 | 0,251,250,1,0,0,0,252,253,1,0,0,0,253,251,1,0,0,0,253,254,1,0,0,0,254,256, 222 | 1,0,0,0,255,257,3,63,31,0,256,255,1,0,0,0,256,257,1,0,0,0,257,275,1,0,0, 223 | 0,258,260,3,61,30,0,259,258,1,0,0,0,260,261,1,0,0,0,261,259,1,0,0,0,261, 224 | 262,1,0,0,0,262,263,1,0,0,0,263,264,3,63,31,0,264,275,1,0,0,0,265,267,5, 225 | 46,0,0,266,268,3,61,30,0,267,266,1,0,0,0,268,269,1,0,0,0,269,267,1,0,0, 226 | 0,269,270,1,0,0,0,270,272,1,0,0,0,271,273,3,63,31,0,272,271,1,0,0,0,272, 227 | 273,1,0,0,0,273,275,1,0,0,0,274,245,1,0,0,0,274,259,1,0,0,0,274,265,1,0, 228 | 0,0,275,84,1,0,0,0,276,278,3,61,30,0,277,276,1,0,0,0,278,279,1,0,0,0,279, 229 | 277,1,0,0,0,279,280,1,0,0,0,280,290,1,0,0,0,281,282,5,48,0,0,282,283,5, 230 | 120,0,0,283,285,1,0,0,0,284,286,3,65,32,0,285,284,1,0,0,0,286,287,1,0,0, 231 | 0,287,285,1,0,0,0,287,288,1,0,0,0,288,290,1,0,0,0,289,277,1,0,0,0,289,281, 232 | 1,0,0,0,290,86,1,0,0,0,291,293,3,61,30,0,292,291,1,0,0,0,293,294,1,0,0, 233 | 0,294,292,1,0,0,0,294,295,1,0,0,0,295,296,1,0,0,0,296,297,7,9,0,0,297,309, 234 | 1,0,0,0,298,299,5,48,0,0,299,300,5,120,0,0,300,302,1,0,0,0,301,303,3,65, 235 | 32,0,302,301,1,0,0,0,303,304,1,0,0,0,304,302,1,0,0,0,304,305,1,0,0,0,305, 236 | 306,1,0,0,0,306,307,7,9,0,0,307,309,1,0,0,0,308,292,1,0,0,0,308,298,1,0, 237 | 0,0,309,88,1,0,0,0,310,315,5,34,0,0,311,314,3,69,34,0,312,314,8,10,0,0, 238 | 313,311,1,0,0,0,313,312,1,0,0,0,314,317,1,0,0,0,315,313,1,0,0,0,315,316, 239 | 1,0,0,0,316,318,1,0,0,0,317,315,1,0,0,0,318,407,5,34,0,0,319,324,5,39,0, 240 | 0,320,323,3,69,34,0,321,323,8,11,0,0,322,320,1,0,0,0,322,321,1,0,0,0,323, 241 | 326,1,0,0,0,324,322,1,0,0,0,324,325,1,0,0,0,325,327,1,0,0,0,326,324,1,0, 242 | 0,0,327,407,5,39,0,0,328,329,5,34,0,0,329,330,5,34,0,0,330,331,5,34,0,0, 243 | 331,336,1,0,0,0,332,335,3,69,34,0,333,335,8,12,0,0,334,332,1,0,0,0,334, 244 | 333,1,0,0,0,335,338,1,0,0,0,336,337,1,0,0,0,336,334,1,0,0,0,337,339,1,0, 245 | 0,0,338,336,1,0,0,0,339,340,5,34,0,0,340,341,5,34,0,0,341,407,5,34,0,0, 246 | 342,343,5,39,0,0,343,344,5,39,0,0,344,345,5,39,0,0,345,350,1,0,0,0,346, 247 | 349,3,69,34,0,347,349,8,12,0,0,348,346,1,0,0,0,348,347,1,0,0,0,349,352, 248 | 1,0,0,0,350,351,1,0,0,0,350,348,1,0,0,0,351,353,1,0,0,0,352,350,1,0,0,0, 249 | 353,354,5,39,0,0,354,355,5,39,0,0,355,407,5,39,0,0,356,357,3,67,33,0,357, 250 | 361,5,34,0,0,358,360,8,13,0,0,359,358,1,0,0,0,360,363,1,0,0,0,361,359,1, 251 | 0,0,0,361,362,1,0,0,0,362,364,1,0,0,0,363,361,1,0,0,0,364,365,5,34,0,0, 252 | 365,407,1,0,0,0,366,367,3,67,33,0,367,371,5,39,0,0,368,370,8,14,0,0,369, 253 | 368,1,0,0,0,370,373,1,0,0,0,371,369,1,0,0,0,371,372,1,0,0,0,372,374,1,0, 254 | 0,0,373,371,1,0,0,0,374,375,5,39,0,0,375,407,1,0,0,0,376,377,3,67,33,0, 255 | 377,378,5,34,0,0,378,379,5,34,0,0,379,380,5,34,0,0,380,384,1,0,0,0,381, 256 | 383,9,0,0,0,382,381,1,0,0,0,383,386,1,0,0,0,384,385,1,0,0,0,384,382,1,0, 257 | 0,0,385,387,1,0,0,0,386,384,1,0,0,0,387,388,5,34,0,0,388,389,5,34,0,0,389, 258 | 390,5,34,0,0,390,407,1,0,0,0,391,392,3,67,33,0,392,393,5,39,0,0,393,394, 259 | 5,39,0,0,394,395,5,39,0,0,395,399,1,0,0,0,396,398,9,0,0,0,397,396,1,0,0, 260 | 0,398,401,1,0,0,0,399,400,1,0,0,0,399,397,1,0,0,0,400,402,1,0,0,0,401,399, 261 | 1,0,0,0,402,403,5,39,0,0,403,404,5,39,0,0,404,405,5,39,0,0,405,407,1,0, 262 | 0,0,406,310,1,0,0,0,406,319,1,0,0,0,406,328,1,0,0,0,406,342,1,0,0,0,406, 263 | 356,1,0,0,0,406,366,1,0,0,0,406,376,1,0,0,0,406,391,1,0,0,0,407,90,1,0, 264 | 0,0,408,409,7,15,0,0,409,410,3,89,44,0,410,92,1,0,0,0,411,414,3,59,29,0, 265 | 412,414,5,95,0,0,413,411,1,0,0,0,413,412,1,0,0,0,414,420,1,0,0,0,415,419, 266 | 3,59,29,0,416,419,3,61,30,0,417,419,5,95,0,0,418,415,1,0,0,0,418,416,1, 267 | 0,0,0,418,417,1,0,0,0,419,422,1,0,0,0,420,418,1,0,0,0,420,421,1,0,0,0,421, 268 | 94,1,0,0,0,422,420,1,0,0,0,36,0,176,181,191,224,229,239,247,253,256,261, 269 | 269,272,274,279,287,289,294,304,308,313,315,322,324,334,336,348,350,361, 270 | 371,384,399,406,413,418,420,1,0,1,0]; 271 | 272 | private static __ATN: ATN; 273 | public static get _ATN(): ATN { 274 | if (!CELLexer.__ATN) { 275 | CELLexer.__ATN = new ATNDeserializer().deserialize(CELLexer._serializedATN); 276 | } 277 | 278 | return CELLexer.__ATN; 279 | } 280 | 281 | 282 | static DecisionsToDFA = CELLexer._ATN.decisionToState.map( (ds: DecisionState, index: number) => new DFA(ds, index) ); 283 | } -------------------------------------------------------------------------------- /src/generated/CELListener.ts: -------------------------------------------------------------------------------- 1 | // Generated from src/grammar/CEL.g4 by ANTLR 4.13.2 2 | 3 | import {ParseTreeListener} from "antlr4"; 4 | 5 | 6 | import { StartContext } from "./CELParser.js"; 7 | import { ExprContext } from "./CELParser.js"; 8 | import { ConditionalOrContext } from "./CELParser.js"; 9 | import { ConditionalAndContext } from "./CELParser.js"; 10 | import { RelationOpContext } from "./CELParser.js"; 11 | import { RelationCalcContext } from "./CELParser.js"; 12 | import { CalcMulDivContext } from "./CELParser.js"; 13 | import { CalcUnaryContext } from "./CELParser.js"; 14 | import { CalcAddSubContext } from "./CELParser.js"; 15 | import { MemberExprContext } from "./CELParser.js"; 16 | import { LogicalNotContext } from "./CELParser.js"; 17 | import { NegateContext } from "./CELParser.js"; 18 | import { SelectOrCallContext } from "./CELParser.js"; 19 | import { PrimaryExprContext } from "./CELParser.js"; 20 | import { IndexContext } from "./CELParser.js"; 21 | import { CreateMessageContext } from "./CELParser.js"; 22 | import { IdentOrGlobalCallContext } from "./CELParser.js"; 23 | import { NestedContext } from "./CELParser.js"; 24 | import { CreateListContext } from "./CELParser.js"; 25 | import { CreateStructContext } from "./CELParser.js"; 26 | import { ConstantLiteralContext } from "./CELParser.js"; 27 | import { ExprListContext } from "./CELParser.js"; 28 | import { FieldInitializerListContext } from "./CELParser.js"; 29 | import { MapInitializerListContext } from "./CELParser.js"; 30 | import { IntContext } from "./CELParser.js"; 31 | import { UintContext } from "./CELParser.js"; 32 | import { DoubleContext } from "./CELParser.js"; 33 | import { StringContext } from "./CELParser.js"; 34 | import { BytesContext } from "./CELParser.js"; 35 | import { BoolTrueContext } from "./CELParser.js"; 36 | import { BoolFalseContext } from "./CELParser.js"; 37 | import { NullContext } from "./CELParser.js"; 38 | 39 | 40 | /** 41 | * This interface defines a complete listener for a parse tree produced by 42 | * `CELParser`. 43 | */ 44 | export default class CELListener extends ParseTreeListener { 45 | /** 46 | * Enter a parse tree produced by `CELParser.start`. 47 | * @param ctx the parse tree 48 | */ 49 | enterStart?: (ctx: StartContext) => void; 50 | /** 51 | * Exit a parse tree produced by `CELParser.start`. 52 | * @param ctx the parse tree 53 | */ 54 | exitStart?: (ctx: StartContext) => void; 55 | /** 56 | * Enter a parse tree produced by `CELParser.expr`. 57 | * @param ctx the parse tree 58 | */ 59 | enterExpr?: (ctx: ExprContext) => void; 60 | /** 61 | * Exit a parse tree produced by `CELParser.expr`. 62 | * @param ctx the parse tree 63 | */ 64 | exitExpr?: (ctx: ExprContext) => void; 65 | /** 66 | * Enter a parse tree produced by `CELParser.conditionalOr`. 67 | * @param ctx the parse tree 68 | */ 69 | enterConditionalOr?: (ctx: ConditionalOrContext) => void; 70 | /** 71 | * Exit a parse tree produced by `CELParser.conditionalOr`. 72 | * @param ctx the parse tree 73 | */ 74 | exitConditionalOr?: (ctx: ConditionalOrContext) => void; 75 | /** 76 | * Enter a parse tree produced by `CELParser.conditionalAnd`. 77 | * @param ctx the parse tree 78 | */ 79 | enterConditionalAnd?: (ctx: ConditionalAndContext) => void; 80 | /** 81 | * Exit a parse tree produced by `CELParser.conditionalAnd`. 82 | * @param ctx the parse tree 83 | */ 84 | exitConditionalAnd?: (ctx: ConditionalAndContext) => void; 85 | /** 86 | * Enter a parse tree produced by the `RelationOp` 87 | * labeled alternative in `CELParser.relation`. 88 | * @param ctx the parse tree 89 | */ 90 | enterRelationOp?: (ctx: RelationOpContext) => void; 91 | /** 92 | * Exit a parse tree produced by the `RelationOp` 93 | * labeled alternative in `CELParser.relation`. 94 | * @param ctx the parse tree 95 | */ 96 | exitRelationOp?: (ctx: RelationOpContext) => void; 97 | /** 98 | * Enter a parse tree produced by the `RelationCalc` 99 | * labeled alternative in `CELParser.relation`. 100 | * @param ctx the parse tree 101 | */ 102 | enterRelationCalc?: (ctx: RelationCalcContext) => void; 103 | /** 104 | * Exit a parse tree produced by the `RelationCalc` 105 | * labeled alternative in `CELParser.relation`. 106 | * @param ctx the parse tree 107 | */ 108 | exitRelationCalc?: (ctx: RelationCalcContext) => void; 109 | /** 110 | * Enter a parse tree produced by the `CalcMulDiv` 111 | * labeled alternative in `CELParser.calc`. 112 | * @param ctx the parse tree 113 | */ 114 | enterCalcMulDiv?: (ctx: CalcMulDivContext) => void; 115 | /** 116 | * Exit a parse tree produced by the `CalcMulDiv` 117 | * labeled alternative in `CELParser.calc`. 118 | * @param ctx the parse tree 119 | */ 120 | exitCalcMulDiv?: (ctx: CalcMulDivContext) => void; 121 | /** 122 | * Enter a parse tree produced by the `CalcUnary` 123 | * labeled alternative in `CELParser.calc`. 124 | * @param ctx the parse tree 125 | */ 126 | enterCalcUnary?: (ctx: CalcUnaryContext) => void; 127 | /** 128 | * Exit a parse tree produced by the `CalcUnary` 129 | * labeled alternative in `CELParser.calc`. 130 | * @param ctx the parse tree 131 | */ 132 | exitCalcUnary?: (ctx: CalcUnaryContext) => void; 133 | /** 134 | * Enter a parse tree produced by the `CalcAddSub` 135 | * labeled alternative in `CELParser.calc`. 136 | * @param ctx the parse tree 137 | */ 138 | enterCalcAddSub?: (ctx: CalcAddSubContext) => void; 139 | /** 140 | * Exit a parse tree produced by the `CalcAddSub` 141 | * labeled alternative in `CELParser.calc`. 142 | * @param ctx the parse tree 143 | */ 144 | exitCalcAddSub?: (ctx: CalcAddSubContext) => void; 145 | /** 146 | * Enter a parse tree produced by the `MemberExpr` 147 | * labeled alternative in `CELParser.unary`. 148 | * @param ctx the parse tree 149 | */ 150 | enterMemberExpr?: (ctx: MemberExprContext) => void; 151 | /** 152 | * Exit a parse tree produced by the `MemberExpr` 153 | * labeled alternative in `CELParser.unary`. 154 | * @param ctx the parse tree 155 | */ 156 | exitMemberExpr?: (ctx: MemberExprContext) => void; 157 | /** 158 | * Enter a parse tree produced by the `LogicalNot` 159 | * labeled alternative in `CELParser.unary`. 160 | * @param ctx the parse tree 161 | */ 162 | enterLogicalNot?: (ctx: LogicalNotContext) => void; 163 | /** 164 | * Exit a parse tree produced by the `LogicalNot` 165 | * labeled alternative in `CELParser.unary`. 166 | * @param ctx the parse tree 167 | */ 168 | exitLogicalNot?: (ctx: LogicalNotContext) => void; 169 | /** 170 | * Enter a parse tree produced by the `Negate` 171 | * labeled alternative in `CELParser.unary`. 172 | * @param ctx the parse tree 173 | */ 174 | enterNegate?: (ctx: NegateContext) => void; 175 | /** 176 | * Exit a parse tree produced by the `Negate` 177 | * labeled alternative in `CELParser.unary`. 178 | * @param ctx the parse tree 179 | */ 180 | exitNegate?: (ctx: NegateContext) => void; 181 | /** 182 | * Enter a parse tree produced by the `SelectOrCall` 183 | * labeled alternative in `CELParser.member`. 184 | * @param ctx the parse tree 185 | */ 186 | enterSelectOrCall?: (ctx: SelectOrCallContext) => void; 187 | /** 188 | * Exit a parse tree produced by the `SelectOrCall` 189 | * labeled alternative in `CELParser.member`. 190 | * @param ctx the parse tree 191 | */ 192 | exitSelectOrCall?: (ctx: SelectOrCallContext) => void; 193 | /** 194 | * Enter a parse tree produced by the `PrimaryExpr` 195 | * labeled alternative in `CELParser.member`. 196 | * @param ctx the parse tree 197 | */ 198 | enterPrimaryExpr?: (ctx: PrimaryExprContext) => void; 199 | /** 200 | * Exit a parse tree produced by the `PrimaryExpr` 201 | * labeled alternative in `CELParser.member`. 202 | * @param ctx the parse tree 203 | */ 204 | exitPrimaryExpr?: (ctx: PrimaryExprContext) => void; 205 | /** 206 | * Enter a parse tree produced by the `Index` 207 | * labeled alternative in `CELParser.member`. 208 | * @param ctx the parse tree 209 | */ 210 | enterIndex?: (ctx: IndexContext) => void; 211 | /** 212 | * Exit a parse tree produced by the `Index` 213 | * labeled alternative in `CELParser.member`. 214 | * @param ctx the parse tree 215 | */ 216 | exitIndex?: (ctx: IndexContext) => void; 217 | /** 218 | * Enter a parse tree produced by the `CreateMessage` 219 | * labeled alternative in `CELParser.member`. 220 | * @param ctx the parse tree 221 | */ 222 | enterCreateMessage?: (ctx: CreateMessageContext) => void; 223 | /** 224 | * Exit a parse tree produced by the `CreateMessage` 225 | * labeled alternative in `CELParser.member`. 226 | * @param ctx the parse tree 227 | */ 228 | exitCreateMessage?: (ctx: CreateMessageContext) => void; 229 | /** 230 | * Enter a parse tree produced by the `IdentOrGlobalCall` 231 | * labeled alternative in `CELParser.primary`. 232 | * @param ctx the parse tree 233 | */ 234 | enterIdentOrGlobalCall?: (ctx: IdentOrGlobalCallContext) => void; 235 | /** 236 | * Exit a parse tree produced by the `IdentOrGlobalCall` 237 | * labeled alternative in `CELParser.primary`. 238 | * @param ctx the parse tree 239 | */ 240 | exitIdentOrGlobalCall?: (ctx: IdentOrGlobalCallContext) => void; 241 | /** 242 | * Enter a parse tree produced by the `Nested` 243 | * labeled alternative in `CELParser.primary`. 244 | * @param ctx the parse tree 245 | */ 246 | enterNested?: (ctx: NestedContext) => void; 247 | /** 248 | * Exit a parse tree produced by the `Nested` 249 | * labeled alternative in `CELParser.primary`. 250 | * @param ctx the parse tree 251 | */ 252 | exitNested?: (ctx: NestedContext) => void; 253 | /** 254 | * Enter a parse tree produced by the `CreateList` 255 | * labeled alternative in `CELParser.primary`. 256 | * @param ctx the parse tree 257 | */ 258 | enterCreateList?: (ctx: CreateListContext) => void; 259 | /** 260 | * Exit a parse tree produced by the `CreateList` 261 | * labeled alternative in `CELParser.primary`. 262 | * @param ctx the parse tree 263 | */ 264 | exitCreateList?: (ctx: CreateListContext) => void; 265 | /** 266 | * Enter a parse tree produced by the `CreateStruct` 267 | * labeled alternative in `CELParser.primary`. 268 | * @param ctx the parse tree 269 | */ 270 | enterCreateStruct?: (ctx: CreateStructContext) => void; 271 | /** 272 | * Exit a parse tree produced by the `CreateStruct` 273 | * labeled alternative in `CELParser.primary`. 274 | * @param ctx the parse tree 275 | */ 276 | exitCreateStruct?: (ctx: CreateStructContext) => void; 277 | /** 278 | * Enter a parse tree produced by the `ConstantLiteral` 279 | * labeled alternative in `CELParser.primary`. 280 | * @param ctx the parse tree 281 | */ 282 | enterConstantLiteral?: (ctx: ConstantLiteralContext) => void; 283 | /** 284 | * Exit a parse tree produced by the `ConstantLiteral` 285 | * labeled alternative in `CELParser.primary`. 286 | * @param ctx the parse tree 287 | */ 288 | exitConstantLiteral?: (ctx: ConstantLiteralContext) => void; 289 | /** 290 | * Enter a parse tree produced by `CELParser.exprList`. 291 | * @param ctx the parse tree 292 | */ 293 | enterExprList?: (ctx: ExprListContext) => void; 294 | /** 295 | * Exit a parse tree produced by `CELParser.exprList`. 296 | * @param ctx the parse tree 297 | */ 298 | exitExprList?: (ctx: ExprListContext) => void; 299 | /** 300 | * Enter a parse tree produced by `CELParser.fieldInitializerList`. 301 | * @param ctx the parse tree 302 | */ 303 | enterFieldInitializerList?: (ctx: FieldInitializerListContext) => void; 304 | /** 305 | * Exit a parse tree produced by `CELParser.fieldInitializerList`. 306 | * @param ctx the parse tree 307 | */ 308 | exitFieldInitializerList?: (ctx: FieldInitializerListContext) => void; 309 | /** 310 | * Enter a parse tree produced by `CELParser.mapInitializerList`. 311 | * @param ctx the parse tree 312 | */ 313 | enterMapInitializerList?: (ctx: MapInitializerListContext) => void; 314 | /** 315 | * Exit a parse tree produced by `CELParser.mapInitializerList`. 316 | * @param ctx the parse tree 317 | */ 318 | exitMapInitializerList?: (ctx: MapInitializerListContext) => void; 319 | /** 320 | * Enter a parse tree produced by the `Int` 321 | * labeled alternative in `CELParser.literal`. 322 | * @param ctx the parse tree 323 | */ 324 | enterInt?: (ctx: IntContext) => void; 325 | /** 326 | * Exit a parse tree produced by the `Int` 327 | * labeled alternative in `CELParser.literal`. 328 | * @param ctx the parse tree 329 | */ 330 | exitInt?: (ctx: IntContext) => void; 331 | /** 332 | * Enter a parse tree produced by the `Uint` 333 | * labeled alternative in `CELParser.literal`. 334 | * @param ctx the parse tree 335 | */ 336 | enterUint?: (ctx: UintContext) => void; 337 | /** 338 | * Exit a parse tree produced by the `Uint` 339 | * labeled alternative in `CELParser.literal`. 340 | * @param ctx the parse tree 341 | */ 342 | exitUint?: (ctx: UintContext) => void; 343 | /** 344 | * Enter a parse tree produced by the `Double` 345 | * labeled alternative in `CELParser.literal`. 346 | * @param ctx the parse tree 347 | */ 348 | enterDouble?: (ctx: DoubleContext) => void; 349 | /** 350 | * Exit a parse tree produced by the `Double` 351 | * labeled alternative in `CELParser.literal`. 352 | * @param ctx the parse tree 353 | */ 354 | exitDouble?: (ctx: DoubleContext) => void; 355 | /** 356 | * Enter a parse tree produced by the `String` 357 | * labeled alternative in `CELParser.literal`. 358 | * @param ctx the parse tree 359 | */ 360 | enterString?: (ctx: StringContext) => void; 361 | /** 362 | * Exit a parse tree produced by the `String` 363 | * labeled alternative in `CELParser.literal`. 364 | * @param ctx the parse tree 365 | */ 366 | exitString?: (ctx: StringContext) => void; 367 | /** 368 | * Enter a parse tree produced by the `Bytes` 369 | * labeled alternative in `CELParser.literal`. 370 | * @param ctx the parse tree 371 | */ 372 | enterBytes?: (ctx: BytesContext) => void; 373 | /** 374 | * Exit a parse tree produced by the `Bytes` 375 | * labeled alternative in `CELParser.literal`. 376 | * @param ctx the parse tree 377 | */ 378 | exitBytes?: (ctx: BytesContext) => void; 379 | /** 380 | * Enter a parse tree produced by the `BoolTrue` 381 | * labeled alternative in `CELParser.literal`. 382 | * @param ctx the parse tree 383 | */ 384 | enterBoolTrue?: (ctx: BoolTrueContext) => void; 385 | /** 386 | * Exit a parse tree produced by the `BoolTrue` 387 | * labeled alternative in `CELParser.literal`. 388 | * @param ctx the parse tree 389 | */ 390 | exitBoolTrue?: (ctx: BoolTrueContext) => void; 391 | /** 392 | * Enter a parse tree produced by the `BoolFalse` 393 | * labeled alternative in `CELParser.literal`. 394 | * @param ctx the parse tree 395 | */ 396 | enterBoolFalse?: (ctx: BoolFalseContext) => void; 397 | /** 398 | * Exit a parse tree produced by the `BoolFalse` 399 | * labeled alternative in `CELParser.literal`. 400 | * @param ctx the parse tree 401 | */ 402 | exitBoolFalse?: (ctx: BoolFalseContext) => void; 403 | /** 404 | * Enter a parse tree produced by the `Null` 405 | * labeled alternative in `CELParser.literal`. 406 | * @param ctx the parse tree 407 | */ 408 | enterNull?: (ctx: NullContext) => void; 409 | /** 410 | * Exit a parse tree produced by the `Null` 411 | * labeled alternative in `CELParser.literal`. 412 | * @param ctx the parse tree 413 | */ 414 | exitNull?: (ctx: NullContext) => void; 415 | } 416 | 417 | -------------------------------------------------------------------------------- /src/generated/CELVisitor.ts: -------------------------------------------------------------------------------- 1 | // Generated from src/grammar/CEL.g4 by ANTLR 4.13.2 2 | 3 | import {ParseTreeVisitor} from 'antlr4'; 4 | 5 | 6 | import { StartContext } from "./CELParser.js"; 7 | import { ExprContext } from "./CELParser.js"; 8 | import { ConditionalOrContext } from "./CELParser.js"; 9 | import { ConditionalAndContext } from "./CELParser.js"; 10 | import { RelationOpContext } from "./CELParser.js"; 11 | import { RelationCalcContext } from "./CELParser.js"; 12 | import { CalcMulDivContext } from "./CELParser.js"; 13 | import { CalcUnaryContext } from "./CELParser.js"; 14 | import { CalcAddSubContext } from "./CELParser.js"; 15 | import { MemberExprContext } from "./CELParser.js"; 16 | import { LogicalNotContext } from "./CELParser.js"; 17 | import { NegateContext } from "./CELParser.js"; 18 | import { SelectOrCallContext } from "./CELParser.js"; 19 | import { PrimaryExprContext } from "./CELParser.js"; 20 | import { IndexContext } from "./CELParser.js"; 21 | import { CreateMessageContext } from "./CELParser.js"; 22 | import { IdentOrGlobalCallContext } from "./CELParser.js"; 23 | import { NestedContext } from "./CELParser.js"; 24 | import { CreateListContext } from "./CELParser.js"; 25 | import { CreateStructContext } from "./CELParser.js"; 26 | import { ConstantLiteralContext } from "./CELParser.js"; 27 | import { ExprListContext } from "./CELParser.js"; 28 | import { FieldInitializerListContext } from "./CELParser.js"; 29 | import { MapInitializerListContext } from "./CELParser.js"; 30 | import { IntContext } from "./CELParser.js"; 31 | import { UintContext } from "./CELParser.js"; 32 | import { DoubleContext } from "./CELParser.js"; 33 | import { StringContext } from "./CELParser.js"; 34 | import { BytesContext } from "./CELParser.js"; 35 | import { BoolTrueContext } from "./CELParser.js"; 36 | import { BoolFalseContext } from "./CELParser.js"; 37 | import { NullContext } from "./CELParser.js"; 38 | 39 | 40 | /** 41 | * This interface defines a complete generic visitor for a parse tree produced 42 | * by `CELParser`. 43 | * 44 | * @param The return type of the visit operation. Use `void` for 45 | * operations with no return type. 46 | */ 47 | export default class CELVisitor extends ParseTreeVisitor { 48 | /** 49 | * Visit a parse tree produced by `CELParser.start`. 50 | * @param ctx the parse tree 51 | * @return the visitor result 52 | */ 53 | visitStart?: (ctx: StartContext) => Result; 54 | /** 55 | * Visit a parse tree produced by `CELParser.expr`. 56 | * @param ctx the parse tree 57 | * @return the visitor result 58 | */ 59 | visitExpr?: (ctx: ExprContext) => Result; 60 | /** 61 | * Visit a parse tree produced by `CELParser.conditionalOr`. 62 | * @param ctx the parse tree 63 | * @return the visitor result 64 | */ 65 | visitConditionalOr?: (ctx: ConditionalOrContext) => Result; 66 | /** 67 | * Visit a parse tree produced by `CELParser.conditionalAnd`. 68 | * @param ctx the parse tree 69 | * @return the visitor result 70 | */ 71 | visitConditionalAnd?: (ctx: ConditionalAndContext) => Result; 72 | /** 73 | * Visit a parse tree produced by the `RelationOp` 74 | * labeled alternative in `CELParser.relation`. 75 | * @param ctx the parse tree 76 | * @return the visitor result 77 | */ 78 | visitRelationOp?: (ctx: RelationOpContext) => Result; 79 | /** 80 | * Visit a parse tree produced by the `RelationCalc` 81 | * labeled alternative in `CELParser.relation`. 82 | * @param ctx the parse tree 83 | * @return the visitor result 84 | */ 85 | visitRelationCalc?: (ctx: RelationCalcContext) => Result; 86 | /** 87 | * Visit a parse tree produced by the `CalcMulDiv` 88 | * labeled alternative in `CELParser.calc`. 89 | * @param ctx the parse tree 90 | * @return the visitor result 91 | */ 92 | visitCalcMulDiv?: (ctx: CalcMulDivContext) => Result; 93 | /** 94 | * Visit a parse tree produced by the `CalcUnary` 95 | * labeled alternative in `CELParser.calc`. 96 | * @param ctx the parse tree 97 | * @return the visitor result 98 | */ 99 | visitCalcUnary?: (ctx: CalcUnaryContext) => Result; 100 | /** 101 | * Visit a parse tree produced by the `CalcAddSub` 102 | * labeled alternative in `CELParser.calc`. 103 | * @param ctx the parse tree 104 | * @return the visitor result 105 | */ 106 | visitCalcAddSub?: (ctx: CalcAddSubContext) => Result; 107 | /** 108 | * Visit a parse tree produced by the `MemberExpr` 109 | * labeled alternative in `CELParser.unary`. 110 | * @param ctx the parse tree 111 | * @return the visitor result 112 | */ 113 | visitMemberExpr?: (ctx: MemberExprContext) => Result; 114 | /** 115 | * Visit a parse tree produced by the `LogicalNot` 116 | * labeled alternative in `CELParser.unary`. 117 | * @param ctx the parse tree 118 | * @return the visitor result 119 | */ 120 | visitLogicalNot?: (ctx: LogicalNotContext) => Result; 121 | /** 122 | * Visit a parse tree produced by the `Negate` 123 | * labeled alternative in `CELParser.unary`. 124 | * @param ctx the parse tree 125 | * @return the visitor result 126 | */ 127 | visitNegate?: (ctx: NegateContext) => Result; 128 | /** 129 | * Visit a parse tree produced by the `SelectOrCall` 130 | * labeled alternative in `CELParser.member`. 131 | * @param ctx the parse tree 132 | * @return the visitor result 133 | */ 134 | visitSelectOrCall?: (ctx: SelectOrCallContext) => Result; 135 | /** 136 | * Visit a parse tree produced by the `PrimaryExpr` 137 | * labeled alternative in `CELParser.member`. 138 | * @param ctx the parse tree 139 | * @return the visitor result 140 | */ 141 | visitPrimaryExpr?: (ctx: PrimaryExprContext) => Result; 142 | /** 143 | * Visit a parse tree produced by the `Index` 144 | * labeled alternative in `CELParser.member`. 145 | * @param ctx the parse tree 146 | * @return the visitor result 147 | */ 148 | visitIndex?: (ctx: IndexContext) => Result; 149 | /** 150 | * Visit a parse tree produced by the `CreateMessage` 151 | * labeled alternative in `CELParser.member`. 152 | * @param ctx the parse tree 153 | * @return the visitor result 154 | */ 155 | visitCreateMessage?: (ctx: CreateMessageContext) => Result; 156 | /** 157 | * Visit a parse tree produced by the `IdentOrGlobalCall` 158 | * labeled alternative in `CELParser.primary`. 159 | * @param ctx the parse tree 160 | * @return the visitor result 161 | */ 162 | visitIdentOrGlobalCall?: (ctx: IdentOrGlobalCallContext) => Result; 163 | /** 164 | * Visit a parse tree produced by the `Nested` 165 | * labeled alternative in `CELParser.primary`. 166 | * @param ctx the parse tree 167 | * @return the visitor result 168 | */ 169 | visitNested?: (ctx: NestedContext) => Result; 170 | /** 171 | * Visit a parse tree produced by the `CreateList` 172 | * labeled alternative in `CELParser.primary`. 173 | * @param ctx the parse tree 174 | * @return the visitor result 175 | */ 176 | visitCreateList?: (ctx: CreateListContext) => Result; 177 | /** 178 | * Visit a parse tree produced by the `CreateStruct` 179 | * labeled alternative in `CELParser.primary`. 180 | * @param ctx the parse tree 181 | * @return the visitor result 182 | */ 183 | visitCreateStruct?: (ctx: CreateStructContext) => Result; 184 | /** 185 | * Visit a parse tree produced by the `ConstantLiteral` 186 | * labeled alternative in `CELParser.primary`. 187 | * @param ctx the parse tree 188 | * @return the visitor result 189 | */ 190 | visitConstantLiteral?: (ctx: ConstantLiteralContext) => Result; 191 | /** 192 | * Visit a parse tree produced by `CELParser.exprList`. 193 | * @param ctx the parse tree 194 | * @return the visitor result 195 | */ 196 | visitExprList?: (ctx: ExprListContext) => Result; 197 | /** 198 | * Visit a parse tree produced by `CELParser.fieldInitializerList`. 199 | * @param ctx the parse tree 200 | * @return the visitor result 201 | */ 202 | visitFieldInitializerList?: (ctx: FieldInitializerListContext) => Result; 203 | /** 204 | * Visit a parse tree produced by `CELParser.mapInitializerList`. 205 | * @param ctx the parse tree 206 | * @return the visitor result 207 | */ 208 | visitMapInitializerList?: (ctx: MapInitializerListContext) => Result; 209 | /** 210 | * Visit a parse tree produced by the `Int` 211 | * labeled alternative in `CELParser.literal`. 212 | * @param ctx the parse tree 213 | * @return the visitor result 214 | */ 215 | visitInt?: (ctx: IntContext) => Result; 216 | /** 217 | * Visit a parse tree produced by the `Uint` 218 | * labeled alternative in `CELParser.literal`. 219 | * @param ctx the parse tree 220 | * @return the visitor result 221 | */ 222 | visitUint?: (ctx: UintContext) => Result; 223 | /** 224 | * Visit a parse tree produced by the `Double` 225 | * labeled alternative in `CELParser.literal`. 226 | * @param ctx the parse tree 227 | * @return the visitor result 228 | */ 229 | visitDouble?: (ctx: DoubleContext) => Result; 230 | /** 231 | * Visit a parse tree produced by the `String` 232 | * labeled alternative in `CELParser.literal`. 233 | * @param ctx the parse tree 234 | * @return the visitor result 235 | */ 236 | visitString?: (ctx: StringContext) => Result; 237 | /** 238 | * Visit a parse tree produced by the `Bytes` 239 | * labeled alternative in `CELParser.literal`. 240 | * @param ctx the parse tree 241 | * @return the visitor result 242 | */ 243 | visitBytes?: (ctx: BytesContext) => Result; 244 | /** 245 | * Visit a parse tree produced by the `BoolTrue` 246 | * labeled alternative in `CELParser.literal`. 247 | * @param ctx the parse tree 248 | * @return the visitor result 249 | */ 250 | visitBoolTrue?: (ctx: BoolTrueContext) => Result; 251 | /** 252 | * Visit a parse tree produced by the `BoolFalse` 253 | * labeled alternative in `CELParser.literal`. 254 | * @param ctx the parse tree 255 | * @return the visitor result 256 | */ 257 | visitBoolFalse?: (ctx: BoolFalseContext) => Result; 258 | /** 259 | * Visit a parse tree produced by the `Null` 260 | * labeled alternative in `CELParser.literal`. 261 | * @param ctx the parse tree 262 | * @return the visitor result 263 | */ 264 | visitNull?: (ctx: NullContext) => Result; 265 | } 266 | 267 | -------------------------------------------------------------------------------- /src/grammar/CEL.g4: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | grammar CEL; 16 | 17 | // Grammar Rules 18 | // ============= 19 | 20 | start 21 | : e=expr EOF 22 | ; 23 | 24 | expr 25 | : e=conditionalOr (op='?' e1=conditionalOr ':' e2=expr)? 26 | ; 27 | 28 | conditionalOr 29 | : e=conditionalAnd (ops+='||' e1+=conditionalAnd)* 30 | ; 31 | 32 | conditionalAnd 33 | : e=relation (ops+='&&' e1+=relation)* 34 | ; 35 | 36 | relation 37 | : calc # RelationCalc 38 | | relation op=('<'|'<='|'>='|'>'|'=='|'!='|'in') relation # RelationOp 39 | ; 40 | 41 | calc 42 | : unary # CalcUnary 43 | | calc op=('*'|'/'|'%') calc # CalcMulDiv 44 | | calc op=('+'|'-') calc # CalcAddSub 45 | ; 46 | 47 | unary 48 | : member # MemberExpr 49 | | (ops+='!')+ member # LogicalNot 50 | | (ops+='-')+ member # Negate 51 | ; 52 | 53 | member 54 | : primary # PrimaryExpr 55 | | member op='.' id=IDENTIFIER (open='(' args=exprList? ')')? # SelectOrCall 56 | | member op='[' index=expr ']' # Index 57 | | member op='{' entries=fieldInitializerList? ','? '}' # CreateMessage 58 | ; 59 | 60 | primary 61 | : leadingDot='.'? id=IDENTIFIER (op='(' args=exprList? ')')? # IdentOrGlobalCall 62 | | '(' e=expr ')' # Nested 63 | | op='[' elems=exprList? ','? ']' # CreateList 64 | | op='{' entries=mapInitializerList? ','? '}' # CreateStruct 65 | | literal # ConstantLiteral 66 | ; 67 | 68 | exprList 69 | : e+=expr (',' e+=expr)* 70 | ; 71 | 72 | fieldInitializerList 73 | : fields+=IDENTIFIER cols+=':' values+=expr (',' fields+=IDENTIFIER cols+=':' values+=expr)* 74 | ; 75 | 76 | mapInitializerList 77 | : keys+=expr cols+=':' values+=expr (',' keys+=expr cols+=':' values+=expr)* 78 | ; 79 | 80 | literal 81 | : sign=MINUS? tok=NUM_INT # Int 82 | | tok=NUM_UINT # Uint 83 | | sign=MINUS? tok=NUM_FLOAT # Double 84 | | tok=STRING # String 85 | | tok=BYTES # Bytes 86 | | tok='true' # BoolTrue 87 | | tok='false' # BoolFalse 88 | | tok='null' # Null 89 | ; 90 | 91 | // Lexer Rules 92 | // =========== 93 | 94 | EQUALS : '=='; 95 | NOT_EQUALS : '!='; 96 | IN: 'in'; 97 | LESS : '<'; 98 | LESS_EQUALS : '<='; 99 | GREATER_EQUALS : '>='; 100 | GREATER : '>'; 101 | LOGICAL_AND : '&&'; 102 | LOGICAL_OR : '||'; 103 | 104 | LBRACKET : '['; 105 | RPRACKET : ']'; 106 | LBRACE : '{'; 107 | RBRACE : '}'; 108 | LPAREN : '('; 109 | RPAREN : ')'; 110 | DOT : '.'; 111 | COMMA : ','; 112 | MINUS : '-'; 113 | EXCLAM : '!'; 114 | QUESTIONMARK : '?'; 115 | COLON : ':'; 116 | PLUS : '+'; 117 | STAR : '*'; 118 | SLASH : '/'; 119 | PERCENT : '%'; 120 | TRUE : 'true'; 121 | FALSE : 'false'; 122 | NULL : 'null'; 123 | 124 | fragment BACKSLASH : '\\'; 125 | fragment LETTER : 'A'..'Z' | 'a'..'z' ; 126 | fragment DIGIT : '0'..'9' ; 127 | fragment EXPONENT : ('e' | 'E') ( '+' | '-' )? DIGIT+ ; 128 | fragment HEXDIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; 129 | fragment RAW : 'r' | 'R'; 130 | 131 | fragment ESC_SEQ 132 | : ESC_CHAR_SEQ 133 | | ESC_BYTE_SEQ 134 | | ESC_UNI_SEQ 135 | | ESC_OCT_SEQ 136 | ; 137 | 138 | fragment ESC_CHAR_SEQ 139 | : BACKSLASH ('a'|'b'|'f'|'n'|'r'|'t'|'v'|'"'|'\''|'\\'|'?'|'`') 140 | ; 141 | 142 | fragment ESC_OCT_SEQ 143 | : BACKSLASH ('0'..'3') ('0'..'7') ('0'..'7') 144 | ; 145 | 146 | fragment ESC_BYTE_SEQ 147 | : BACKSLASH ( 'x' | 'X' ) HEXDIGIT HEXDIGIT 148 | ; 149 | 150 | fragment ESC_UNI_SEQ 151 | : BACKSLASH 'u' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT 152 | | BACKSLASH 'U' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT 153 | ; 154 | 155 | WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ -> channel(HIDDEN) ; 156 | COMMENT : '//' (~'\n')* -> channel(HIDDEN) ; 157 | 158 | NUM_FLOAT 159 | : ( DIGIT+ ('.' DIGIT+) EXPONENT? 160 | | DIGIT+ EXPONENT 161 | | '.' DIGIT+ EXPONENT? 162 | ) 163 | ; 164 | 165 | NUM_INT 166 | : ( DIGIT+ | '0x' HEXDIGIT+ ); 167 | 168 | NUM_UINT 169 | : DIGIT+ ( 'u' | 'U' ) 170 | | '0x' HEXDIGIT+ ( 'u' | 'U' ) 171 | ; 172 | 173 | STRING 174 | : '"' (ESC_SEQ | ~('\\'|'"'|'\n'|'\r'))* '"' 175 | | '\'' (ESC_SEQ | ~('\\'|'\''|'\n'|'\r'))* '\'' 176 | | '"""' (ESC_SEQ | ~('\\'))*? '"""' 177 | | '\'\'\'' (ESC_SEQ | ~('\\'))*? '\'\'\'' 178 | | RAW '"' ~('"'|'\n'|'\r')* '"' 179 | | RAW '\'' ~('\''|'\n'|'\r')* '\'' 180 | | RAW '"""' .*? '"""' 181 | | RAW '\'\'\'' .*? '\'\'\'' 182 | ; 183 | 184 | BYTES : ('b' | 'B') STRING; 185 | 186 | IDENTIFIER : (LETTER | '_') ( LETTER | DIGIT | '_')*; 187 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { Runtime } from './Runtime'; 2 | export { Evaluator } from './Evaluator'; 3 | -------------------------------------------------------------------------------- /tests/TypeChecker.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { Runtime } from '../src'; 3 | 4 | describe('TypeChecker Tests using Runtime', () => { 5 | it('should return correct type for int literal', () => { 6 | const expression = "5"; 7 | const typeCheckResult = Runtime.typeCheck(expression, {}); 8 | expect(typeCheckResult.success).toBe(true); 9 | }); 10 | 11 | it('should return correct type for float literal', () => { 12 | const expression = "3.14"; 13 | const typeCheckResult = Runtime.typeCheck(expression, {}); 14 | expect(typeCheckResult.success).toBe(true); 15 | }); 16 | 17 | it('should return correct type for string literal', () => { 18 | const expression = '"hello"'; 19 | const typeCheckResult = Runtime.typeCheck(expression, {}); 20 | expect(typeCheckResult.success).toBe(true); 21 | }); 22 | 23 | it('should return correct type for boolean literal (true)', () => { 24 | const expression = "true"; 25 | const typeCheckResult = Runtime.typeCheck(expression, {}); 26 | expect(typeCheckResult.success).toBe(true); 27 | }); 28 | 29 | it('should return correct type for boolean literal (false)', () => { 30 | const expression = "false"; 31 | const typeCheckResult = Runtime.typeCheck(expression, {}); 32 | expect(typeCheckResult.success).toBe(true); 33 | }); 34 | 35 | it('should return correct type for list literal', () => { 36 | const expression = "[1, 2, 3]"; 37 | const typeCheckResult = Runtime.typeCheck(expression, {}); 38 | expect(typeCheckResult.success).toBe(true); 39 | }); 40 | 41 | it('should return correct type for addition of integers', () => { 42 | const expression = "5 + 3"; 43 | const typeCheckResult = Runtime.typeCheck(expression, {}); 44 | expect(typeCheckResult.error).toBeUndefined(); 45 | expect(typeCheckResult.success).toBe(true); 46 | }); 47 | 48 | it('should return correct types for addition of integers with types context', () => { 49 | const expression = "a + b"; 50 | const typeCheckResult = Runtime.typeCheck(expression, {a: 2, b: 4}); 51 | expect(typeCheckResult.error).toBeUndefined(); 52 | expect(typeCheckResult.success).toBe(true); 53 | }); 54 | 55 | it('should return error when using a string and a int to uses as an add on', () => { 56 | const expression = "a + b"; 57 | const typeCheckResult = Runtime.typeCheck(expression, {a: 2, b: "4"}); 58 | expect(typeCheckResult.success).toBe(false); 59 | expect(typeCheckResult.error).toContain("Operator '+' requires matching types, but got 'int' and 'string'"); 60 | }); 61 | 62 | it('should return correct type for addition of float and int', () => { 63 | const expression = "5.5 + 3"; 64 | const typeCheckResult = Runtime.typeCheck(expression, {}); 65 | expect(typeCheckResult.success).toBe(false); 66 | expect(typeCheckResult.error).toContain("Operator '+' requires matching types, but got 'float' and 'int'"); 67 | }); 68 | 69 | it('should throw error for invalid operator in arithmetic expression', () => { 70 | const expression = "5 + 'hello'"; 71 | const typeCheckResult = Runtime.typeCheck(expression, {}); 72 | expect(typeCheckResult.success).toBe(false); 73 | expect(typeCheckResult.error).toContain("Operator '+' requires matching types, but got 'int' and 'string'"); 74 | }); 75 | 76 | it('should throw error for invalid types in function call', () => { 77 | const expression = "max('string', 1)"; 78 | const typeCheckResult = Runtime.typeCheck(expression, {}); 79 | expect(typeCheckResult.success).toBe(false); 80 | expect(typeCheckResult.error).toContain("Argument 1 of function 'max' expects type 'int', but got 'string'"); 81 | }); 82 | 83 | it('should be valid as the example of the readme', () => { 84 | const expression = "user.age >= 18"; 85 | const context = { 86 | user: { 87 | name: 'Alice', 88 | age: 20, 89 | }, 90 | }; 91 | const typeCheckResult = Runtime.typeCheck(expression, context); 92 | expect(typeCheckResult.success).toBe(true); 93 | }); 94 | 95 | it('should be valid when we have multiple nested objects', () => { 96 | const expression = "user.age.really >= 18"; 97 | const context = { 98 | user: { 99 | name: 'Alice', 100 | age: { 101 | really: 20 102 | }, 103 | }, 104 | }; 105 | const typeCheckResult = Runtime.typeCheck(expression, context); 106 | expect(typeCheckResult.success).toBe(true); 107 | }); 108 | 109 | it('should return correct type for logical NOT expression', () => { 110 | const expression = "!true"; 111 | const typeCheckResult = Runtime.typeCheck(expression, {}); 112 | expect(typeCheckResult.success).toBe(true); 113 | }); 114 | 115 | it('should throw error for non-boolean operand in logical NOT expression', () => { 116 | const expression = "!5"; 117 | const typeCheckResult = Runtime.typeCheck(expression, {}); 118 | expect(typeCheckResult.success).toBe(false); 119 | expect(typeCheckResult.error).toContain("requires boolean operand"); 120 | }); 121 | 122 | it('should throw error for mismatched types in conditional AND expression', () => { 123 | const expression = "true && 5"; 124 | const typeCheckResult = Runtime.typeCheck(expression, {}); 125 | expect(typeCheckResult.success).toBe(false); 126 | expect(typeCheckResult.error).toContain("requires boolean operands"); 127 | }); 128 | 129 | it('Should return error when a comparison has a type mismatch', () => { 130 | const expression = "2 == '2'"; 131 | const typeCheckResult = Runtime.typeCheck(expression, {}); 132 | expect(typeCheckResult.success).toBe(false); 133 | expect(typeCheckResult.error).toContain("Mismatching types"); 134 | }); 135 | 136 | it('Should return error when a comparison has a type mismatch', () => { 137 | const expression = "'23' == 23"; 138 | const typeCheckResult = Runtime.typeCheck(expression, {}); 139 | expect(typeCheckResult.success).toBe(false); 140 | expect(typeCheckResult.error).toContain("Mismatching types"); 141 | }); 142 | 143 | it('Should return error when a comparison has a type mismatch', () => { 144 | const expression = "'23' == true"; 145 | const typeCheckResult = Runtime.typeCheck(expression, {}); 146 | expect(typeCheckResult.success).toBe(false); 147 | expect(typeCheckResult.error).toContain("Mismatching types"); 148 | }); 149 | 150 | it('Should return error when a comparison has a type mismatch', () => { 151 | const expression = "'true' == true"; 152 | const typeCheckResult = Runtime.typeCheck(expression, {}); 153 | expect(typeCheckResult.success).toBe(false); 154 | expect(typeCheckResult.error).toContain("Mismatching types"); 155 | }); 156 | 157 | it('Should return error when a comparison has a type mismatch', () => { 158 | const expression = "'true' == true"; 159 | const typeCheckResult = Runtime.typeCheck(expression, {}); 160 | expect(typeCheckResult.success).toBe(false); 161 | expect(typeCheckResult.error).toContain("Mismatching types"); 162 | }); 163 | 164 | 165 | it('Should return error when a comparison has a type mismatch', () => { 166 | const expression = "1 == 1.2"; 167 | const typeCheckResult = Runtime.typeCheck(expression, {}); 168 | expect(typeCheckResult.success).toBe(false); 169 | expect(typeCheckResult.error).toContain("Mismatching types"); 170 | }); 171 | 172 | }); 173 | -------------------------------------------------------------------------------- /tests/context.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { Runtime } from '../src/Runtime'; 3 | 4 | describe('CEL Context Tests', () => { 5 | 6 | it('should handle context variables correctly', () => { 7 | const expression = "a + b"; 8 | const context = { a: 10, b: 20 }; 9 | const runtime = new Runtime(expression); 10 | const result = runtime.evaluate(context); 11 | expect(result).toBe(30); 12 | }); 13 | 14 | it('should throw an error for undefined variables', () => { 15 | const expression = "a + b"; 16 | const context = { a: 10 }; 17 | const runtime = new Runtime(expression); 18 | expect(() => runtime.evaluate(context)).toThrow('Variable \'b\' is not defined'); 19 | }); 20 | 21 | it('should allow dynamic context changes', () => { 22 | const expression = "a + b"; 23 | const context1 = { a: 10, b: 20 }; 24 | const runtime = new Runtime(expression); 25 | const result1 = runtime.evaluate(context1); 26 | expect(result1).toBe(30); 27 | 28 | const context2 = { a: 5, b: 15 }; 29 | const result2 = runtime.evaluate(context2); 30 | expect(result2).toBe(20); 31 | }); 32 | 33 | it('should evaluate context with nested expressions', () => { 34 | const expression = "(a + b) * c"; 35 | const context = { a: 2, b: 3, c: 4 }; 36 | const runtime = new Runtime(expression); 37 | const result = runtime.evaluate(context); 38 | expect(result).toBe(20); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /tests/evaluation.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { Runtime } from '../src'; 3 | 4 | describe('CEL Evaluation Tests', () => { 5 | 6 | it('should evaluate simple arithmetic expression', () => { 7 | const expression = "2 + 3"; 8 | const runtime = new Runtime(expression); 9 | const result = runtime.evaluate({}); 10 | expect(result).toBe(5); 11 | }); 12 | 13 | it('should evaluate expression with variables', () => { 14 | const expression = "a + b"; 15 | const runtime = new Runtime(expression); 16 | const context = { a: 2, b: 3 }; 17 | const result = runtime.evaluate(context); 18 | expect(result).toBe(5); 19 | }); 20 | 21 | it('should evaluate a comparison expression', () => { 22 | const expression = "a == b"; 23 | const runtime = new Runtime(expression); 24 | const context = { a: 2, b: 2 }; 25 | const result = runtime.evaluate(context); 26 | expect(result).toBe(true); 27 | }); 28 | 29 | it('should work with the readme example', () => { 30 | const expression = "user.age >= 18"; 31 | const context = { 32 | user: { 33 | name: 'Alice', 34 | age: 20, 35 | }, 36 | }; 37 | const runtime = new Runtime(expression); 38 | const result = runtime.evaluate(context); 39 | expect(result).toBe(true); 40 | }); 41 | 42 | it('Should return error when a comparison has a type mismatch', () => { 43 | const expression = "2 == '2'"; 44 | const runtime = new Runtime(expression); 45 | expect(() => runtime.evaluate(expression)).toThrow('Mismatching types: Cannot compare \'int\' and \'string\' with \'==\''); 46 | }); 47 | 48 | it('Should return error when a comparison has a type mismatch string and boolean', () => { 49 | const expression = "'2' == true"; 50 | const runtime = new Runtime(expression); 51 | expect(() => runtime.evaluate(expression)).toThrow('Mismatching types: Cannot compare \'string\' and \'bool\' with \'==\''); 52 | }); 53 | 54 | it('Should return error when multiplying with int and a float', () => { 55 | const expression = "33 * 33.3"; 56 | const runtime = new Runtime(expression); 57 | expect(() => runtime.evaluate(expression)).toThrow('Operator \'*\' requires matching numeric operands, but got \'int\' and \'float\''); 58 | }); 59 | 60 | it('Should return error when dividing with int and a float', () => { 61 | const expression = "12.5 / 6"; 62 | const runtime = new Runtime(expression); 63 | expect(() => runtime.evaluate(expression)).toThrow('Operator \'/\' requires matching numeric operands, but got \'float\' and \'int\''); 64 | }); 65 | 66 | it('should evaluate an expression with arithmetic and comparison', () => { 67 | const expression = "a + b > c"; 68 | const runtime = new Runtime(expression); 69 | const context = { a: 2, b: 3, c: 4 }; 70 | const result = runtime.evaluate(context); 71 | expect(result).toBe(true); 72 | }); 73 | 74 | it('should throw an error for undefined variables', () => { 75 | const expression = "x"; 76 | const runtime = new Runtime(expression); 77 | expect(() => runtime.evaluate({})).toThrow('Variable \'x\' is not defined'); 78 | }); 79 | 80 | it('should evaluate nested arithmetic expressions', () => { 81 | const expression = "(2 + 3) * (4 - 1)"; 82 | const runtime = new Runtime(expression); 83 | const result = runtime.evaluate({}); 84 | expect(result).toBe(15); // (5) * (3) = 15 85 | }); 86 | 87 | it('should return a error when evaluate nested arithmetic expressions with a mismatching type', () => { 88 | const expression = "(2 + 3.3) * (4 - 1)"; 89 | const runtime = new Runtime(expression); 90 | expect(() => runtime.evaluate(expression)).toThrow('Operator \'+\' requires matching types, but got \'int\' and \'float\''); 91 | }); 92 | 93 | it('should return a error when evaluate nested arithmetic expressions with a mismatching type and try float with .0', () => { 94 | const expression = "(2 + 3.0) * (4 - 1)"; 95 | const runtime = new Runtime(expression); 96 | expect(() => runtime.evaluate(expression)).toThrow('Operator \'+\' requires matching types, but got \'int\' and \'float\''); 97 | }); 98 | 99 | it('should evaluate logical expressions with AND and OR', () => { 100 | const expression = "a > b && b < c || a == c"; 101 | const runtime = new Runtime(expression); 102 | const context = { a: 5, b: 3, c: 5 }; 103 | const result = runtime.evaluate(context); 104 | expect(result).toBe(true); 105 | }); 106 | 107 | // it('should evaluate ternary conditional expressions', () => { 108 | // const expression = "a > b ? 'greater' : 'lesser'"; 109 | // const runtime = new Runtime(expression); 110 | // const context = { a: 5, b: 3 }; 111 | // const result = runtime.evaluate(context); 112 | // expect(result).toBe('greater'); 113 | // }); 114 | 115 | // it('Should correctly handle ternary operator', () => { 116 | // const expression = 'true ? "yes" : "no"'; 117 | // runtime = new Runtime(expression); 118 | // const expected = "yes"; 119 | // const result = runtime.evaluate("{}"); 120 | // expect(result).toBe(expected); 121 | // }); 122 | 123 | it('should handle arithmetic with mixed types (string concatenation)', () => { 124 | const expression = "'Hello ' + 'World'"; 125 | const runtime = new Runtime(expression); 126 | const result = runtime.evaluate({}); 127 | expect(result).toBe('Hello World'); 128 | }); 129 | 130 | it('should handle arithmetic with nested parentheses', () => { 131 | const expression = "2 * (3 + (4 - 1))"; 132 | const runtime = new Runtime(expression); 133 | const result = runtime.evaluate({}); 134 | expect(result).toBe(12); 135 | }); 136 | 137 | it('should handle boolean negation', () => { 138 | const expression = "!true"; 139 | const runtime = new Runtime(expression); 140 | const result = runtime.evaluate({}); 141 | expect(result).toBe(false); 142 | }); 143 | 144 | 145 | it('should evaluate expressions with nested logical operations', () => { 146 | const expression = "!(a > b) || (c < d && e >= f)"; 147 | const runtime = new Runtime(expression); 148 | const context = { a: 3, b: 5, c: 2, d: 4, e: 5, f: 5 }; 149 | const result = runtime.evaluate(context); 150 | expect(result).toBe(true); 151 | }); 152 | 153 | 154 | it('Should return correct result when expression has nested operations', () => { 155 | const expression = '1 + (2 * 3) - (4 / 2)'; 156 | const runtime = new Runtime(expression); 157 | const expected = 5; 158 | const result = runtime.evaluate("{}"); 159 | expect(result).toBe(expected); 160 | }); 161 | 162 | it('Should return true for a logical expression', () => { 163 | const expression = 'true && false || true'; 164 | const runtime = new Runtime(expression); 165 | const expected = true; 166 | const result = runtime.evaluate("{}"); 167 | expect(result).toBe(expected); 168 | }); 169 | 170 | it('Should return correct string concatenation result', () => { 171 | const expression = '"hello" + " " + "world"'; 172 | const runtime = new Runtime(expression); 173 | const expected = "hello world"; 174 | const result = runtime.evaluate("{}"); 175 | expect(result).toBe(expected); 176 | }); 177 | 178 | it('Should correctly handle complex numerical expressions', () => { 179 | const expression = '2 * (3 + 4) - (10 / 2) + 7'; 180 | const runtime = new Runtime(expression); 181 | const expected = 16; 182 | const result = runtime.evaluate({}); 183 | expect(result).toBe(expected); 184 | }); 185 | 186 | it('Should correctly parse and evaluate a boolean negation', () => { 187 | const expression = '!true'; 188 | const runtime = new Runtime(expression); 189 | const expected = false; 190 | const result = runtime.evaluate("{}"); 191 | expect(result).toBe(expected); 192 | }); 193 | 194 | it('Should return error for undefined variable', () => { 195 | const expression = 'undefinedVar + 1'; 196 | const runtime = new Runtime(expression); 197 | expect(() => runtime.evaluate("{}")).toThrow('Variable \'undefinedVar\' is not defined'); 198 | }); 199 | 200 | it('should return error that you can not compare a float with a int', () => { 201 | const expression = "a > b && b < c || a == c"; 202 | const runtime = new Runtime(expression); 203 | const context = { a: 5, b: 3.3, c: 5 }; 204 | expect(() => runtime.evaluate(context)).toThrow('Mismatching types: Cannot compare \'int\' and \'float\' with \'>\''); 205 | }); 206 | 207 | it('should return error when comparing with numerics instead of boolean for ||', () => { 208 | const expression = "1 || 2.3 == 4.0"; 209 | const runtime = new Runtime(expression); 210 | expect(() => runtime.evaluate({})).toThrow('Logical \'||\' requires boolean operands, but got \'int\' and \'bool'); 211 | }); 212 | 213 | it('should return error when comparing with ||', () => { 214 | const expression = "1 <= 2.3"; 215 | const runtime = new Runtime(expression); 216 | expect(() => runtime.evaluate({})).toThrow('Mismatching types: Cannot compare \'int\' and \'float\' with \'<=\''); 217 | }); 218 | }); 219 | -------------------------------------------------------------------------------- /tests/functions.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { Runtime } from '../src/Runtime'; 3 | 4 | describe('CEL Built-in Functions', () => { 5 | let runtime: Runtime; 6 | 7 | // Arithmetic Functions 8 | it('should evaluate min function', () => { 9 | const expression = "min(a, b)"; 10 | runtime = new Runtime(expression); 11 | const expected = 10; 12 | const result = runtime.evaluate({ a: 10, b: 20 }); 13 | expect(result).toBe(expected); 14 | }); 15 | 16 | it('should evaluate max function', () => { 17 | const expression = "max(a, b)"; 18 | runtime = new Runtime(expression); 19 | const expected = 20; 20 | const result = runtime.evaluate({ a: 10, b: 20 }); 21 | expect(result).toBe(expected); 22 | }); 23 | 24 | it('should evaluate abs function', () => { 25 | const expression = "abs(a)"; 26 | runtime = new Runtime(expression); 27 | const expected = 10; 28 | const result = runtime.evaluate({ a: -10 }); 29 | expect(result).toBe(expected); 30 | }); 31 | 32 | it('should evaluate ceil function', () => { 33 | const expression = "ceil(a)"; 34 | runtime = new Runtime(expression); 35 | const expected = 11; 36 | const result = runtime.evaluate({ a: 10.1 }); 37 | expect(result).toBe(expected); 38 | }); 39 | 40 | it('should evaluate floor function', () => { 41 | const expression = "floor(a)"; 42 | runtime = new Runtime(expression); 43 | const expected = 10; 44 | const result = runtime.evaluate({ a: 10.9 }); 45 | expect(result).toBe(expected); 46 | }); 47 | 48 | it('should evaluate round function', () => { 49 | const expression = "round(a)"; 50 | runtime = new Runtime(expression); 51 | const expected = 11; 52 | const result = runtime.evaluate({ a: 10.5 }); 53 | expect(result).toBe(expected); 54 | }); 55 | 56 | // String Functions 57 | it('should evaluate contains function', () => { 58 | const expression = "contains(s, 'world')"; 59 | runtime = new Runtime(expression); 60 | const expected = true; 61 | const result = runtime.evaluate({ s: "hello world" }); 62 | expect(result).toBe(expected); 63 | }); 64 | 65 | it('should evaluate endsWith function', () => { 66 | const expression = "endsWith(s, 'world')"; 67 | runtime = new Runtime(expression); 68 | const expected = true; 69 | const result = runtime.evaluate({ s: "hello world" }); 70 | expect(result).toBe(expected); 71 | }); 72 | 73 | it('should evaluate indexOf function', () => { 74 | const expression = "indexOf(s, 'world')"; 75 | runtime = new Runtime(expression); 76 | const expected = 6; 77 | const result = runtime.evaluate({ s: "hello world" }); 78 | expect(result).toBe(expected); 79 | }); 80 | 81 | it('should evaluate length function', () => { 82 | const expression = "length(s)"; 83 | runtime = new Runtime(expression); 84 | const expected = 11; 85 | const result = runtime.evaluate({ s: "hello world" }); 86 | expect(result).toBe(expected); 87 | }); 88 | 89 | it('should evaluate lower function', () => { 90 | const expression = "lower(s)"; 91 | runtime = new Runtime(expression); 92 | const expected = "hello world"; 93 | const result = runtime.evaluate({ s: "HELLO WORLD" }); 94 | expect(result).toBe(expected); 95 | }); 96 | 97 | it('should evaluate replace function', () => { 98 | const expression = "replace(s, 'world', 'everyone')"; 99 | runtime = new Runtime(expression); 100 | const expected = "hello everyone"; 101 | const result = runtime.evaluate({ s: "hello world" }); 102 | expect(result).toBe(expected); 103 | }); 104 | 105 | it('should evaluate split function', () => { 106 | const expression = "split(s, ' ')"; 107 | runtime = new Runtime(expression); 108 | const expected = ["hello", "world"]; 109 | const result = runtime.evaluate({ s: "hello world" }); 110 | expect(result).toEqual(expected); 111 | }); 112 | 113 | it('should evaluate startsWith function', () => { 114 | const expression = "startsWith(s, 'hello')"; 115 | runtime = new Runtime(expression); 116 | const expected = true; 117 | const result = runtime.evaluate({ s: "hello world" }); 118 | expect(result).toBe(expected); 119 | }); 120 | 121 | it('should evaluate upper function', () => { 122 | const expression = "upper(s)"; 123 | runtime = new Runtime(expression); 124 | const expected = "HELLO WORLD"; 125 | const result = runtime.evaluate({ s: "hello world" }); 126 | expect(result).toBe(expected); 127 | }); 128 | 129 | // List Functions 130 | it('should evaluate size function for a list', () => { 131 | const expression = "size(lst)"; 132 | runtime = new Runtime(expression); 133 | const expected = 3; 134 | const result = runtime.evaluate({ lst: [1, 2, 3] }); 135 | expect(result).toBe(expected); 136 | }); 137 | 138 | // Type Conversion Functions 139 | it('should evaluate int function', () => { 140 | const expression = "int(a)"; 141 | runtime = new Runtime(expression); 142 | const expected = 10; 143 | const result = runtime.evaluate({ a: "10" }); 144 | expect(result).toBe(expected); 145 | }); 146 | 147 | it('should evaluate uint function', () => { 148 | const expression = "uint(a)"; 149 | runtime = new Runtime(expression); 150 | const expected = 10; 151 | const result = runtime.evaluate({ a: "10" }); 152 | expect(result).toBe(expected); 153 | }); 154 | 155 | it('should evaluate double function', () => { 156 | const expression = "double(a)"; 157 | runtime = new Runtime(expression); 158 | const expected = 10.5; 159 | const result = runtime.evaluate({ a: "10.5" }); 160 | expect(result).toBe(expected); 161 | }); 162 | 163 | it('should evaluate string function', () => { 164 | const expression = "string(a)"; 165 | runtime = new Runtime(expression); 166 | const expected = "10"; 167 | const result = runtime.evaluate({ a: 10 }); 168 | expect(result).toBe(expected); 169 | }); 170 | 171 | it('should evaluate bool function', () => { 172 | const expression = "bool(a)"; 173 | runtime = new Runtime(expression); 174 | const expected = true; 175 | const result = runtime.evaluate({ a: 1 }); 176 | expect(result).toBe(expected); 177 | }); 178 | 179 | // Null Handling Functions 180 | it('should evaluate exists function for null check', () => { 181 | const expression = "exists(a)"; 182 | runtime = new Runtime(expression); 183 | const expected = false; 184 | const result = runtime.evaluate({ a: null }); 185 | expect(result).toBe(expected); 186 | }); 187 | 188 | it('should evaluate existsOne function for null check', () => { 189 | const expression = "existsOne(lst)"; 190 | runtime = new Runtime(expression); 191 | const expected = true; 192 | const result = runtime.evaluate({ lst: [null, 1, null] }); 193 | expect(result).toBe(expected); 194 | }); 195 | 196 | it('should evaluate duration function', () => { 197 | const expression = "duration(a)"; 198 | runtime = new Runtime(expression); 199 | const expected = "10s"; 200 | const result = runtime.evaluate({ a: 10 }); 201 | expect(result).toBe(expected); 202 | }); 203 | 204 | it('should evaluate time function', () => { 205 | const expression = "time(2024, 8, 2, 12, 0, 0, 0)"; 206 | runtime = new Runtime(expression); 207 | const expected = "2024-08-02T12:00:00.000Z"; 208 | const result = runtime.evaluate({}); 209 | expect(result).toBe(expected); 210 | }); 211 | 212 | it('should evaluate date function', () => { 213 | const expression = "date(2024, 8, 2)"; 214 | runtime = new Runtime(expression); 215 | const expected = "2024-08-02"; 216 | const result = runtime.evaluate({}); 217 | expect(result).toBe(expected); 218 | }); 219 | 220 | it('should evaluate getFullYear function', () => { 221 | const expression = "getFullYear(timestamp)"; 222 | runtime = new Runtime(expression); 223 | const expected = 2024; 224 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2)) }); 225 | expect(result).toBe(expected); 226 | }); 227 | 228 | it('should evaluate getMonth function', () => { 229 | const expression = "getMonth(timestamp)"; 230 | runtime = new Runtime(expression); 231 | const expected = 7; // August is month 7 in 0-indexed Date object 232 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2)) }); 233 | expect(result).toBe(expected); 234 | }); 235 | 236 | it('should evaluate getDate function', () => { 237 | const expression = "getDate(timestamp)"; 238 | runtime = new Runtime(expression); 239 | const expected = 2; 240 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2)) }); 241 | expect(result).toBe(expected); 242 | }); 243 | 244 | it('should evaluate getHours function', () => { 245 | const expression = "getHours(timestamp)"; 246 | runtime = new Runtime(expression); 247 | const expected = 12; 248 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2, 12)) }); 249 | expect(result).toBe(expected); 250 | }); 251 | 252 | it('should evaluate getMinutes function', () => { 253 | const expression = "getMinutes(timestamp)"; 254 | runtime = new Runtime(expression); 255 | const expected = 0; 256 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2, 12, 0)) }); 257 | expect(result).toBe(expected); 258 | }); 259 | 260 | it('should evaluate getSeconds function', () => { 261 | const expression = "getSeconds(timestamp)"; 262 | runtime = new Runtime(expression); 263 | const expected = 0; 264 | const result = runtime.evaluate({ timestamp: new Date(Date.UTC(2024, 7, 2, 12, 0, 0)) }); 265 | expect(result).toBe(expected); 266 | }); 267 | }); 268 | -------------------------------------------------------------------------------- /tests/parsing.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { Runtime } from '../src/Runtime'; 3 | 4 | describe('CEL Parsing Tests', () => { 5 | 6 | it('should parse a simple arithmetic expression', () => { 7 | const expression = "1 + 1"; 8 | const result = Runtime.parseString(expression); 9 | expect(result.success).toBe(true); 10 | }); 11 | 12 | it('should fail to parse an invalid expression', () => { 13 | const expression = "1 +"; 14 | const result = Runtime.parseString(expression); 15 | expect(result.success).toBe(false); 16 | }); 17 | 18 | it('should return error message for invalid expression', () => { 19 | const expression = "1 + "; 20 | const result = Runtime.parseString(expression); 21 | expect(result.success).toBe(false); 22 | expect(result.error).toBeDefined(); 23 | }); 24 | 25 | it('should parse a comparison expression', () => { 26 | const expression = "a == b"; 27 | const result = Runtime.parseString(expression); 28 | expect(result.success).toBe(true); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "typeRoots": ["./node_modules/@types", "./typings"] 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "lib"] 15 | } 16 | --------------------------------------------------------------------------------