Skip to content

Commit f50211a

Browse files
committed
Custom model editor: Allow ==/!= true/false for areas
1 parent 9022f78 commit f50211a

File tree

3 files changed

+34
-17
lines changed

3 files changed

+34
-17
lines changed

web-bundle/src/main/js/custom-model-editor/src/complete.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ function complete(expression, pos, categories, areas) {
2929
const suggestions = parseResult.completions.filter(c => {
3030
// we need to remove our dummy character for the filtering
3131
const partialToken = tokenPos.token.substring(0, tokenPos.token.length - 1);
32-
// todo: be careful with boolean encoded values later! c might not be a string here...
33-
// todo: not supported in IE11 for example, need to provide alternative!
3432
return startsWith(c, partialToken);
3533
});
3634
return {

web-bundle/src/main/js/custom-model-editor/src/parse.js

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ function parse(expression, categories, areas) {
4040
* Parses a given list of tokens according to the following grammar.
4141
*
4242
* expression -> comparison (logicOperator comparison)*
43-
* comparison -> enumCategory comparator value | numericCategory numericComparator number | boolean | booleanCategory | booleanCategory comparator boolean | 'in_area_' area | value'(' expression ')'
43+
* comparison -> enumCategory comparator value | numericCategory numericComparator number | boolean | booleanCategory |
44+
* booleanCategory comparator boolean | 'in_area_' area | 'in_area_' area comparator boolean | value'(' expression ')'
4445
* logicOperator -> '&&' | '||'
4546
* comparator -> '==' | '!='
4647
* numericComparator -> '>' | '<' | '>=' | '<=' | '==' | '!='
@@ -50,7 +51,7 @@ function parse(expression, categories, areas) {
5051
*
5152
* Note that we do not care about operator precedence between && and || because our aim is not
5253
* actually evaluating the expression, but rather checking the validity.
53-
*
54+
*
5455
* The categories parameter is an object that maps category names to objects that contain the category type
5556
* `enum`, `boolean` or `numeric` and a list of possible (string) values (for `enum` only).
5657
*
@@ -75,8 +76,7 @@ function parseTokens(tokens, categories, areas) {
7576
if (v.type === 'enum') {
7677
if (v.values.length < 1)
7778
return error(`no values given for enum category ${k}`);
78-
}
79-
else if (v.type !== 'boolean' && v.type !== 'numeric')
79+
} else if (v.type !== 'boolean' && v.type !== 'numeric')
8080
return error(`unknown category type: ${v.type} for category ${k}`)
8181
}
8282

@@ -156,10 +156,10 @@ function parseBooleanLiteral() {
156156

157157
function parseBooleanComparison() {
158158
// rule: comparison -> booleanCategory
159-
if (_idx+1 === _tokens.length) {
159+
if (_idx + 1 === _tokens.length) {
160160
_idx++;
161161
return valid();
162-
} else if (comparisonOperators.indexOf(_tokens[_idx+1]) < 0) {
162+
} else if (comparisonOperators.indexOf(_tokens[_idx + 1]) < 0) {
163163
_idx++;
164164
return valid();
165165
}
@@ -168,7 +168,7 @@ function parseBooleanComparison() {
168168
return parseTripleComparison(
169169
comparisonOperators,
170170
(category, operator, value) => isBoolean(value),
171-
(category, operator, value) =>['true', 'false']
171+
(category, operator, value) => ['true', 'false']
172172
);
173173
}
174174

@@ -180,10 +180,23 @@ function parseArea() {
180180
}
181181
const area = token.substring(`in_area_`.length)
182182
if (_areas.indexOf(area) < 0) {
183-
return error(`unknown area: '${area}'`, [_idx, _idx+1], _areas.map(a => 'in_area_' + a));
183+
return error(`unknown area: '${area}'`, [_idx, _idx + 1], _areas.map(a => 'in_area_' + a));
184184
}
185-
_idx++;
186-
return valid();
185+
186+
// rule: comparison -> 'in_area_' area
187+
if (_idx + 1 === _tokens.length) {
188+
_idx++;
189+
return valid();
190+
} else if (comparisonOperators.indexOf(_tokens[_idx + 1]) < 0) {
191+
_idx++;
192+
return valid();
193+
}
194+
// rule: comparison -> 'in_area_' area comparator boolean
195+
return parseTripleComparison(
196+
comparisonOperators,
197+
(category, operator, value) => isBoolean(value),
198+
(category, operator, value) => ['true', 'false']
199+
);
187200
}
188201

189202
function parseInvalidAreaOperator() {
@@ -192,7 +205,7 @@ function parseInvalidAreaOperator() {
192205
console.error(`${token} is a valid area operator and should have been detected earlier`);
193206
return;
194207
}
195-
return error(`area names must be prefixed with 'in_area_'`, [_idx, _idx+1], _areas.map(a => 'in_area_' + a));
208+
return error(`area names must be prefixed with 'in_area_'`, [_idx, _idx + 1], _areas.map(a => 'in_area_' + a));
196209
}
197210

198211
function parseTripleComparison(allowedComparators, isValid, getAllowedValues) {
@@ -265,7 +278,7 @@ function isArea() {
265278
function isInvalidAreaOperator() {
266279
const token = _tokens[_idx];
267280
// typing something like in_area might be a common error so we provide some support for it
268-
return typeof token === 'string' && (token.substr(0, 3) === 'in_' || _areas.indexOf(token) >= 0);
281+
return typeof token === 'string' && (token.substr(0, 3) === 'in_' || _areas.indexOf(token) >= 0);
269282
}
270283

271284
function isCategory() {
@@ -293,11 +306,11 @@ function isBoolean(value) {
293306
}
294307

295308
function error(error, range, completions) {
296-
return { error, range, completions };
309+
return {error, range, completions};
297310
}
298311

299312
function valid() {
300-
return { error: null, range: [], completions: [] };
313+
return {error: null, range: [], completions: []};
301314
}
302315

303-
export { parse, parseTokens };
316+
export {parse, parseTokens};

web-bundle/src/main/js/custom-model-editor/src/parse.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ describe("parse", () => {
3434
test_parseTokens_valid(['(', '(', 'a', '!=', 'a1', ')', ')']);
3535
test_parseTokens_valid(['in_area_area1']);
3636
test_parseTokens_valid(['(', 'in_area_area1', ')']);
37+
test_parseTokens_valid(['in_area_area2', '==', 'true']);
38+
test_parseTokens_valid(['(', 'in_area_area1', '!=', 'false', ')']);
3739
test_parseTokens_valid(['true']);
3840
test_parseTokens_valid(['(', 'false', ')']);
3941
test_parseTokens_valid(['bool1']);
@@ -71,6 +73,7 @@ describe("parse", () => {
7173
test_parseTokens(['in_area_area404'], `unknown area: 'area404'`, [0, 1], ['in_area_area1', 'in_area_area2', 'in_area_area3']);
7274
test_parseTokens(['in_area1'], `area names must be prefixed with 'in_area_'`, [0, 1], ['in_area_area1', 'in_area_area2', 'in_area_area3']);
7375
test_parseTokens(['area2'], `area names must be prefixed with 'in_area_'`, [0, 1], ['in_area_area1', 'in_area_area2', 'in_area_area3']);
76+
test_parseTokens(['in_area_area1', '<=', 'true'], `unexpected token '<='`, [1, 2], ['||', '&&']);
7477
});
7578

7679
test("parse single comparison, invalid, numeric and boolean", () => {
@@ -87,6 +90,7 @@ describe("parse", () => {
8790
test_parseTokens_valid(['a', '==', 'a1', '||', '(', 'b', '==', 'b1', ')', '&&', 'a', '!=', 'a2']);
8891
test_parseTokens_valid(['in_area_area3', '&&', 'a', '==', 'a1']);
8992
test_parseTokens_valid(['b', '!=', 'b1', '||', 'in_area_area3']);
93+
test_parseTokens_valid(['b', '!=', 'b1', '||', 'in_area_area2', '!=', 'true']);
9094
test_parseTokens_valid(['bool1', '==', 'false', '&&', 'bool2', '||', 'bool1']);
9195
});
9296

@@ -130,6 +134,7 @@ describe("parse", () => {
130134
test_parse_valid('num1>0.3 && bool1');
131135
test_parse_valid('a != a1 && (bool1 || (bool2 && in_area_area2))');
132136
test_parse_valid('a != a1 && (bool1 != false || (in_area_area2 && bool2))');
137+
test_parse_valid('a != a1 && (bool1 != false || ((in_area_area1 == false) && bool2))');
133138
});
134139

135140
test("parse, invalid", () => {
@@ -152,6 +157,7 @@ describe("parse", () => {
152157
test_parse('a == a1 || area_1', `unexpected token 'area_1'`, [11, 17], allowedLefts);
153158
test_parse(' == bool1', `unexpected token '=='`, [1, 3], allowedLefts);
154159
test_parse('bool1 != a2', `invalid bool1: 'a2'`, [9, 11], ['true', 'false']);
160+
test_parse('in_area_area1 == tru || a == a1', `invalid in_area_area1: 'tru'`, [17, 20], ['true', 'false']);
155161
});
156162

157163
function test_parse_valid(expression) {

0 commit comments

Comments
 (0)