@trebco/treb 32.1.1 → 32.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/treb-spreadsheet.mjs +15 -15
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +20 -0
- package/treb-calculator/src/calculator.ts +2 -0
- package/treb-calculator/src/descriptors.ts +7 -1
- package/treb-calculator/src/expression-calculator.ts +129 -54
- package/treb-calculator/src/functions/base-functions.ts +1 -0
- package/treb-calculator/src/functions/fp.ts +339 -0
- package/treb-calculator/src/functions/gamma.ts +143 -0
- package/treb-calculator/src/functions/statistics-functions.ts +88 -0
- package/treb-data-model/src/sheet.ts +2 -2
- package/treb-embed/src/embedded-spreadsheet.ts +10 -2
- package/treb-grid/src/editors/editor.ts +14 -0
- package/treb-grid/src/types/grid.ts +70 -25
- package/treb-parser/src/parser-types.ts +3 -0
- package/treb-parser/src/parser.ts +33 -7
- package/treb-calculator/tsconfig.json +0 -7
package/package.json
CHANGED
|
@@ -318,6 +318,26 @@ export const Style = {
|
|
|
318
318
|
};
|
|
319
319
|
},
|
|
320
320
|
|
|
321
|
+
Serialize: (style: CellStyle): string => {
|
|
322
|
+
const clone: CellStyle = JSON.parse(JSON.stringify(style));
|
|
323
|
+
|
|
324
|
+
// scrub border colors without widths
|
|
325
|
+
if (clone.border_bottom_fill && !clone.border_bottom) {
|
|
326
|
+
clone.border_bottom_fill = undefined;
|
|
327
|
+
}
|
|
328
|
+
if (clone.border_top_fill && !clone.border_top) {
|
|
329
|
+
clone.border_top_fill = undefined;
|
|
330
|
+
}
|
|
331
|
+
if (clone.border_left_fill && !clone.border_left) {
|
|
332
|
+
clone.border_left_fill = undefined;
|
|
333
|
+
}
|
|
334
|
+
if (clone.border_right_fill && !clone.border_right) {
|
|
335
|
+
clone.border_right_fill = undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return JSON.stringify(clone);
|
|
339
|
+
},
|
|
340
|
+
|
|
321
341
|
/**
|
|
322
342
|
* merge. returns a new object, does not update dest in place.
|
|
323
343
|
* NOTE: if it does not update dest in place, then what would be
|
|
@@ -48,6 +48,7 @@ import { ComplexFunctionLibrary } from './functions/complex-functions';
|
|
|
48
48
|
import { MatrixFunctionLibrary } from './functions/matrix-functions';
|
|
49
49
|
import { RegexFunctionLibrary } from './functions/regex-functions';
|
|
50
50
|
import { LambdaFunctionLibrary } from './functions/lambda-functions';
|
|
51
|
+
import { FPFunctionLibrary } from './functions/fp';
|
|
51
52
|
|
|
52
53
|
import { Variance } from './functions/statistics-functions';
|
|
53
54
|
|
|
@@ -242,6 +243,7 @@ export class Calculator extends Graph {
|
|
|
242
243
|
MatrixFunctionLibrary,
|
|
243
244
|
RegexFunctionLibrary,
|
|
244
245
|
LambdaFunctionLibrary,
|
|
246
|
+
FPFunctionLibrary,
|
|
245
247
|
);
|
|
246
248
|
|
|
247
249
|
// aliases
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type { RenderFunction, ClickFunction, UnionValue, ICellAddress, IArea } from 'treb-base-types';
|
|
22
|
+
import type { RenderFunction, ClickFunction, UnionValue, ICellAddress, IArea, FunctionUnion } from 'treb-base-types';
|
|
23
23
|
import type { ExpressionUnit } from 'treb-parser';
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -28,6 +28,9 @@ import type { ExpressionUnit } from 'treb-parser';
|
|
|
28
28
|
export interface FunctionContext {
|
|
29
29
|
address: ICellAddress;
|
|
30
30
|
area?: IArea;
|
|
31
|
+
|
|
32
|
+
/** application function for fp functions */
|
|
33
|
+
apply?: (fn: FunctionUnion, args: UnionValue[]) => UnionValue;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
// FIXME: at least some of this could move to base types
|
|
@@ -235,6 +238,9 @@ export interface CompositeFunctionDescriptor {
|
|
|
235
238
|
descriptors: ArgumentDescriptor[];
|
|
236
239
|
}) => ContextResult | undefined;
|
|
237
240
|
|
|
241
|
+
/** flag indicating this function needs fp support */
|
|
242
|
+
fp?: boolean;
|
|
243
|
+
|
|
238
244
|
}
|
|
239
245
|
|
|
240
246
|
export interface FunctionMap {
|
|
@@ -27,11 +27,13 @@ import type { Cell, ICellAddress,
|
|
|
27
27
|
UndefinedUnion,
|
|
28
28
|
ComplexUnion,
|
|
29
29
|
DimensionedQuantityUnion,
|
|
30
|
-
IArea
|
|
30
|
+
IArea,
|
|
31
|
+
FunctionUnion} from 'treb-base-types';
|
|
31
32
|
import { ValueType, GetValueType, Area } from 'treb-base-types';
|
|
32
33
|
import type { Parser, ExpressionUnit, UnitBinary, UnitIdentifier,
|
|
33
34
|
UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference,
|
|
34
|
-
UnitImplicitCall
|
|
35
|
+
UnitImplicitCall,
|
|
36
|
+
UnitArray} from 'treb-parser';
|
|
35
37
|
import type { DataModel, MacroFunction, Sheet } from 'treb-data-model';
|
|
36
38
|
import { NameError, ReferenceError, ExpressionError, UnknownError, SpillError, ValueError, ArgumentError } from './function-error';
|
|
37
39
|
|
|
@@ -448,6 +450,123 @@ export class ExpressionCalculator {
|
|
|
448
450
|
|
|
449
451
|
}
|
|
450
452
|
|
|
453
|
+
/**
|
|
454
|
+
* split out from ImplicitCall so we can reuse
|
|
455
|
+
*/
|
|
456
|
+
public ImplicitCallTail(result: FunctionUnion, args: ExpressionUnit[]) {
|
|
457
|
+
|
|
458
|
+
const value = result.value as {
|
|
459
|
+
bindings: ExpressionUnit[];
|
|
460
|
+
func: ExpressionUnit|undefined;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
if (!value.func || !value.bindings) {
|
|
464
|
+
return ExpressionError();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// let frame = value.bindings(expr.args);
|
|
468
|
+
|
|
469
|
+
const frame: BindingFrame = {};
|
|
470
|
+
|
|
471
|
+
for (let i = 0; i < value.bindings.length; i++) {
|
|
472
|
+
const name = value.bindings[i];
|
|
473
|
+
if (name?.type === 'identifier') {
|
|
474
|
+
frame[name.name.toUpperCase()] = args[i] || { type: 'missing' };
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
// should not happen, error
|
|
478
|
+
return ExpressionError();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// frame = this.NormalizeBindings(frame); // inline
|
|
483
|
+
|
|
484
|
+
const munged = JSON.parse(JSON.stringify(value.func));
|
|
485
|
+
|
|
486
|
+
this.parser.Walk2(munged, (unit: ExpressionUnit) => {
|
|
487
|
+
if (unit.type === 'identifier') {
|
|
488
|
+
const upper_case = unit.name.toUpperCase();
|
|
489
|
+
const binding = frame[upper_case];
|
|
490
|
+
if (binding) {
|
|
491
|
+
return JSON.parse(JSON.stringify(binding));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return true;
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// console.info({frame, func, munged});
|
|
498
|
+
|
|
499
|
+
// const exec_result = this.CalculateExpression(munged as ExtendedExpressionUnit);
|
|
500
|
+
// return exec_result || ExpressionError();
|
|
501
|
+
|
|
502
|
+
return this.CalculateExpression(munged as ExtendedExpressionUnit);
|
|
503
|
+
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* an FP call will need to set bindings and call an expression,
|
|
508
|
+
* possibly multiple times. this is a support function for that.
|
|
509
|
+
*/
|
|
510
|
+
protected Apply(fn: FunctionUnion, args: UnionValue[]) {
|
|
511
|
+
|
|
512
|
+
// kind of going backwards here, converting values to expressions...
|
|
513
|
+
// the reason is that we rewrite lambdas to support recursion
|
|
514
|
+
|
|
515
|
+
const mapped: ExpressionUnit[] = args.map(arg => {
|
|
516
|
+
switch (arg.type) {
|
|
517
|
+
case ValueType.number:
|
|
518
|
+
case ValueType.boolean:
|
|
519
|
+
case ValueType.string:
|
|
520
|
+
return {
|
|
521
|
+
type: 'literal', value: arg.value, id: 0, position: 0,
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
case ValueType.error:
|
|
525
|
+
return {
|
|
526
|
+
type: 'literal', value: '#' + arg.value, id: 0, position: 0,
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
case ValueType.array:
|
|
530
|
+
{
|
|
531
|
+
const values: UnitArray['values'] = [];
|
|
532
|
+
for (let c = 0; c < arg.value.length; c++) {
|
|
533
|
+
const col = arg.value[c];
|
|
534
|
+
const mapped_col: UnitArray['values'][0] = [];
|
|
535
|
+
for (let r = 0; r < col.length; r++ ) {
|
|
536
|
+
const val = col[r];
|
|
537
|
+
switch (val.type) {
|
|
538
|
+
case ValueType.boolean:
|
|
539
|
+
case ValueType.number:
|
|
540
|
+
case ValueType.string:
|
|
541
|
+
mapped_col.push(val.value || undefined);
|
|
542
|
+
break;
|
|
543
|
+
default:
|
|
544
|
+
console.warn('unhandled array value', val);
|
|
545
|
+
mapped_col.push(undefined);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
values.push(mapped_col);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
type: 'array',
|
|
553
|
+
values,
|
|
554
|
+
id: 0, position: 0,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
default:
|
|
559
|
+
console.warn('unhandled parameter value', arg);
|
|
560
|
+
|
|
561
|
+
}
|
|
562
|
+
return { type: 'missing', id: 0 };
|
|
563
|
+
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
return this.ImplicitCallTail(fn, mapped);
|
|
567
|
+
|
|
568
|
+
}
|
|
569
|
+
|
|
451
570
|
/**
|
|
452
571
|
* this method can take a `call` type if it looked like a call at
|
|
453
572
|
* the parsing stage, it's a simple translation between the two
|
|
@@ -473,53 +592,7 @@ export class ExpressionCalculator {
|
|
|
473
592
|
const result = this.CalculateExpression(expr.call as ExtendedExpressionUnit);
|
|
474
593
|
|
|
475
594
|
if (result.type === ValueType.function) {
|
|
476
|
-
|
|
477
|
-
const value = result.value as {
|
|
478
|
-
bindings: ExpressionUnit[];
|
|
479
|
-
func: ExpressionUnit|undefined;
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
if (!value.func || !value.bindings) {
|
|
483
|
-
return ExpressionError();
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// let frame = value.bindings(expr.args);
|
|
487
|
-
|
|
488
|
-
const frame: BindingFrame = {};
|
|
489
|
-
|
|
490
|
-
for (let i = 0; i < value.bindings.length; i++) {
|
|
491
|
-
const name = value.bindings[i];
|
|
492
|
-
if (name?.type === 'identifier') {
|
|
493
|
-
frame[name.name.toUpperCase()] = expr.args[i] || { type: 'missing' };
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
// should not happen, error
|
|
497
|
-
return ExpressionError();
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// frame = this.NormalizeBindings(frame); // inline
|
|
502
|
-
|
|
503
|
-
const munged = JSON.parse(JSON.stringify(value.func));
|
|
504
|
-
|
|
505
|
-
this.parser.Walk2(munged, (unit: ExpressionUnit) => {
|
|
506
|
-
if (unit.type === 'identifier') {
|
|
507
|
-
const upper_case = unit.name.toUpperCase();
|
|
508
|
-
const binding = frame[upper_case];
|
|
509
|
-
if (binding) {
|
|
510
|
-
return JSON.parse(JSON.stringify(binding));
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return true;
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// console.info({frame, func, munged});
|
|
517
|
-
|
|
518
|
-
// const exec_result = this.CalculateExpression(munged as ExtendedExpressionUnit);
|
|
519
|
-
// return exec_result || ExpressionError();
|
|
520
|
-
|
|
521
|
-
return this.CalculateExpression(munged as ExtendedExpressionUnit);
|
|
522
|
-
|
|
595
|
+
return this.ImplicitCallTail(result, expr.args)
|
|
523
596
|
}
|
|
524
597
|
|
|
525
598
|
return ExpressionError();
|
|
@@ -721,11 +794,12 @@ export class ExpressionCalculator {
|
|
|
721
794
|
start: { ...this.context.area.start, },
|
|
722
795
|
end: { ...this.context.area.end, },
|
|
723
796
|
} : undefined,
|
|
797
|
+
apply: func.fp ? this.Apply.bind(this) : undefined,
|
|
724
798
|
};
|
|
725
799
|
|
|
726
|
-
|
|
800
|
+
const result = func.fn.apply(ctx, mapped_args);
|
|
727
801
|
|
|
728
|
-
|
|
802
|
+
if (func.return_type === 'reference') {
|
|
729
803
|
|
|
730
804
|
if (return_reference) {
|
|
731
805
|
return result;
|
|
@@ -744,7 +818,7 @@ export class ExpressionCalculator {
|
|
|
744
818
|
|
|
745
819
|
}
|
|
746
820
|
|
|
747
|
-
return func.fn.apply(ctx, mapped_args);
|
|
821
|
+
return result; // func.fn.apply(ctx, mapped_args);
|
|
748
822
|
|
|
749
823
|
};
|
|
750
824
|
|
|
@@ -1162,11 +1236,11 @@ export class ExpressionCalculator {
|
|
|
1162
1236
|
|
|
1163
1237
|
switch (upper_case){
|
|
1164
1238
|
case 'FALSE':
|
|
1165
|
-
case 'F':
|
|
1239
|
+
// case 'F':
|
|
1166
1240
|
return () => {return {value: false, type: ValueType.boolean}};
|
|
1167
1241
|
|
|
1168
1242
|
case 'TRUE':
|
|
1169
|
-
case 'T':
|
|
1243
|
+
// case 'T':
|
|
1170
1244
|
return () => {return {value: true, type: ValueType.boolean}};
|
|
1171
1245
|
|
|
1172
1246
|
case 'UNDEFINED':
|
|
@@ -1215,6 +1289,7 @@ export class ExpressionCalculator {
|
|
|
1215
1289
|
return value;
|
|
1216
1290
|
}
|
|
1217
1291
|
}
|
|
1292
|
+
|
|
1218
1293
|
return undefined;
|
|
1219
1294
|
}
|
|
1220
1295
|
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* functional programming
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { FunctionMap } from '../descriptors';
|
|
6
|
+
import type { FunctionUnion, UnionValue} from 'treb-base-types';
|
|
7
|
+
import { ValueType } from 'treb-base-types';
|
|
8
|
+
import { ArgumentError, ValueError } from '../function-error';
|
|
9
|
+
|
|
10
|
+
export const FPFunctionLibrary: FunctionMap = {
|
|
11
|
+
|
|
12
|
+
MakeArray: {
|
|
13
|
+
description: 'Create an array using a function',
|
|
14
|
+
arguments: [
|
|
15
|
+
{ name: 'rows' },
|
|
16
|
+
{ name: 'columns' },
|
|
17
|
+
{
|
|
18
|
+
name: 'lambda',
|
|
19
|
+
description: 'Function to apply',
|
|
20
|
+
boxed: true,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
fp: true,
|
|
24
|
+
fn: function(rows: number, columns: number, lambda: FunctionUnion) {
|
|
25
|
+
if (rows > 0 && columns > 0 && lambda?.type === ValueType.function) {
|
|
26
|
+
const apply = this?.apply;
|
|
27
|
+
if (!apply) {
|
|
28
|
+
return ValueError();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const value: UnionValue[][] = [];
|
|
32
|
+
for (let c = 0; c < columns; c++) {
|
|
33
|
+
const values_col: UnionValue[] = [];
|
|
34
|
+
for (let r = 0; r < rows; r++) {
|
|
35
|
+
values_col.push(apply(lambda, [
|
|
36
|
+
{ type: ValueType.number, value: r + 1 },
|
|
37
|
+
{ type: ValueType.number, value: c + 1 },
|
|
38
|
+
]));
|
|
39
|
+
}
|
|
40
|
+
value.push(values_col);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
type: ValueType.array,
|
|
45
|
+
value,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
return ArgumentError();
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
Reduce: {
|
|
54
|
+
description: 'Accumulates a value by applying a function to a set of values',
|
|
55
|
+
arguments: [
|
|
56
|
+
{
|
|
57
|
+
name: 'initial value',
|
|
58
|
+
boxed: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'data',
|
|
62
|
+
description: 'Input data',
|
|
63
|
+
boxed: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'lambda',
|
|
67
|
+
description: 'Function to apply',
|
|
68
|
+
boxed: true,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
fp: true,
|
|
72
|
+
fn: function(initial: UnionValue, data: UnionValue, lambda: FunctionUnion) {
|
|
73
|
+
|
|
74
|
+
if (!this?.apply) {
|
|
75
|
+
return ValueError();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (lambda.type !== ValueType.function) {
|
|
79
|
+
return ArgumentError();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (data.type !== ValueType.array) {
|
|
83
|
+
data = {
|
|
84
|
+
type: ValueType.array,
|
|
85
|
+
value: [[data]],
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let r = 0; r < data.value.length; r++) {
|
|
90
|
+
const row = data.value[r];
|
|
91
|
+
for (let c = 0; c < row.length; c++) {
|
|
92
|
+
const apply_args: UnionValue[] = [initial, row[c]];
|
|
93
|
+
const result = this.apply(lambda, apply_args)
|
|
94
|
+
initial = result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return initial;
|
|
99
|
+
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
Scan: {
|
|
104
|
+
description: 'Applies a function to a set of values, iteratively',
|
|
105
|
+
arguments: [
|
|
106
|
+
{
|
|
107
|
+
name: 'initial value',
|
|
108
|
+
boxed: true,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'data',
|
|
112
|
+
description: 'Input data',
|
|
113
|
+
boxed: true,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'lambda',
|
|
117
|
+
description: 'Function to apply',
|
|
118
|
+
boxed: true,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
fp: true,
|
|
122
|
+
fn: function(initial: UnionValue, data: UnionValue, lambda: FunctionUnion) {
|
|
123
|
+
|
|
124
|
+
if (!this?.apply) {
|
|
125
|
+
return ValueError();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (lambda.type !== ValueType.function) {
|
|
129
|
+
return ArgumentError();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (data.type !== ValueType.array) {
|
|
133
|
+
data = {
|
|
134
|
+
type: ValueType.array,
|
|
135
|
+
value: [[data]],
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const results: UnionValue[][] = [];
|
|
140
|
+
for (let r = 0; r < data.value.length; r++) {
|
|
141
|
+
const row = data.value[r];
|
|
142
|
+
const results_row: UnionValue[] = [];
|
|
143
|
+
for (let c = 0; c < row.length; c++) {
|
|
144
|
+
const apply_args: UnionValue[] = [initial, row[c]];
|
|
145
|
+
const result = this.apply(lambda, apply_args)
|
|
146
|
+
results_row.push(result);
|
|
147
|
+
initial = result;
|
|
148
|
+
}
|
|
149
|
+
results.push(results_row);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
type: ValueType.array,
|
|
154
|
+
value: results,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
ByRow: {
|
|
161
|
+
description: 'Apply a function to each row in an array',
|
|
162
|
+
arguments: [
|
|
163
|
+
{
|
|
164
|
+
name: 'data',
|
|
165
|
+
description: 'Input data',
|
|
166
|
+
repeat: true,
|
|
167
|
+
boxed: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'lambda',
|
|
171
|
+
description: 'Function to apply',
|
|
172
|
+
boxed: true,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
fp: true,
|
|
176
|
+
fn: function(data: UnionValue, lambda: FunctionUnion) {
|
|
177
|
+
if (!this?.apply) { return ValueError(); }
|
|
178
|
+
|
|
179
|
+
if (lambda.type !== ValueType.function) {
|
|
180
|
+
return ArgumentError();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (data.type !== ValueType.array) {
|
|
184
|
+
data = {
|
|
185
|
+
type: ValueType.array,
|
|
186
|
+
value: [[data]],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const cols = data.value.length;
|
|
191
|
+
const rows = data.value[0].length;
|
|
192
|
+
|
|
193
|
+
const value: UnionValue[][] = [[]];
|
|
194
|
+
for (let r = 0; r < rows; r++) {
|
|
195
|
+
const args: UnionValue[] = [];
|
|
196
|
+
for (let c = 0; c < cols; c++) {
|
|
197
|
+
args.push(data.value[c][r]);
|
|
198
|
+
}
|
|
199
|
+
value[0].push(this.apply(lambda, [{
|
|
200
|
+
type: ValueType.array, value: [args],
|
|
201
|
+
}]));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
type: ValueType.array,
|
|
206
|
+
value,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
ByCol: {
|
|
214
|
+
description: 'Apply a function to each column in an array',
|
|
215
|
+
arguments: [
|
|
216
|
+
{
|
|
217
|
+
name: 'data',
|
|
218
|
+
description: 'Input data',
|
|
219
|
+
repeat: true,
|
|
220
|
+
boxed: true,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'lambda',
|
|
224
|
+
description: 'Function to apply',
|
|
225
|
+
boxed: true,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
fp: true,
|
|
229
|
+
fn: function(data: UnionValue, lambda: FunctionUnion) {
|
|
230
|
+
if (!this?.apply) { return ValueError(); }
|
|
231
|
+
|
|
232
|
+
if (lambda.type !== ValueType.function) {
|
|
233
|
+
return ArgumentError();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (data.type !== ValueType.array) {
|
|
237
|
+
data = {
|
|
238
|
+
type: ValueType.array,
|
|
239
|
+
value: [[data]],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const cols = data.value.length;
|
|
244
|
+
const rows = data.value[0].length;
|
|
245
|
+
|
|
246
|
+
const value: UnionValue[][] = [];
|
|
247
|
+
for (let c = 0; c < cols; c++) {
|
|
248
|
+
const args: UnionValue[] = [];
|
|
249
|
+
for (let r = 0; r < rows; r++) {
|
|
250
|
+
args.push(data.value[c][r]);
|
|
251
|
+
}
|
|
252
|
+
value.push([this.apply(lambda, [{
|
|
253
|
+
type: ValueType.array, value: [args],
|
|
254
|
+
}])]);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
type: ValueType.array,
|
|
259
|
+
value,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
Map: {
|
|
266
|
+
description: 'Apply a function to a set of values',
|
|
267
|
+
arguments: [
|
|
268
|
+
{
|
|
269
|
+
name: 'data',
|
|
270
|
+
description: 'Input data',
|
|
271
|
+
repeat: true,
|
|
272
|
+
boxed: true,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'lambda',
|
|
276
|
+
description: 'Function to apply',
|
|
277
|
+
boxed: true,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
|
|
281
|
+
fp: true,
|
|
282
|
+
fn: function(...args: UnionValue[]) {
|
|
283
|
+
|
|
284
|
+
if (args.length < 2) {
|
|
285
|
+
return ArgumentError();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const lambda = args[args.length - 1];
|
|
289
|
+
if (lambda.type !== ValueType.function) {
|
|
290
|
+
return ArgumentError();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const apply = this?.apply;
|
|
294
|
+
if (!apply) {
|
|
295
|
+
return ValueError();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
299
|
+
const arg = args[i];
|
|
300
|
+
if (arg.type !== ValueType.array) {
|
|
301
|
+
args[i] = {
|
|
302
|
+
type: ValueType.array,
|
|
303
|
+
value: [[arg]],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const key = args[0];
|
|
309
|
+
const results: UnionValue[][] = [];
|
|
310
|
+
if (key.type === ValueType.array) {
|
|
311
|
+
for (let r = 0; r < key.value.length; r++) {
|
|
312
|
+
const row = key.value[r];
|
|
313
|
+
const results_row: UnionValue[] = [];
|
|
314
|
+
for (let c = 0; c < row.length; c++) {
|
|
315
|
+
const apply_args: UnionValue[] = [row[c]];
|
|
316
|
+
|
|
317
|
+
for (let i = 1; i < args.length - 1; i++) {
|
|
318
|
+
const arg = args[i];
|
|
319
|
+
if (arg.type === ValueType.array) {
|
|
320
|
+
apply_args.push(arg.value[r][c])
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
results_row.push(apply(lambda, apply_args));
|
|
325
|
+
}
|
|
326
|
+
results.push(results_row);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
type: ValueType.array,
|
|
332
|
+
value: results,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
};
|