@trebco/treb 23.6.5 → 25.0.0-rc1
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/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +285 -269
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +43 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1227 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5362 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +320 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* base type, for common data (atm only ID). id is intended to support
|
|
24
|
+
* a unique ID within the context of a single parse pass. (NOTE: in theory
|
|
25
|
+
* you could use the 'position' field... although that's not present in
|
|
26
|
+
* all cases?)
|
|
27
|
+
*/
|
|
28
|
+
export interface BaseUnit {
|
|
29
|
+
id: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface UnitLiteralNumber extends BaseUnit {
|
|
33
|
+
type: 'literal';
|
|
34
|
+
position: number;
|
|
35
|
+
value: number;
|
|
36
|
+
text?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface UnitLiteralString extends BaseUnit {
|
|
39
|
+
type: 'literal';
|
|
40
|
+
position: number;
|
|
41
|
+
value: string;
|
|
42
|
+
text?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface UnitLiteralBoolean extends BaseUnit {
|
|
45
|
+
type: 'literal';
|
|
46
|
+
position: number;
|
|
47
|
+
value: boolean;
|
|
48
|
+
text?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* expression unit representing a literal: string, number, boolean.
|
|
53
|
+
* FIXME: would be nice if we had subtypes so we could specify
|
|
54
|
+
* /
|
|
55
|
+
export interface UnitLiteral extends BaseUnit {
|
|
56
|
+
type: 'literal';
|
|
57
|
+
position: number;
|
|
58
|
+
value: string | boolean | number;
|
|
59
|
+
text?: string;
|
|
60
|
+
}
|
|
61
|
+
*/
|
|
62
|
+
export type UnitLiteral = UnitLiteralNumber|UnitLiteralBoolean|UnitLiteralString;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* testing: complex
|
|
66
|
+
*/
|
|
67
|
+
export interface UnitComplex extends BaseUnit {
|
|
68
|
+
type: 'complex';
|
|
69
|
+
position: number;
|
|
70
|
+
real: number;
|
|
71
|
+
imaginary: number;
|
|
72
|
+
text?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* this flag takes the place of the old "imaginary" unit type;
|
|
76
|
+
* it's an indication that this unit has been composited, so don't
|
|
77
|
+
* do it again. not sure this is actually needed by the parser... is it?
|
|
78
|
+
*/
|
|
79
|
+
composited?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* *
|
|
83
|
+
* testing: complex
|
|
84
|
+
* this represents just the imaginary part. it's for internal use and should
|
|
85
|
+
* never be returned as a value.
|
|
86
|
+
* /
|
|
87
|
+
export interface UnitImaginary extends BaseUnit {
|
|
88
|
+
type: 'imaginary';
|
|
89
|
+
position: number;
|
|
90
|
+
value: number;
|
|
91
|
+
text?: string;
|
|
92
|
+
}
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* expression unit representing an array of primitive values. array
|
|
97
|
+
* can contain mixed values, and holes. array cannot contain arrays,
|
|
98
|
+
* or any other complex type.
|
|
99
|
+
*/
|
|
100
|
+
export interface UnitArray extends BaseUnit {
|
|
101
|
+
type: 'array';
|
|
102
|
+
position: number;
|
|
103
|
+
values: Array < Array <string|boolean|number|undefined> >;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* expression unit representing a missing value, intended for missing
|
|
108
|
+
* arguments in function calls.
|
|
109
|
+
*/
|
|
110
|
+
export interface UnitMissing extends BaseUnit {
|
|
111
|
+
type: 'missing';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* expression unit representing an opaque name or identifier.
|
|
116
|
+
*/
|
|
117
|
+
export interface UnitIdentifier extends BaseUnit {
|
|
118
|
+
type: 'identifier';
|
|
119
|
+
position: number;
|
|
120
|
+
name: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* "structured reference" for offset into named table
|
|
125
|
+
*/
|
|
126
|
+
export interface UnitStructuredReference extends BaseUnit {
|
|
127
|
+
type: 'structured-reference';
|
|
128
|
+
label: string;
|
|
129
|
+
position: number;
|
|
130
|
+
table: string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* row refers to "this row". "all" means all values, including the
|
|
134
|
+
* header. "column" means all values except the header.
|
|
135
|
+
*/
|
|
136
|
+
scope: 'row'|'all'|'column';
|
|
137
|
+
column: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* expression unit representing a group of units; like parentheses in an
|
|
142
|
+
* expression. intended to prevent precendence reordering of operations.
|
|
143
|
+
*/
|
|
144
|
+
export interface UnitGroup extends BaseUnit {
|
|
145
|
+
type: 'group';
|
|
146
|
+
elements: ExpressionUnit[];
|
|
147
|
+
|
|
148
|
+
// this flag indicates whether the group was expressly inserted by
|
|
149
|
+
// the user (true) or generated as part of parsing (false). that
|
|
150
|
+
// information is used when unparsing (reconstructing formula).
|
|
151
|
+
|
|
152
|
+
explicit: boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* expression unit representing a function call: has call and arguments.
|
|
157
|
+
*/
|
|
158
|
+
export interface UnitCall extends BaseUnit {
|
|
159
|
+
type: 'call';
|
|
160
|
+
name: string;
|
|
161
|
+
position: number;
|
|
162
|
+
args: ExpressionUnit[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* this isn't an output type (unless parsing fails), but it's useful
|
|
167
|
+
* to be able to pass these around with the same semantics.
|
|
168
|
+
*/
|
|
169
|
+
export interface UnitOperator extends BaseUnit {
|
|
170
|
+
type: 'operator';
|
|
171
|
+
position: number;
|
|
172
|
+
operator: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* expression unit representing a binary operation. operations may be
|
|
177
|
+
* re-ordered based on precendence.
|
|
178
|
+
*/
|
|
179
|
+
export interface UnitBinary extends BaseUnit {
|
|
180
|
+
type: 'binary';
|
|
181
|
+
left: ExpressionUnit;
|
|
182
|
+
operator: string;
|
|
183
|
+
right: ExpressionUnit;
|
|
184
|
+
position: number; // this is the _operator_ position, since that will be the error
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* expression unit representing a unary operation.
|
|
189
|
+
*/
|
|
190
|
+
export interface UnitUnary extends BaseUnit {
|
|
191
|
+
type: 'unary';
|
|
192
|
+
operator: string;
|
|
193
|
+
operand: ExpressionUnit;
|
|
194
|
+
position: number;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* expression unit representing a spreadsheet address
|
|
199
|
+
*/
|
|
200
|
+
export interface UnitAddress extends BaseUnit {
|
|
201
|
+
type: 'address';
|
|
202
|
+
sheet?: string;
|
|
203
|
+
sheet_id?: number;
|
|
204
|
+
label: string;
|
|
205
|
+
row: number;
|
|
206
|
+
column: number;
|
|
207
|
+
absolute_row?: boolean;
|
|
208
|
+
absolute_column?: boolean;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* this means the row is a relative offset from the current row. this
|
|
212
|
+
* happens if you use R1C1 syntax with square brackets.
|
|
213
|
+
*/
|
|
214
|
+
offset_row?: boolean;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* this means the column is a relative offset from the current column.
|
|
218
|
+
* this happens if you use R1C1 syntax with square brackets.
|
|
219
|
+
*/
|
|
220
|
+
offset_column?: boolean;
|
|
221
|
+
|
|
222
|
+
/** the formula was originally in R1C1. we probably want to translate it. */
|
|
223
|
+
r1c1?: boolean;
|
|
224
|
+
|
|
225
|
+
position: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* expression unit representing a spreadsheet range
|
|
230
|
+
*/
|
|
231
|
+
export interface UnitRange extends BaseUnit {
|
|
232
|
+
type: 'range';
|
|
233
|
+
label: string;
|
|
234
|
+
start: UnitAddress;
|
|
235
|
+
end: UnitAddress;
|
|
236
|
+
position: number;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface UnitDimensionedQuantity extends BaseUnit {
|
|
240
|
+
type: 'dimensioned';
|
|
241
|
+
expression: BaseExpressionUnit; // <!-- does not support recursive DQs
|
|
242
|
+
unit: UnitIdentifier;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* discriminated union. this version allows any expression unit _except_ dimensioned quantity
|
|
247
|
+
*/
|
|
248
|
+
export type BaseExpressionUnit =
|
|
249
|
+
| UnitLiteral
|
|
250
|
+
| UnitComplex
|
|
251
|
+
| UnitArray
|
|
252
|
+
| UnitIdentifier
|
|
253
|
+
| UnitCall
|
|
254
|
+
| UnitMissing
|
|
255
|
+
| UnitGroup
|
|
256
|
+
| UnitOperator
|
|
257
|
+
| UnitBinary
|
|
258
|
+
| UnitUnary
|
|
259
|
+
| UnitAddress
|
|
260
|
+
| UnitRange
|
|
261
|
+
| UnitStructuredReference
|
|
262
|
+
;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* discriminated union for type guards, all types
|
|
266
|
+
*/
|
|
267
|
+
export type ExpressionUnit =
|
|
268
|
+
| BaseExpressionUnit
|
|
269
|
+
| UnitDimensionedQuantity;
|
|
270
|
+
|
|
271
|
+
/** list of addresses and ranges in the formula, for graphs */
|
|
272
|
+
export interface DependencyList {
|
|
273
|
+
addresses: { [index: string]: UnitAddress };
|
|
274
|
+
ranges: { [index: string]: UnitRange };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* argument separator type for i18n
|
|
279
|
+
*/
|
|
280
|
+
export enum ArgumentSeparatorType {
|
|
281
|
+
Comma = ',',
|
|
282
|
+
Semicolon = ';',
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* decimal mark for i18n
|
|
287
|
+
*/
|
|
288
|
+
export enum DecimalMarkType {
|
|
289
|
+
Period = '.',
|
|
290
|
+
Comma = ',',
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* compound result of a parse operation includes dependency list
|
|
295
|
+
* and an error flag (inverted)
|
|
296
|
+
*/
|
|
297
|
+
export interface ParseResult {
|
|
298
|
+
expression?: ExpressionUnit;
|
|
299
|
+
valid: boolean;
|
|
300
|
+
error_position?: number;
|
|
301
|
+
error?: string;
|
|
302
|
+
dependencies: DependencyList;
|
|
303
|
+
separator?: string;
|
|
304
|
+
decimal_mark?: string;
|
|
305
|
+
full_reference_list?: Array<UnitRange | UnitAddress | UnitIdentifier | UnitStructuredReference>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
//
|
|
309
|
+
|
|
310
|
+
export interface ParserFlags {
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* flag: support spreadsheet addresses (e.g. "A1"). this is the default,
|
|
314
|
+
* as it's useful in spreadsheets. however if we want to use the parser
|
|
315
|
+
* non-spreadsheet things, it might be preferable to treat things that look
|
|
316
|
+
* like spreadsheet addresses as tokens instead.
|
|
317
|
+
*
|
|
318
|
+
* this is default so it won't break existing behavior.
|
|
319
|
+
*/
|
|
320
|
+
spreadsheet_semantics: boolean,
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* flag: support expressions with units, like `3mm` or `=3mm + 2in`.
|
|
324
|
+
* this is for parametric modeling. testing/dev atm.
|
|
325
|
+
*/
|
|
326
|
+
dimensioned_quantities: boolean,
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* support fractions. this is kind of a weird edge case, mostly it should
|
|
330
|
+
* be handled by the value parser. (actually there might be some need for
|
|
331
|
+
* separate parsing with dimensioned quantities).
|
|
332
|
+
*
|
|
333
|
+
* in any case, if you type `=1/2` that should be a binary expression.
|
|
334
|
+
* if you type `=3 1/2`, though, that means 3.5 and we need to treat it
|
|
335
|
+
* as such.
|
|
336
|
+
*
|
|
337
|
+
* rules:
|
|
338
|
+
*
|
|
339
|
+
* - must be a binary "/" (divide) operation, with integer operands.
|
|
340
|
+
* - must be [literal integer] [fraction] where the interval must be one space.
|
|
341
|
+
* - can be negated (e.g. "-3 1/2", so that makes things more complicated.
|
|
342
|
+
* - if we do translate, translate hard so this becomes a literal number.
|
|
343
|
+
*
|
|
344
|
+
* ...default? since we didn't support this before, we could leave it
|
|
345
|
+
* off for now. needs some more testing.
|
|
346
|
+
*
|
|
347
|
+
*/
|
|
348
|
+
fractions: boolean,
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* support R1C1 addressing. we support absolute (`R2C3`) and relative
|
|
352
|
+
* (R[-1]C[0]) addresses. can we squeeze this into the existing address
|
|
353
|
+
* structure, or do we need a new structure? (...)
|
|
354
|
+
*/
|
|
355
|
+
r1c1: boolean,
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
/* *
|
|
359
|
+
* what if we do want =1/2 to be a fraction? more importantly, if we are
|
|
360
|
+
* using dimensioned quantities we might want =1/2C to be 0.5C, as opposed
|
|
361
|
+
* to a binary operation =1 / (2C)
|
|
362
|
+
*
|
|
363
|
+
* ...
|
|
364
|
+
*
|
|
365
|
+
* actually now that I think about it, that's equivalent. I was worred about
|
|
366
|
+
* the concept of 1 / (2C) but logically that's the same as 2C / 4C^2 (multiply
|
|
367
|
+
* numerator and denominator by denominator)) which is === (1/2)C. basically
|
|
368
|
+
* as long as you only have one value with a dimension/unit, then division
|
|
369
|
+
* and multiplication are a wash. it's only a concern when you have two
|
|
370
|
+
* values with dimensions/units.
|
|
371
|
+
*
|
|
372
|
+
* so essentially this isn't necessary except for representation, which can
|
|
373
|
+
* be handled separately.
|
|
374
|
+
*
|
|
375
|
+
* ALTHOUGH, if you do that, you have to do the math before you do any
|
|
376
|
+
* unit conversion. because otherwise the ratios get screwed up.
|
|
377
|
+
*
|
|
378
|
+
*/
|
|
379
|
+
// aggressive_fractions: false,
|
|
380
|
+
|
|
381
|
+
/* *
|
|
382
|
+
* flag: support complex numbers. it might be useful to turn this off if it
|
|
383
|
+
* conflicts with dimensioned quantities (it doesn't, really, there's no i unit).
|
|
384
|
+
*/
|
|
385
|
+
// complex_numbers: true,
|
|
386
|
+
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export interface RenderOptions {
|
|
390
|
+
offset: { rows: number; columns: number };
|
|
391
|
+
missing: string;
|
|
392
|
+
convert_decimal: DecimalMarkType;
|
|
393
|
+
convert_argument_separator: ArgumentSeparatorType;
|
|
394
|
+
convert_imaginary_number: 'i'|'j';
|
|
395
|
+
long_structured_references: boolean;
|
|
396
|
+
table_name: string;
|
|
397
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// import { Parser, UnitLiteral, UnitBinary, UnitUnary, DecimalMarkType, UnitCall, ArgumentSeparatorType } from '../';
|
|
23
|
+
import { Parser } from './parser';
|
|
24
|
+
import { ArgumentSeparatorType,
|
|
25
|
+
UnitLiteral, UnitBinary, UnitUnary, DecimalMarkType, UnitCall } from './parser-types';
|
|
26
|
+
|
|
27
|
+
const parser = new Parser();
|
|
28
|
+
|
|
29
|
+
test('constructor', () => {
|
|
30
|
+
expect(typeof parser).toBe('object');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('basic parsing', () => {
|
|
34
|
+
|
|
35
|
+
test('3 + 4', () => {
|
|
36
|
+
const result = parser.Parse('3 + 4');
|
|
37
|
+
expect(result).toBeDefined();
|
|
38
|
+
expect(result.valid).toBeTruthy();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('/ 2', () => {
|
|
42
|
+
const result = parser.Parse('/ 2');
|
|
43
|
+
expect(result).toBeDefined();
|
|
44
|
+
expect(result.valid).toBeFalsy();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('parsing/rendering', () => {
|
|
50
|
+
|
|
51
|
+
const expression = '2.2 + (3 / foo(bar("1"), 8))';
|
|
52
|
+
test(expression, () => {
|
|
53
|
+
const result = parser.Parse(expression);
|
|
54
|
+
expect(result.valid).toBeTruthy();
|
|
55
|
+
expect(result.expression).toBeDefined();
|
|
56
|
+
if (result.expression){
|
|
57
|
+
const rendered = parser.Render(result.expression);
|
|
58
|
+
expect(rendered).toEqual(expression);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('converting separators', () => {
|
|
63
|
+
const result = parser.Parse(expression);
|
|
64
|
+
expect(result.valid).toBeTruthy();
|
|
65
|
+
expect(result.expression).toBeDefined();
|
|
66
|
+
if (result.expression){
|
|
67
|
+
const rendered = parser.Render(result.expression,
|
|
68
|
+
undefined, undefined, DecimalMarkType.Comma, ArgumentSeparatorType.Semicolon);
|
|
69
|
+
expect(rendered).toEqual('2,2 + (3 / foo(bar("1"); 8))');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('number parsing', () => {
|
|
76
|
+
|
|
77
|
+
const decimals = [1, 1.11, 2.2343, 123819238, -6, -7.77, -0.00012];
|
|
78
|
+
|
|
79
|
+
decimals.forEach((decimal) => {
|
|
80
|
+
const as_string = decimal.toString();
|
|
81
|
+
test(as_string, () => {
|
|
82
|
+
const result = parser.Parse(as_string);
|
|
83
|
+
expect(result).toBeDefined();
|
|
84
|
+
expect(result.expression).toBeDefined();
|
|
85
|
+
if (result.expression) {
|
|
86
|
+
expect(result.expression.type).toBe('literal');
|
|
87
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(decimal);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('2.2e-7', () => {
|
|
93
|
+
const result = parser.Parse('2.2e-7');
|
|
94
|
+
expect(result).toBeDefined();
|
|
95
|
+
expect(result.expression).toBeDefined();
|
|
96
|
+
if (result.expression) {
|
|
97
|
+
expect(result.expression.type).toBe('literal');
|
|
98
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(2.2e-7);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('-1.123e8', () => {
|
|
103
|
+
const result = parser.Parse('-1.123e8');
|
|
104
|
+
expect(result).toBeDefined();
|
|
105
|
+
expect(result.expression).toBeDefined();
|
|
106
|
+
if (result.expression) {
|
|
107
|
+
expect(result.expression.type).toBe('literal');
|
|
108
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(-1.123e8);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('33.33%', () => {
|
|
113
|
+
const result = parser.Parse('33.33%');
|
|
114
|
+
expect(result).toBeDefined();
|
|
115
|
+
expect(result.expression).toBeDefined();
|
|
116
|
+
if (result.expression) {
|
|
117
|
+
expect(result.expression.type).toBe('literal');
|
|
118
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(.3333);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('comma decimal parsing', () => {
|
|
125
|
+
|
|
126
|
+
test('1,23', () => {
|
|
127
|
+
parser.decimal_mark = DecimalMarkType.Comma;
|
|
128
|
+
const result = parser.Parse('1,23');
|
|
129
|
+
parser.decimal_mark = DecimalMarkType.Period;
|
|
130
|
+
expect(result).toBeDefined();
|
|
131
|
+
expect(result.expression).toBeDefined();
|
|
132
|
+
if (result.expression) {
|
|
133
|
+
expect(result.expression.type).toBe('literal');
|
|
134
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(1.23);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('-2231,909', () => {
|
|
139
|
+
parser.decimal_mark = DecimalMarkType.Comma;
|
|
140
|
+
const result = parser.Parse('-2231,909');
|
|
141
|
+
parser.decimal_mark = DecimalMarkType.Period;
|
|
142
|
+
expect(result).toBeDefined();
|
|
143
|
+
expect(result.expression).toBeDefined();
|
|
144
|
+
if (result.expression) {
|
|
145
|
+
expect(result.expression.type).toBe('literal');
|
|
146
|
+
expect((result.expression as UnitLiteral).value).toBeCloseTo(-2231.909);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('unary operators', () => {
|
|
152
|
+
|
|
153
|
+
test('2 + -3', () => {
|
|
154
|
+
const result = parser.Parse('2 + -3');
|
|
155
|
+
expect(result).toBeDefined();
|
|
156
|
+
expect(result.expression).toBeDefined();
|
|
157
|
+
if (result.expression) {
|
|
158
|
+
expect(result.expression.type).toBe('binary');
|
|
159
|
+
const right = (result.expression as UnitBinary).right;
|
|
160
|
+
expect(right).toBeDefined();
|
|
161
|
+
if (right){
|
|
162
|
+
expect(right.type).toBe('unary');
|
|
163
|
+
const unary = (right as UnitUnary);
|
|
164
|
+
expect(unary.operator).toBe('-');
|
|
165
|
+
expect(unary.operand.type).toBe('literal');
|
|
166
|
+
expect((unary.operand as UnitLiteral).value).toBeCloseTo(3);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('op() / +3', () => {
|
|
172
|
+
const result = parser.Parse('op() / +3');
|
|
173
|
+
expect(result).toBeDefined();
|
|
174
|
+
expect(result.expression).toBeDefined();
|
|
175
|
+
if (result.expression) {
|
|
176
|
+
expect(result.expression.type).toBe('binary');
|
|
177
|
+
const right = (result.expression as UnitBinary).right;
|
|
178
|
+
expect(right).toBeDefined();
|
|
179
|
+
if (right){
|
|
180
|
+
expect(right.type).toBe('unary');
|
|
181
|
+
const unary = (right as UnitUnary);
|
|
182
|
+
expect(unary.operator).toBe('+');
|
|
183
|
+
expect(unary.operand.type).toBe('literal');
|
|
184
|
+
expect((unary.operand as UnitLiteral).value).toBeCloseTo(3);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('binary operators', () => {
|
|
191
|
+
|
|
192
|
+
test('10 * 8', () => {
|
|
193
|
+
const result = parser.Parse('10 * 8');
|
|
194
|
+
expect(result).toBeDefined();
|
|
195
|
+
expect(result.expression).toBeDefined();
|
|
196
|
+
if (result.expression) {
|
|
197
|
+
expect(result.expression.type).toBe('binary');
|
|
198
|
+
const binary = result.expression as UnitBinary;
|
|
199
|
+
expect(binary.operator).toBe('*');
|
|
200
|
+
expect((binary.left as UnitLiteral).value).toBe(10);
|
|
201
|
+
expect((binary.right as UnitLiteral).value).toBe(8);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('grouping/ordering', () => {
|
|
208
|
+
test('(2 / (1 + (2 * 3))) * 4', () => {
|
|
209
|
+
const result = parser.Parse('(2 / (1 + (2 * 3))) * 4');
|
|
210
|
+
expect(result).toBeDefined();
|
|
211
|
+
expect(result.expression).toBeDefined();
|
|
212
|
+
if (result.expression){
|
|
213
|
+
expect(result.expression.type).toBe('binary');
|
|
214
|
+
const binary = result.expression as UnitBinary;
|
|
215
|
+
expect(binary.right.type).toBe('literal');
|
|
216
|
+
expect((binary.right as UnitLiteral).value).toBe(4);
|
|
217
|
+
expect(binary.left.type).toBe('group');
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('function calls', () => {
|
|
223
|
+
|
|
224
|
+
test('foo()', () => {
|
|
225
|
+
const result = parser.Parse('foo()');
|
|
226
|
+
expect(result).toBeDefined();
|
|
227
|
+
expect(result.expression).toBeDefined();
|
|
228
|
+
if (result.expression) {
|
|
229
|
+
expect(result.expression.type).toBe('call');
|
|
230
|
+
const call = result.expression as UnitCall;
|
|
231
|
+
expect(call.name).toBe('foo');
|
|
232
|
+
expect(call.args.length).toBe(0);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('oof(1, "bar", 3.3)', () => {
|
|
237
|
+
const result = parser.Parse('oof(1, "bar", 3.3)');
|
|
238
|
+
expect(result).toBeDefined();
|
|
239
|
+
expect(result.expression).toBeDefined();
|
|
240
|
+
if (result.expression) {
|
|
241
|
+
expect(result.expression.type).toBe('call');
|
|
242
|
+
const call = result.expression as UnitCall;
|
|
243
|
+
expect(call.name).toBe('oof');
|
|
244
|
+
expect(call.args.length).toBe(3);
|
|
245
|
+
if (call.args.length === 3){
|
|
246
|
+
expect(call.args[0].type).toBe('literal');
|
|
247
|
+
expect((call.args[0] as UnitLiteral).value).toBe(1);
|
|
248
|
+
expect(call.args[1].type).toBe('literal');
|
|
249
|
+
expect((call.args[1] as UnitLiteral).value).toBe('bar');
|
|
250
|
+
expect(call.args[2].type).toBe('literal');
|
|
251
|
+
expect((call.args[2] as UnitLiteral).value).toBe(3.3);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe('addresses', () => {
|
|
259
|
+
test('=A1 + $B2 - C$3 - $ZZ$40', () => {
|
|
260
|
+
const result = parser.Parse('=A1 + $B2 - C$3 - $ZZ$40');
|
|
261
|
+
expect(result).toBeDefined();
|
|
262
|
+
expect(result.expression).toBeDefined();
|
|
263
|
+
if (result.expression) {
|
|
264
|
+
expect(parser.Render(result.expression)).toBe('A1 + $B2 - C$3 - $ZZ$40');
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('semicolon-separated arguments', () => {
|
|
270
|
+
|
|
271
|
+
test('xfoo(1; "xbar"; 3,33)', () => {
|
|
272
|
+
parser.decimal_mark = DecimalMarkType.Comma;
|
|
273
|
+
parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
274
|
+
const result = parser.Parse('xfoo(1; "xbar"; 3,33)');
|
|
275
|
+
parser.decimal_mark = DecimalMarkType.Period;
|
|
276
|
+
parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
277
|
+
|
|
278
|
+
expect(result).toBeDefined();
|
|
279
|
+
expect(result.expression).toBeDefined();
|
|
280
|
+
if (result.expression) {
|
|
281
|
+
expect(result.expression.type).toBe('call');
|
|
282
|
+
const call = result.expression as UnitCall;
|
|
283
|
+
expect(call.name).toBe('xfoo');
|
|
284
|
+
expect(call.args.length).toBe(3);
|
|
285
|
+
if (call.args.length === 3){
|
|
286
|
+
expect(call.args[0].type).toBe('literal');
|
|
287
|
+
expect((call.args[0] as UnitLiteral).value).toBe(1);
|
|
288
|
+
expect(call.args[1].type).toBe('literal');
|
|
289
|
+
expect((call.args[1] as UnitLiteral).value).toBe('xbar');
|
|
290
|
+
expect(call.args[2].type).toBe('literal');
|
|
291
|
+
expect((call.args[2] as UnitLiteral).value).toBeCloseTo(3.33);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
|