Skip to content

Commit

Permalink
[PORT]Add array return type (#2007)
Browse files Browse the repository at this point in the history
  • Loading branch information
Danieladu authored Apr 9, 2020
1 parent d54da72 commit 39d3592
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 66 deletions.
4 changes: 3 additions & 1 deletion libraries/adaptive-expressions/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { Expression, ReturnType } from './expression';
import { ExpressionEvaluator } from './expressionEvaluator';
import { ExpressionType } from './expressionType';
import { ExpressionFunctions } from './expressionFunctions';

/**
* Construct an expression constant.
Expand All @@ -28,7 +29,8 @@ export class Constant extends Expression {
typeof theValue === 'string' ? ReturnType.String
: typeof theValue === 'boolean' ? ReturnType.Boolean
: typeof theValue === 'number' && !Number.isNaN(theValue) ? ReturnType.Number
: ReturnType.Object;
: Array.isArray(theValue) ? ReturnType.Array
: ReturnType.Object;

this._value = theValue;
}
Expand Down
13 changes: 9 additions & 4 deletions libraries/adaptive-expressions/src/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,27 @@ export enum ReturnType {
/**
* True or false boolean value.
*/
Boolean = 'boolean',
Boolean = 1,

/**
* Numerical value like int, float, double, ...
*/
Number = 'number',
Number = 2,

/**
* Any value is possible.
*/
Object = 'object',
Object = 4,

/**
* String value.
*/
String = 'string'
String = 8,

/**
* Array value.
*/
Array = 16,
}

/**
Expand Down
128 changes: 67 additions & 61 deletions libraries/adaptive-expressions/src/expressionFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,37 +64,21 @@ export class ExpressionFunctions {
* @param expression Expression to validate.
* @param minArity Minimum number of children.
* @param maxArity Maximum number of children.
* @param types Allowed return types for children.
* @param returnType Allowed return types for children.
* If a child has a return type of Object then validation will happen at runtime.
*/
public static validateArityAndAnyType(expression: Expression, minArity: number, maxArity: number, ...types: ReturnType[]): void {
public static validateArityAndAnyType(expression: Expression, minArity: number, maxArity: number, returnType: ReturnType = ReturnType.Object): void {
if (expression.children.length < minArity) {
throw new Error(`${ expression } should have at least ${ minArity } children.`);
}
if (expression.children.length > maxArity) {
throw new Error(`${ expression } can't have more than ${ maxArity } children.`);
}

if (types.length > 0) {
if ((returnType & ReturnType.Object) === 0) {
for (const child of expression.children) {

if (child.returnType !== ReturnType.Object && !types.includes(child.returnType)) {
if (types.length === 1) {
throw new Error(`${ child } is not a ${ types[0] } expression in ${ expression.toString() }.`);
} else {
const builder = `${ child } in ${ expression.toString() } is not any of [`;
let first = true;
for (const type of types) {
if (first) {
first = false;
} else {
builder.concat('. ');
}
builder.concat(type.toString());
}
builder.concat('].');
throw new Error(builder);
}
if ((child.returnType & ReturnType.Object) === 0 && (returnType & child.returnType) === 0) {
throw new Error(ExpressionFunctions.buildTypeValidatorError(returnType, child, expression));
}
}
}
Expand All @@ -119,8 +103,10 @@ export class ExpressionFunctions {
for (let i = 0; i < types.length; i++) {
const child: Expression = expression.children[i];
const type: ReturnType = types[i];
if (type !== ReturnType.Object && child.returnType !== ReturnType.Object && child.returnType !== type) {
throw new Error(`${ child } in ${ expression } is not a ${ type }.`);
if ((type & ReturnType.Object) == 0
&& (child.returnType & ReturnType.Object) == 0
&& (type & child.returnType) == 0) {
throw new Error(ExpressionFunctions.buildTypeValidatorError(type, child, expression));
}
}

Expand All @@ -131,8 +117,10 @@ export class ExpressionFunctions {
}
const child: Expression = expression.children[ic];
const type: ReturnType = optional[i];
if (type !== ReturnType.Object && child.returnType !== ReturnType.Object && child.returnType !== type) {
throw new Error(`${ child } in ${ expression } is not a ${ type }.`);
if ((type & ReturnType.Object) == 0
&& (child.returnType & ReturnType.Object) == 0
&& (type & child.returnType) == 0) {
throw new Error(ExpressionFunctions.buildTypeValidatorError(type, child, expression));
}
}
}
Expand Down Expand Up @@ -192,7 +180,7 @@ export class ExpressionFunctions {
* @param expression Expression to validate.
*/
public static validateBinaryNumberOrString(expression: Expression): void {
ExpressionFunctions.validateArityAndAnyType(expression, 2, 2, ReturnType.Number, ReturnType.String);
ExpressionFunctions.validateArityAndAnyType(expression, 2, 2, ReturnType.Number | ReturnType.String);
}

/**
Expand Down Expand Up @@ -692,7 +680,7 @@ export class ExpressionFunctions {
return { value: result, error };
},
ReturnType.String,
(expr: Expression): void => ExpressionFunctions.validateArityAndAnyType(expr, 2, 3, ReturnType.String, ReturnType.Number));
(expr: Expression): void => ExpressionFunctions.validateOrder(expr, [ReturnType.String], ReturnType.String, ReturnType.Number));
}

/**
Expand Down Expand Up @@ -810,6 +798,24 @@ export class ExpressionFunctions {
}
}

private static buildTypeValidatorError(returnType: ReturnType, childExpr: Expression, expr: Expression): string {
const names = Object.keys(ReturnType).filter((x): boolean => !(parseInt(x) >= 0));
let types = [];
for (const name of names) {
const value = ReturnType[name] as number;
if ((returnType & value) !== 0) {
types.push(name);
}
}

if (types.length === 1) {
return `${ childExpr } is not a ${ types[0] } expression in ${ expr }.`;
} else {
const typesStr = types.join(', ');
return `${ childExpr } in ${ expr } is not any of [${ typesStr }].`;
}
}

private static addOrdinal(num: number): string {
let hasResult = false;
let ordinalResult: string = num.toString();
Expand Down Expand Up @@ -874,7 +880,7 @@ export class ExpressionFunctions {
if (children.length > 2) {
throw new Error(`${ expression } has more than 2 children.`);
}
if (children.length === 2 && children[1].returnType !== ReturnType.Object) {
if (children.length === 2 && (children[1].returnType & ReturnType.Object) === 0) {
throw new Error(`${ expression } must have an object as its second argument.`);
}
}
Expand Down Expand Up @@ -1980,7 +1986,7 @@ export class ExpressionFunctions {
(args: any []): number => args[0].reduce((x: number, y: number): number => x + y),
ExpressionFunctions.verifyNumericList),
ReturnType.Number,
ExpressionFunctions.validateUnary),
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Array)),
new ExpressionEvaluator(
ExpressionType.Add,
ExpressionFunctions.applySequenceWithError(
Expand Down Expand Up @@ -2010,8 +2016,8 @@ export class ExpressionFunctions {
return {value, error};
},
ExpressionFunctions.verifyNumberOrStringOrNull),
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 2, Number.MAX_SAFE_INTEGER)),
ReturnType.String | ReturnType.Number,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 2, Number.MAX_SAFE_INTEGER, ReturnType.String | ReturnType.Number)),
new ExpressionEvaluator(
ExpressionType.Count,
ExpressionFunctions.apply(
Expand All @@ -2029,7 +2035,7 @@ export class ExpressionFunctions {
},
ExpressionFunctions.verifyContainer),
ReturnType.Number,
ExpressionFunctions.validateUnary),
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.String | ReturnType.Array)),
new ExpressionEvaluator(
ExpressionType.Range,
ExpressionFunctions.applyWithError(
Expand All @@ -2045,7 +2051,7 @@ export class ExpressionFunctions {
},
ExpressionFunctions.verifyInteger
),
ReturnType.Object,
ReturnType.Array,
ExpressionFunctions.validateBinaryNumber
),
new ExpressionEvaluator(
Expand All @@ -2060,8 +2066,8 @@ export class ExpressionFunctions {
return Array.from(new Set(result));
},
ExpressionFunctions.verifyList),
ReturnType.Object,
ExpressionFunctions.validateAtLeastOne
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 1, Number.MAX_SAFE_INTEGER, ReturnType.Array)
),
new ExpressionEvaluator(
ExpressionType.Intersection,
Expand All @@ -2075,38 +2081,38 @@ export class ExpressionFunctions {
return Array.from(new Set(result));
},
ExpressionFunctions.verifyList),
ReturnType.Object,
ExpressionFunctions.validateAtLeastOne
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 1, Number.MAX_SAFE_INTEGER, ReturnType.Array)
),
new ExpressionEvaluator(
ExpressionType.Skip,
ExpressionFunctions.skip,
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Object, ReturnType.Number)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Array, ReturnType.Number)
),
new ExpressionEvaluator(
ExpressionType.Take,
ExpressionFunctions.take,
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Object, ReturnType.Number)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Array, ReturnType.Number)
),
new ExpressionEvaluator(
ExpressionType.SubArray,
ExpressionFunctions.subArray,
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.Number], ReturnType.Object, ReturnType.Number)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.Number], ReturnType.Array, ReturnType.Number)
),
new ExpressionEvaluator(
ExpressionType.SortBy,
ExpressionFunctions.sortBy(false),
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Object)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Array)
),
new ExpressionEvaluator(
ExpressionType.SortByDescending,
ExpressionFunctions.sortBy(true),
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Object)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Array)
),
new ExpressionEvaluator(
ExpressionType.Flatten,
Expand All @@ -2116,18 +2122,18 @@ export class ExpressionFunctions {
let depth = args.length > 1 ? args[1] : 100;
return ExpressionFunctions.flatten(array, depth);
}),
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.Number], ReturnType.Object)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.Number], ReturnType.Array)
),
new ExpressionEvaluator(
ExpressionType.Unique,
ExpressionFunctions.apply((args: any []): any[] => [... new Set(args[0])]),
ReturnType.Object,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Object)
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.Array)
),
new ExpressionEvaluator(ExpressionType.IndicesAndValues,
(expression: Expression, state: any, options: Options): {value: any; error: string} => ExpressionFunctions.indicesAndValues(expression, state, options),
ReturnType.Object, ExpressionFunctions.validateUnary),
ReturnType.Array, ExpressionFunctions.validateUnary),
ExpressionFunctions.comparison(
ExpressionType.LessThan,
(args: any []): boolean => args[0] < args[1], ExpressionFunctions.validateBinaryNumberOrString, ExpressionFunctions.verifyNumberOrString),
Expand Down Expand Up @@ -2253,7 +2259,7 @@ export class ExpressionFunctions {
new ExpressionEvaluator(
ExpressionType.Split,
ExpressionFunctions.apply((args: any []): string[] => ExpressionFunctions.parseStringOrNull(args[0]).split(ExpressionFunctions.parseStringOrNull(args[1] || '')), ExpressionFunctions.verifyStringOrNull),
ReturnType.Object,
ReturnType.Array,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 1, 2, ReturnType.String)),
new ExpressionEvaluator(
ExpressionType.Substring,
Expand Down Expand Up @@ -2317,7 +2323,7 @@ export class ExpressionFunctions {
return { value, error };
},
ReturnType.Number,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 2, 2, ReturnType.String, ReturnType.Boolean, ReturnType.Number, ReturnType.Object)
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.String | ReturnType.Array, ReturnType.Object)
),
new ExpressionEvaluator(
ExpressionType.LastIndexOf,
Expand Down Expand Up @@ -2345,7 +2351,7 @@ export class ExpressionFunctions {
return { value, error };
},
ReturnType.Number,
(expression: Expression): void => ExpressionFunctions.validateArityAndAnyType(expression, 2, 2, ReturnType.String, ReturnType.Boolean, ReturnType.Number, ReturnType.Object)
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [], ReturnType.String | ReturnType.Array, ReturnType.Object)
),
new ExpressionEvaluator(
ExpressionType.Join,
Expand Down Expand Up @@ -2374,7 +2380,7 @@ export class ExpressionFunctions {
return { value, error };
},
ReturnType.String,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Object, ReturnType.String)),
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, [ReturnType.String], ReturnType.Array, ReturnType.String)),
// datetime
ExpressionFunctions.timeTransform(ExpressionType.AddDays, (ts: moment.Moment, num: any): any => ts.add(num, 'd')),
ExpressionFunctions.timeTransform(ExpressionType.AddHours, (ts: moment.Moment, num: any): any => ts.add(num, 'h')),
Expand Down Expand Up @@ -2524,7 +2530,7 @@ export class ExpressionFunctions {
},
this.verifyString),
ReturnType.String,
(expression: Expression): void => ExpressionFunctions.validateOrder(expression, undefined, ReturnType.String)),
ExpressionFunctions.validateUnaryString),
new ExpressionEvaluator(
ExpressionType.GetFutureTime,
(expr: Expression, state: any, options: Options): { value: any; error: string } => {
Expand Down Expand Up @@ -2907,7 +2913,7 @@ export class ExpressionFunctions {
ExpressionFunctions.verifyInteger),
ReturnType.Number,
ExpressionFunctions.validateBinaryNumber),
new ExpressionEvaluator(ExpressionType.CreateArray, ExpressionFunctions.apply((args: any []): any[] => Array.from(args)), ReturnType.Object),
new ExpressionEvaluator(ExpressionType.CreateArray, ExpressionFunctions.apply((args: any []): any[] => Array.from(args)), ReturnType.Array),
new ExpressionEvaluator(
ExpressionType.Binary,
ExpressionFunctions.apply((args: any []): string => this.toBinary(args[0]), ExpressionFunctions.verifyString),
Expand Down Expand Up @@ -3037,9 +3043,9 @@ export class ExpressionFunctions {
this.setPathToValue,
ReturnType.Object,
this.validateBinary),
new ExpressionEvaluator(ExpressionType.Select, ExpressionFunctions.foreach, ReturnType.Object, ExpressionFunctions.validateForeach),
new ExpressionEvaluator(ExpressionType.Foreach, ExpressionFunctions.foreach, ReturnType.Object, ExpressionFunctions.validateForeach),
new ExpressionEvaluator(ExpressionType.Where, ExpressionFunctions.where, ReturnType.Object, ExpressionFunctions.validateWhere),
new ExpressionEvaluator(ExpressionType.Select, ExpressionFunctions.foreach, ReturnType.Array, ExpressionFunctions.validateForeach),
new ExpressionEvaluator(ExpressionType.Foreach, ExpressionFunctions.foreach, ReturnType.Array, ExpressionFunctions.validateForeach),
new ExpressionEvaluator(ExpressionType.Where, ExpressionFunctions.where, ReturnType.Array, ExpressionFunctions.validateWhere),

//URI Parsing Functions
new ExpressionEvaluator(ExpressionType.UriHost, ExpressionFunctions.applyWithError((args: Readonly<any>): any => this.uriHost(args[0]), ExpressionFunctions.verifyString),
Expand Down

0 comments on commit 39d3592

Please sign in to comment.