@trebco/treb 31.8.2 → 32.1.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 +8 -8
- 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 +2 -2
- 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 +18 -1
- package/treb-calculator/src/descriptors.ts +42 -3
- package/treb-calculator/src/expression-calculator.ts +182 -6
- package/treb-calculator/src/functions/base-functions.ts +48 -0
- package/treb-calculator/src/functions/lambda-functions.ts +96 -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 +17 -0
- package/treb-embed/src/embedded-spreadsheet.ts +12 -3
- package/treb-embed/style/autocomplete.scss +4 -0
- package/treb-embed/style/dropdown-select.scss +3 -0
- package/treb-embed/style/font-stacks.scss +2 -0
- package/treb-embed/style/grid.scss +11 -11
- package/treb-embed/style/layout.scss +12 -9
- package/treb-embed/style/mouse-mask.scss +3 -0
- package/treb-embed/style/note.scss +4 -0
- package/treb-embed/style/overlay-editor.scss +3 -0
- package/treb-embed/style/tab-bar.scss +4 -0
- package/treb-embed/style/table.scss +3 -0
- package/treb-embed/style/theme-defaults.scss +2 -22
- package/treb-embed/style/tooltip.scss +4 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +1 -1
- package/treb-export/src/workbook-style2.ts +0 -39
- package/treb-grid/src/render/svg_selection_block.ts +1 -37
- package/treb-grid/src/types/grid.ts +4 -3
- package/treb-parser/src/parser-types.ts +21 -0
- package/treb-parser/src/parser.ts +126 -218
- package/dist/treb-export-worker.mjs +0 -2
- package/dist/treb.d.ts +0 -2235
|
@@ -78,7 +78,7 @@ export class SVGSelectionBlock {
|
|
|
78
78
|
// and use currentColor, but we can't set opacity separately so we
|
|
79
79
|
// need another node. which is a waste, but ergonomics ftw!
|
|
80
80
|
|
|
81
|
-
this.fill = DOM.SVG('rect', 'fill');
|
|
81
|
+
this.fill = DOM.SVG('rect', 'treb-selection-fill');
|
|
82
82
|
|
|
83
83
|
// this.SetThemeColor(0);
|
|
84
84
|
// if (theme.additional_selection_line_dash_array) {
|
|
@@ -95,42 +95,6 @@ export class SVGSelectionBlock {
|
|
|
95
95
|
this.g.setAttribute('transform', `translate(${offset.x}, ${offset.y})`);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/*
|
|
99
|
-
public SetThemeColor(index = 0) {
|
|
100
|
-
|
|
101
|
-
if (Array.isArray(this.theme.additional_selection_color)) {
|
|
102
|
-
if (index >= this.theme.additional_selection_color.length) {
|
|
103
|
-
index = index % this.theme.additional_selection_color.length;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (this.theme.additional_selection_overlay_color) {
|
|
108
|
-
if (typeof this.theme.additional_selection_overlay_color === 'string') {
|
|
109
|
-
this.outline.setAttribute('fill', this.theme.additional_selection_overlay_color);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
this.outline.setAttribute('fill', this.theme.additional_selection_overlay_color[index] || '');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
this.outline.setAttribute('fill', '');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (this.theme.additional_selection_color) {
|
|
120
|
-
if (typeof this.theme.additional_selection_color === 'string') {
|
|
121
|
-
this.outline.setAttribute('stroke', this.theme.additional_selection_color);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
this.outline.setAttribute('stroke', this.theme.additional_selection_color[index] || '');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
this.outline.setAttribute('stroke', '');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
*/
|
|
133
|
-
|
|
134
98
|
public Show(show = true) {
|
|
135
99
|
this.g.style.display = show ? 'block' : 'none';
|
|
136
100
|
}
|
|
@@ -1284,7 +1284,7 @@ export class Grid extends GridBase {
|
|
|
1284
1284
|
this.model.named.Reset();
|
|
1285
1285
|
|
|
1286
1286
|
if (import_data.named) {
|
|
1287
|
-
this.model.UnserializeNames(import_data.named, this.active_sheet);
|
|
1287
|
+
this.model.UnserializeNames(import_data.named, this.active_sheet, true);
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
1290
|
// FIXME: do we need to rebuild autocomplete here (A: yes)
|
|
@@ -2350,11 +2350,12 @@ export class Grid extends GridBase {
|
|
|
2350
2350
|
|
|
2351
2351
|
|
|
2352
2352
|
// this is public so we need to (un)translate.
|
|
2353
|
-
|
|
2353
|
+
// console.info("PRE", {r1c1}, data);
|
|
2354
|
+
data = this.model.UntranslateData(data, { r1c1 });
|
|
2355
|
+
// console.info("POST", data, "\n");
|
|
2354
2356
|
|
|
2355
2357
|
// single value, easiest
|
|
2356
2358
|
if (!Array.isArray(data)) {
|
|
2357
|
-
|
|
2358
2359
|
if (recycle || array) {
|
|
2359
2360
|
this.ExecCommand({ key: CommandKey.SetRange, area: range, value: data, array, r1c1 });
|
|
2360
2361
|
}
|
|
@@ -167,6 +167,17 @@ export interface UnitCall extends BaseUnit {
|
|
|
167
167
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* new call type: implicit. we might merge these.
|
|
172
|
+
*/
|
|
173
|
+
export interface UnitImplicitCall extends BaseUnit {
|
|
174
|
+
type: 'implicit-call';
|
|
175
|
+
position: number;
|
|
176
|
+
args: ExpressionUnit[];
|
|
177
|
+
call: ExpressionUnit;
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
170
181
|
/**
|
|
171
182
|
* this isn't an output type (unless parsing fails), but it's useful
|
|
172
183
|
* to be able to pass these around with the same semantics.
|
|
@@ -177,6 +188,14 @@ export interface UnitOperator extends BaseUnit {
|
|
|
177
188
|
operator: string;
|
|
178
189
|
}
|
|
179
190
|
|
|
191
|
+
/**
|
|
192
|
+
* also not an output type
|
|
193
|
+
*/
|
|
194
|
+
export interface UnitGroupSeparator extends BaseUnit {
|
|
195
|
+
type: 'group-separator';
|
|
196
|
+
position: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
180
199
|
/**
|
|
181
200
|
* expression unit representing a binary operation. operations may be
|
|
182
201
|
* re-ordered based on precendence.
|
|
@@ -259,9 +278,11 @@ export type BaseExpressionUnit =
|
|
|
259
278
|
| UnitArray
|
|
260
279
|
| UnitIdentifier
|
|
261
280
|
| UnitCall
|
|
281
|
+
| UnitImplicitCall
|
|
262
282
|
| UnitMissing
|
|
263
283
|
| UnitGroup
|
|
264
284
|
| UnitOperator
|
|
285
|
+
| UnitGroupSeparator
|
|
265
286
|
| UnitBinary
|
|
266
287
|
| UnitUnary
|
|
267
288
|
| UnitAddress
|
|
@@ -411,6 +411,13 @@ export class Parser {
|
|
|
411
411
|
}
|
|
412
412
|
break;
|
|
413
413
|
|
|
414
|
+
case 'implicit-call':
|
|
415
|
+
if (func(unit)) {
|
|
416
|
+
unit.call = this.Walk2(unit.call, func);
|
|
417
|
+
unit.args = unit.args.map(source => this.Walk2(source, func));
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
|
|
414
421
|
case 'call':
|
|
415
422
|
if (func(unit)) {
|
|
416
423
|
unit.args = unit.args.map(source => this.Walk2(source, func));
|
|
@@ -477,6 +484,15 @@ export class Parser {
|
|
|
477
484
|
}
|
|
478
485
|
return;
|
|
479
486
|
|
|
487
|
+
case 'implicit-call':
|
|
488
|
+
if (func(unit)) {
|
|
489
|
+
this.Walk(unit.call, func);
|
|
490
|
+
for (const arg of unit.args) {
|
|
491
|
+
this.Walk(arg, func);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return;
|
|
495
|
+
|
|
480
496
|
case 'call':
|
|
481
497
|
if (func(unit)) {
|
|
482
498
|
for (const arg of unit.args) {
|
|
@@ -755,6 +771,10 @@ export class Parser {
|
|
|
755
771
|
.map((x) => this.Render(x, options)).join(separator);
|
|
756
772
|
}
|
|
757
773
|
|
|
774
|
+
case 'implicit-call':
|
|
775
|
+
return this.Render(unit.call, options) +
|
|
776
|
+
'(' + unit.args.map(element => this.Render(element, options)).join(separator) + ')';
|
|
777
|
+
|
|
758
778
|
case 'call':
|
|
759
779
|
return (
|
|
760
780
|
unit.name +
|
|
@@ -948,8 +968,13 @@ export class Parser {
|
|
|
948
968
|
'\'' + address.sheet + '\'' : address.sheet) + '!';
|
|
949
969
|
}
|
|
950
970
|
|
|
971
|
+
const row = address.offset_row ? `[${address.row}]` : address.row + 1;
|
|
972
|
+
const column = address.offset_column ? `[${address.column}]` : address.column + 1;
|
|
973
|
+
|
|
974
|
+
/*
|
|
951
975
|
const row = (address.absolute_row || !base) ? (address.row + 1).toString() : `[${address.row - base.row}]`;
|
|
952
976
|
const column = (address.absolute_column || !base) ? (address.column + 1).toString() : `[${address.column - base.column}]`;
|
|
977
|
+
*/
|
|
953
978
|
|
|
954
979
|
label += `R${row}C${column}`;
|
|
955
980
|
|
|
@@ -1009,11 +1034,12 @@ export class Parser {
|
|
|
1009
1034
|
*
|
|
1010
1035
|
* @param exit exit on specific characters
|
|
1011
1036
|
*/
|
|
1012
|
-
protected ParseGeneric(exit: number[] = [0]): ExpressionUnit | null {
|
|
1037
|
+
protected ParseGeneric(exit: number[] = [0], explicit_group = false): ExpressionUnit | null {
|
|
1013
1038
|
let stream: ExpressionUnit[] = [];
|
|
1014
1039
|
|
|
1015
1040
|
for (; this.index < this.length;) {
|
|
1016
1041
|
const unit = this.ParseNext(stream.length === 0);
|
|
1042
|
+
|
|
1017
1043
|
if (typeof unit === 'number') {
|
|
1018
1044
|
|
|
1019
1045
|
if (exit.some((test) => unit === test)) {
|
|
@@ -1025,8 +1051,11 @@ export class Parser {
|
|
|
1025
1051
|
// so we only have to worry about grouping. parse
|
|
1026
1052
|
// up to the closing paren...
|
|
1027
1053
|
|
|
1054
|
+
// actually now we have implicit calls, so we need
|
|
1055
|
+
// to manage that here.
|
|
1056
|
+
|
|
1028
1057
|
this.index++; // open paren
|
|
1029
|
-
const group = this.ParseGeneric([CLOSE_PAREN]);
|
|
1058
|
+
const group = this.ParseGeneric([CLOSE_PAREN], true);
|
|
1030
1059
|
this.index++; // close paren
|
|
1031
1060
|
|
|
1032
1061
|
// and wrap up in a group element to prevent reordering.
|
|
@@ -1051,6 +1080,21 @@ export class Parser {
|
|
|
1051
1080
|
if (operator) {
|
|
1052
1081
|
stream.push(operator);
|
|
1053
1082
|
}
|
|
1083
|
+
else if (explicit_group && unit === this.argument_separator_char) {
|
|
1084
|
+
|
|
1085
|
+
// adding a new unit type here to explicitly show we're in
|
|
1086
|
+
// a group; prevents later passes from treating arguments as
|
|
1087
|
+
// fractions or something else. we just need to remove these
|
|
1088
|
+
// later
|
|
1089
|
+
|
|
1090
|
+
stream.push({
|
|
1091
|
+
type: 'group-separator',
|
|
1092
|
+
position: this.index,
|
|
1093
|
+
id: this.id_counter++,
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
this.index++;
|
|
1097
|
+
}
|
|
1054
1098
|
else {
|
|
1055
1099
|
this.error = `unexpected character [1]: ${String.fromCharCode(unit)}, 0x${unit.toString(16)}`;
|
|
1056
1100
|
this.valid = false;
|
|
@@ -1231,13 +1275,6 @@ export class Parser {
|
|
|
1231
1275
|
// fix ordering of binary operations based on precedence; also
|
|
1232
1276
|
// convert and validate ranges
|
|
1233
1277
|
|
|
1234
|
-
// return this.BinaryToRange(this.ArrangeUnits(stream));
|
|
1235
|
-
// return this.ArrangeUnits(stream);
|
|
1236
|
-
|
|
1237
|
-
// const arranged = this.ArrangeUnits(stream);
|
|
1238
|
-
// const result = this.BinaryToComplex(arranged);
|
|
1239
|
-
// return result;
|
|
1240
|
-
|
|
1241
1278
|
return this.BinaryToComplex(this.ArrangeUnits(stream));
|
|
1242
1279
|
|
|
1243
1280
|
}
|
|
@@ -1612,206 +1649,13 @@ export class Parser {
|
|
|
1612
1649
|
|
|
1613
1650
|
|
|
1614
1651
|
/**
|
|
1615
|
-
*
|
|
1616
|
-
* validates that there are no colon operations with non-address operands
|
|
1617
|
-
* (which is why it's called after precendence reordering; colon has the
|
|
1618
|
-
* highest preference). recursive only over binary ops AND unary ops.
|
|
1619
|
-
*
|
|
1620
|
-
* NOTE: there are other legal arguments to a colon operator. specifically:
|
|
1621
|
-
*
|
|
1622
|
-
* (1) two numbers, in either order
|
|
1623
|
-
*
|
|
1624
|
-
* 15:16
|
|
1625
|
-
* 16:16
|
|
1626
|
-
* 16:15
|
|
1627
|
-
*
|
|
1628
|
-
* (2) with one or both optionally having a $
|
|
1629
|
-
*
|
|
1630
|
-
* 15:$16
|
|
1631
|
-
* $16:$16
|
|
1632
|
-
*
|
|
1633
|
-
* (3) two column identifiers, in either order
|
|
1634
|
-
*
|
|
1635
|
-
* A:F
|
|
1636
|
-
* B:A
|
|
1637
|
-
*
|
|
1638
|
-
* (4) and the same with $
|
|
1639
|
-
*
|
|
1640
|
-
* $A:F
|
|
1641
|
-
* $A:$F
|
|
1652
|
+
* reorders operations for precendence
|
|
1642
1653
|
*
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
1654
|
+
* this method was written with the assumption that groups were
|
|
1655
|
+
* always an error. that's no longer true, with implicit calls.
|
|
1656
|
+
* we should still error if it's not an _explicit_ group, i.e. there's
|
|
1657
|
+
* just a bunch of naked tokens.
|
|
1647
1658
|
*
|
|
1648
|
-
* FIXME: will need some updated to rendering these, we don't have any
|
|
1649
|
-
* handler for rendering infinity
|
|
1650
|
-
*/
|
|
1651
|
-
protected BinaryToRangeX(unit: ExpressionUnit): ExpressionUnit {
|
|
1652
|
-
if (unit.type === 'binary') {
|
|
1653
|
-
if (unit.operator === ':') {
|
|
1654
|
-
|
|
1655
|
-
let range: UnitRange|undefined;
|
|
1656
|
-
let label = '';
|
|
1657
|
-
|
|
1658
|
-
if (unit.left.type === 'address' && unit.right.type === 'address') {
|
|
1659
|
-
// construct a label using the full text. there's a possibility,
|
|
1660
|
-
// I suppose, that there are spaces (this should probably not be
|
|
1661
|
-
// legal). this is a canonical label, though (generated)
|
|
1662
|
-
|
|
1663
|
-
// it might be better to let this slip, or treat it as an error
|
|
1664
|
-
// and force a correction... not sure (TODO/FIXME)
|
|
1665
|
-
|
|
1666
|
-
const start_index = unit.left.position + unit.left.label.length;
|
|
1667
|
-
const end_index = unit.right.position;
|
|
1668
|
-
|
|
1669
|
-
range = {
|
|
1670
|
-
type: 'range',
|
|
1671
|
-
id: this.id_counter++,
|
|
1672
|
-
position: unit.left.position,
|
|
1673
|
-
start: unit.left,
|
|
1674
|
-
end: unit.right,
|
|
1675
|
-
label:
|
|
1676
|
-
unit.left.label +
|
|
1677
|
-
this.expression.substring(start_index, end_index) +
|
|
1678
|
-
unit.right.label,
|
|
1679
|
-
};
|
|
1680
|
-
|
|
1681
|
-
label = range.start.label + ':' + range.end.label;
|
|
1682
|
-
|
|
1683
|
-
this.address_refcount[range.start.label]--;
|
|
1684
|
-
this.address_refcount[range.end.label]--;
|
|
1685
|
-
|
|
1686
|
-
// remove entries from the list for start, stop
|
|
1687
|
-
const positions = [unit.left.position, unit.right.position];
|
|
1688
|
-
this.full_reference_list = this.full_reference_list.filter((test) => {
|
|
1689
|
-
return (
|
|
1690
|
-
test.position !== positions[0] && test.position !== positions[1]
|
|
1691
|
-
);
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
}
|
|
1695
|
-
else if ((unit.left.type === 'literal' || unit.left.type === 'identifier')
|
|
1696
|
-
&& (unit.right.type === 'literal' || unit.right.type === 'identifier')) {
|
|
1697
|
-
|
|
1698
|
-
// see if we can plausibly interpret both of these as rows or columns
|
|
1699
|
-
|
|
1700
|
-
const left = this.UnitToAddress(unit.left);
|
|
1701
|
-
const right = this.UnitToAddress(unit.right);
|
|
1702
|
-
|
|
1703
|
-
// and they need to match
|
|
1704
|
-
|
|
1705
|
-
if (left && right
|
|
1706
|
-
&& ((left.column === Infinity && right.column === Infinity)
|
|
1707
|
-
|| (left.row === Infinity && right.row === Infinity))) {
|
|
1708
|
-
|
|
1709
|
-
label = left.label + ':' + right.label;
|
|
1710
|
-
|
|
1711
|
-
// we don't support out-of-order ranges, so we should correct.
|
|
1712
|
-
// they just won't work otherwise. (TODO/FIXME)
|
|
1713
|
-
|
|
1714
|
-
range = {
|
|
1715
|
-
type: 'range',
|
|
1716
|
-
id: this.id_counter++,
|
|
1717
|
-
position: unit.left.position,
|
|
1718
|
-
start: left,
|
|
1719
|
-
end: right,
|
|
1720
|
-
label,
|
|
1721
|
-
};
|
|
1722
|
-
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
/*
|
|
1728
|
-
else if ( unit.left.type === 'literal'
|
|
1729
|
-
&& unit.right.type === 'literal'
|
|
1730
|
-
&& typeof unit.left.value === 'number'
|
|
1731
|
-
&& typeof unit.right.value === 'number') {
|
|
1732
|
-
|
|
1733
|
-
// technically we don't want to support any number that has
|
|
1734
|
-
// a decimal place, but I'm not sure we have a useful way of
|
|
1735
|
-
// measuring that... could look at the original text?
|
|
1736
|
-
|
|
1737
|
-
if (unit.left.value > 0
|
|
1738
|
-
&& unit.right.value > 0
|
|
1739
|
-
&& !/\./.test(unit.left.text||'')
|
|
1740
|
-
&& !/\./.test(unit.right.text||'')
|
|
1741
|
-
) {
|
|
1742
|
-
|
|
1743
|
-
label = unit.left.value.toString() + ':' + unit.right.value.toString();
|
|
1744
|
-
|
|
1745
|
-
console.info('m2:', label);
|
|
1746
|
-
|
|
1747
|
-
const left: UnitAddress = {
|
|
1748
|
-
type: 'address',
|
|
1749
|
-
position: unit.left.position,
|
|
1750
|
-
label: unit.left.value.toString(),
|
|
1751
|
-
row: unit.left.value - 1,
|
|
1752
|
-
id: this.id_counter++,
|
|
1753
|
-
column: Infinity,
|
|
1754
|
-
};
|
|
1755
|
-
|
|
1756
|
-
const right: UnitAddress = {
|
|
1757
|
-
type: 'address',
|
|
1758
|
-
position: unit.right.position,
|
|
1759
|
-
label: unit.right.value.toString(),
|
|
1760
|
-
row: unit.right.value - 1,
|
|
1761
|
-
id: this.id_counter++,
|
|
1762
|
-
column: Infinity,
|
|
1763
|
-
};
|
|
1764
|
-
|
|
1765
|
-
range = {
|
|
1766
|
-
type: 'range',
|
|
1767
|
-
id: this.id_counter++,
|
|
1768
|
-
position: unit.left.position,
|
|
1769
|
-
start: left,
|
|
1770
|
-
end: right,
|
|
1771
|
-
label,
|
|
1772
|
-
};
|
|
1773
|
-
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
}
|
|
1777
|
-
*/
|
|
1778
|
-
|
|
1779
|
-
if (range) {
|
|
1780
|
-
|
|
1781
|
-
this.dependencies.ranges[label] = range;
|
|
1782
|
-
|
|
1783
|
-
// and add the range
|
|
1784
|
-
this.full_reference_list.push(range);
|
|
1785
|
-
|
|
1786
|
-
return range;
|
|
1787
|
-
|
|
1788
|
-
}
|
|
1789
|
-
else {
|
|
1790
|
-
this.error = `unexpected character: :`;
|
|
1791
|
-
this.valid = false;
|
|
1792
|
-
// console.info('xx', unit);
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
// recurse
|
|
1798
|
-
|
|
1799
|
-
unit.left = this.BinaryToRangeX(unit.left);
|
|
1800
|
-
unit.right = this.BinaryToRangeX(unit.right);
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
// this should no longer be required, because we explicitly check
|
|
1804
|
-
// when we construct the unary operations...
|
|
1805
|
-
|
|
1806
|
-
// else if (unit.type === 'unary') {
|
|
1807
|
-
// unit.operand = this.BinaryToRange(unit.operand);
|
|
1808
|
-
// }
|
|
1809
|
-
|
|
1810
|
-
return unit;
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
/**
|
|
1814
|
-
* reorders operations for precendence
|
|
1815
1659
|
*/
|
|
1816
1660
|
protected ArrangeUnits(stream: ExpressionUnit[]): ExpressionUnit {
|
|
1817
1661
|
|
|
@@ -1838,6 +1682,10 @@ export class Parser {
|
|
|
1838
1682
|
for (let index = 0; index < stream.length; index++) {
|
|
1839
1683
|
let element = stream[index];
|
|
1840
1684
|
|
|
1685
|
+
if (element.type === 'group-separator') {
|
|
1686
|
+
continue; // drop
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1841
1689
|
// given that we need to support unary operators, the logic needs
|
|
1842
1690
|
// to be a little different. operators are OK at any position, provided
|
|
1843
1691
|
// we can construct either a unary or binary operation.
|
|
@@ -1848,14 +1696,7 @@ export class Parser {
|
|
|
1848
1696
|
// in this case we do it with recursion.
|
|
1849
1697
|
|
|
1850
1698
|
if (unary_operators[element.operator]) {
|
|
1851
|
-
|
|
1852
|
-
// MARK X
|
|
1853
|
-
|
|
1854
|
-
// const right = this.BinaryToRange(
|
|
1855
|
-
// this.ArrangeUnits(stream.slice(index + 1)),
|
|
1856
|
-
//);
|
|
1857
|
-
|
|
1858
|
-
// const right = this.ArrangeUnits(stream.slice(index + 1));
|
|
1699
|
+
|
|
1859
1700
|
const right = this.BinaryToComplex(this.ArrangeUnits(stream.slice(index + 1)));
|
|
1860
1701
|
|
|
1861
1702
|
// this ensures we return the highest-level group, even if we recurse
|
|
@@ -1932,7 +1773,35 @@ export class Parser {
|
|
|
1932
1773
|
if (stack.length === 1) {
|
|
1933
1774
|
const a = stack[0].type;
|
|
1934
1775
|
|
|
1935
|
-
|
|
1776
|
+
// support for lambdas
|
|
1777
|
+
|
|
1778
|
+
if (element.type === 'group' && element.explicit) {
|
|
1779
|
+
if (a === 'address' || a === 'call' || a === 'identifier' || a === 'implicit-call') {
|
|
1780
|
+
|
|
1781
|
+
// our parser seems to create implicit groups from these
|
|
1782
|
+
// values in parens. we should fix that, but we can unpack it.
|
|
1783
|
+
|
|
1784
|
+
let args = element.elements;
|
|
1785
|
+
if (args.length === 1 && args[0].type === 'group' && !args[0].explicit) {
|
|
1786
|
+
args = args[0].elements;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// create an implicit call. replace on the stack.
|
|
1790
|
+
|
|
1791
|
+
stack[0] = {
|
|
1792
|
+
type: 'implicit-call',
|
|
1793
|
+
call: stack[0],
|
|
1794
|
+
args,
|
|
1795
|
+
id: this.id_counter++,
|
|
1796
|
+
position: stack[0].position,
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
continue;
|
|
1800
|
+
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
/*
|
|
1804
|
+
else if (a !== 'operator') {
|
|
1936
1805
|
|
|
1937
1806
|
// console.warn("unexpected element", stack[0], element);
|
|
1938
1807
|
|
|
@@ -1947,6 +1816,7 @@ export class Parser {
|
|
|
1947
1816
|
};
|
|
1948
1817
|
|
|
1949
1818
|
}
|
|
1819
|
+
*/
|
|
1950
1820
|
|
|
1951
1821
|
}
|
|
1952
1822
|
|
|
@@ -1996,6 +1866,8 @@ export class Parser {
|
|
|
1996
1866
|
stack.splice(-2, 2, operation);
|
|
1997
1867
|
}
|
|
1998
1868
|
else {
|
|
1869
|
+
|
|
1870
|
+
/*
|
|
1999
1871
|
this.error = `multiple expressions`;
|
|
2000
1872
|
this.error_position = (element as {position?: number}).position;
|
|
2001
1873
|
this.valid = false;
|
|
@@ -2005,9 +1877,22 @@ export class Parser {
|
|
|
2005
1877
|
elements: stream,
|
|
2006
1878
|
explicit: false,
|
|
2007
1879
|
};
|
|
1880
|
+
*/
|
|
1881
|
+
|
|
1882
|
+
stack.push(element);
|
|
1883
|
+
|
|
2008
1884
|
}
|
|
2009
1885
|
}
|
|
2010
1886
|
|
|
1887
|
+
if (stack.length > 1) {
|
|
1888
|
+
return {
|
|
1889
|
+
type: 'group',
|
|
1890
|
+
id: this.id_counter++,
|
|
1891
|
+
elements: stack,
|
|
1892
|
+
explicit: false,
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
|
|
2011
1896
|
return stack[0];
|
|
2012
1897
|
}
|
|
2013
1898
|
|
|
@@ -2385,6 +2270,20 @@ export class Parser {
|
|
|
2385
2270
|
|
|
2386
2271
|
this.ConsumeWhiteSpace();
|
|
2387
2272
|
|
|
2273
|
+
// UPDATE: UNLESS the token is an address, because that's not
|
|
2274
|
+
// a legal function name. so change that precedence rule, address
|
|
2275
|
+
// comes first.
|
|
2276
|
+
|
|
2277
|
+
// erm -- that's not 100% correct. LOG10 is a valid cell address
|
|
2278
|
+
// and a valid function name. there might be others as well.
|
|
2279
|
+
|
|
2280
|
+
if (this.flags.spreadsheet_semantics) {
|
|
2281
|
+
const address = this.ConsumeAddress(str, position);
|
|
2282
|
+
if (address) return address;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// [FIXME: what about braces? (...)]
|
|
2286
|
+
|
|
2388
2287
|
const next_char = this.data[this.index];
|
|
2389
2288
|
if (next_char === OPEN_PAREN) {
|
|
2390
2289
|
const args = this.ConsumeArguments();
|
|
@@ -2404,8 +2303,9 @@ export class Parser {
|
|
|
2404
2303
|
// range operator, and a second address. that will be turned into a range
|
|
2405
2304
|
// later.
|
|
2406
2305
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2306
|
+
// moved up
|
|
2307
|
+
// const address = this.ConsumeAddress(str, position);
|
|
2308
|
+
// if (address) return address;
|
|
2409
2309
|
|
|
2410
2310
|
// check for structured reference, if we had square brackets
|
|
2411
2311
|
|
|
@@ -2638,6 +2538,14 @@ export class Parser {
|
|
|
2638
2538
|
if (!r) return null;
|
|
2639
2539
|
position = r.position;
|
|
2640
2540
|
|
|
2541
|
+
// special hack for LOG10. ugh. can't find any other functions with
|
|
2542
|
+
// this problem, in english at least. btw what's the translation for
|
|
2543
|
+
// log10?
|
|
2544
|
+
|
|
2545
|
+
if (c.column === 8508 && r.row === 9) {
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2641
2549
|
const label = sheet ?
|
|
2642
2550
|
sheet + token.substr(sheet.length, position - index).toUpperCase() :
|
|
2643
2551
|
token.substr(0, position - index).toUpperCase();
|