@trebco/treb 31.9.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/i18n/languages/treb-i18n-da.mjs +1 -0
- package/i18n/languages/treb-i18n-de.mjs +1 -0
- package/i18n/languages/treb-i18n-es.mjs +1 -0
- package/i18n/languages/treb-i18n-fr.mjs +1 -0
- package/i18n/languages/treb-i18n-it.mjs +1 -0
- package/i18n/languages/treb-i18n-nl.mjs +1 -0
- package/i18n/languages/treb-i18n-no.mjs +1 -0
- package/i18n/languages/treb-i18n-pl.mjs +1 -0
- package/i18n/languages/treb-i18n-pt.mjs +1 -0
- package/i18n/languages/treb-i18n-sv.mjs +1 -0
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +20 -0
- package/treb-base-types/src/union.ts +6 -0
- package/treb-base-types/src/value-type.ts +13 -0
- package/treb-calculator/src/calculator.ts +20 -1
- package/treb-calculator/src/descriptors.ts +49 -4
- package/treb-calculator/src/expression-calculator.ts +263 -12
- package/treb-calculator/src/functions/base-functions.ts +49 -0
- package/treb-calculator/src/functions/fp.ts +339 -0
- package/treb-calculator/src/functions/gamma.ts +143 -0
- package/treb-calculator/src/functions/lambda-functions.ts +96 -0
- package/treb-calculator/src/functions/statistics-functions.ts +88 -0
- package/treb-data-model/src/data_model.ts +28 -13
- package/treb-data-model/src/named.ts +7 -0
- package/treb-data-model/src/sheet.ts +19 -2
- package/treb-embed/src/embedded-spreadsheet.ts +22 -5
- package/treb-embed/style/theme-defaults.scss +0 -22
- package/treb-grid/src/editors/editor.ts +14 -0
- package/treb-grid/src/types/grid.ts +74 -28
- package/treb-parser/src/parser-types.ts +24 -0
- package/treb-parser/src/parser.ts +157 -223
- package/dist/treb-export-worker.mjs +0 -2
- package/dist/treb.d.ts +0 -2235
- package/treb-calculator/tsconfig.json +0 -7
|
@@ -27,14 +27,18 @@ 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
|
-
UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference
|
|
34
|
+
UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference,
|
|
35
|
+
UnitImplicitCall,
|
|
36
|
+
UnitArray} from 'treb-parser';
|
|
34
37
|
import type { DataModel, MacroFunction, Sheet } from 'treb-data-model';
|
|
35
|
-
import { NameError, ReferenceError, ExpressionError, UnknownError, SpillError, ValueError } from './function-error';
|
|
38
|
+
import { NameError, ReferenceError, ExpressionError, UnknownError, SpillError, ValueError, ArgumentError } from './function-error';
|
|
36
39
|
|
|
37
40
|
import * as Primitives from './primitives';
|
|
41
|
+
import type { ContextResult } from './descriptors';
|
|
38
42
|
|
|
39
43
|
//////////
|
|
40
44
|
|
|
@@ -82,10 +86,17 @@ export interface ReferenceMetadata {
|
|
|
82
86
|
format?: string;
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
export type BindingFrame = Record<string, ExpressionUnit>; // FIXME (type?)
|
|
90
|
+
export type PositionalFrame = ExpressionUnit[];
|
|
91
|
+
|
|
85
92
|
export interface CalculationContext {
|
|
86
93
|
address: ICellAddress;
|
|
87
94
|
area?: IArea;
|
|
88
95
|
volatile: boolean;
|
|
96
|
+
|
|
97
|
+
/** new for lambdas */
|
|
98
|
+
bindings: BindingFrame[];
|
|
99
|
+
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
export class ExpressionCalculator {
|
|
@@ -93,6 +104,7 @@ export class ExpressionCalculator {
|
|
|
93
104
|
public context: CalculationContext = {
|
|
94
105
|
address: { row: -1, column: -1 },
|
|
95
106
|
volatile: false,
|
|
107
|
+
bindings: [],
|
|
96
108
|
};
|
|
97
109
|
|
|
98
110
|
// --- public API -----------------------------------------------------------
|
|
@@ -438,6 +450,164 @@ export class ExpressionCalculator {
|
|
|
438
450
|
|
|
439
451
|
}
|
|
440
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
|
+
|
|
570
|
+
/**
|
|
571
|
+
* this method can take a `call` type if it looked like a call at
|
|
572
|
+
* the parsing stage, it's a simple translation between the two
|
|
573
|
+
*/
|
|
574
|
+
protected ImplicitCall(): (expr: UnitImplicitCall|UnitCall) => UnionValue {
|
|
575
|
+
return (expr: UnitImplicitCall|UnitCall) => {
|
|
576
|
+
|
|
577
|
+
if (expr.type === 'call') {
|
|
578
|
+
expr = {
|
|
579
|
+
type: 'implicit-call',
|
|
580
|
+
args: expr.args,
|
|
581
|
+
call: {
|
|
582
|
+
type: 'identifier',
|
|
583
|
+
name: expr.name,
|
|
584
|
+
position: expr.position,
|
|
585
|
+
id: 0,
|
|
586
|
+
},
|
|
587
|
+
position: expr.position,
|
|
588
|
+
id: 0,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const result = this.CalculateExpression(expr.call as ExtendedExpressionUnit);
|
|
593
|
+
|
|
594
|
+
if (result.type === ValueType.function) {
|
|
595
|
+
return this.ImplicitCallTail(result, expr.args)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return ExpressionError();
|
|
599
|
+
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
protected NormalizeBindings(context: BindingFrame) {
|
|
604
|
+
const frame: Record<string, ExpressionUnit> = {};
|
|
605
|
+
for (const [key, value] of Object.entries(context)) {
|
|
606
|
+
frame[key.toUpperCase()] = value;
|
|
607
|
+
}
|
|
608
|
+
return frame;
|
|
609
|
+
}
|
|
610
|
+
|
|
441
611
|
/**
|
|
442
612
|
* excute a function call
|
|
443
613
|
*/
|
|
@@ -451,6 +621,18 @@ export class ExpressionCalculator {
|
|
|
451
621
|
|
|
452
622
|
if (!func) {
|
|
453
623
|
|
|
624
|
+
const upper_case = outer.name.toUpperCase();
|
|
625
|
+
|
|
626
|
+
const binding = this.LookupBinding(upper_case);
|
|
627
|
+
if (binding) {
|
|
628
|
+
return this.ImplicitCall();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const named = this.data_model.GetName(upper_case, this.context.address.sheet_id || 0);
|
|
632
|
+
if (named) {
|
|
633
|
+
return this.ImplicitCall();
|
|
634
|
+
}
|
|
635
|
+
|
|
454
636
|
if (process.env.NODE_ENV !== 'production') {
|
|
455
637
|
console.info('(dev) missing function', outer.name);
|
|
456
638
|
}
|
|
@@ -474,9 +656,36 @@ export class ExpressionCalculator {
|
|
|
474
656
|
let skip_argument_index = -1;
|
|
475
657
|
let argument_error: UnionValue|undefined;
|
|
476
658
|
|
|
477
|
-
|
|
659
|
+
// possibly create a binding frame. if we do that we may need
|
|
660
|
+
// to adjust the arguments as well (and the descriptors)
|
|
661
|
+
|
|
662
|
+
let args = expr.args;
|
|
663
|
+
let argument_descriptors = func.arguments || []; // map
|
|
664
|
+
|
|
665
|
+
let binding: ContextResult|undefined;
|
|
666
|
+
if (func.create_binding_context) {
|
|
667
|
+
|
|
668
|
+
// if this does not return a binding frame, it's an error.
|
|
669
|
+
|
|
670
|
+
binding = func.create_binding_context.call(0, {
|
|
671
|
+
args: expr.args,
|
|
672
|
+
descriptors: argument_descriptors,
|
|
673
|
+
});
|
|
478
674
|
|
|
479
|
-
|
|
675
|
+
if (binding) {
|
|
676
|
+
args = binding.args;
|
|
677
|
+
if (binding.argument_descriptors) {
|
|
678
|
+
argument_descriptors = binding.argument_descriptors;
|
|
679
|
+
}
|
|
680
|
+
this.context.bindings.unshift(this.NormalizeBindings(binding.context));
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
argument_error = ArgumentError();
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const mapped_args = args.map((arg, arg_index) => {
|
|
480
689
|
|
|
481
690
|
// short circuit
|
|
482
691
|
if (argument_error) {
|
|
@@ -485,8 +694,18 @@ export class ExpressionCalculator {
|
|
|
485
694
|
|
|
486
695
|
// get descriptor. if the number of arguments exceeds
|
|
487
696
|
// the number of descriptors, recycle the last one
|
|
697
|
+
|
|
698
|
+
// FIXME: we have a repeat flag, so we should repeat the
|
|
699
|
+
// correct argument(s). I guess we could default to this
|
|
700
|
+
// behavior.
|
|
701
|
+
|
|
488
702
|
const descriptor = argument_descriptors[Math.min(arg_index, argument_descriptors.length - 1)] || {};
|
|
489
|
-
|
|
703
|
+
|
|
704
|
+
// new for lambdas
|
|
705
|
+
if (descriptor.passthrough) {
|
|
706
|
+
return arg;
|
|
707
|
+
}
|
|
708
|
+
|
|
490
709
|
// if function, wrong branch
|
|
491
710
|
if (arg_index === skip_argument_index) {
|
|
492
711
|
return descriptor.boxed ? { type: ValueType.undefined } : undefined;
|
|
@@ -559,6 +778,10 @@ export class ExpressionCalculator {
|
|
|
559
778
|
|
|
560
779
|
});
|
|
561
780
|
|
|
781
|
+
if (binding) {
|
|
782
|
+
this.context.bindings.shift();
|
|
783
|
+
}
|
|
784
|
+
|
|
562
785
|
if (argument_error) {
|
|
563
786
|
return argument_error;
|
|
564
787
|
}
|
|
@@ -571,11 +794,12 @@ export class ExpressionCalculator {
|
|
|
571
794
|
start: { ...this.context.area.start, },
|
|
572
795
|
end: { ...this.context.area.end, },
|
|
573
796
|
} : undefined,
|
|
797
|
+
apply: func.fp ? this.Apply.bind(this) : undefined,
|
|
574
798
|
};
|
|
575
799
|
|
|
576
|
-
|
|
800
|
+
const result = func.fn.apply(ctx, mapped_args);
|
|
577
801
|
|
|
578
|
-
|
|
802
|
+
if (func.return_type === 'reference') {
|
|
579
803
|
|
|
580
804
|
if (return_reference) {
|
|
581
805
|
return result;
|
|
@@ -594,7 +818,7 @@ export class ExpressionCalculator {
|
|
|
594
818
|
|
|
595
819
|
}
|
|
596
820
|
|
|
597
|
-
return func.fn.apply(ctx, mapped_args);
|
|
821
|
+
return result; // func.fn.apply(ctx, mapped_args);
|
|
598
822
|
|
|
599
823
|
};
|
|
600
824
|
|
|
@@ -1012,11 +1236,11 @@ export class ExpressionCalculator {
|
|
|
1012
1236
|
|
|
1013
1237
|
switch (upper_case){
|
|
1014
1238
|
case 'FALSE':
|
|
1015
|
-
case 'F':
|
|
1239
|
+
// case 'F':
|
|
1016
1240
|
return () => {return {value: false, type: ValueType.boolean}};
|
|
1017
1241
|
|
|
1018
1242
|
case 'TRUE':
|
|
1019
|
-
case 'T':
|
|
1243
|
+
// case 'T':
|
|
1020
1244
|
return () => {return {value: true, type: ValueType.boolean}};
|
|
1021
1245
|
|
|
1022
1246
|
case 'UNDEFINED':
|
|
@@ -1025,8 +1249,13 @@ export class ExpressionCalculator {
|
|
|
1025
1249
|
|
|
1026
1250
|
return () => {
|
|
1027
1251
|
|
|
1028
|
-
const
|
|
1252
|
+
const binding = this.LookupBinding(upper_case);
|
|
1253
|
+
if (binding) {
|
|
1254
|
+
return this.CalculateExpression(binding as ExtendedExpressionUnit);
|
|
1255
|
+
}
|
|
1029
1256
|
|
|
1257
|
+
const named = this.data_model.GetName(upper_case, this.context.address.sheet_id || 0);
|
|
1258
|
+
|
|
1030
1259
|
switch (named?.type) {
|
|
1031
1260
|
case 'range':
|
|
1032
1261
|
if (named.area.count === 1) {
|
|
@@ -1046,6 +1275,24 @@ export class ExpressionCalculator {
|
|
|
1046
1275
|
|
|
1047
1276
|
}
|
|
1048
1277
|
|
|
1278
|
+
/**
|
|
1279
|
+
* look up an identifier in any binding frames. we do LIFO binding.
|
|
1280
|
+
*
|
|
1281
|
+
* @param name
|
|
1282
|
+
* @returns
|
|
1283
|
+
*/
|
|
1284
|
+
protected LookupBinding(name: string) {
|
|
1285
|
+
name = name.toUpperCase();
|
|
1286
|
+
for (const frame of this.context.bindings) {
|
|
1287
|
+
const value = frame[name];
|
|
1288
|
+
if (value) {
|
|
1289
|
+
return value;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
return undefined;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1049
1296
|
protected GroupExpression(x: UnitGroup): (expr: UnitGroup) => UnionValue /*UnionOrArray*/ {
|
|
1050
1297
|
|
|
1051
1298
|
// a group is an expression in parentheses, either explicit
|
|
@@ -1075,6 +1322,10 @@ export class ExpressionCalculator {
|
|
|
1075
1322
|
}
|
|
1076
1323
|
|
|
1077
1324
|
switch (expr.type){
|
|
1325
|
+
|
|
1326
|
+
case 'implicit-call':
|
|
1327
|
+
return (expr.fn = this.ImplicitCall())(expr);
|
|
1328
|
+
|
|
1078
1329
|
case 'call':
|
|
1079
1330
|
{
|
|
1080
1331
|
const macro = this.data_model.macro_functions.get(expr.name.toUpperCase());
|
|
@@ -351,6 +351,54 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
351
351
|
fn: () => { return { type: ValueType.number, value: Math.random() }},
|
|
352
352
|
},
|
|
353
353
|
|
|
354
|
+
RandArray: {
|
|
355
|
+
volatile: true,
|
|
356
|
+
arguments: [
|
|
357
|
+
{name: 'rows'},
|
|
358
|
+
{name: 'columns'},
|
|
359
|
+
{name: 'min'},
|
|
360
|
+
{name: 'max'},
|
|
361
|
+
{name: 'integer'},
|
|
362
|
+
],
|
|
363
|
+
description: 'Returns an array of uniformly-distributed random numbers',
|
|
364
|
+
fn: (rows = 1, columns = 1, min = 0, max = 1, integer = false) => {
|
|
365
|
+
const value: UnionValue[][] = [];
|
|
366
|
+
|
|
367
|
+
if (integer) {
|
|
368
|
+
const range = max - min + 1;
|
|
369
|
+
for (let i = 0; i < rows; i++) {
|
|
370
|
+
const row: UnionValue[] = [];
|
|
371
|
+
for (let j = 0; j < columns; j++) {
|
|
372
|
+
row.push({
|
|
373
|
+
type: ValueType.number,
|
|
374
|
+
value: Math.floor(Math.random() * range + min),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
value.push(row);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
const range = max - min;
|
|
382
|
+
|
|
383
|
+
for (let i = 0; i < rows; i++) {
|
|
384
|
+
const row: UnionValue[] = [];
|
|
385
|
+
for (let j = 0; j < columns; j++) {
|
|
386
|
+
row.push({
|
|
387
|
+
type: ValueType.number,
|
|
388
|
+
value: Math.random() * range + min,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
value.push(row);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
type: ValueType.array,
|
|
397
|
+
value,
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
|
|
354
402
|
RandBetween: {
|
|
355
403
|
arguments: [{name: 'min'}, {name: 'max'}],
|
|
356
404
|
volatile: true,
|
|
@@ -2719,6 +2767,7 @@ const block_list = [
|
|
|
2719
2767
|
'imul',
|
|
2720
2768
|
'clz32',
|
|
2721
2769
|
'fround',
|
|
2770
|
+
'f16round',
|
|
2722
2771
|
];
|
|
2723
2772
|
|
|
2724
2773
|
const block_map: Record<string, string> = {};
|