@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.
Files changed (35) hide show
  1. package/dist/treb-spreadsheet.mjs +15 -15
  2. package/i18n/languages/treb-i18n-da.mjs +1 -0
  3. package/i18n/languages/treb-i18n-de.mjs +1 -0
  4. package/i18n/languages/treb-i18n-es.mjs +1 -0
  5. package/i18n/languages/treb-i18n-fr.mjs +1 -0
  6. package/i18n/languages/treb-i18n-it.mjs +1 -0
  7. package/i18n/languages/treb-i18n-nl.mjs +1 -0
  8. package/i18n/languages/treb-i18n-no.mjs +1 -0
  9. package/i18n/languages/treb-i18n-pl.mjs +1 -0
  10. package/i18n/languages/treb-i18n-pt.mjs +1 -0
  11. package/i18n/languages/treb-i18n-sv.mjs +1 -0
  12. package/package.json +1 -1
  13. package/treb-base-types/src/style.ts +20 -0
  14. package/treb-base-types/src/union.ts +6 -0
  15. package/treb-base-types/src/value-type.ts +13 -0
  16. package/treb-calculator/src/calculator.ts +20 -1
  17. package/treb-calculator/src/descriptors.ts +49 -4
  18. package/treb-calculator/src/expression-calculator.ts +263 -12
  19. package/treb-calculator/src/functions/base-functions.ts +49 -0
  20. package/treb-calculator/src/functions/fp.ts +339 -0
  21. package/treb-calculator/src/functions/gamma.ts +143 -0
  22. package/treb-calculator/src/functions/lambda-functions.ts +96 -0
  23. package/treb-calculator/src/functions/statistics-functions.ts +88 -0
  24. package/treb-data-model/src/data_model.ts +28 -13
  25. package/treb-data-model/src/named.ts +7 -0
  26. package/treb-data-model/src/sheet.ts +19 -2
  27. package/treb-embed/src/embedded-spreadsheet.ts +22 -5
  28. package/treb-embed/style/theme-defaults.scss +0 -22
  29. package/treb-grid/src/editors/editor.ts +14 -0
  30. package/treb-grid/src/types/grid.ts +74 -28
  31. package/treb-parser/src/parser-types.ts +24 -0
  32. package/treb-parser/src/parser.ts +157 -223
  33. package/dist/treb-export-worker.mjs +0 -2
  34. package/dist/treb.d.ts +0 -2235
  35. 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} from 'treb-base-types';
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 } from 'treb-parser';
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
- const argument_descriptors = func.arguments || []; // map
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
- const mapped_args = expr.args.map((arg, arg_index) => {
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
- if (func.return_type === 'reference') {
800
+ const result = func.fn.apply(ctx, mapped_args);
577
801
 
578
- const result = func.fn.apply(ctx, mapped_args);
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 named = this.data_model.GetName(upper_case, this.context.address.sheet_id || 0);
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> = {};