Comparing version 0.28.0 to 0.29.0
@@ -9,2 +9,6 @@ # Changelog | ||
## 0.29.0 | ||
* `FEAT`: support named function invocation | ||
## 0.28.0 | ||
@@ -11,0 +15,0 @@ |
declare const names: string[]; | ||
declare const builtins: { | ||
date: () => never; | ||
'date and time': (...args: any[]) => any; | ||
'date and time': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
time: () => never; | ||
number: () => never; | ||
string: (...args: any[]) => any; | ||
string: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
duration: () => never; | ||
'years and months duration': () => never; | ||
not: (...args: any[]) => any; | ||
substring: (...args: any[]) => any; | ||
'string length': (...args: any[]) => any; | ||
'upper case': (...args: any[]) => any; | ||
'lower case': (...args: any[]) => any; | ||
'substring before': (...args: any[]) => any; | ||
'substring after': (...args: any[]) => any; | ||
replace: (...args: any[]) => any; | ||
contains: (...args: any[]) => any; | ||
'starts with': (...args: any[]) => any; | ||
'ends with': (...args: any[]) => any; | ||
split: (...args: any[]) => any; | ||
'list contains': (...args: any[]) => any; | ||
count: (...args: any[]) => any; | ||
not: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
substring: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'string length': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'upper case': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'lower case': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'substring before': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'substring after': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
replace: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
contains: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'starts with': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'ends with': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
split: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'list contains': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
count: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
min: (...args: any[]) => any; | ||
@@ -30,12 +78,42 @@ max: (...args: any[]) => any; | ||
any: (...args: any[]) => any; | ||
sublist: (...args: any[]) => any; | ||
append: (...args: any[]) => any; | ||
concatenate: (...args: any[]) => any; | ||
'insert before': (...args: any[]) => any; | ||
remove: (...args: any[]) => any; | ||
reverse: (...args: any[]) => any; | ||
'index of': (...args: any[]) => any; | ||
union: (...args: any[]) => any; | ||
'distinct values': (...args: any[]) => any; | ||
flatten: (...args: any[]) => any; | ||
sublist: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
append: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
concatenate: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'insert before': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
remove: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
reverse: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'index of': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
union: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'distinct values': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
flatten: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
product: (...args: any[]) => any; | ||
@@ -45,35 +123,128 @@ median: (...args: any[]) => any; | ||
mode: (...args: any[]) => any; | ||
decimal: (...args: any[]) => any; | ||
floor: (...args: any[]) => any; | ||
ceiling: (...args: any[]) => any; | ||
abs: (...args: any[]) => any; | ||
modulo: (...args: any[]) => any; | ||
sqrt: (...args: any[]) => any; | ||
log: (...args: any[]) => any; | ||
exp: (...args: any[]) => any; | ||
odd: (...args: any[]) => any; | ||
even: (...args: any[]) => any; | ||
is: (...args: any[]) => any; | ||
before: (...args: any[]) => any; | ||
after: (...args: any[]) => any; | ||
meets: (...args: any[]) => any; | ||
'met by': (...args: any[]) => any; | ||
overlaps: (...args: any[]) => any; | ||
'overlaps before': (...args: any[]) => any; | ||
'overlaps after': (...args: any[]) => any; | ||
finishes: (...args: any[]) => any; | ||
'finished by': (...args: any[]) => any; | ||
includes: (...args: any[]) => any; | ||
during: (...args: any[]) => any; | ||
starts: (...args: any[]) => any; | ||
'started by': (...args: any[]) => any; | ||
coincides: (...args: any[]) => any; | ||
'day of year': (...args: any[]) => any; | ||
'day of week': (...args: any[]) => any; | ||
'month of year': (...args: any[]) => any; | ||
'week of year': (...args: any[]) => any; | ||
decimal: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
floor: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
ceiling: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
abs: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
modulo: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
sqrt: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
log: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
exp: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
odd: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
even: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
is: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
before: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
after: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
meets: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'met by': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
overlaps: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'overlaps before': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'overlaps after': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
finishes: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'finished by': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
includes: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
during: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
starts: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'started by': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
coincides: { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'day of year': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'day of week': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'month of year': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'week of year': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
sort: () => never; | ||
'get value': (...args: any[]) => any; | ||
'get entries': (...args: any[]) => any; | ||
'get value': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
'get entries': { | ||
(...args: any[]): any; | ||
$args: any; | ||
}; | ||
}; | ||
export { names, builtins }; |
@@ -45,2 +45,18 @@ import { parser, trackVariables, normalizeContext } from 'lezer-feel'; | ||
function parseParameterNames(fn) { | ||
if (Array.isArray(fn.$args)) { | ||
return fn.$args; | ||
} | ||
var code = fn.toString(); | ||
var match = /^(?:[^(]*\s*)?\(([^)]+)?\)/.exec(code); | ||
if (!match) { | ||
throw new Error('failed to parse params: ' + code); | ||
} | ||
var _ = match[0], params = match[1]; | ||
if (!params) { | ||
return []; | ||
} | ||
return params.split(',').map(function (p) { return p.trim(); }); | ||
} | ||
var builtins = { | ||
@@ -445,3 +461,3 @@ // 10.3.4.1 Conversion functions | ||
var checkArgs = createArgsValidator(argDefinitions); | ||
return function () { | ||
var wrappedFn = function () { | ||
var args = []; | ||
@@ -457,2 +473,4 @@ for (var _i = 0; _i < arguments.length; _i++) { | ||
}; | ||
wrappedFn.$args = parseParameterNames(fnDefinition); | ||
return wrappedFn; | ||
} | ||
@@ -665,3 +683,3 @@ function sum(list) { | ||
var fnBody = args[4]; | ||
return function () { | ||
return wrapFunction(function () { | ||
var args = []; | ||
@@ -677,3 +695,3 @@ for (var _i = 0; _i < arguments.length; _i++) { | ||
return fnBody(fnContext); | ||
}; | ||
}, parameterNames); | ||
}; | ||
@@ -741,3 +759,13 @@ case 'ContextEntry': return function (context) { | ||
case 'StringLiteral': return tag(function (_context) { return input.slice(1, -1); }, 'string'); | ||
case 'PositionalParameters': return function (_context) { return args; }; | ||
case 'PositionalParameters': return function (context) { return args.map(function (arg) { return arg(context); }); }; | ||
case 'NamedParameter': return function (context) { | ||
var name = args[0]; | ||
var value = args[1](context); | ||
return [name, value]; | ||
}; | ||
case 'NamedParameters': return function (context) { return args.reduce(function (args, arg) { | ||
var _c = arg(context), name = _c[0], value = _c[1]; | ||
args[name] = value; | ||
return args; | ||
}, {}); }; | ||
case 'DateTimeConstructor': return function (context) { | ||
@@ -748,8 +776,8 @@ return getBuiltin(input); | ||
case 'FunctionInvocation': return function (context) { | ||
var fn = args[0](context); | ||
if (typeof fn !== 'function') { | ||
var wrappedFn = wrapFunction(args[0](context)); | ||
if (!wrappedFn) { | ||
throw new Error("Failed to evaluate " + input + ": Target is not a function"); | ||
} | ||
var fnArgs = args[2](context).map(function (fn) { return fn(context); }); | ||
return fn.apply(void 0, fnArgs); | ||
var contextOrArgs = args[2](context); | ||
return wrappedFn.invoke(contextOrArgs); | ||
}; | ||
@@ -1055,3 +1083,31 @@ case 'IfExpression': return (function () { | ||
} | ||
/** | ||
* @param {Function} fn | ||
* @param {string[]} [parameterNames] | ||
* | ||
* @return {WrappedFn} | ||
*/ | ||
function wrapFunction(fn, parameterNames) { | ||
if (parameterNames === void 0) { parameterNames = null; } | ||
if (fn instanceof WrappedFn) { | ||
return fn; | ||
} | ||
if (!fn) { | ||
return null; | ||
} | ||
return new WrappedFn(fn, parameterNames || parseParameterNames(fn)); | ||
} | ||
function WrappedFn(fn, parameterNames) { | ||
this.invoke = function (contextOrArgs) { | ||
var params; | ||
if (Array.isArray(contextOrArgs)) { | ||
params = contextOrArgs; | ||
} | ||
else { | ||
params = parameterNames.map(function (n) { return contextOrArgs[n]; }); | ||
} | ||
return fn.call.apply(fn, __spreadArrays([null], params)); | ||
}; | ||
} | ||
export { evaluate, parseExpressions, parseUnaryTests, unaryTest }; |
@@ -49,2 +49,18 @@ 'use strict'; | ||
function parseParameterNames(fn) { | ||
if (Array.isArray(fn.$args)) { | ||
return fn.$args; | ||
} | ||
var code = fn.toString(); | ||
var match = /^(?:[^(]*\s*)?\(([^)]+)?\)/.exec(code); | ||
if (!match) { | ||
throw new Error('failed to parse params: ' + code); | ||
} | ||
var _ = match[0], params = match[1]; | ||
if (!params) { | ||
return []; | ||
} | ||
return params.split(',').map(function (p) { return p.trim(); }); | ||
} | ||
var builtins = { | ||
@@ -449,3 +465,3 @@ // 10.3.4.1 Conversion functions | ||
var checkArgs = createArgsValidator(argDefinitions); | ||
return function () { | ||
var wrappedFn = function () { | ||
var args = []; | ||
@@ -461,2 +477,4 @@ for (var _i = 0; _i < arguments.length; _i++) { | ||
}; | ||
wrappedFn.$args = parseParameterNames(fnDefinition); | ||
return wrappedFn; | ||
} | ||
@@ -669,3 +687,3 @@ function sum(list) { | ||
var fnBody = args[4]; | ||
return function () { | ||
return wrapFunction(function () { | ||
var args = []; | ||
@@ -681,3 +699,3 @@ for (var _i = 0; _i < arguments.length; _i++) { | ||
return fnBody(fnContext); | ||
}; | ||
}, parameterNames); | ||
}; | ||
@@ -745,3 +763,13 @@ case 'ContextEntry': return function (context) { | ||
case 'StringLiteral': return tag(function (_context) { return input.slice(1, -1); }, 'string'); | ||
case 'PositionalParameters': return function (_context) { return args; }; | ||
case 'PositionalParameters': return function (context) { return args.map(function (arg) { return arg(context); }); }; | ||
case 'NamedParameter': return function (context) { | ||
var name = args[0]; | ||
var value = args[1](context); | ||
return [name, value]; | ||
}; | ||
case 'NamedParameters': return function (context) { return args.reduce(function (args, arg) { | ||
var _c = arg(context), name = _c[0], value = _c[1]; | ||
args[name] = value; | ||
return args; | ||
}, {}); }; | ||
case 'DateTimeConstructor': return function (context) { | ||
@@ -752,8 +780,8 @@ return getBuiltin(input); | ||
case 'FunctionInvocation': return function (context) { | ||
var fn = args[0](context); | ||
if (typeof fn !== 'function') { | ||
var wrappedFn = wrapFunction(args[0](context)); | ||
if (!wrappedFn) { | ||
throw new Error("Failed to evaluate " + input + ": Target is not a function"); | ||
} | ||
var fnArgs = args[2](context).map(function (fn) { return fn(context); }); | ||
return fn.apply(void 0, fnArgs); | ||
var contextOrArgs = args[2](context); | ||
return wrappedFn.invoke(contextOrArgs); | ||
}; | ||
@@ -1059,2 +1087,30 @@ case 'IfExpression': return (function () { | ||
} | ||
/** | ||
* @param {Function} fn | ||
* @param {string[]} [parameterNames] | ||
* | ||
* @return {WrappedFn} | ||
*/ | ||
function wrapFunction(fn, parameterNames) { | ||
if (parameterNames === void 0) { parameterNames = null; } | ||
if (fn instanceof WrappedFn) { | ||
return fn; | ||
} | ||
if (!fn) { | ||
return null; | ||
} | ||
return new WrappedFn(fn, parameterNames || parseParameterNames(fn)); | ||
} | ||
function WrappedFn(fn, parameterNames) { | ||
this.invoke = function (contextOrArgs) { | ||
var params; | ||
if (Array.isArray(contextOrArgs)) { | ||
params = contextOrArgs; | ||
} | ||
else { | ||
params = parameterNames.map(function (n) { return contextOrArgs[n]; }); | ||
} | ||
return fn.call.apply(fn, __spreadArrays([null], params)); | ||
}; | ||
} | ||
@@ -1061,0 +1117,0 @@ exports.evaluate = evaluate; |
{ | ||
"name": "feelin", | ||
"version": "0.28.0", | ||
"version": "0.29.0", | ||
"description": "A FEEL parser and interpreter", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -36,3 +36,3 @@ ## Feelin | ||
* [x] Recognizes full FEEL grammar | ||
* [x] Supports names with spaces | ||
* [x] Context sensitive (incl. names with spaces) | ||
* [x] Recovers on errors | ||
@@ -39,0 +39,0 @@ * [ ] Provides built-in functions |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
428964
14
8136