geonetwork-ui 2.10.0-dev.b6696be21 → 2.10.0-dev.bfea85a63
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/fesm2022/geonetwork-ui.mjs +1016 -6093
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +144 -149
- package/index.d.ts.map +1 -1
- package/package.json +23 -33
- package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +5 -4
- package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
- package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
- package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -4
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -5
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +2 -2
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -0
- package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +51 -0
- package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
- package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
- package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
- package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
- package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +4 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +7 -7
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +20 -16
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +9 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +76 -19
- package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
- package/src/libs/feature/editor/src/lib/fields.config.ts +251 -290
- package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +2 -3
- package/src/libs/feature/editor/src/lib/services/editor.service.ts +8 -12
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
- package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
- package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
- package/src/libs/ui/dataviz/src/index.ts +0 -1
- package/src/libs/ui/elements/src/index.ts +1 -0
- package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +48 -56
- package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
- package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
- package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
- package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +4 -7
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
- package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
- package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
- package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
- package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
- package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
- package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
- package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
- package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
- package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
- package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
- package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
- package/src/libs/util/app-config/src/lib/app-config.ts +21 -9
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
- package/src/libs/util/i18n/src/index.ts +0 -1
- package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
- package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
- package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
- package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
- package/src/libs/util/shared/src/lib/links/link-utils.ts +22 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
- package/style.css +0 -1
- package/tailwind.base.css +9 -0
- package/translations/de.json +53 -29
- package/translations/en.json +58 -34
- package/translations/es.json +27 -3
- package/translations/fr.json +55 -31
- package/translations/it.json +53 -29
- package/translations/nl.json +26 -2
- package/translations/pt.json +27 -3
- package/translations/sk.json +27 -3
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
|
@@ -1,16 +1,388 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EditorFieldValue } from './models/'
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface EvaluationContext {
|
|
4
|
+
globals: Record<string, object>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type ExpressionEvaluator = (
|
|
8
|
+
context: EvaluationContext
|
|
9
|
+
) => EditorFieldValue
|
|
10
|
+
|
|
11
|
+
const TOKEN_FUNCTION_CALL_START = '('
|
|
12
|
+
const TOKEN_FUNCTION_CALL_END = ')'
|
|
13
|
+
const TOKEN_PROPERTY_ACCESS = '.'
|
|
14
|
+
const TOKEN_OPERATOR_TERNARY_FIRST = '?'
|
|
15
|
+
const TOKEN_OPERATOR_TERNARY_SECOND = ':'
|
|
16
|
+
const TOKEN_OPERATOR_LOGICAL = ['==', '!=']
|
|
17
|
+
const TOKEN_STRING_LITERAL = ["'", '"']
|
|
18
|
+
const TOKEN_GROUP_START = '('
|
|
19
|
+
const TOKEN_GROUP_END = ')'
|
|
20
|
+
|
|
21
|
+
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence
|
|
22
|
+
const OPERATORS_BY_PRECEDENCE: string[][] = [
|
|
23
|
+
[TOKEN_OPERATOR_TERNARY_FIRST, TOKEN_OPERATOR_TERNARY_SECOND],
|
|
24
|
+
TOKEN_OPERATOR_LOGICAL,
|
|
25
|
+
]
|
|
26
|
+
const ALL_OPERATORS = OPERATORS_BY_PRECEDENCE.flat()
|
|
27
|
+
|
|
28
|
+
export interface CompilationContext {
|
|
29
|
+
errors: string[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isExpression(input: unknown): boolean {
|
|
33
|
+
return typeof input === 'string' && /^\${.*}$/.test(input)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readNext(input: string, start: number, length = 1): string {
|
|
37
|
+
return input.substring(start, start + length)
|
|
38
|
+
}
|
|
4
39
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Will continue looking until an operator is found at the same depth level
|
|
42
|
+
*/
|
|
43
|
+
function readUntilNextOperator(
|
|
44
|
+
input: string,
|
|
45
|
+
start: number,
|
|
46
|
+
currentOperator: string
|
|
47
|
+
): string {
|
|
48
|
+
let pos = start
|
|
49
|
+
let result = ''
|
|
50
|
+
let depth = 0
|
|
51
|
+
while (pos < input.length) {
|
|
52
|
+
const isOperator = ALL_OPERATORS.some(
|
|
53
|
+
(token) => readNext(input, pos, token.length) === token
|
|
54
|
+
)
|
|
55
|
+
if (isOperator && depth === 0) {
|
|
56
|
+
const operatorPrecedence = OPERATORS_BY_PRECEDENCE.findIndex((tokens) =>
|
|
57
|
+
tokens.some((token) => readNext(input, pos, token.length) === token)
|
|
58
|
+
)
|
|
59
|
+
const currentPrecedence =
|
|
60
|
+
OPERATORS_BY_PRECEDENCE.findIndex((tokens) =>
|
|
61
|
+
tokens.some((token) => currentOperator === token)
|
|
62
|
+
) ?? -1
|
|
63
|
+
// the new operator has a lower precedence than the current one: we stop here
|
|
64
|
+
if (operatorPrecedence < currentPrecedence) {
|
|
65
|
+
return result
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (input[pos] === TOKEN_GROUP_START) {
|
|
69
|
+
depth++
|
|
70
|
+
} else if (input[pos] === TOKEN_GROUP_END) {
|
|
71
|
+
depth--
|
|
72
|
+
}
|
|
73
|
+
if (depth < 0) {
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
result += input[pos]
|
|
77
|
+
pos++
|
|
8
78
|
}
|
|
9
|
-
|
|
10
|
-
|
|
79
|
+
return result
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Will look for a specific character or string situated at the same depth level!
|
|
84
|
+
* Returns null if nothing found
|
|
85
|
+
*/
|
|
86
|
+
function readUntil(input: string, start: number, char: string): string | null {
|
|
87
|
+
let pos = start
|
|
88
|
+
let result = ''
|
|
89
|
+
let depth = 0
|
|
90
|
+
while (pos < input.length) {
|
|
91
|
+
if (depth === 0 && readNext(input, pos, char.length) === char) {
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
if (input[pos] === TOKEN_GROUP_START) {
|
|
95
|
+
depth++
|
|
96
|
+
} else if (input[pos] === TOKEN_GROUP_END) {
|
|
97
|
+
depth--
|
|
98
|
+
}
|
|
99
|
+
if (depth < 0) {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
result += input[pos]
|
|
103
|
+
pos++
|
|
104
|
+
}
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getFunctionEvaluator(
|
|
109
|
+
functionName: string,
|
|
110
|
+
context: CompilationContext
|
|
111
|
+
): ExpressionEvaluator {
|
|
112
|
+
switch (functionName) {
|
|
11
113
|
case 'dateNow':
|
|
12
114
|
return () => new Date()
|
|
13
115
|
default:
|
|
14
|
-
|
|
116
|
+
context.errors.push(`Unknown function: ${functionName}`)
|
|
117
|
+
return () => undefined
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* This evaluates an expression using a very light subset of Javascript.
|
|
123
|
+
*
|
|
124
|
+
* Things allowed in expressions are:
|
|
125
|
+
* - comparing values with == and !=
|
|
126
|
+
* - ternary comparisons with a ? b : c
|
|
127
|
+
* - grouping with (...)
|
|
128
|
+
* - functions: for now only dateNow() is supported and returns a timestamp
|
|
129
|
+
* - access to globals such as record.type
|
|
130
|
+
*
|
|
131
|
+
* Expressions are turned into an evaluator that can be called with a context containing the globals.
|
|
132
|
+
* Expressions can return any primitive Javascript type
|
|
133
|
+
* Valid expressions should always be enclosed in ${...}
|
|
134
|
+
*/
|
|
135
|
+
export function evaluate(expression: string): {
|
|
136
|
+
evaluator: ExpressionEvaluator
|
|
137
|
+
errors: string[]
|
|
138
|
+
} {
|
|
139
|
+
if (!/^\${.*}$/.test(expression)) {
|
|
140
|
+
throw new Error('Malformed expression: missing ${} enclosure')
|
|
141
|
+
}
|
|
142
|
+
const context = {
|
|
143
|
+
errors: [],
|
|
144
|
+
}
|
|
145
|
+
const trimmedExpression = expression.slice(2, -1)
|
|
146
|
+
return evaluateInternal(trimmedExpression, context)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function evaluateInternal(
|
|
150
|
+
expression: string,
|
|
151
|
+
currentContext: CompilationContext
|
|
152
|
+
): { evaluator: ExpressionEvaluator; errors: string[] } {
|
|
153
|
+
let pos = 0
|
|
154
|
+
let currentSymbol = null
|
|
155
|
+
let currentEvaluator: ExpressionEvaluator = () => undefined
|
|
156
|
+
|
|
157
|
+
while (pos < expression.length) {
|
|
158
|
+
const isWhitespace = /\s/.test(readNext(expression, pos))
|
|
159
|
+
if (isWhitespace) {
|
|
160
|
+
pos++
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// SYMBOL (NAME)
|
|
165
|
+
const isSymbol = /[a-zA-Z]/.test(readNext(expression, pos))
|
|
166
|
+
if (isSymbol) {
|
|
167
|
+
currentSymbol = ''
|
|
168
|
+
while (pos < expression.length) {
|
|
169
|
+
if (!/[a-zA-Z]/.test(readNext(expression, pos))) {
|
|
170
|
+
break
|
|
171
|
+
}
|
|
172
|
+
currentSymbol += readNext(expression, pos)
|
|
173
|
+
pos++
|
|
174
|
+
}
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// FUNCTION CALL
|
|
179
|
+
const isFunctionCall =
|
|
180
|
+
currentSymbol && readNext(expression, pos) === TOKEN_FUNCTION_CALL_START
|
|
181
|
+
if (isFunctionCall) {
|
|
182
|
+
pos++
|
|
183
|
+
// no support for arguments for now (not needed)
|
|
184
|
+
if (readNext(expression, pos) !== TOKEN_FUNCTION_CALL_END) {
|
|
185
|
+
currentContext.errors.push(
|
|
186
|
+
`Expected token '${TOKEN_FUNCTION_CALL_END}' at position ${pos}, got '${readNext(expression, pos)}' instead`
|
|
187
|
+
)
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
pos++
|
|
191
|
+
currentEvaluator = getFunctionEvaluator(currentSymbol, currentContext)
|
|
192
|
+
currentSymbol = null
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// PROPERTY ACCESS (single level)
|
|
197
|
+
const isPropertyAccess =
|
|
198
|
+
currentSymbol && readNext(expression, pos) === TOKEN_PROPERTY_ACCESS
|
|
199
|
+
if (isPropertyAccess) {
|
|
200
|
+
pos++
|
|
201
|
+
if (!/[a-zA-Z]/.test(readNext(expression, pos))) {
|
|
202
|
+
currentContext.errors.push(
|
|
203
|
+
`Expected a property name after '${TOKEN_PROPERTY_ACCESS}' at position ${pos}, got '${readNext(expression, pos)}' instead`
|
|
204
|
+
)
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
let propertyName = ''
|
|
208
|
+
while (pos < expression.length) {
|
|
209
|
+
if (!/[a-zA-Z]/.test(readNext(expression, pos))) {
|
|
210
|
+
break
|
|
211
|
+
}
|
|
212
|
+
propertyName += readNext(expression, pos)
|
|
213
|
+
pos++
|
|
214
|
+
}
|
|
215
|
+
const variableName = currentSymbol
|
|
216
|
+
currentEvaluator = (context) => {
|
|
217
|
+
if (
|
|
218
|
+
!Object.prototype.hasOwnProperty.call(context.globals, variableName)
|
|
219
|
+
) {
|
|
220
|
+
return undefined
|
|
221
|
+
}
|
|
222
|
+
if (
|
|
223
|
+
!Object.prototype.hasOwnProperty.call(
|
|
224
|
+
context.globals[variableName],
|
|
225
|
+
propertyName
|
|
226
|
+
)
|
|
227
|
+
) {
|
|
228
|
+
return undefined
|
|
229
|
+
}
|
|
230
|
+
return context.globals[variableName][propertyName]
|
|
231
|
+
}
|
|
232
|
+
currentSymbol = null
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// we have a symbol but did not do either function call or property access => error
|
|
237
|
+
if (currentSymbol) {
|
|
238
|
+
currentContext.errors.push(
|
|
239
|
+
`Expected a function call or property access after symbol '${currentSymbol}' at position ${pos}, got '${readNext(expression, pos)}' instead`
|
|
240
|
+
)
|
|
241
|
+
break
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// GROUP OPERATOR
|
|
245
|
+
const isGroupStart = readNext(expression, pos) === TOKEN_GROUP_START
|
|
246
|
+
if (isGroupStart) {
|
|
247
|
+
pos++
|
|
248
|
+
const groupExpression = readUntil(expression, pos, TOKEN_GROUP_END)
|
|
249
|
+
if (groupExpression === null) {
|
|
250
|
+
currentContext.errors.push(
|
|
251
|
+
`Reached end of expression before finding expected token '${TOKEN_GROUP_END}'`
|
|
252
|
+
)
|
|
253
|
+
break
|
|
254
|
+
}
|
|
255
|
+
pos += groupExpression.length + 1
|
|
256
|
+
currentEvaluator = evaluateInternal(
|
|
257
|
+
groupExpression,
|
|
258
|
+
currentContext
|
|
259
|
+
).evaluator
|
|
260
|
+
continue
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// LOGICAL OPERATORS
|
|
264
|
+
const isLogicalOperator = TOKEN_OPERATOR_LOGICAL.some(
|
|
265
|
+
(token) => readNext(expression, pos, token.length) === token
|
|
266
|
+
)
|
|
267
|
+
if (isLogicalOperator) {
|
|
268
|
+
const matchingOperator = TOKEN_OPERATOR_LOGICAL.find(
|
|
269
|
+
(token) => readNext(expression, pos, token.length) === token
|
|
270
|
+
)
|
|
271
|
+
const isEqual = matchingOperator.startsWith('=')
|
|
272
|
+
pos += matchingOperator.length
|
|
273
|
+
const comparedEvaluator = currentEvaluator
|
|
274
|
+
const comparedToExpression = readUntilNextOperator(
|
|
275
|
+
expression,
|
|
276
|
+
pos,
|
|
277
|
+
matchingOperator
|
|
278
|
+
)
|
|
279
|
+
pos += comparedToExpression.length
|
|
280
|
+
const comparedToEvaluator = evaluateInternal(
|
|
281
|
+
comparedToExpression,
|
|
282
|
+
currentContext
|
|
283
|
+
).evaluator
|
|
284
|
+
|
|
285
|
+
currentEvaluator = (context) => {
|
|
286
|
+
return (
|
|
287
|
+
(comparedEvaluator(context) == comparedToEvaluator(context)) ===
|
|
288
|
+
isEqual
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// TERNARY OPERATOR
|
|
295
|
+
const isTernary = readNext(expression, pos) === TOKEN_OPERATOR_TERNARY_FIRST
|
|
296
|
+
if (isTernary) {
|
|
297
|
+
if (!currentEvaluator) {
|
|
298
|
+
currentContext.errors.push(
|
|
299
|
+
`Expected an expression before '${TOKEN_OPERATOR_TERNARY_FIRST}' at position ${pos} but could not find any`
|
|
300
|
+
)
|
|
301
|
+
break
|
|
302
|
+
}
|
|
303
|
+
pos++
|
|
304
|
+
const ifTrueExpression = readUntil(
|
|
305
|
+
expression,
|
|
306
|
+
pos,
|
|
307
|
+
TOKEN_OPERATOR_TERNARY_SECOND
|
|
308
|
+
)
|
|
309
|
+
if (ifTrueExpression === null) {
|
|
310
|
+
currentContext.errors.push(
|
|
311
|
+
`Reached end of expression before finding expected token '${TOKEN_OPERATOR_TERNARY_SECOND}'`
|
|
312
|
+
)
|
|
313
|
+
break
|
|
314
|
+
}
|
|
315
|
+
pos += ifTrueExpression.length + 1
|
|
316
|
+
|
|
317
|
+
const ifFalseExpression = readUntilNextOperator(
|
|
318
|
+
expression,
|
|
319
|
+
pos,
|
|
320
|
+
TOKEN_OPERATOR_TERNARY_FIRST
|
|
321
|
+
)
|
|
322
|
+
pos += ifFalseExpression.length
|
|
323
|
+
|
|
324
|
+
const conditionEvaluator = currentEvaluator
|
|
325
|
+
const ifTrueEvaluator = evaluateInternal(
|
|
326
|
+
ifTrueExpression,
|
|
327
|
+
currentContext
|
|
328
|
+
).evaluator
|
|
329
|
+
const ifFalseEvaluator = evaluateInternal(
|
|
330
|
+
ifFalseExpression,
|
|
331
|
+
currentContext
|
|
332
|
+
).evaluator
|
|
333
|
+
currentEvaluator = (context) => {
|
|
334
|
+
if (conditionEvaluator(context)) {
|
|
335
|
+
return ifTrueEvaluator(context)
|
|
336
|
+
}
|
|
337
|
+
return ifFalseEvaluator(context)
|
|
338
|
+
}
|
|
339
|
+
continue
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// STRING LITERAL
|
|
343
|
+
const isStringLiteral = TOKEN_STRING_LITERAL.some(
|
|
344
|
+
(token) => readNext(expression, pos) === token
|
|
345
|
+
)
|
|
346
|
+
if (isStringLiteral) {
|
|
347
|
+
const openingToken = readNext(expression, pos)
|
|
348
|
+
pos++
|
|
349
|
+
const literal = readUntil(expression, pos, openingToken)
|
|
350
|
+
if (literal === null) {
|
|
351
|
+
currentContext.errors.push(
|
|
352
|
+
`Reached end of expression before finding corresponding closing token ${openingToken} for string literal`
|
|
353
|
+
)
|
|
354
|
+
break
|
|
355
|
+
}
|
|
356
|
+
pos += literal.length + 1
|
|
357
|
+
currentEvaluator = () => literal
|
|
358
|
+
continue
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// NUMBER LITERAL
|
|
362
|
+
const isNumberLiteral = /[0-9]/.test(readNext(expression, pos))
|
|
363
|
+
if (isNumberLiteral) {
|
|
364
|
+
let literal = ''
|
|
365
|
+
while (pos < expression.length) {
|
|
366
|
+
if (!/[0-9]/.test(readNext(expression, pos))) {
|
|
367
|
+
break
|
|
368
|
+
}
|
|
369
|
+
literal += readNext(expression, pos)
|
|
370
|
+
pos++
|
|
371
|
+
}
|
|
372
|
+
const numberLiteral = parseInt(literal, 10)
|
|
373
|
+
currentEvaluator = () => numberLiteral
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// could not figure out what that was: leave
|
|
378
|
+
currentContext.errors.push(
|
|
379
|
+
`Unexpected character '${readNext(expression, pos)}' at position ${pos}`
|
|
380
|
+
)
|
|
381
|
+
break
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
evaluator: currentEvaluator,
|
|
386
|
+
errors: currentContext.errors,
|
|
15
387
|
}
|
|
16
388
|
}
|