Skip to content

Commit

Permalink
Let expressions learns to use custom registered functions (#32)
Browse files Browse the repository at this point in the history
* fixed vulnerabilities

* clarified intent for usage of a type

* removed compiler warning

* Let expressions learned to use custom registered functions (#29)
  • Loading branch information
springcomp authored Oct 25, 2024
1 parent c4c1e0f commit a29e14d
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 31 deletions.
61 changes: 36 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface FunctionTable {

export class Runtime {
_interpreter: TreeInterpreter;
_functionTable: FunctionTable;
TYPE_NAME_TABLE: { [InputArgument: number]: string } = {
[InputArgument.TYPE_NUMBER]: 'number',
[InputArgument.TYPE_ANY]: 'any',
Expand All @@ -78,24 +79,25 @@ export class Runtime {

constructor(interpreter: TreeInterpreter) {
this._interpreter = interpreter;
this._functionTable = this.functionTable
}

registerFunction(
name: string,
customFunction: RuntimeFunction<(JSONValue | ExpressionNode)[], JSONValue>,
signature: InputSignature[],
): void {
if (name in this.functionTable) {
if (name in this._functionTable) {
throw new Error(`Function already defined: ${name}()`);
}
this.functionTable[name] = {
this._functionTable[name] = {
_func: customFunction.bind(this),
_signature: signature,
};
}

callFunction(name: string, resolvedArgs: (JSONValue | ExpressionNode)[]): JSONValue {
const functionEntry = this.functionTable[name];
const functionEntry = this._functionTable[name];
if (functionEntry === undefined) {
throw new Error(`Unknown function: ${name}()`);
}
Expand All @@ -114,7 +116,7 @@ export class Runtime {
private validateArgs(name: string, args: (JSONValue | ExpressionNode)[], signature: InputSignature[]): void {
let pluralized: boolean;
this.validateInputSignatures(name, signature);
const numberOfRequiredArgs = signature.filter(argSignature => !argSignature.optional ?? false).length;
const numberOfRequiredArgs = signature.filter(argSignature => !(argSignature.optional ?? false)).length;
const lastArgIsVariadic = signature[signature.length - 1]?.variadic ?? false;
const tooFewArgs = args.length < numberOfRequiredArgs;
const tooManyArgs = args.length > signature.length;
Expand Down
3 changes: 2 additions & 1 deletion src/TreeInterpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class TreeInterpreter {

withScope(scope: ScopeEntry): TreeInterpreter {
const interpreter = new TreeInterpreter();
interpreter.runtime._functionTable = this.runtime._functionTable;
interpreter._rootValue = this._rootValue;
interpreter._scope = this._scope.withScope(scope);
return interpreter;
Expand All @@ -36,7 +37,7 @@ export class TreeInterpreter {
const identifier = node.name;
let result: JSONValue = null;
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
result = value[identifier] ?? null;
result = (value as JSONObject)[identifier] ?? null;
}
return result;
case 'LetExpression': {
Expand Down
16 changes: 15 additions & 1 deletion test/jmespath-functions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { search } from '../src';
import { search, registerFunction, TYPE_NUMBER } from '../src';
import { expectError } from './error.utils';

describe('Evaluates functions', () => {
Expand Down Expand Up @@ -125,3 +125,17 @@ describe('Type-checks function arguments', () => {
}, 'invalid-value');
});
});

describe('custom functions', () => {
it('must be in scope for let expression', () => {
registerFunction(
'plusplus', // FUNCTION NAME
(resolvedArgs) => { // CUSTOM FUNCTION
const [num] = resolvedArgs;
return num + 1;
},
[{ types: [TYPE_NUMBER] }] //SIGNATURE
);
expect(search({index: 0}, 'let $n = index in plusplus($n)')).toEqual(1);
});
});

0 comments on commit a29e14d

Please sign in to comment.